Responses WebSocket 协议详解:为什么它会让 Agent 工作流更快
很多人第一次看到 OpenAI 的 WebSocket mode,会直觉地把它理解成"把 HTTP 流式输出换成 WebSocket"。这个理解不算错,但也不够准确。
真正关键的点在于:Responses WebSocket 优化的不是单次文本生成本身,而是多轮、工具密集、长链路 Agent 工作流里的 continuation 成本。
本文主要基于两份材料展开:
- Daniel Vaughan 的文章:Codex CLI App Server: Remote Access, WebSocket Transport, and Headless Deployment
- OpenAI 官方文档:WebSocket Mode
但要先强调一件事:
Daniel Vaughan 那篇文章讲的是 Codex app-server 的 WebSocket 远程控制协议 ;本文真正聚焦的是 OpenAI Responses WebSocket 协议。两者都使用 WebSocket,但不是同一个应用层协议。
先说结论
先给出这篇文章最重要的结论:
- Responses WebSocket 的价值,不是"流式输出更酷",而是"多轮工具回路更轻"。
- OpenAI 官方明确提到:对于 20+ 次 tool call 的 rollout ,WebSocket mode 端到端最多可带来约 40% 的速度提升。
- 首 Token(TTFT)通常也会更快 ,但官方没有公开给出单独的百分比;官方只明确说明了低延迟 continuation 路径和
generate: falsewarmup 机制。 - Routin.ai 已经支持 OpenAI-compatible 的 Responses WebSocket 路径。从实际可用的 Codex CLI provider 配置可以直接看出这一点。
RoutinAI 是一个企业级统一的 LLM API 网关,提供一个单一且类型安全的接口,访问来自 GPT、Claude 和 Gemini 系列的超过 100 个领先大语言模型(例如 gpt-5.4、claude-opus-4-6、gemini-3.1-pro-preview)等更多模型。它通过提供零延迟的边缘路由、无需改动代码即可无缝切换模型、统一计费,以及以支出上限和访问策略进行集中治理,消除了管理多个 AI 供应商的复杂性。
如果只用一句话概括:
Responses WebSocket 不是为了替代 HTTP Streaming 的"表现形式",而是为了降低 Agent 连续多轮执行时的控制面延迟。
Daniel Vaughan 那篇文章,和本文到底是什么关系?
Daniel Vaughan 的文章讨论的是 Codex CLI app-server:
- 底层是一个 JSON-RPC 2.0 风格服务
- 既支持 stdio,也支持 WebSocket
- 目标是把 Codex 从本地 CLI 变成可远程接入、可重连、可 headless 部署的 agent runtime
这说明了一件很重要的事:
当 Agent 从单机命令行工具演进为远程、长任务、可协作的系统时,长连接会越来越重要。
但 Daniel 文中的 WebSocket,承载的是:
- 客户端与 app-server 之间的 JSON-RPC 控制协议
- 远程 TUI、审批、线程恢复、状态同步
而 OpenAI 官方文档中的 WebSocket,承载的是:
- 客户端与
/v1/responses之间的模型交互协议 response.create请求response.output_text.delta这类流式事件- 基于
previous_response_id的低延迟 continuation
所以,最准确的理解方式是:
- Codex app-server WebSocket:上层 agent runtime 的远程控制协议
- Responses WebSocket:底层模型 API 的长连接 continuation 协议
两者方向一致,但分工完全不同。
第一层:WebSocket 协议本身在做什么?
在讲 OpenAI 的应用层协议之前,先把 WebSocket 自身讲清楚。
1. 它不是一个独立于 HTTP 的全新握手协议
WebSocket 的建立过程大致分为四步:
- 建立 TCP 连接
- 如果是
wss://,再建立 TLS 会话 - 发起一次 HTTP Upgrade 请求
- 服务端返回
101 Switching Protocols,连接升级为 WebSocket
典型握手请求大致如下:
http
GET /v1/responses HTTP/1.1
Host: api.openai.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: <random_base64>
Authorization: Bearer <OPENAI_API_KEY>
服务端接受升级后会返回:
http
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: <server_hash>
其中 Sec-WebSocket-Accept 是服务端根据客户端的 Sec-WebSocket-Key 加上固定 magic GUID 计算出来的摘要值,用来证明这不是普通 HTTP 响应,而是真正的 WebSocket 升级响应。
2. 建立成功后,通信不再是 HTTP 请求/响应,而是帧(frame)
连接升级后,双方发送的就不是 HTTP body 了,而是 WebSocket 帧。
常见 opcode 如下:
| Opcode | 含义 |
|---|---|
0x1 |
文本帧(text) |
0x2 |
二进制帧(binary) |
0x8 |
关闭连接(close) |
0x9 |
Ping |
0xA |
Pong |
OpenAI 的 Responses WebSocket 在实际使用中,一条业务消息通常就是一个 JSON 文本消息,也就是承载在 text frame 里。
3. WebSocket 的几个关键特征
全双工
客户端和服务端都可以主动发送消息,而不需要等待对方先发起请求。
长连接
建立一次连接后,可以连续发送很多轮业务事件,不必每一轮都重新发起 HTTP 请求。
心跳与保活
协议内建 ping/pong 机制,适合长任务、长连接场景下做连接探活。
显式关闭
通信结束时,可以通过 close frame 做比较干净的关闭流程,而不是粗暴断开 TCP。
4. 一个容易被忽略的细节:客户端帧要 mask
按照 WebSocket 标准:
- 客户端发给服务端的帧必须带 masking
- 服务端发给客户端的帧通常不 mask
这件事通常由 WebSocket 库自动处理,但从协议层理解它很重要:
WebSocket 不是"纯裸 TCP JSON 流",它本身是有严格帧格式和控制语义的标准协议。
第二层:OpenAI Responses WebSocket 的应用层协议
理解了 WebSocket 本体之后,再来看 OpenAI 在这条连接上定义的应用层语义。
1. 连接地址与鉴权
官方文档给出的接入点是:
text
wss://api.openai.com/v1/responses
鉴权方式仍然是标准 Bearer Token:
text
Authorization: Bearer <OPENAI_API_KEY>
2. 客户端不是发 HTTP POST,而是在 socket 上发送事件
WebSocket mode 下,客户端通过发送 response.create 事件来发起一次 Responses 请求。
一个最小示例大致如下:
json
{
"type": "response.create",
"model": "gpt-5.4",
"store": false,
"input": [
{
"type": "message",
"role": "user",
"content": [
{
"type": "input_text",
"text": "Find fizz_buzz()"
}
]
}
],
"tools": []
}
它和普通 POST /responses 的请求体非常接近,但有一个关键差异:
- WebSocket mode 不使用
stream - WebSocket mode 不使用
background
因为流式能力本身已经由 WebSocket 连接承载了。
3. 服务端返回的是一串 Responses streaming events
OpenAI 明确说明:WebSocket mode 的服务端事件,与现有 Responses streaming event model 保持一致。
也就是说,WebSocket 只替换了传输方式,没有重新发明一套新的流式事件语义。
最常见的一组事件包括:
response.createdresponse.in_progressresponse.output_item.addedresponse.content_part.addedresponse.output_text.deltaresponse.output_text.doneresponse.output_item.doneresponse.completederror
如果是工具调用场景,还会看到类似事件:
response.function_call_arguments.deltaresponse.function_call_arguments.doneresponse.mcp_call_arguments.deltaresponse.mcp_call_arguments.doneresponse.file_search_call.in_progressresponse.web_search_call.searching
4. 为什么这些事件很重要?
因为 Responses API 从一开始就不是"只产出一段文本"的接口,它的输出是一个 output item 流,里面可能包含:
- assistant message
- function call
- mcp call
- shell call
- reasoning summary
- refusal
- file search / web search 等 hosted tool 输出
所以更准确的说法不是"模型在输出 token",而是:
模型在一个统一的事件流里,逐步产出文本、工具调用、工具结果状态与最终完成信号。
5. sequence_number 的意义
每个服务端事件都带有 sequence_number。
这意味着:
- 客户端可以按顺序消费事件
- 即使前端内部有异步 UI 渲染,也能根据序号恢复正确顺序
- 对调试和事件回放很有帮助
这也是 Responses WebSocket 比"手搓文本流协议"更工程化的地方。
一个完整的 Responses WebSocket 工作流长什么样?
1. 普通单轮生成
sequenceDiagram participant Client participant OpenAI Client->>OpenAI: WebSocket connect (wss://api.openai.com/v1/responses) Client->>OpenAI: response.create OpenAI-->>Client: response.created OpenAI-->>Client: response.in_progress OpenAI-->>Client: response.output_item.added OpenAI-->>Client: response.content_part.added OpenAI-->>Client: response.output_text.delta OpenAI-->>Client: response.output_text.done OpenAI-->>Client: response.output_item.done OpenAI-->>Client: response.completed
2. 带工具调用的 Agent 回路
真正能体现 WebSocket 价值的,是下面这种多轮工具回路:
sequenceDiagram participant Client participant OpenAI Client->>OpenAI: response.create(input, tools) OpenAI-->>Client: response.created / in_progress OpenAI-->>Client: function_call_arguments.delta OpenAI-->>Client: function_call_arguments.done Client->>Client: 本地执行工具 Client->>OpenAI: response.create(previous_response_id, function_call_output) OpenAI-->>Client: response.in_progress OpenAI-->>Client: response.output_text.delta OpenAI-->>Client: response.completed
这里最关键的一点是:
第二轮 continuation 并不是把整段历史重发一遍。
你只需要发送:
previous_response_id- 新增的
inputitems
例如:
json
{
"type": "response.create",
"model": "gpt-5.4",
"store": false,
"previous_response_id": "resp_123",
"input": [
{
"type": "function_call_output",
"call_id": "call_123",
"output": "tool result"
},
{
"type": "message",
"role": "user",
"content": [
{
"type": "input_text",
"text": "Now optimize it."
}
]
}
],
"tools": []
}
为什么它比 HTTP + SSE 更适合 Agent?
如果只是单轮问答,HTTP + SSE 已经足够好用。但 Agent 工作流和单轮问答完全不是一个问题。
HTTP + SSE 的优势
- 接入简单
- 浏览器和后端生态成熟
- 单次流式输出体验好
但 Agent 工作流天然更偏向双向、连续、低开销
典型 Agent 回路通常是:
- 模型输出中间解释
- 模型发起工具调用
- 客户端执行工具
- 客户端把工具输出回传给模型
- 模型继续下一轮
- 如有需要继续重复多次
这时问题就来了:
- SSE 擅长的是服务端单向下行流
- Agent 需要的是客户端与服务端双向持续交换事件
- continuation 次数一多,HTTP 请求边界本身就会变成额外延迟来源
所以,WebSocket 更适合 Agent,不是因为它"更先进",而是因为它更符合 Agent 的通信形态。
性能提升到底来自哪里?
OpenAI 官方文档把逻辑写得很清楚,核心可以归结为三层。
1. 长连接减少每轮 continuation 的固定开销
如果使用 HTTP,每一次工具回传都需要重新进入一次请求生命周期:
- 发起 HTTP 请求
- 附带请求头
- 重新过一遍请求分发
- 重新解析 continuation 请求
WebSocket 模式下,连接已经建立完成,直接在活跃连接上继续发送 response.create 即可。
这类开销单看一次不大,但在多轮工具回路中会反复出现。
2. 只发送增量输入,而不是重复发送整段历史
在 WebSocket mode 下,continuation 的方式是:
previous_response_id = 上一轮 response idinput = 仅新增 items
这意味着你不需要在每一轮都重发整段上下文。
直接收益包括:
- 更少的请求序列化成本
- 更小的网络传输体积
- 更少的服务端输入解析成本
3. 活跃连接上存在 connection-local 的最近响应缓存
这是官方文档里最关键的一点之一。
OpenAI 明确说明:
- 在活跃 WebSocket 连接上
- 服务端会把 最近一次 previous response state 保存在 connection-local in-memory cache 中
- 如果你继续的是这一次最近 response,服务端可以直接复用这份内存态上下文
这带来的收益非常直接:
continuation 不必每一轮都从更慢的持久化状态恢复路径出发,而是可以命中连接内的低延迟路径。
官方给出的量化收益:20+ Tool Calls 最多约 40% 提速
这是目前官方最明确的一条公开性能数字:
对于 20 次以上工具调用的 rollout,WebSocket mode 端到端最多可带来约 40% 的速度提升。
这条数字为什么合理?
因为在工具密集场景里,真正占时间的不只是模型推理,还包括:
- continuation 请求建立
- 历史上下文重传
- response state 恢复
- 每轮串行控制流的固定管理成本
当这些固定成本在 20+ 轮中反复出现时,WebSocket 的收益会被明显放大。
哪些场景收益最大?
- coding agent
- orchestration loop
- 多次 function calling
- 多次 shell / MCP / hosted tools 往返
哪些场景收益不会特别夸张?
- 一次性问答
- 单轮短回答
- 没有工具调用
- 没有 continuation
所以,不要把这 40% 简化成"所有请求都更快 40%"。
更准确的说法是:
越像 Agent,收益越大。
首 Token 为什么通常也会更快?
这里需要讲得严谨一点。
可以明确说的部分
首 Token(TTFT,Time To First Token)通常更快,主要不是因为模型本身突然"每秒解码更快",而是因为控制面延迟降低了。
具体来源包括:
- 少了一次新的 HTTP continuation 开销
- 少了大历史 payload 的上传与解析
- 命中连接内最近 response 的内存缓存
- 可以使用
generate: false进行 warmup
generate: false 是什么?
OpenAI 文档明确提到,客户端可以先发送一条:
json
{
"type": "response.create",
"model": "gpt-5.4",
"generate": false,
"tools": [...],
"instructions": "...",
"input": [...]
}
它不会直接返回模型输出,但会:
- 预热 request state
- 返回一个 response ID
- 让后续真正要生成的那一轮可以更快开始
这个机制对即将进入复杂工具回路的请求尤其有价值。
但不能过度宣传的部分
官方没有公开给出 TTFT 的单独提升百分比。
因此,最稳妥的技术表述应该是:
Responses WebSocket 往往能够改善首 Token 延迟,尤其是在 continuation turn、工具密集工作流和 warmup 之后的下一轮生成阶段;但 OpenAI 官方目前公开量化的数据,主要是 20+ tool calls 场景下端到端最多约 40% 的提速,而不是单独的 TTFT 百分比。
重要但容易被误解的一点:更快,不等于更省 Token 费用
previous_response_id 能让 continuation 更轻、更快,但这不意味着计费时只算新增输入。
OpenAI 在 Conversation State 文档里明确说明:
即便使用
previous_response_id,链路中之前的输入 token 在计费上仍然会作为 input tokens 参与统计。
所以要把两个概念分开:
- 传输与调度成本:WebSocket 会优化
- 上下文 token 计费:不会因为你只发送增量 items 就自动变成"只付增量 token"
这是架构设计时必须知道的边界。
工程限制与边界条件
WebSocket mode 很强,但它不是"无限制银弹"。
1. 单连接只有一个 in-flight response
官方明确说明:
- 一个 WebSocket 连接上可以收到多个
response.create - 但它们是 顺序执行 的
- 当前 不支持 multiplexing
这意味着如果你需要并行跑多个独立任务,就应该:
- 建多条 WebSocket 连接
- 而不是把并发期待建立在一条连接上
2. 连接时长限制为 60 分钟
到达上限时,会返回类似错误:
websocket_connection_limit_reached
因此生产环境必须做好:
- reconnect
- continuation recovery
- 长任务分段续跑
3. store=false 与 ZDR 是兼容的
这点很重要,因为它直接决定了合规与性能是否能兼得。
OpenAI 明确写道:
- WebSocket mode 与
store=false兼容 - 也与 Zero Data Retention 兼容
原因在于:最近响应状态只保留在连接本地内存里,不写入磁盘。
4. 但 store=false 更依赖活跃连接
如果你使用:
store=false- 并且连接断了
- 或者引用的
previous_response_id已经不在连接本地缓存里
那么就没有持久化回退路径了,可能会收到:
previous_response_not_found
所以最准确的工程总结是:
store=false + WebSocket可以很快,也更利于隐私与 ZDR,但它更依赖"连接还活着、最近状态仍在缓存里"。
5. 失败会驱逐缓存
官方还提到:
- 如果某一轮 continuation 失败(4xx 或 5xx)
- 服务端会把这次引用的
previous_response_id从连接本地缓存中驱逐
这样做是为了避免后续继续复用一份已经可能不一致的缓存状态。
Compaction 在 WebSocket 模式下怎么理解?
长链路 Agent 不可避免会遇到上下文膨胀,因此 compaction 是配套机制,而不是可选附属功能。
OpenAI 文档把它分成两种情况。
1. 服务端 compaction:context_management
如果你开启服务端 compaction,例如配置 compact_threshold:
- 仍然按正常方式继续发送
response.create - 仍然使用最新的
previous_response_id - 仍然只发送新增 input items
也就是说,对调用方来说,continuation 模式不变。
2. 独立 POST /responses/compact
这个接口返回的是:
- 一个 新的 compacted input window
- 而不是一个新的 response id
所以 compact 完成后,你需要:
- 在 WebSocket 上开启一条新的 response chain
- 把 compacted output 当作新的
input - 此时可以省略
previous_response_id或显式设为null
这说明 WebSocket mode 并不是"上下文无限长,永远不用管",而是和 compaction 机制一起工作。
Codex app-server WebSocket 为什么值得被拿来做对照?
回到 Daniel Vaughan 那篇文章,它的真正价值在于:
它展示了 Agent 产品形态正在如何变化。
从文章可以看到,Codex app-server 正在走向:
- 远程接入
- 轻客户端 / 重服务端
- 可重连
- 可审批
- 可 headless 部署
- 用 WebSocket 做长连接控制通道
这与 Responses WebSocket 的关系并不是"同一个协议",而是:
- 前者解决 agent runtime 的远程控制问题
- 后者解决模型 API 的低延迟 continuation 问题
也正因为这两层同时成立,现代 coding agent 才能真的做到:
- 前端很轻
- 模型与工具回路很长
- 任务中断后可恢复
- 远程服务端持续运行
Routin.ai 已经支持 Responses WebSocket 路径
这一点不需要猜,直接看你提供的 Codex CLI 配置就很清楚:
toml
model = "gpt-5.4"
model_provider = "meteor-ai"
disable_response_storage = true
approval_policy = "never"
sandbox_mode = "danger-full-access"
model_supports_reasoning_summaries = true
rmcp_client = true
model_reasoning_effort = "xhigh"
personality = "pragmatic"
service_tier = "fast"
[model_providers.meteor-ai]
name = "meteor-ai"
base_url = "https://api.routin.ai/v1"
env_key = "OPENAI_API_KEY"
wire_api = "responses"
supports_websockets = true
requires_openai_auth = true
[features]
unified_exec = true
shell_snapshot = true
steer = true
skills = true
powershell_utf8 = true
collaboration_modes = true
fast_mode = true
multi_agent = true
responses_websockets_v2 = true
这段配置至少说明了四件事:
1. wire_api = "responses"
说明它走的是 Responses 协议栈,而不是旧的 Chat Completions 兼容层。
2. supports_websockets = true
说明这个 provider 支持 Responses over WebSocket。
3. base_url = "https://api.routin.ai/v1"
说明 Routin.ai 暴露的是 OpenAI-compatible API 基址。
4. responses_websockets_v2 = true
这是 Codex CLI 侧的功能开关,说明当前这条链路已经启用了它所期待的新版 Responses WebSocket 集成能力。
需要注意的是:
responses_websockets_v2 = true是客户端侧能力开关,不是 OpenAI 官方 API 路径里真的有一个/v2/responses/websocket这样的版本号。
因此,最稳妥、也最技术化的表述是:
从实际可运行的 Codex CLI provider 配置来看,Routin.ai 已经把 OpenAI-compatible 的 Responses WebSocket 工作流完整打通,并且能够直接承载 Codex CLI 所需的 Responses WebSocket 路径。
最后总结
如果把本文压缩成几句话,结论如下:
- WebSocket 是传输协议,Responses events 是应用层协议。
- OpenAI Responses WebSocket 复用了现有 Responses 的事件模型,只是把请求/续跑方式改成了长连接事件流。
- 它最大的价值不在于"流式文本更丝滑",而在于 Agent 多轮工具回路的 continuation 更轻、更快。
- 官方公开的量化收益是:20+ tool calls 的 rollout 最多约 40% 端到端提速。
- 首 Token 通常也会更快 ,但官方没有公开 TTFT 百分比;可确认的原因包括连接复用、增量输入、最近响应缓存与
generate: falsewarmup。 - Daniel Vaughan 文章中的 Codex app-server WebSocket 很值得参考,但它讨论的是 remote agent runtime 控制协议,不是 OpenAI 的 Responses WebSocket 协议本身。
- 从实际配置可见,Routin.ai 已经支持 OpenAI-compatible 的 Responses WebSocket 工作流。
如果要用一句最准确的话为全文收尾,那就是:
Responses WebSocket 的真正价值,不是"把流式输出搬到 WebSocket 上",而是"把 Agent continuation 的固定成本压低到足够小"。