OpenAI 如何为 9 亿用户提供低延迟语音 AI(17 分钟阅读)
摘要
OpenAI 的语音 AI 架构使用 WebRTC,并采用分离的中继-收发器设计,为 9 亿周活跃用户处理低延迟音频。
本文探讨了 OpenAI 如何利用 WebRTC 为其 9 亿用户提供语音服务。
查看缓存全文
缓存时间: 2026/07/02 17:20
# OpenAI 如何为 9 亿用户提供低延迟语音 AI
来源:https://blog.bytebytego.com/p/how-openai-delivers-low-latency-voice
[](https://go.bytebytego.com/Matic_070126)
Matic 是第一款视觉智能扫地机器人,它能像你一样看清你的家,从而按你的意愿进行清洁。Matic 的主要特性:
- 完全依靠摄像头灵活避开障碍物
- 识别地板类型,自动在吸尘和拖地模式间切换
- 大轮子和可调高度的清洁头可处理厚地毯
- 比对话还安静,仅 55 dB
- 处理宠物毛发不堵塞、不缠绕
- 单个集尘袋收集干湿垃圾——尿布盐凝胶化脏水,抗菌粉末防止发霉
- 每个集尘袋附带全新 HEPA 滤网,空气更洁净,无需清洗或更换
体验 Matic 的自主清洁,提供 180 天退款保证。
立即获取您的 Matic (https://go.bytebytego.com/Matic_070126)
OpenAI 每周为 9 亿用户运行语音 AI,他们使用 WebRTC 来实现,因为替代方案意味着要重新发明互联网处理实时音频的方式。
关键在于,WebRTC 是为具有固定 IP 和端口的服务器设计的,而 Kubernetes 将这些地址视为可丢弃的。在这种规模下,常规的解决方案是采用 SFU,它适用于多方负载(如群组视频通话),但 OpenAI 的流量绝大多数是一个用户与一个模型对话。
为了解决这个问题,OpenAI 的架构将技术栈分为两部分:
- 一个**无状态中继**在地理边缘层处理协议感知的数据包路由。
- 一个**有状态收发器**负责所有 WebRTC 繁重的状态。
将两者结合起来的技巧是使用 ICE ufrag(一个在建立连接时协议已交换的字段)作为路由键,中继可以在新会话的第一个数据包上读取该字段。其他所有方面——从 Global Relay 到用户态 Go 实现,再到 Redis 缓存和细致的套接字级优化——都是建立在这个核心思想之上。
在本文中,我们将详细审视整个过程,以及 OpenAI 工程团队面临的挑战。
*免责声明:本文基于 OpenAI 工程团队公开分享的*细节*。如有不准确之处,请评论指出。*
语音 AI 要么感觉像一次交谈,要么感觉像对讲机。这两种体验之间的界限由毫秒决定。
当网络在听到用户和响应之间出现停顿,这种幻觉就破灭了。停顿变得尴尬,中断被截断,用户被迫在 AI 说话中途打断它,这也有点不礼貌。换句话说,只有当对话以说话的速度进行时,语音 AI 才感觉自然。
更严苛的约束在于连续流的特性。音频必须以稳定的流形式到达模型,而不是用户说完后一次性上传。正是这个流让模型能够在用户还在说话时就开始转录、推理和调用工具。一旦这个流中断,体验就会退化为“按下即说”模式。
对 OpenAI 而言,这些限制转化为三个具体需求:
- 系统必须能够覆盖全球 9 亿周活跃用户,无论他们身在何处。
- 连接建立必须足够快,以便用户在会话开始时就能立即说话。
- 音频的往返时间必须保持低且稳定,这样才能让轮流发言感觉干脆利落。
WebRTC 是业界为此类工作构建的协议。它是一组较小协议的集合(ICE 用于让两个端点穿越防火墙相互找到,DTLS 用于加密通道,SRTP 用于音频数据包,RTCP 用于质量反馈)。WebRTC 的原始架构师之一 Justin Uberti,以及维护 OpenAI 所依赖的 Pion 库的 Sean DuBois,如今都在 OpenAI 工作。这种协议深度在他们部署的架构中有所体现。
OpenAI WebRTC 基础设施的第一个版本是一个基于 Pion 构建的单一 Go 服务。它在一个地方处理了两项工作:
- 在信令方面,该服务协商 SDP(客户端和服务器用来描述会话的格式),选择编解码器,生成 ICE 凭证,并建立会话。
- 在媒体方面,该服务终止来自客户端的 WebRTC 连接,并维护到运行 AI 模型的后端服务的上游连接,包括推理、转录、语音生成、工具使用和编排。
[](https://substackcdn.com/image/fetch/$s_!cbRK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F970e2ede-f20b-498c-962c-1d69fbdfcb4a_1742x906.png)
这个组合服务至今仍为 ChatGPT 语音、Realtime API 的 WebRTC 端点以及多个研究项目提供支持,并且表现出色。OpenAI 面临的问题是如何将它部署到 Kubernetes 上——这个容器编排系统运行着大多数现代云基础设施。
Kubernetes 假设计算资源是廉价且可迁移的。Pod 启动后,被调度到任何有容量的地方运行一段时间,然后被重新调度或替换。标准的 WebRTC 部署模式则假设相反的情况。这种不匹配体现在两个具体方面。
第一个是端口耗尽。
部署 WebRTC 的传统方式是每个会话使用一个 UDP 端口。在 OpenAI 的规模下,这意味着每个服务需要数万个公共 UDP 端口。云负载均衡器是为少数几个知名端口设计的,因此每个额外的端口范围都会增加负载均衡器配置、健康检查、防火墙策略和发布安全性的运维复杂性。暴露的表面积也使安全审计更加困难。Kubernetes 的自动扩缩与预留大量稳定端口范围的需求相冲突,这使得弹性变得脆弱。
第二个是状态粘性。
每个服务器运行一个 UDP 端口并在其后对会话进行多路复用解决了端口问题。然而,ICE 和 DTLS 是有状态的协议。启动会话的进程必须持续接收其数据包,以验证连通性检查、完成 DTLS 握手、解密 SRTP,并处理后续的会话更改(如 ICE 重启)。如果现有会话的数据包落到了不同的进程上,则连接建立失败,或媒体中断。
这两种压力都指向同一个答案。部署架构必须改变,而客户端体验保持不变。
OpenAI 部署的架构将数据包路由与协议终止分离开来。
一个**无状态中继**位于前端,向互联网呈现一个小的公共占用面。一个**有状态收发器**位于其后,负责所有繁重的 WebRTC 状态。信令仍然直接发送到收发器。媒体则先经过中继。
中继的作用范围被刻意限制得很窄。它只读取每个数据包中的必要信息来选择目的地,然后将剩余部分作为不透明的载荷转发。音频在传输过程中保持加密,ICE 状态机留在收发器处,编解码协商在其他地方进行。从客户端的角度来看,WebRTC 会话在任何方面都看起来正常。
收发器负责 WebRTC 中需要记住状态的那些部分。ICE 连接性检查、DTLS 握手、SRTP 加密密钥以及会话生命周期都在这里。收发器是完成握手并加密/解密实际媒体的端点。
请参见下图:
[](https://substackcdn.com/image/fetch/$s_!FWPc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe0321c23-ae81-4191-971c-7b79b2da73eb_1552x964.png)
有一个明显的替代方案,团队评估后选择了放弃。
SFU,即选择性转发单元,是 WebRTC 大规模部署的标准媒体服务器架构。它为每个参与者终止一个 WebRTC 连接,并在他们之间选择性转发流。AI 作为另一个参与者加入。
这对于本质上是多方的产品(如群组通话、教室、协作会议)很有效。OpenAI 的负载则不同。大多数会话是 1:1 的,一个用户与一个模型对话。对于这种流量,SFU 模型增加了开销,并迫使后端服务本身表现得像 WebRTC 对等端。收发器模型则让后端保持为普通服务。
请参见下图,展示了 SFU 方法:
[](https://substackcdn.com/image/fetch/$s_!h1MA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4131e5c-f1b8-4e13-8bf8-68bb961d023e_1552x964.png)
TURN 也曾被考虑过,但最终被搁置。
TURN 是用于 NAT 穿越的标准协议终止中继。问题在于,TURN 分配在媒体流可以开始之前需要额外的建立往返时间,并且跨服务器迁移或恢复它们很困难。对于对延迟敏感的负载来说,这些额外的往返时间影响很大。
分离架构原则上解决了端口和状态问题。剩下的问题是让中继正确路由第一个数据包。
任何新会话的第一个数据包是最难处理的。
后续数据包很容易,因为中继有一个映射表,表明来自这个源 IP 和端口的数据包应该发送到某个收发器。第一个数据包正是创建这个映射的,因此中继必须从数据包本身判断出应该将其发送到哪里。
请参见下图:
[](https://substackcdn.com/image/fetch/$s_!-0BM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14bb750c-4f36-4b95-ae5c-36b49c77e330_1842x988.png)
有两种天真的方案:
- 在热路径上进行数据库查找会增加延迟,并引入对外部服务健康状态的硬依赖。
- 路由到随机收发器并在内部转发虽然可行,但会增加一跳。
OpenAI 选择了第三种方案。
答案存在于 WebRTC 已经交换的一个字段中。每个 WebRTC 会话都带有一个 ICE 用户名片段,称为 ufrag,它在会话建立期间生成,并在 STUN 绑定请求中回显。STUN 绑定请求是 ICE 用来验证两个端点是否能够实际互相到达的数据包,通常是客户端在媒体路径上发送的第一个东西。
关键在于,OpenAI 在信令期间生成服务器端的 ufrag。他们可以在其中放入任何内容,因此他们将路由元数据编码到其中。中继仅解析第一个 STUN 绑定请求的足够部分来读取 ufrag,解码路由提示,并将数据包转发到拥有该会话的收发器。第一个数据包之后的所有数据包都通过已建立的会话映射流动,完全跳过 ufrag 解析步骤。
请参见下图,详细展示了连接建立序列:
[](https://substackcdn.com/image/fetch/$s_!F2qy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54f8e247-d061-4465-bdaa-f7bece30f1b0_1752x1900.png)
集群中的每个收发器都监听一个共享的 UDP 套接字,这是一个绑定到内部 IP 和端口的操作系统端点。该收发器的所有会话都复用在它后面。
在信令期间,收发器在 SDP 应答中返回一个共享的中继 VIP 和 UDP 端口。VIP 是一个虚拟 IP 地址,代表整个中继集群,因此客户端看到的是一个稳定的目标(如 203.0.113.10:3478),即使后面有多个中继实例。从客户端角度来看,有一个地方可以发送数据包,并且在会话生命周期内保持不变。
中继的状态被刻意保持得很小。它持有一个源地址到收发器目的地的内存映射,加上一些用于监控的计数器和用于会话清理的定时器。如果中继实例重启并丢失了映射,下一个 STUN 数据包会从 ufrag 重建它。为了加快恢复速度,一旦路由建立,Redis 缓存会保存源到目的地的映射。重启的中继可以立即从 Redis 查找映射。
这个原则在广义上很实用。当我们需要热路径上的数据时,看看协议已经在交换什么。解析载荷中的一个字段基本上是免费的。而一次新的查找会带来延迟、依赖和另一个可能出问题的地方。
一旦公共 UDP 表面减少到一小部分固定的地址,同样的中继模式就可以在全球部署。
Global Relay 是 OpenAI 在地理上分布的中继入口点集群。它们都运行上述相同的数据包转发行为。唯一变化的是它们在物理上的位置。
[](https://substackcdn.com/image/fetch/$s_!rOyx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F420967ca-3b7d-4221-891a-02dc6e1d992a_2116x988.png)
地理分布缩短了客户端到 OpenAI 的第一跳。在靠近用户(地理上和网络拓扑上)的中继点进入网络的数据包,比需要穿越公共互联网到达远端区域的数据包要容易得多。实际效果是:延迟更低、时间更稳定,并且在流量到达 OpenAI 骨干网之前有更干净的丢包特征曲线。
OpenAI 在信令侧使用 Cloudflare 进行地理和就近性路由。建立会话的初始 HTTP 或 WebSocket 请求被路由到附近的收发器集群。然后,请求上下文决定了在 SDP 应答中向客户端宣告哪个 Global Relay 入口点。ufrag 携带足够的信息,使 Global Relay 能够将媒体路由到正确的集群,并且集群内的中继能够路由到正确的收发器。
结果是,信令路径和媒体路径都在靠近用户的位置进入 OpenAI 网络,而会话本身在其整个生命周期内锚定在特定的一个收发器上。建立往返时间和第一次 ICE 连通性检查都缩短了,这直接减少了用户在能说话之前的等待时间。
中继是一个在用户空间运行的 Go 服务,即一个从普通 UDP 套接字读取数据的普通进程。
Linux 内核从网络接口接收 UDP 数据包,将它们交付给绑定到中继 IP 和端口的套接字,然后 Go 进程从该套接字读取数据,更新少量流状态,并将每个数据包转发到正确的收发器。
OpenAI 评估了内核旁路框架(允许用户空间进程直接轮询网卡队列),并选择不使用。旁路提高了数据包吞吐量,但增加了运维复杂性。团队的工作负载在精心实现的 Go 程序可处理范围内。
三个实现选择承担了大部分性能提升。
- `SO_REUSEPORT` 是一个 Linux 套接字选项,允许同一台机器上的多个工作者绑定同一个 UDP 端口。内核随后将入站数据包分配给这些工作者,从而消除了单个读取循环的瓶颈。
- `runtime.LockOSThread` 将每个读取 UDP 的 goroutine(Go 中的轻量级线程)固定到特定的操作系统线程。结合 `SO_REUSEPORT`,这倾向于使同一流的数据包保持在同一个 CPU 核心上,有助于缓存局部性并减少上下文切换。
- 预分配缓冲区和最小的数据包解析拷贝,保持分配开销较低,并避免给 Go 的垃圾回收器带来压力。
要点是,普通的优化就足够。中继在相对较小的资源占用上处理了 OpenAI 的全球实时媒体流量。
每种架构都带有权衡,这个架构也有几个值得理解的方面。
- 整个设计围绕 1:1 会话构建。如果 OpenAI 将来想添加多方功能(群组语音通话、单个 AI 会话中的多个参与者
相似文章
OpenAI 如何实现大规模低延迟语音 AI 部署
OpenAI 详细介绍了其重新架构的 WebRTC 技术栈,旨在为超过 9 亿用户提供大规模低延迟语音 AI 服务。文章阐述了全新的 split-relay 和 transceiver 架构如何优化媒体路由与连接建立,以支持 ChatGPT 语音等实时交互场景。
OpenAI 的 WebRTC 问题
一篇技术博客文章中,一位自称 WebRTC 专家的作者批评了 OpenAI 将 WebRTC 应用于语音 AI 的做法,认为该协议设计用于实时会议,采用激进的丢包机制,这与语音 AI 的应用场景相悖——在语音 AI 中,准确性比极低延迟更为关键。
OpenAI的新语音模型不止于回话
OpenAI推出了三个新的实时音频模型,支持连续、多任务的语音交互,优先考虑长上下文推理、实时翻译和无缝工具使用。
实时 API 介绍
OpenAI 推出实时 API,使开发者能够构建低延迟多模态语音对话体验,由 GPT-4o 驱动的自然语音交互。该 API 支持六种预设声音,简化开发流程,无需集成多个模型。
构建实时语音翻译应用(阅读时长:28 分钟)
OpenAI 发布了 gpt-realtime-translate,这是一款专为实时口译优化的低延迟语音转语音模型,并附有开发者实战指南,用于构建支持多语言的浏览器、移动及视频应用。