OpenAI 复盘:我们是如何在 6 个月内完成构建 GPT-Live 实时语音系统的
让 AI 说话不卡,光模型快没用,从你按下按钮那一刻起,每一环都得不掉链子。
- 以前的语音 AI 都得靠一个小模型猜「你说完没」,猜早了打断你、猜晚了显得迟钝,这一代把它整个删了。
- 难题它派给 GPT-5.5 在后台跑,自己继续陪你聊,文里有段真实录音,后台干活那十几秒,前台的嘴一直没停。
- 还有个顺手做的:他们重做了 WebRTC 的连接握手,六个来回压成一个,改动已经提交 IETF,两大实现都支持了。
以前的语音 AI,为什么听起来总是呆呆的
OpenAI 发了一篇少见的工程复盘,把 GPT-Live 这套实时语音系统六个月里怎么搭起来的全摊开讲了,从删掉那个猜「你说完没」的小模型,一路讲到重做 WebRTC 的连接握手,每一环的账都算给你看。对做产品的人来说这篇比发布公告值钱:它把「响应快」从一个模型指标,还原成了一条从按钮到耳朵的完整链路。
先别急着看架构。三段录音摆在这儿,你自己听,差别在耳朵里:
前两段的别扭感,来自两个病根。
病根一:一个专门猜「你说完没」的小模型
过去的语音 AI 是回合制的,跟对讲机一样:你说,说完,它答。中间需要一个叫 turn detector(回合检测器)的小模型,专门判断你到底说完没有。
这个判断两头不讨好。猜早了,你话说到一半被打断;猜晚了,你说完半天它还愣着,显得反应迟钝。而且它主要靠「静音」来判断,所以你中间停顿想一下、旁边有人说话、马路上过一辆车,都可能被当成「这人说完了」。
更要命的是顺序:这个小模型得先做完决定,后面那个大得多的模型才能开始干活。两段延迟是叠加的,不是并排跑的。
病根二:三个模型排成一队
再往前一代更慢。最早的 ChatGPT 语音是三个模型串起来的:语音转文字,大模型想答案,文字转语音。一环接一环排队跑,延迟层层累加。而且你说话时的语气、语速、停顿,在转成文字那一步就全丢了,后面的模型看到的只是一行干巴巴的字。
GPT-Live 做了两件事:拿掉检测器,再让说话的和思考的分家
这两个病根,OpenAI 是分两刀下的。
第一刀:让模型能同时听和说
GPT-Live 的语音模型是 full-duplex(全双工)的,能一边听一边说,像打电话,不像对讲机。对讲机得等对方松开按钮你才能出声,打电话你俩可以同时说话。
既然它自己每秒能做很多次「现在该出声,还是该接着听」的判断,那个专门猜回合的小模型就没有存在的必要了,直接从音频链路上拿掉。你停顿想一下,它不会抢;你想插话,它接得住。
第二刀:把「说话」和「深度思考」拆成两个模型
但快的模型往往不够聪明。你问一句需要联网查资料的问题,一个反应飞快的语音模型答不好。
所以他们干了第二件事:前台放 GPT-Live-1 负责实时对话,后台放 GPT-5.5 负责搜索、写代码、检索资料。你问了个要查的问题,前台把活派给后台,自己继续跟你聊着,结果回来了再顺进对话里。这叫委派。
这件事听起来抽象,但它是能被听见的。下面这段是官方放的真实对话录音,配了三条轨,你说话、GPT-Live-1 应答、GPT-5.5 在底下跑搜索:
发布页把这两刀说得很明确:GPT-Live 就是靠这两个架构改动解决问题的。全双工负责让对话跟得上人的节奏,委派负责让它答得上难题,缺哪一个,另一个都立不住。
快通道与慢通道分开:音频主干怎么都不卡
但是把活派出去容易,难的是派出去这段时间里,你不能听出任何停顿。
整篇文章的核心设计原则就一句话:声音必须流动,别的都可以等。
具体做法是划一条专用快车道。音频在客户端和语音模型之间直来直往,中间不夹任何业务逻辑;派活、调工具、存对话记录,全都挪到一个异步的边界后面。这样一个慢工具调用只会拖慢它自己的结果,绝不会把声音卡住。
顺带还有个好处:应用层怎么改都不影响响应速度。换工具、改策略、改后端逻辑,快车道那部分不用动。
p95 追平了原来的 p50
光有设计不够,得看落地效果。他们把这条链路的代码从 Python 的 asyncio 重写成了 Go,帧的输送平稳了很多,新系统的 p95,追平了老系统的 p50。
这个数字得翻译一下。p50 是「一半情况比这快」,p95 是「最差的那 5% 里最好的那个」。新系统最糟糕的表现,等于老系统的中等表现。
对语音来说这特别重要,因为一次卡顿就是一次能听见的杂音或停顿。你不在乎平均值,你在乎最差的那几次。
包晚到了怎么办:把音频悄悄拉长一点
传输层用的是 WebRTC,浏览器和手机 App 里内置的那套实时音视频标准,视频通话基本都建在它上面。它有个很实用的本事:包晚到了,它会把已有的音频悄悄拉长一点点填上空当,等包追上来再稍微加速播回去。听的人基本察觉不到,但要是没这一手,那儿就是一个洞。
派活这条路本身也得快
前台能靠寒暄撑一小会儿,撑不住无限久。所以从派活到拿回结果这一整条路,路由、处理提示词、推理、调工具,全都算进「响应预算」里。核心思路是能提前做的都提前做:
| 做法 | 省下的是什么 |
|---|---|
| 语音会话一开始,就先把 GPT-5.5 的推理会话建好、初始上下文预填进去 | 等你真问出那个要查的问题时,提示词早处理完了,不用从头排队 |
| 这个推理会话整通电话都留着,后续请求固定发到同一个 worker(会话黏性),再配上提示缓存 | 重复的上下文不用反复算;因为只是「固定」不是「绑死」,某个 worker 挂了也能恢复 |
| 推理力度、输出长度上限、工具的描述格式、模型和工具之间来回的次数,一个个调 | 把结果送到对话里的时间往前挪 |
无缝接力:聊到上下文塞满了,怎么换个脑子还不让你察觉
但是短对话这么办没问题,一通电话打半小时呢。
两个麻烦一起来。一是机器要换:推理实例按需求增减,不可能让你这一路会话永远钉在同一台机器上。二是上下文会满:聊得越久,累积的上下文越长,迟早超过模型能装的上限。
第二件事得先说清楚为什么会造成卡顿,不然后面的解法看不出妙在哪。
上下文满了就要做压缩,把前面的内容缩短。但压缩有个副作用:它改动了过去的上下文。模型的 KV 缓存,把之前算过的注意力中间结果存下来、省得每次重算的那份存货,因此全部作废,得重新预填充一遍。这个过程要花时间,而这段时间里模型没法及时出声。放到语音场景,就是当场卡住。
他们对这两件事用了同一招:别在原地做手术,另起一台,好了再切。
顺序是这样的:老机器 A 一直陪你说话不停,同时旁边把新机器 B 预热起来,把压缩好的上下文快照灌进 B,让 B 追上当前进度;中间有一段两台并行跑的重叠期;B 完全就绪了才切过去,A 才停。
重活全在快车道外面干完了,对你来说整个过程听不出任何异样。这也是为什么他们敢支持很长的通话,上下文什么时候满了就什么时候压,压多少次都不影响说话。
连绵的说话,怎么切成一条条能存档的消息
既然对话这头已经连续了,那 ChatGPT 界面上那些消息气泡还得一条条显示,怎么办。
这是个有点意外的麻烦。语音模型这边是连续的声音流,没有回合;但周围的系统还是按「一条条消息」工作的,ChatGPT 的对话界面要显示气泡,分析和安全那套也要一条条记录。所以应用服务器得把连绵的、还经常重叠的对话,切成一条条消息。
难点有三层:
边听边切,最新那条消息是临时的,它的文字、时间、算谁说的,都可能随着后面的声音变化,得等一个人稳定占住话头足够久,才敢定稿。两个人声音叠一起更麻烦:你说话的时候它「嗯哼」一声,那不该单独算一条消息;但它要是插进来说了句有内容的话,那通常该算。而且切早了,历史记录变得碎、顺序还乱;切晚了,字幕出得慢,依赖字幕的功能跟着慢。
解法是同时维护两份账本:
记当前状态,随时可以改。
界面用这一份,反正对话界面本来就能刷新,一条消息的文字和归属改了,重画一遍就是。
记最终说了什么,定稿才写。
写进分析管线的必须用这一份,日志和统计要的是不会再变的事实。
这样一来,ChatGPT 的其他部分能拿到一份稳定的对话记录,而实时语音那条路上并没有被强行塞回回合制。
首包秒连:把握手延迟打到极致
但是前面说的都是聊起来之后,还有个更早的地方在偷时间。
你按下语音按钮那一刻,系统得先把媒体通道建起来、把音频喂进模型,对话才能开始。这段时间全在关键路径上。
为什么标准 WebRTC 要握手六个来回
因为 WebRTC 本身就是好几个协议摞起来的三明治:ICE 负责打通网络,DTLS 负责加密,SCTP 负责可靠传输,DCEP 负责开数据通道。每一层当初都是独立设计的,各自带一套防 DoS 攻击的握手,摞在一起,就有重复劳动了。
按官方那张对比图的账:媒体要 3 个来回才能开始传,数据通道要 6 个。一个来回(RTT)就是数据从你这儿发到服务器、服务器再回你这儿,实打实等网络跑一趟,六个来回就得等六趟。
为什么这事今天才值得修
这个问题不是新出现的,但它是最近才变得难忍的。IETF 草案里交代了因果:
2011 年 WebRTC 设计的时候,这几个来回跟当时用 TCP/TLS 建一个 WebSocket 的开销差不多,不算慢。后来 QUIC 出来了,把同样的事做到 1 个来回,对比之下就显得极慢。
更关键的是用法变了。当年 WebRTC 主要用来做人对人的视频通话,建连接那点时间被「等对方接电话」盖住了,根本没人察觉;现在是人对着服务器说话,慢就直接被听见。
WARP:四个改动,吃掉五个来回
他们的方案叫 WARP(WebRTC Abridged Roundtrip Protocol,WebRTC 简化往返协议),由四个互相独立、且都向后兼容的改动组成:
还剩最后一个尾巴:Instant Connect
握手快了,但 WebRTC 连接之前还得先交换一次 SDP 参数,双方互相通报「我支持什么编码、用什么密钥」。这一趟还在关键路径上。
所以他们又做了个叫 Instant Connect 的东西:提前把这些参数谈好,但不占服务器资源;等第一个媒体包到了,服务器直接把会话变出来。万一预先谈的参数过期或者无效也不亏,标准的信令流程本来就在同时跑着,退回去不多花一点时间。
WARP 和 Instant Connect 不是一个东西的两种说法,它们收拾的是不同的来回:WARP 管传输层那几层协议的握手,Instant Connect 管它们之前那趟 SDP 信令交换。
这一点在数字上能对出来。IETF 草案的摘要写的是「六个来回降到两个」,博客写的是「降到一个」,草案那两个是 1 个信令来回加 1 个媒体来回,博客只算媒体那一趟,因为信令那一趟正好被 Instant Connect 挪出了关键路径。两边都没错,是把哪些算进来的口径不同。
两个加起来,客户端一个 UDP 包就能开一路会话,服务器立刻应答。
省下来的到底是多少
博客本身没给建连提速的实测数字,但 IETF 草案里有一组:在一个真实的生产部署中,WARP 让建连延迟在 p50 少了约 4 个来回,p95 少了约 12 个。
p95 省得比 p50 多这么多,草案给的解释是:握手包的数量变少了,撞上丢包的概率也就跟着小了。网络越差的用户,省得越狠。
为什么这套东西能推这么快
WARP 是当成公开标准做的,正在 IETF 的 TSVWG 工作组推进,libwebrtc 和 Pion 这两个主要实现都已经加上了支持。草案的共同作者是 Meta 的 Philipp Hancke,两家竞争公司的工程师一起写的。
推进得快也有原因:WebRTC 最早的架构师之一 Justin Uberti,和 Pion 的作者 Sean DuBois,现在都在 OpenAI。Uberti 也是这篇博客的作者之一,等于是同一个人回头动自己十几年前定下的东西。
影子测试:拿真实流量炼一遍钢
但是系统在纸面上快,遇到真实流量还是可能趴窝。
正式让用户用之前,他们做了一次影子测试:把一小部分真实的 ChatGPT 语音会话同时喂给老的 Advanced Voice Mode 和新系统,比例逐步加大。老系统照常服务用户,新系统只读地跑推理,结果不给任何人听。这样能拿真实的客户端、真实的网络、真实的通话时长和地理分布来压,用户那边一点感觉都没有。
压出来四件事:
语音会话是一直开着、一直在送帧的,所以 CPU 那边的流处理、队列、网络路径都得跟着推理一起扩容。真实负载下,有个配套组件比压测估的更早就饱和了(博客没说是哪个),推理请求开始堆积,延迟层层放大。
于是他们把容量问题的问法整个换了,从「一块 GPU 能扛多少请求」,换成「系统能同时撑住多少路会话,还能保证每一帧都准时」。
会话被路由到远处的机房,启动和传输好几个环节都会加延迟。他们开始把模型上线和区域容量、流量调度配置放在一起验证,再按用户所在地区拆开看延迟。
把推理挪近用户有用,但更大的教训是:端到端的响应速度取决于路径上的每一个服务,不只是模型服务器。
长通话把内存和持久化的压力压了出来;重连会触发上下文压缩和状态恢复;用户正常挂断,暴露了关闭流程里的竞态。
这些在短时压测里基本碰不到,因为它们依赖时间、依赖累积的状态、依赖跨服务的行为,压测跑得再猛,跑得不够久、状态不够脏,就是压不出来。
他们发现有的指标把不同来源的延迟混在一起算了;有的仪表盘看总数是好的,个别不健康的实例被平均值盖住了;还有测试环境和线上环境的配置漂移。
后来补了更细的遥测、拿已知可用的配置做校验、分阶段放量、单条路径能快速隔离或关掉。这次影子测试最后成了一次上线预演,不只演能接多少流量,更是演出了事多快能发现、能控住、能恢复。
- 全双工架构:模型能同时听和说,那个专门猜「你说完没」的小模型直接从音频链路上删了,换来的是你停顿想一下它不抢话,你想插话它接得住。
- 说话和思考分家:前台 GPT-Live-1 一直陪你聊,需要搜索推理的难题甩给后台 GPT-5.5,换来的是快和聪明不用二选一,后台干活时对话不冷场。
- 快慢通道分离:音频走专用快车道不夹业务逻辑,派活调工具全挪到异步边界后面,这条链路还从 Python 换成了 Go,换来的是新系统最糟的表现(p95)等于老系统的中等表现(p50)。
- 无缝接力:换机器和压上下文都不在原地做,而是另起一台预热好、追上进度再切,换来的是通话再长也不会因为清理上下文而断音。
- WARP 加 Instant Connect:把 WebRTC 握手从六个来回压到一个,再把 SDP 那趟信令挪出关键路径,换来的是客户端一个 UDP 包就能开一路会话;而且这是公开标准,libwebrtc 和 Pion 都已支持,任何用 WebRTC 的实时应用都能吃到。
OpenAI 复盘 GPT-Live:让 AI 说话不卡,是从按钮到耳朵每一环都不掉链子换来的
OpenAI 工程团队摊开六个月的改造账本,从删掉那个猜「你说完没」的小模型,到重做十几年没人动过的网络握手,一页带图讲完。
↓ 一页读完 · 有一张会动的图
你用过的语音 AI 大多是回合制的,跟对讲机一样:你说完,它答。中间站着一个回合检测器(turn detector,专门判断「用户说完了没」的小模型),它不发话,后面那个大得多的模型不许开口。
它主要靠听静音来猜,你停顿想一下、旁边过一辆车,都可能被当成说完了。而且它做完决定,大模型才开工,两段延迟是叠加的。
GPT-Live 的语音模型是全双工的(full-duplex,能同时听和说,像打电话不像对讲机),每秒能做很多次「该出声还是接着听」的判断,那个猜回合的小模型就没必要存在了。
但反应快的模型往往答不好难题。第二刀是让说话的和思考的分家:前台 GPT-Live-1 管实时对话,后台 GPT-5.5 管搜索、写代码、查资料。前台把活派出去,自己接着聊,结果回来再顺进对话,这叫委派。
音频在你和语音模型之间走一条专用快车道,中间不夹业务逻辑;派活、调工具、存对话记录全挪到异步边界后面,一个慢工具只拖慢它自己的结果,卡不住声音。
长通话是另一道坎。上下文满了要压缩,而压缩改动了过去的上下文,KV 缓存(模型之前算过的中间结果,前面一改就全作废)得重算一遍,这段时间它出不了声,放在语音里就是当场卡住。
KV 缓存全部作废,得重新预填充
这段时间模型出不了声
你听到的就是一次当场卡顿
旁边把新机器 B 预热:灌入压缩好的快照、追上当前进度
中间留一段两台并行的重叠期
B 完全就绪才切过去,A 才停
重活全在快车道外面干完,你听不出异样
落地效果有个硬数字:这条链路的代码从 Python 重写成 Go 之后,新系统的 p95 追平了老系统的 p50,最差那 5% 的表现,等于老系统的中等水平。语音在乎的正是最差的那几次,一次卡顿就是一次能听见的杂音。
你按下按钮那一刻,系统得先把媒体通道建起来,这段时间也全在关键路径上。WebRTC(浏览器和手机里内置的实时音视频标准)是四个协议摞起来的三明治,ICE 打通网络、DTLS 加密、SCTP 可靠传输、DCEP 开数据通道,每层当初独立设计、各带一套握手,摞在一起就有重复劳动。
来回数是 OpenAI 官方对比图的口径,博客没给毫秒数;IETF 公开草案里另有一组真实部署实测,WARP 让建连延迟 p50 少约 4 个来回、p95 少约 12 个,握手包变少,撞上丢包的概率也跟着小,网络越差的用户省得越多。WARP 是当公开标准做的,正在 IETF 推进,草案共同作者来自 Meta,libwebrtc 和 Pion 两个主要实现都已支持。
还剩一个尾巴:连接之前双方还得交换一次 SDP 参数,互相通报支持什么编码、用什么密钥。他们又做了个 Instant Connect,提前谈好参数但不占服务器资源,第一个媒体包一到,服务器直接把会话变出来。两个加起来,客户端一个 UDP 包就能开一路会话。
正式让用户用之前,他们做了影子测试:把一小部分真实的 ChatGPT 语音会话同时喂给老系统和新系统,比例逐步加大。老系统照常服务,新系统只读地跑推理、结果不给任何人听。
✔ 重连会触发上下文压缩和状态恢复
✔ 用户正常挂断,暴露了关闭流程里的竞态
✘ 短时压测碰不到:这些故障依赖时间、依赖累积的状态、依赖跨服务的行为
✘ 光加 GPU 也不管用:语音会话一直开着一直送帧,先饱和的是 CPU 那边的流处理、队列和网络路径
另外两条同样值钱:会话被路由到远处机房,启动和传输好几个环节都加延迟,端到端的响应速度取决于路径上每一个服务,不只是模型服务器;还有指标把不同来源的延迟混着算、仪表盘的总数盖住个别不健康的实例,可观测性也被逼着重做。
站着一个小模型
一边说
两头都能出声
都可以等
改写成 Go
等于老系统的
中等水平
建连要跑的来回数
实打实等网络跑一趟
- × DTLS 握手搭 ICE 便车,省 1
- × DTLS 1.2 升 1.3,省 1
- × SCTP 握手提前谈好,省 2
- × 数据通道预先约定,免现场协商
第一个包到,
会话当场变出来
重连触发压缩和恢复
正常挂断暴露关闭的竞态
CPU 那边的流处理、
队列和网络路径