🏠 home › concepts › openai-webrtc-relay-transceiver
tags
[WebRTC, 实时音视频, 边缘网络, 基础设施, 低延迟, OpenAI, 语音AI]
created
2026-05-07
updated
2026-05-07
sources
[raw/notes/openai-webrtc-relay-transceiver-2026-05-04.md]

定义

OpenAI 2026-05-04 公开的 WebRTC 重架构:把"包路由"与"协议终止"切成两层——无状态 relay(轻量 UDP 转发 + 全球 PoP + 小公网 footprint)+ 有状态 transceiver(持有 ICE / DTLS / SRTP / session lifecycle 全套 WebRTC 协议状态 + 与推理后端的内部协议转换)。设计目标 = 在不改客户端 WebRTC 行为的前提下,让 WebRTC 媒体能跑在 Kubernetes 上,且 first-hop 延迟够低支撑 ChatGPT voice / Realtime API / 研究项目的全球 900M+ WAU 规模。

驱动业务 = ChatGPT voice、Realtime API 的 WebRTC endpoint、若干研究项目。作者 Yi Zhang + William McDonald(OpenAI Members of Technical Staff)。

三个 collide at scale 的约束

原文:"three constraints that started to collide at scale":

  1. One-port-per-session media termination 不适合 OpenAI 基础设施 — 大段公网 UDP 端口范围在云 LB / Kubernetes service / 防火墙 / 滚动发布里都难管理
  2. 有状态 ICE 与 DTLS session 需要稳定所有权 — single-port-per-server 会让同会话的包打到不同进程,DTLS 握手或 SRTP 解密直接失败
  3. 全球路由必须保持 first-hop 低延迟 — first hop 决定 setup 与媒体的下限,跨公网到远端 region 不可接受

三条同时成立的解 = stateless forwarder + stateful terminator,是表 4 个候选方案中唯一同时满足三约束的。

为什么不是 SFU:1:1 latency-sensitive 选 transceiver 模型

SFU(Selective Forwarding Unit)= 多方场景标准 WebRTC media server,把 AI 当成"另一个参与者"加入。适合 group call / classroom / collaborative meeting。即便在 client-to-AI 产品里 SFU 也常是 default 起点,因为可以复用一套成熟系统做 signaling / media 路由 / 录制 / 可观测性 / 未来扩展(人接管 / 多参与者)。

OpenAI 的 workload 不同:most sessions are 1:1(一用户对一模型 / 一 application 对一 real-time agent),每个 turn 都对延迟敏感。所以选 transceiver 模型——一个 WebRTC edge service 终止客户端连接,再把 media 与 events 转换成更简单的内部协议给推理 / 转写 / 语音生成 / 工具调用 / 编排服务用。

transceiver 是唯一拥有 WebRTC session state 的服务(ICE 连通性检查、DTLS 握手、SRTP 加密密钥、session lifecycle)。把状态压在一个地方让会话所有权可推理,让后端服务能像普通服务那样 scale,而不是表现得像 WebRTC peer——这与 agora-rtsa-sdk "把 SDK 边界设计成只做不可省的事,把设备相关的留给应用"是同构的边界设计判断。

最初实现 = 单一 Go service 基于 Pion(Sean DuBois 创建并维护的开源 WebRTC Go 实现,DuBois 现在 OpenAI),同时处理 signaling 与 media termination。

候选方案对照(OpenAI 表)

方案 优点 缺点
Unique IP:port per session(native direct UDP) 直接 client-to-server,无转发层 每会话一公网 UDP 端口;大端口范围难暴露/难安全;K8s/云 LB 不适配
Unique IP:port per server 公网 UDP 比 per-session 小很多;单 socket 应用层多路复用 单主机干净,跨 LB fleet 不行;首包仍可能落错实例
TURN relay(协议终止式) client 只需知道 TURN 地址 + 端口;可在边缘集中策略 TURN allocation 加 setup RTT;跨 TURN server 迁移/恢复 allocation 难
Stateless forwarder + stateful terminator(OpenAI 选) 公网 UDP footprint 小;transceiver 仍持完整 WebRTC session 媒体到 owning transceiver 多一跳转发;需要 relay 与 transceiver 之间的自定义协调

首包路由:协议原生字段做路由 hint

首包路由是这套设计的核心难点——relay 必须在 packet path 本身没有 session 之前就路由这个包,不能停下来查外部 lookup service。

WebRTC session 自带一个协议原生路由钩子:ICE username fragment(ufrag)——session setup 时交换、STUN 连通性检查里 echo 回来的短标识符。OpenAI 在服务端生成 ufrag 时把路由元数据编进去,刚好够 relay 推断 destination cluster + owning transceiver。

完整流程:

  1. signaling 时,transceiver 分配 session state,在 SDP answer 返回 shared relay VIP + UDP port(如 203.0.113.10:3478)——VIP 是虚拟 IP 前置整个 relay fleet
  2. 客户端首个 media-path 包通常是 STUN binding request——ICE 用它验证可达
  3. relay 解析这第一个 STUN 包刚好够读出 server ufrag、解码路由 hint、转发到 owning transceiver
  4. 每个 transceiver 监听一个 shared UDP socket(一个 OS 端点 bind 到内部 IP:port,不是 per-session 一个 socket)
  5. relay 创建 client source IP:port → transceiver destination 的 session 之后,后续 DTLS / RTP / RTCP 包不再重新解 ufrag
  6. relay session 故意最小化:只有 in-memory session(驱动转发)+ 监控计数器 + session 过期清理 timer
  7. relay 重启丢 session 时,下一个 STUN 包从 ufrag 重建 session
  8. 进一步用 Redis cache 持久化 <client IP:port, transceiver IP:port> 映射——relay 重启或新流可以从 Redis 恢复,不用等 STUN 重建

这是 agent-tool-design "在标准协议里塞自己语义、不引入新字段"的网络版——不查外部服务、不做应用层握手,把 ICE 协议已有字段(ufrag)重新解读出路由含义。

Global Relay:地理化部署

公网 UDP surface 收敛到一小批稳定地址 + 端口后,可以把同一个 relay 模式全球部署——Global Relay = 地理分布的 relay ingress 点,全部实现同样的包转发行为。

Broad geographic ingress shortens the first client-to-OpenAI hop because a packet can enter our network at a relay close to the user, in both geography and network topology, instead of crossing the public internet to a distant region first.

信令侧:Cloudflare 做 geo / proximity steering

OpenAI 用 Cloudflare geo + proximity steering 让初始 HTTP / WebSocket 请求落到附近 transceiver cluster:

geo-steered signaling + Global Relay 把 setup 与 media 都放到附近 entry path 上,同时保持 session 锚定在一个 transceiver 上——直接缩短用户从开口到服务可响应的等待时间

cloudflare-mesh 已记录 Cloudflare 是 Agent 时代私有组网首选;本架构给"Cloudflare 是前沿 AI 公司基础设施 default"加新证据形态——不是 Mesh / Workers,而是边缘 DNS / proximity routing 这一层。

relay 实现:Go + Linux 调优、不上 kernel bypass

写在 Go 里,故意保持窄实现。Linux 内核 networking stack 收 UDP 包并交给 socket,relay 在用户态:普通 Go 进程从 socket 读包头、更新少量 flow state、转发——不终止 WebRTC没有用 kernel-bypass 框架(如 DPDK / AF_XDP)。

关键设计选择

效率措施

"This implementation handled our global real-time media traffic with a relatively small relay footprint, so we kept the simpler design instead of taking on a kernel bypass route." —— first-principles-deletion "必须这么久 / 必须这么重吗" 的工程化范例:常见 case 优化够用就先不上更复杂的方案。

四条核心选择(原文 takeaways)

  1. Preserve protocol semantics at the edge — 客户端仍说标准 WebRTC,浏览器 + 移动端互通不变
  2. Keep hard session states in one place — transceiver 持 ICE / DTLS / SRTP / session lifecycle,relay 只转发
  3. Route on information already present in setup — ICE ufrag 给了 first-packet 路由 hook,无需热路径外部 lookup
  4. Optimize for the common case before reaching for kernel bypass — 窄 Go + SO_REUSEPORT + thread pinning + 低分配解析对当前 workload 已经够用

与 Sentino 路径的对照

Sentino 走 Agora SD-RTN(agora-rtsa-sdk / agora-rtc-voice / sentino-iot),把"全球低延迟实时媒体到云端 AI"问题外包给多租户基础设施供应商。OpenAI 自建 Global Relay = 同一类问题的 in-house 解。两者形成同源问题、不同规模阶段、不同自建/外包姿态的对照:

维度 OpenAI(in-house Global Relay) Sentino(Agora SD-RTN)
全球可达 自建 PoP + Cloudflare geo steering Agora SD-RTN(多租户共享)
WebRTC 终止 自家 transceiver(Pion + Go) Agora 服务端 + Agora ConvoAI Agent
与推理后端连接 "simpler internal protocols"(未披露) MQTT 信令 + Agora ConvoAI HTTP callback / Sentino LLM endpoint
Kubernetes 适配 需要重新架构(split relay) 由 Agora 屏蔽,Sentino 不感知
模型与媒体的耦合 gpt-realtime 走自家栈 Agora ConvoAI Agent + Sentino LLM endpoint 半自定义模式
数据控制 / 合规自由度 完整(自家 fleet) 受 Agora 数据治理边界约束(agora-rtsa-sdk E2E 加密段)
触发自建的规模阈值 900M+ WAU Sentino 远低于此

含义:对 Sentino 当前规模,自建无意义;但本文是"为什么 SD-RTN 这类服务有价值"的反向证据——OpenAI 在 900M WAU 的规模下重新做这件事的工程量极大(split relay + ufrag 编路由 + Cloudflare 编排 + Go 调优 + Pion 维护)。Agora 屏蔽掉的就是这套底层路由 / 状态粘性 / 端口管理问题。

watching point:Sentino 当前用 Agora ConvoAI 的"AI as participant in SFU"模型(Agent 在频道里以参与者身份加入),OpenAI 明确说这是大多数客户的 default 起点,但 1:1 latency-sensitive workload 应该走 transceiver 模型。当某个 Sentino 客户的并发量 / 延迟敏感度大幅升高时(比如未来的实时 barge-in 重度场景),是否需要考虑迁移到 transceiver 模型 = 待跟踪开放问题。

SFU 迁移到 transceiver 模型:Sentino 视角的具体对比

OpenAI 选 transceiver 不是偶然——是 1:1 latency-sensitive workload 的最优形态。Sentino 当前走 SFU 形态(Agora ConvoAI Agent 作为参与者加入频道),与 OpenAI 的 transceiver 形态在协议状态归属、路径跳数、多租户隔离、客户改造成本几个维度上有结构性差异。下面把"如果 Sentino 未来要迁移"的代价 / 收益拆出来,作为长期 watching point 的具体抓手。

维度对比

维度 当前 SFU 形态(Agora ConvoAI) 迁移后 transceiver 形态(OpenAI 范式)
WebRTC session 终止点 Agora 服务端 SFU;Agent 是另一个 WebRTC peer 自家 transceiver;推理后端走简化内部协议(不是 WebRTC peer)
Agent 角色 在 Agora 频道里以"参与者"身份加入,与设备同等地位 Agent 不感知 WebRTC,只看 transceiver 转过来的"已解码音频帧 + 事件"
媒体路径跳数 设备 → Agora SD-RTN edge → SFU → Agent 出口 → ASR/LLM/TTS 设备 → relay → transceiver(一处终止)→ 推理后端
路径上的协议解码次数 至少 2 次(设备入口 + Agent 出口) 1 次(transceiver)
会话所有权管理 Agora 服务端透明处理 自家 transceiver 持有 ICE/DTLS/SRTP;relay 用 ufrag 路由
公网 UDP footprint Agora SD-RTN(与 Sentino 无关) 自家小而稳定的 VIP 集合(要自己管)
Kubernetes 适配复杂度 Agora 屏蔽,Sentino 不感知 自己解决(split relay + ufrag 路由 + Redis cache + SO_REUSEPORT …)
首包路由到 owning instance Agora 内部解决 必须自建(ICE ufrag 编路由元数据 + relay 解析)
Geo steering 信令 Agora 边缘网络自动就近 自己接 Cloudflare geo + proximity steering
AI 进入对话的延迟 Agent 走 join channel 路径 + 一段冷启动;首字延迟受 Agora SFU 调度影响 transceiver 直接对接推理后端;首字延迟受自家栈控制
Barge-in / turn-taking 控制权 受 Agora ConvoAI v2 join API 的 turn_detection 配置约束(agora-convoai-join-api 完全在自家 transceiver + 推理后端协调,无中间 SFU 调度
E2E 加密 vs 服务端 ASR 互斥agora-rtsa-sdk 启用 E2E → ConvoAI 服务端 ASR 不可用 transceiver 在自家 trust boundary 内解密 → ASR 与 E2E 不互斥(trust 边界从 Agora 收回到 Sentino)
数据治理 / 合规自由度 受 Agora 数据治理边界约束 完整自家控制
多租户隔离机制 Agora 频道 + token 自家 transceiver 做租户隔离设计
客户端改造 无(设备已跑 Agora RTSA / RTC SDK) 设备改走纯 WebRTC,丢弃 Agora SDK 依赖(这是最大破坏点)
运维负担 几乎只看 Agora 控制台 自家 PoP / relay 健康 / Pion 跟进 / 跨 region 故障切换全自担
触发自建的规模阈值 低(任何规模都能跑) 极高(OpenAI 900M+ WAU;Discord 250 万并发语音;Sentino 远低于此)
协议互通失去什么agora-rtsa-sdk "RTSA ↔ RTC ↔ Web 协议互通") 设备 / Web / 移动端跨形态在同一频道互通 不同形态需要各自接入 transceiver;天然失去多端在同一会话的能力(对 Sentino 当前 1:1 设备 ↔ Agent 形态影响小,但对未来"人 + 设备 + AI 三方在同一会话"场景是失去)

触发迁移的三类场景(Sentino 视角)

  1. 单客户并发 + 延迟敏感度同时升高到 Agora 多租户调度成为瓶颈 — 例如某客户的实时 barge-in 重度场景(用户 / Agent 几乎不停打断对方)发现 Agora SFU 的调度延迟对体感影响明显。但这要求单客户量级足够大让 Agora 公允分配的边缘资源不够用——Sentino 远未到这个规模
  2. E2E 加密 + 服务端 ASR 同时强需求 — 像 sentino-tenga 这种性健康对话场景,如果未来同时需要"端到端加密 + 用户语音必须经 ASR 转写",当前 Agora ConvoAI 服务端 ASR 与 E2E 加密互斥(agora-rtsa-sdk "反直觉副作用"段)。迁移到自家 transceiver 可以把 trust 边界从 Agora 收回到 Sentino,让 ASR 在自家 trust boundary 内做。但这条有更便宜的中间方案——设备端做 ASR / 在 Agora ConvoAI 之外接私有 ASR endpoint,不需要重做 RTC 栈
  3. 数据主权 / 合规要求强到 Agora 数据治理 unacceptable — 例如某 enterprise 客户合规要求"音频 metadata 不出 Sentino 控制平面"。同样有更便宜的方案——选择 Agora 的私有化部署 / Region 隔离方案,不需要重做 RTC 栈

三类场景的共性:每一种"必须迁移"的硬触发点都有更便宜的中间方案可以缓解。OpenAI 走自建是因为 900M WAU + 全球分布的边际成本曲线让自建变成正向 ROI;Sentino 短中期没有任何场景能让自建 ROI 成立

渐进式迁移路径(如果未来真要走)

如果某一天硬触发点出现,迁移不是一步到位,而是分阶段:

  1. 第一步:保留 Agora SD-RTN 做 client 侧入口,但把 Agora ConvoAI Agent 替换成自家 transceiver pod—— Agent 不再"作为参与者加入",而是设备的 RTC stream 在 Agora 出口被引流到自家服务做"ConvoAI 兼容协议"接入。这一步不需要客户端改造。Agora 这个层面的能力是否对外暴露需要确认
  2. 第二步:把"Agora SD-RTN 入口 → 自家 transceiver"路径换成"自家 relay PoP → 自家 transceiver",但仍接受 Agora SDK 客户端(设备 / Web 端兼容)。需要在 SDP / ICE 协议层做兼容工作,跨度大
  3. 第三步:完全去 Agora 化——设备 / Web 端改走纯 WebRTC(Pion / 浏览器原生),自家 transceiver 接管所有协议状态。需要客户端 SDK 替换,对已出货设备是 OTA 升级风险

每一步的代价都比上一步大一个量级。对 Sentino 当前阶段,第一步以前的所有事情都不用做——保持现状即可,把这条路径作为长期 watching point

真正的预警信号(不是"现在该迁移",是"该开始评估")

Sentino 应该在以下任一信号出现时启动评估,而不是等到不得不迁移:

Watch 而不 Act 是当前正确姿态——本节存在的意义是给"什么时候该 act" 一个明确清单,不是建议现在就动。

反常识 / 工程细节

  1. Pion 是 Go 写的开源 WebRTC 实现,OpenAI 的 transceiver 直接基于它 — 印证开源协议栈选 Go 实现的可行性,不是必须 C++(agora-rtsa-sdk 走 C 是嵌入式约束,OpenAI 服务端走 Go 是 K8s + 工程效率约束)
  2. Pion 的创建者 Sean DuBois + WebRTC 原始架构师 Justin Uberti 现在都在 OpenAI — 标准协议生态侧的关键人物物理上集中到一家公司,是"协议层的人才聚拢"信号;与 Anthropic 在 mcp-protocol 协议层的姿态对照,OpenAI 在 WebRTC 协议层有同等深度的人才布局
  3. 不上 kernel bypass 是有意识的选择,不是技术不行 — DPDK / AF_XDP 这种方案常被互联网公司视为高并发 UDP 必备;OpenAI 明确说"窄 Go 实现 + SO_REUSEPORT + thread pinning + 低分配解析对我们 workload 够用",是 first-principles-deletion "必须用最快方案吗"的反向选择
  4. Redis 不是 transceiver 的协议状态存储,只是 relay 路由 cache — 真正的 WebRTC 协议状态在 transceiver 内存里。Redis 让 relay 重启更平滑而已。这与"Redis = session 持久化"的常见架构假设是反向的——OpenAI 选择把协议状态留在 transceiver 内存里、靠"重启从 ufrag 重建"做容错
  5. 客户端无感知 — 整个架构的复杂度对客户端不可见,浏览器 / 移动端跑标准 WebRTC、不需要任何 SDK 特殊改动。这是 harness-engineering "标准开放、最佳实现封闭"在网络协议层的应用

待办与开放问题

相关概念