Realtime Agent 与 Voice 管线
DEV-06: Realtime Agent 与 Voice 管线
学习目标
- 理解
RealtimeAgent与Agent的基类关系 - 掌握
gpt-realtime-2模型 + WebSocket 传输 - 理解 Voice Pipeline(STT → LLM → TTS)
- 掌握语音相关的专用 tracing span
前置知识
本章涉及语音接口的通用原理,建议先阅读:
下文直接聚焦 OpenAI Agents SDK 的具体实现。
项目实践
RealtimeAgent vs Agent
RealtimeAgent 与 Agent 共享 AgentBase,但有不同的 hook 类型和工具使用行为。这反映了实时语音 Agent 与文本 Agent 的根本差异——语音流是连续的,而非 turn-based。
WebSocket 传输
responses_websocket_session() 是 async context manager:
async with responses_websocket_session() as session: result = await session.run(agent, "用户语音输入")内部机制:
- 创建
MultiProvider,设置openai_use_responses_websocket=True - 包装在
ResponsesWebSocketSession中,固定共享RunConfig和OpenAIProvider - Context 退出时调用
aclose()清理 WebSocket 连接
Voice Pipeline
专用 span 类型:
| Span Data | 捕获内容 |
|---|---|
TranscriptionSpanData | 音频输入路径、转写文本、STT 模型 |
SpeechSpanData | 输入文本、音频输出、TTS 模型、音频格式 |
SpeechGroupSpanData | 将多个 Speech spans 分组 |
自定义 Voice 对象(v0.17 新增)
v0.17.4 支持自定义 voice 对象,允许开发者自定义 TTS 输出的声音特征(音色、语调等)。
问题与规避
| 问题 | 规避方案 |
|---|---|
| WebSocket 连接在长会话中断开 | responses_websocket_session() 的 context manager 确保退出时清理,自动重连 |
| Voice Pipeline tracing 数据量过大 | SpeechGroupSpanData 分组多个 speech spans,减少导出条目 |
gpt-realtime-2 模型不可用 | 回退到文本 Agent + 外部 STT/TTS 服务 |
设计取舍
为什么 RealtimeAgent 与 Agent 共享基类而非继承
两者共享 AgentBase(name、handoff_description、tools、mcp_servers),而非让 RealtimeAgent 继承 Agent。这样做:(1) 避免 Agent 的文本专属字段(instructions、handoffs、output_type)污染 RealtimeAgent;(2) 允许各自的 hook 系统独立演进(RunHooks vs RealtimeHooks);(3) 类型系统更清晰——不会意外将 RealtimeAgent 传给 Runner.run()。
为什么 Voice Pipeline 有专用 tracing span
通用 FunctionSpanData 无法捕获音频输入/输出的结构化数据(音频格式、采样率、转写置信度)。专用 span 类型使得 Voice Pipeline 的 tracing 数据可用于专门的语音质量分析。