首页
/ openai-agents-python 语音管道深度解析:VoicePipeline 如何将 Agent 工作流变为语音应用

openai-agents-python 语音管道深度解析:VoicePipeline 如何将 Agent 工作流变为语音应用

2026-09-09 17:09:13作者:平淮齐Percy

openai-agents-python 中,VoicePipeline(位于 src/agents/voice/pipeline.py)是把 Agent 型工作流转换为语音应用的核心入口:你只需传入一段要执行的工作流,管道就会自动完成输入语音的转录(STT)、在合适的时机调用你的工作流、并把工作流产出的文本重新合成为语音(TTS)。读完本文,你将掌握管道三大配置项(workflow / STT 与 TTS 模型 / config)的完整参数、两种音频输入方式(AudioInputStreamedAudioInput)的适用场景、StreamedAudioResult 事件流的处理与错误语义,以及基于生命周期事件实现“打断/静音”等最佳实践。

语音管道的三步架构

VoicePipeline 的类文档把它描述为一个“有观点(opinionated)”的三步流程,源码注释与 docs/voice/pipeline.md 的架构图完全一致:

  1. 将音频输入转录为文本(speech-to-text);
  2. 运行你提供的 workflow,产生一段文本响应序列;
  3. 将文本响应转换为流式音频输出。
graph LR
    %% Input
    A["🎤 Audio Input"]

    %% Voice Pipeline
    subgraph Voice_Pipeline [Voice Pipeline]
        direction TB
        B["Transcribe (speech-to-text)"]
        C["Your Code"]:::highlight
        D["Text-to-speech"]
        B --> C --> D
    end

    %% Output
    E["🎧 Audio Output"]

    %% Flow
    A --> Voice_Pipeline
    Voice_Pipeline --> E

    %% Custom styling
    classDef highlight fill:#ffcc66,stroke:#333,stroke-width:1px,font-weight:700;

其中高亮的 “Your Code” 就是工作流(workflow),它是整个管道中唯一由开发者掌控的环节。官方文档 docs/voice/pipeline.mddocs/voice/quickstart.md 对这三步有相同表述,可结合快速上手文档中的完整可运行示例(麦克风 I/O 使用第三方库 sounddevice,通过 pip install 'openai-agents[voice]' 安装可选语音依赖)一起理解。

管道配置:workflow、模型与 VoicePipelineConfig

创建 VoicePipeline 时(见 src/agents/voice/pipeline.py)可设置三类内容,全部为关键字参数:

  1. workflow(必填):类型为 VoiceWorkflowBase。它是“每次新音频被转录出来时运行的代码”。VoiceWorkflowBase 是抽象基类,核心契约只有一个抽象方法 run(transcription: str) -> AsyncIterator[str]:接收一段转写文本,产出一段段将被 TTS 朗读的文本。大多数场景下你可以直接使用现成的 SingleAgentVoiceWorkflow(见下文),或者继承 VoiceWorkflowBase 编写多 Agent、自定义消息历史等复杂逻辑。
  2. stt_model / tts_model:语音转文本与文本转语音模型,类型是 STTModel | str | NoneTTSModel | str | None。传字符串时,管道在首次需要模型时通过 config.model_provider 按名称解析(见 pipeline.py 中的 _get_tts_model / _get_stt_model 惰性解析逻辑);不传时使用 OpenAI 默认模型——从 openai_model_provider.py 可确认默认值为 gpt-4o-transcribe(STT)和 gpt-4o-mini-tts(TTS)。
  3. configVoicePipelineConfig | dict[str, Any] | None。注意源码会用 coerce_dataclass_config 允许直接传字典,缺省时构造默认配置。pipeline_config.py 中定义的完整字段如下:
字段 类型 / 默认值 说明
model_provider VoiceModelProvider,默认 OpenAIVoiceModelProvider() 模型提供者,负责把模型名映射为 STT/TTS 模型实例
tracing_disabled bool,默认 False 是否禁用管道的追踪(tracing)
tracing TracingConfig | None,默认 None 本管道的追踪配置
trace_include_sensitive_data bool,默认 True 追踪中是否包含敏感数据(仅针对管道本身,不含工作流内部)
trace_include_sensitive_audio_data bool,默认 True 追踪中是否上传音频数据(对应“是否上传音频文件”)
workflow_name str,默认 "Voice Agent" 追踪中使用的名称
group_id str,默认随机生成 用于把同一会话/进程的多个 trace 关联起来的分组 ID
trace_metadata dict[str, Any] | None 附加到 trace 的元数据
stt_settings STTModelSettings STT 模型设置
tts_settings TTSModelSettings TTS 模型设置

其中 stt_settings / tts_settings 两个子配置同样支持传字典(__post_init__ 中做了类型强制转换)。从 model.py 可以看到其字段:

  • STTModelSettingsprompt(给模型的指令)、language(音频语言)、temperatureturn_detection(流式输入时的“轮次/活动检测”设置,见下文)、languagesgpt-transcribe/gpt-live-transcribe 支持的多语言候选,优先于 language)、keywords(引导转写的关键词)。
  • TTSModelSettingsvoice(内置音色如 alloynova 等或自定义音色 ID)、buffer_size(流式输出的最小音频块数,默认 120)、dtype(输出音频的 numpy 数据类型,默认 np.int16,也可为 np.float32)、transform_data(对音频数组的自定义变换函数)、instructions(控制语气的 TTS 指令,默认提示“你收到的是不完整句子,不要补全,照读即可”)、text_splitter(把累积文本切句的函数,默认基于句子边界切分,避免等整段文本才送 TTS)、speed(0.25~4.0)。

运行管道:两种音频输入形式

调用 await pipeline.run(audio_input) 即可运行(pipeline.py)。run() 内部按输入类型分派:AudioInput_run_single_turnStreamedAudioInput_run_multi_turn;传入其他类型会抛出 UserError

AudioInput:完整音频,单次结果

AudioInput 用于“已经拥有完整音频输入、只需产出一个结果”的场景——无需检测说话者何时结束,例如预录音频,或用户按完按钮即表示说尽的“按住说话(push-to-talk)”应用。它是一个 dataclass:

  • buffer:numpy 数组,必须为 int16float32
  • frame_rate:采样率,默认 24000(模块级常量 DEFAULT_SAMPLE_RATE);
  • sample_width:默认 2 字节;channels:默认 1 声道。

它还提供 to_audio_file()(内部把 PCM 数据封装为 WAV 字节流,供 STT 上传)与 to_base64() 方法。quickstart 文档中的最小用法就是:

import numpy as np
from agents.voice import AudioInput

# 实际中来自麦克风;示例用 3 秒静音
buffer = np.zeros(24000 * 3, dtype=np.int16)
audio_input = AudioInput(buffer=buffer)

StreamedAudioInput:流式音频与活动检测

StreamedAudioInput 用于需要检测“用户何时说完了”的场景。它内部是一个 asyncio.Queue,应用侧通过 add_audio(audio) 把检测到的音频块(int16/float32 numpy 数组)依次推入队列;传入 None 表示流结束。

管道会基于“活动检测(activity detection)”在合适的时机自动运行工作流。从源码看,其落地方式是:_run_multi_turn 调用 stt_model.create_session(...) 创建一个 StreamedTranscriptionSession(定义于 model.py),然后 async for input_text in transcription_session.transcribe_turns() 逐轮产出转写文本——每一轮文本都会触发一次 workflow.run(input_text)。检测参数正是上文 STTModelSettings.turn_detection;在 OpenAI 实现中,缺省时会套用 DEFAULT_TURN_DETECTION(见 openai_stt.py),该设置会随 websocket 连接参数下发给转写服务。仓库中的 examples/voice/streamed 提供了基于麦克风的完整流式示例(含 my_workflow.py 自定义工作流)。

从源码结构还可以看到一个细节:多轮流程在开始监听之前会先调用工作流可选的 on_start() 钩子(workflow.py),把开场白文本送进 TTS 输出,并在有产出时立即 _turn_done() 收尾——注释解释了原因:让开场白在启动阶段就结束一个 turn,否则它会一直挂着等待句子终结标点,或错误地与第一个用户 turn 合并。

结果与事件流:StreamedAudioResult 及三类事件

run() 的返回值是 StreamedAudioResult。它是一个“事件随产生即流式推出”的对象,通过 async for event in result.stream() 消费。事件类型定义在 events.py

  1. VoiceStreamEventAudiotype == "voice_stream_event_audio"):包含一块音频数据(data,numpy int16/float32 数组,格式由 tts_settings.dtype 决定),直接写入播放设备即可;
  2. VoiceStreamEventLifecycletype == "voice_stream_event_lifecycle"):生命周期事件,event 字段取值 turn_started / turn_ended / session_ended(源码比文档多列出的 session_ended 表示整个会话结束,见 events.py);
  3. VoiceStreamEventErrortype == "voice_stream_event_error"):错误事件,携带 error 异常对象。

文档给出的标准消费模式:

result = await pipeline.run(input)

async for event in result.stream():
    if event.type == "voice_stream_event_audio":
        # 播放音频
        pass
    elif event.type == "voice_stream_event_lifecycle":
        # 生命周期事件
        pass
    elif event.type == "voice_stream_event_error":
        # 错误
        pass

终态错误的语义(重点)

pipeline 文档对错误传播有明确约定,且与 result.py 的实现一一对应:

  • 管道的终态错误在应用消费 stream() 期间送出/抛出,而不是在 run() 返回时;
  • 如果整体运行正常、但 STT 转写会话最终关闭失败,流不会无限期等待,而是把该 close 错误作为错误事件送出——对应 _run_multi_turnfinally 块对 transcription_session.close()asyncio.shield 包裹与结果检查(pipeline.py);
  • 如果该 turn 本身已失败,而关闭转写会话又失败,则流保留原始 turn 错误作为主要错误primary_exception 机制,close 失败只记日志、不覆盖主错误)。

另外从 result.pystream() 实现可以看到更多工程细节:收到 VoiceStreamEventErrorsession_ended 后停止消费并检查内部任务异常;正常退出时还会等待生产者任务收尾,以便活动中的 trace 上下文能先发出 trace_end 再清理;被取消时 _cancel() 会保证终态事件仍然按序送达。

底层工作原理:从文本到 PCM 的流式链路

结合 result.py 源码,管道内部的音频生产链路可以概括为:

  • _add_text(text):工作流每产出一段文本就累积到 _text_buffer,同时用 tts_settings.text_splitter 按句子边界切出“已完整”的句子;每切出一句就创建一个本地队列 + 一个 TTS 音频任务(_create_audio_task),做到句子一完整就开始合成,而不必等整段回复;
  • _stream_audio(...):每个句子独立调用 tts_model.run(text, settings) 获取 PCM 字节流,按 buffer_size(默认 120 块)攒够后转成 numpy 数组(int16,或按配置转为 float32 并除以 32767),经可选的 transform_data 变换后入队;
  • _dispatch_audio():一个顺序分发器,按句子入队顺序把各段音频块搬运到最终事件队列,保证输出顺序正确;
  • _turn_done():turn 结束时把剩余未切句的尾巴文本作为最后一段送去合成,并触发 turn_ended 生命周期事件;_done() 则标记会话完成并最终发出 session_ended

追踪方面,_run_single_turn / _run_multi_turn 都用 TraceCtxManager 维持整个处理期的 trace 作用域(携带 workflow_namegroup_idtrace_metadata),而 speech_group_span / speech_span 会为每个 turn 与每次 TTS 调用记录输入文本、音色参数、首字节时间(first_content_at)等信息;trace_include_sensitive_data / trace_include_sensitive_audio_data 控制这些敏感内容是否真正写入 span(关闭音频追踪时甚至不会在内存中保留整段 PCM,见 result.py 注释)。

最佳实践:处理打断(Interruptions)

官方文档给出的明确结论是:Agents SDK 目前不为 StreamedAudioInput 提供内置打断处理——每个检测出的 turn 都会独立触发一次工作流运行。若要在应用层实现“用户插话时打断模型播报”,推荐做法是监听 VoiceStreamEventLifecycle 事件:

  • turn_started:表示新一轮已转写完成、开始处理(在 result.py_start_turn() 中发出,每个 turn 仅一次);
  • turn_ended:表示该 turn 关联的所有音频已分发完毕。

典型策略:在收到 turn_started静音扬声器/麦克风(或停止播放上一轮残留音频),在本应用播完该 turn 的全部音频(收到对应 turn_ended 之后)再解除静音。这套事件驱动的机制与 examples/voice/streamed 中的实时麦克风示例可以配合使用,实现自然的对话式打断体验。

小结与延伸

VoicePipeline 的设计哲学是把 STT、活动检测、TTS 的复杂时序封装成“输入音频、消费事件”的极简接口,而把智能逻辑完全留给工作流:

  • 简单单 Agent 场景:VoicePipeline(workflow=SingleAgentVoiceWorkflow(agent))SingleAgentVoiceWorkflow 会自动维护输入历史、跟随 handoff 更新 last_agentworkflow.py),并支持传入 context 与回调;
  • 复杂场景:继承 VoiceWorkflowBase 自行实现 run(),内部可多次调用 Runner.run_streamed(),并用 VoiceWorkflowHelper.stream_text_from(result) 从流事件中提取文本增量;
  • 实战参考:静态音频示例见 examples/voice/static,流式麦克风示例见 examples/voice/streamed,两者均为可直接运行的完整程序;
  • 更多 API 细节可查阅 docs/voice/ 下的 quickstart、tracing 等文档,以及 docs/ref/ 中 voice 模块的自动生成的参考页。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.16 K
2.78 K
kernelkernel
deepin linux kernel
C
34
18
docsdocs
暂无描述
Markdown
904
5.83 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
934
1.86 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
862
1.36 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.96 K
1.03 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.38 K
1.47 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
535
606
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
549
398
leetcodeleetcode
🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解
Markdown
77
23