雨天小六

读懂 Codex(5.26):Realtime WebSocket 与普通 Responses 的差异

· 更新于 2026-08-02 · 专栏:读懂 Codex

#Codex#Agent Runtime#Responses API#流式协议#软件架构

Realtime 和 Responses 都能看到 WebSocket 与 response.create,但不能共用同一状态机。Responses 服务一次离散采样和工具循环;Realtime 管理长连接音频、转录、VAD 打断、会话更新与 Codex handoff,是独立的对话子系统。

具体问题与启用条件

本节只比较 Runtime 边界,不展开实时音频产品的完整实现;这与项目非重点范围一致。

条件来源决定字段或状态对本机制的影响
协议 OpRealtimeConversationStart/Audio/Text/Speech/Close进入 RealtimeConversationManager
版本/适配器V1、FramelessBidi、RealtimeV2选择不同 wire message 与 parser
模式Conversational/Transcription + output modality决定音频、转录和 response.create 行为

协议、类型与状态所有权

状态或协议所有者生命周期关键不变量
RealtimeConversationManager.stateSession service一次长会话新 start 会先停旧会话
audio/text/handoff channelsRealtime input task长连接期间并发输入在单任务中串行发送
RealtimeEventRealtime parser/fanout媒体/转录事件不是普通 ResponseEvent
ResponseCreateQueueRealtime input taskV2 活动 response 期间避免并发默认 response.create
handoff stateRealtime managerRealtime↔Codex 协作按 handoff_id 过滤迟到更新

机制调用链如下:

RealtimeConversationStart
→ 选择 wire adapter/session mode/voice/modality
→ 建立 Realtime WS 或 WebRTC+sideband
→ session.update + initial items
→ audio/text/handoff channels 与 server events select
→ transcript/audio/response/handoff 事件
→ VAD 可 truncate 输出音频
→ Close/stop token
→ flush transcript tail/通知 Closed
Realtime 长会话与普通 Codex Responses 通过 handoff 连接
图 5.26-1:媒体事件留在 Realtime 子系统,Agent 工作通过显式 handoff 交换。

机制怎样工作

普通 Responses 每次请求发送完整逻辑 Prompt,产出 ResponseItem、Done 与 Completed,随后可能进入工具执行和下一次采样。Realtime 的 SessionConfig 则包含 instructions、initial conversation items、model、session mode、output modality 和 voice;出站消息包括 audio append、conversation item create、session update/close、response.create 与 handoff append。入站事件是 SessionUpdated、输入/输出转录 Delta/Done、AudioOut、speech started、response created/cancelled/done、handoff/noop/error等。

Realtime V2 的 RealtimeResponseCreateQueue 确保默认响应活动时,新 create 先置 pending,等 ResponseDone/Cancelled 后再发,避免服务端 active-response 竞态。用户讲话时可根据最后输出音频位置发送 conversation.item.truncate,这是普通文字 Responses 没有的媒体时间轴语义。Coding Agent 能通过 handoff 与 Realtime 协作:Realtime 请求后台 Codex 工作,Codex 的 commentary/final 文本再作为 conversation item 或 handoff append 回送;这不是把普通 ResponseEvent 直接塞进 Realtime parser。

Python 风格伪代码

这段伪代码保留生产实现中会改变结果的状态、分支和异步边界;认证 SDK、遥测字段和 Rust 所有权样板被折叠为明确的领域对象。

class RealtimeConversation:
    async def run(self):
        await self.writer.session_update(self.config)
        for item in self.config.initial_items:
            await self.writer.conversation_item_create(item)
        while not self.stop.is_set():
            source, value = await select(
                audio=self.audio_queue.get(),
                text=self.text_queue.get(),
                handoff=self.handoff_queue.get(),
                server=self.events.next(),
                stop=self.stop.wait(),
            )
            if source == "audio":
                await self.writer.audio_append(value)
            elif source == "text":
                await self.writer.conversation_item_create(value)
            elif source == "handoff":
                await self.route_codex_output(value)
            elif source == "server":
                await self.handle_realtime_event(value)
        await self.writer.session_close()

class ResponseCreateQueue:
    async def request(self):
        if self.active:
            self.pending = True
        else:
            await writer.response_create(); self.active = True

失败、取消与恢复

普通 Responses 离散采样与 Realtime 长会话协议对比
图 5.26-2:相同的 WebSocket 术语不意味着相同的完成边界、事件类型或状态所有者。
故障或边界已发生的状态对上层的结果能否直接重试恢复动作
再次 Start旧 Realtime 会话仍可能运行先 stop/await 旧 state不适用再安装新 state
active response 时重复 create服务端可能拒绝本地设 pending延后一次Done/Cancelled 后发送
迟到 handoff updateactive handoff ID 已变化丢弃 stale 更新等待当前 ID
Realtime stream 关闭/ApiError长会话结束RealtimeEvent::Error + Closed由 UI 重启stop tasks/flush tail
用户打断输出音频已有播放时间位置发送 truncate不适用清 output audio state

设计取舍与验证

独立子系统避免把媒体、VAD 和 handoff 状态强塞进 ResponseItem Agent Loop;代价是认证、WebSocket 和错误映射有部分相似代码。多 wire adapter 支持协议演进,却要求每种出站消息和入站 event 都做兼容测试。

可验证契约证据方式预期结果
ResponseCreate 活动时只排一个 pendingqueue 单元/集成场景完成后恰好再发一次
speech started 截断当前音频Realtime V2 fixturetruncate item_id/audio_end_ms 正确
stale handoff update 被丢弃切换 handoff ID 后发送旧更新wire 上无旧内容

Mini Codex 对照

Mini Codex 不应在第一版实现 Realtime。若需要语音外壳,把 Realtime 作为独立适配器,通过明确 handoff 接口调用普通 Agent Runtime;不要让音频帧进入对话 History 的 ResponseItem 列表。

本节边界

第五章到此完成:普通 Responses 的目录、请求、传输、事件、恢复和取消已经闭合;Realtime 只建立系统边界。下一章将沿已完成 ToolCall Item 继续追踪本地工具如何验证、审批和产生副作用。

评论


← 返回文章列表