Realtime 和 Responses 都能看到 WebSocket 与 response.create,但不能共用同一状态机。Responses 服务一次离散采样和工具循环;Realtime 管理长连接音频、转录、VAD 打断、会话更新与 Codex handoff,是独立的对话子系统。
具体问题与启用条件
本节只比较 Runtime 边界,不展开实时音频产品的完整实现;这与项目非重点范围一致。
| 条件来源 | 决定字段或状态 | 对本机制的影响 |
|---|---|---|
| 协议 Op | RealtimeConversationStart/Audio/Text/Speech/Close | 进入 RealtimeConversationManager |
| 版本/适配器 | V1、FramelessBidi、RealtimeV2 | 选择不同 wire message 与 parser |
| 模式 | Conversational/Transcription + output modality | 决定音频、转录和 response.create 行为 |
协议、类型与状态所有权
| 状态或协议 | 所有者 | 生命周期 | 关键不变量 |
|---|---|---|---|
| RealtimeConversationManager.state | Session service | 一次长会话 | 新 start 会先停旧会话 |
| audio/text/handoff channels | Realtime input task | 长连接期间 | 并发输入在单任务中串行发送 |
| RealtimeEvent | Realtime parser/fanout | 媒体/转录事件 | 不是普通 ResponseEvent |
| ResponseCreateQueue | Realtime input task | V2 活动 response 期间 | 避免并发默认 response.create |
| handoff state | Realtime manager | Realtime↔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
机制怎样工作
普通 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
失败、取消与恢复
| 故障或边界 | 已发生的状态 | 对上层的结果 | 能否直接重试 | 恢复动作 |
|---|---|---|---|---|
| 再次 Start | 旧 Realtime 会话仍可能运行 | 先 stop/await 旧 state | 不适用 | 再安装新 state |
| active response 时重复 create | 服务端可能拒绝 | 本地设 pending | 延后一次 | Done/Cancelled 后发送 |
| 迟到 handoff update | active 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 活动时只排一个 pending | queue 单元/集成场景 | 完成后恰好再发一次 |
| speech started 截断当前音频 | Realtime V2 fixture | truncate 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 继续追踪本地工具如何验证、审批和产生副作用。
评论
登录后即可评论