跳转到内容

Realtime Agent 与 Voice 管线

DEV-06: Realtime Agent 与 Voice 管线

学习目标

  • 理解 RealtimeAgentAgent 的基类关系
  • 掌握 gpt-realtime-2 模型 + WebSocket 传输
  • 理解 Voice Pipeline(STT → LLM → TTS)
  • 掌握语音相关的专用 tracing span

前置知识

本章涉及语音接口的通用原理,建议先阅读:

下文直接聚焦 OpenAI Agents SDK 的具体实现。

项目实践

RealtimeAgent vs Agent

RealtimeAgentAgent 共享 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 中,固定共享 RunConfigOpenAIProvider
  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 数据可用于专门的语音质量分析。

参考来源