共绩算力

OpenAI 如何做低延迟规模化语音 AI(WebRTC 导读)

2026年5月13日
"OpenAI 如何用 WebRTC 为全球超大规模实时语音兜底:中继(Relay)+ 收发器(Transceiver)拆分、基于 ICE ufrag 的首包路由,以及与 Kubernetes / 全球入站协同的工程取舍。"
Shiyuh
Shiyuh
技术传道者/AI 应用落地

只有当对话节奏逼近 人类说话的自然节拍 时,语音 AI 才会「好听、好用」。网络一旦在中间插一脚,人类会立刻听成:尴尬停顿、抢话不完整、打断(barge-in)变慢。这对 ChatGPT 语音模式、使用 Realtime API 的开发者、交互式 Agent 流水线,以及「边听边推理」的模型形式都成立。

在 OpenAI 自称的体量下,问题被压成三条 硬指标:

  1. 全球可达:服务 9 亿 + 周活 级别的用户分布;
  2. 连接建立要快:会话一开始就能说话,而不是等一串握手转圈;
  3. 媒体路径 RTT 低且稳:抖动、丢包可控,话轮切换(turn-taking)才显得干脆。

负责实时交互的团队近期 重做了 WebRTC 栈,用来缓解三件事在规模上来以后开始「互相打架」的约束:

下文整理原文脉络:在客户端仍保持标准 WebRTC 语义 的前提下,分机房内部如何改包的路由形态——也就是他们所称的 split relay + transceiver(拆分中继 + 收发器)架构。


为什么实时语音产品会押注 WebRTC?

WebRTC 是面向浏览器、移动 App 与服务器之间 低延迟音视频与数据 的开放标准。很多人把它和 P2P 通话绑在一起,但它同样是 客户端—服务器实时系统 的务实底座,因为把最难的几块「协议事实标准」封好了:

对 AI 产品来说,关键性质是:音频以连续流到达。语音 Agent 可以在用户 尚未说完 时就开始 转写、推理、调工具或合成回复——这是 真·对话感 与 对讲机式 push-to-talk 的分水岭。

OpenAI 也点名了生态里的基础工作:例如 Justin Uberti(WebRTC 早期架构师之一)与 Pion 作者 Sean DuBois 等;两人现均在 OpenAI 任职——团队得以在 久经沙场的媒体栈 上继续往前推,而不是从零发明低层传输、加密与拥塞控制。


媒体架构选型:SFU vs Transceiver(收发器)

在哪里终结 WebRTC(由谁持有连接、解密媒体、承担会话状态)决定了延迟、路由、故障域与扩展方式。

方案一:SFU(选择性转发单元)

SFU 从每个参与者收一路 WebRTC,再 选择性转发 给其他参与者;每个参与者各有一条独立 WebRTC 连接,AI 也可以作为 又一个参与者 入会。适合 天然多人 场景:群组通话、教室、协作会议;编解码、RTCP、数据通道、录制、按流策略都能 集中在一处。

即便在「人对模型」产品里,SFU 也常常是默认起点:一套系统同时覆盖 信令、媒体路由、观测与未来扩展(例如人工接管、再加参与者)。

方案二:Transceiver——边缘终结 WebRTC,后端走「更简单」的协议

OpenAI 的主体流量形态是 1:1:一个人对一只模型,或一个应用对一只实时 Agent,每一轮都对延迟极端敏感。

他们选择 transceiver 模型:WebRTC 边缘服务 在边缘 终结客户端连接,再把媒体与事件 转换成内部更简单的协议,对接推理、转写、语音合成、工具与编排。

在此设计里,只有 transceiver 拥有完整 WebRTC 会话状态:ICE 连通性检查、DTLS 握手、SRTP 密钥、会话生命周期。「终结」意味着 握手与加解密媒体 都在 transceiver 完成;后端服务 不必再扮演 WebRTC 对端,可以像普通微服务一样扩缩。


核心矛盾:WebRTC 遇上 Kubernetes

第一版实现是 单个 Go 服务(基于 Pion)同时扛 信令 + 媒体终结,支撑 ChatGPT 语音、Realtime API 的 WebRTC 入口以及若干研究项目。

从职责上 transceiver 做两件事:

  1. 信令:SDP 协商、编解码选择、ICE 凭据、会话搭建;
  2. 媒体:对下终结用户 WebRTC;对上维护连到推理/编排后端的连接。

团队希望它像其它服务一样跑在 Kubernetes 上——随负载扩缩、在节点间迁移。但「每会话一个 UDP 端口」的传统 WebRTC 部署方式,在这里会集体踩雷。

端口耗尽与运维面

高并发下要对外暴露、管理 巨大范围的公网 UDP 端口。

这也是许多 WebRTC 系统最终走向「单台服务器一个 UDP 端口 + 应用层多路复用」的原因。

状态粘性(stickiness)

单端口/多路复用缓解端口问题,但带来第二个问题:会话归属。

ICE 与 DTLS 是有状态协议。创建会话的进程必须持续收到该会话的数据包,才能完成连通性检查、DTLS、SRTP 解密,以及后续 ICE restart 等变更。若同一会话的包落到 另一个进程,建连失败或媒体直接断。

目标因此非常具体:对外只暴露 少量、固定 的 UDP 入口,同时 把每个包 Deterministic 地送到拥有该 WebRTC 会话的 transceiver。


架构路线对比

路线

优点

缺点

每会话独立公网 IP:端口(直连 UDP)

媒体路径直接;数据面无转发层

每会话一个公网 UDP 端口;大端口区间难暴露与加固;与 K8s / 云 LB 契合度差

每服务器唯一 IP:端口

公网 UDP 面远小于「每会话一端口」;单进程可共享 socket 多路复用

单机内好做;跨 负载均衡集群 时首包仍可能落到「错误实例」,仍需 确定性 steering

TURN 中继(协议终结型)

客户端只需打到一个中继地址;策略可集中在边缘

TURN allocation 增加建连 RTT;跨 TURN 迁移/恢复仍难

无状态转发层 + 有状态终结点(OpenAI:Relay + Transceiver)

公网 UDP 面小;transceiver 仍完整持有 WebRTC 状态

媒体多一跳转发;relay 与 transceiver 间的协作需自研


Relay + Transceiver:把「路由」与「终结」拆开

上线的架构 分离了包路由与协议终结:

Relay 是轻量 UDP 转发层,对外 UDP 面很小;transceiver 是躲在后面的 有状态 WebRTC 终结点。

Relay 只做转发,不终结 WebRTC

Relay 不解密媒体、不跑 ICE 状态机、不参与编解码协商;只读取 够用即可 的元数据来选择目的地,然后把包交给 持有会话的 transceiver。对客户端而言,WebRTC 会话语义不变。

用 ICE 凭据(ufrag)做首包路由

难点是:中继必须在「路径上尚无完整会话上下文」之前,就能路由客户端的第一帧有效包——而不是先卡去查中心化服务。

WebRTC 里现成的挂钩是 ICE username fragment(ufrag):建连时在信令里交换,并会出现在 STUN 连通性检测报文中。OpenAI 生成服务端 ufrag,让其中编入 刚刚好 的提示信息,中继据此推断 目标集群与所属 transceiver。

流程概要:

  1. 信令阶段,transceiver 分配会话状态,并在 SDP Answer 里返回 共享的 Relay VIP + UDP 端口(VIP 是中继集群前的虚拟地址,形如 203.0.113.10:3478,背后是多实例)。
  2. 客户端第一条走媒体路径的数据往往是 STUN Binding Request,用于 ICE 校验可达性。
  3. Relay 只解析这层 STUN,读出 服务器侧 ufrag,解码路由提示,把包转给 owner transceiver。
  4. 各 transceiver 监听 共享 UDP socket(一个 OS 级 IP:Port 端点,不是每会话一个 socket)。Relay 为「客户端源 IP:端口 → transceiver 目的地」建 极简转发会话;后续 DTLS / RTP / RTCP 在同一 flow 上继续走,无需每包重解 ufrag。

Relay 的状态刻意保持 短命、内存内:转发所需的最小 map、监控计数、过期清理定时器。若 Relay 重启丢状态,下一帧 STUN 仍可凭 ufrag 重建路由。为更稳,文中还提到用 Redis 缓存 〈客户端 IP+ 端口,transceiver IP+ 端口〉 映射,在下一帧 STUN 到来前 就有机会恢复路径。


Global Relay 与地理亲和信令

把公网 UDP 收敛到少量稳定地址后,同一套 Relay 模式 可以 全球复制:Global Relay = 地理上分散的中继入站点,行为一致。

地缘入站 缩短 用户 → OpenAI 的第一跳,在地理与运营商拓扑上都更「就近」,通常带来 更低 RTT、更少抖动与可避免丢包突刺,再进入骨干网。

信令侧,他们使用 Cloudflare 的地理与就近 steering,让首次 HTTP / WebSocket 到达 附近的 transceiver 集群;请求上下文决定 会话落点 以及向客户端公布的 Global Relay 入站点。SDP Answer 给 Global Relay 地址,ufrag 再携带 足够信息 让 Global Relay 把媒体送到指定集群、并由 relay 送到具体 transceiver。

地理亲和信令 + Global Relay 同时压缩 信令 RTT 与 首包 ICE 检测 RTT,直接缩短 从点「开始说话」到媒体真正跑起来 的等待。


Relay 的实现与性能取向

Relay 用 Go 编写,刻意保持 窄职责。Linux 内核把网卡上的 UDP 交给 socket;Relay 在用户态读包头、维护少量流状态、转发 不落地终结 WebRTC。团队表示 未上 kernel bypass(如 DPDK 一类)——那能换更高 PPS,但运维复杂度也上去,对他们 当前流量形态 不划算。

几个关键工程点(原文要点):


结果与经验总结

该架构让 OpenAI 能在 Kubernetes 上跑 WebRTC 媒体,而 不必对公网撒成千上万 UDP 端口:收敛 UDP 面 带来 更好负载均衡与更安全 的边界,也让基础设施 按常规云原生方式扩缩。

同时,客户端仍是标准 WebRTC,浏览器与移动生态的互操作性得以保留;团队认为对其 点对点、延迟敏感、后端不应扮演 WebRTC peer 的 workload,默认不走 SFU 是正确默认。

文中提炼的几条「更要紧的选择」:

  1. 把复杂度加在薄路由层,而不是摊进每个后端服务,也不要求客户端魔改协议。
  2. 边缘保持协议语义;硬状态只留在一处(transceiver 握 ICE/DTLS/SRTP/生命周期)。
  3. 路由信息来自建连阶段已有的字段(ICE ufrag),避免热路径依赖额外 lookup。
  4. 先把常见路径优化到极致,再考虑 kernel bypass。

延伸阅读(原文参考文献)

  1. How Discord Handles Two and Half Million Concurrent Voice Users using WebRTC
  2. l7mp/stunner:面向 Kubernetes 的 WebRTC 媒体网关
  3. WebRTC Ports in a nutshell - BlogGeek.me
  4. LiveKit:Deploy to Kubernetes
  5. mediasoup:单 UDP 端口承载媒体连接
  6. Cloudflare Calls: millions of cascading trees all the way down

准备好开始您的 AI 之旅了吗?

读完这篇文章,想必您对 AI 技术有了更深的了解。现在就来体验共绩算力,让您的想法快速变成现实。

✓ 已有 10 万 + 开发者在使用

✓ 99.9% 服务可用性

✓ 开箱即用的容器托管