跳转到内容

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, "用户语音输入")

内部机制:

  1. 创建 MultiProvider,设置 openai_use_responses_websocket=True
  2. 包装在 ResponsesWebSocketSession 中,固定共享 RunConfig 和 OpenAIProvider
  3. 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 数据可用于专门的语音质量分析。

参考来源