语音 Agent 和文本 Agent 最大的区别不是"多了一个语音通道",而是延迟容忍度完全不同。文本对话里 2 秒的等待用户觉得正常,语音对话里超过 500ms 的沉默就会让人怀疑对方是不是掉线了。Amazon Nova Sonic、Bedrock AgentCore 和 Strands BidiAgent 这套组合,本质上是在解决一个工程问题:怎么在保持低延迟的同时,让语音 Agent 能做复杂的事。
这篇文章拆解三种主流语音 Agent 架构模式,分析各自的延迟瓶颈和适用场景,并给出可直接改造的代码示例。
语音 Agent 的延迟从哪来
一个完整的语音交互链路大致是这样:
用户语音 → ASR(语音转文本) → LLM推理 → TTS(文本转语音) → 播放
每一步都在往延迟里加码。ASR 需要等用户说完一句话才能开始识别(流式 ASR 可以缩短但仍有延迟),LLM 掤出第一个 token 需要几百毫秒到几秒,TTS 生成音频片段也有处理时间。如果 Agent 还要调用外部工具(查订单、查库存),中间再加一次 HTTP 往返,总延迟轻松超过 3 秒。
所以语音 Agent 的架构设计核心是:把必须串行的步骤尽量并行化,把可以提前做的事提前做。
三种架构模式对比
模式一:单 Agent 全包
一个 Agent 处理所有对话逻辑、工具调用和状态管理。
用户语音 → Nova Sonic(ASR+TTS) → 单个LLM Agent → 工具调用 → 回复
优点:实现简单,状态集中管理,上下文不会丢失。缺点:Agent 越复杂,LLM 的推理时间越长;工具调用失败会阻塞整个回复;无法针对不同任务做延迟优化。
适合场景:对话逻辑简单、工具调用少、用户量不大的内部系统。
模式二:多 Agent 协作
把不同职能拆给多个 Agent——一个负责对话路由(Orchestrator),其他负责具体任务(查订单 Agent、退款 Agent 等)。
用户语音 → Nova Sonic → Orchestrator Agent → 分派给专项Agent → 结果汇总 → TTS回复
优点:每个专项 Agent 的 prompt 更短,推理更快;可以独立迭代和部署;故障隔离更好。缺点:Orchestrator 到专项 Agent 之间有一次额外调用;上下文传递需要设计;调试链路更长。
关键优化点:Orchestrator 不要等专项 Agent 完全返回再开始 TTS,而是拿到关键信息就开始流式生成回复。
模式三:会话分段(Session Segmentation)
把一次长对话拆成多个短会话段,每段有独立的上下文窗口和状态。Nova Sonic 本身支持会话分段机制,配合 Bedrock AgentCore 的会话管理能力,可以在段之间传递摘要而非完整历史。
长对话 → [段1: 开场+意图识别] → [段2: 执行任务] → [段3: 确认+结束]
每段独立上下文,段间传递摘要
优点:每段的 LLM 输入更短,首 token 延迟更低;可以针对不同段选不同模型(意图识别用快模型,复杂推理用强模型);长对话不会因为上下文溢出而退化。缺点:段间摘要可能丢失细节;需要额外的状态管理逻辑。
这是三种模式里对延迟优化最激进的一种,也是生产环境大规模部署时最值得考虑的。
实战:用 Strands BidiAgent 搭一个多 Agent 语音服务
下面是一个基于 Strands SDK 和 Bedrock AgentCore 的多 Agent 语音服务示例。核心思路:用 BidiAgent 处理双向音频流,Orchestrator 做意图路由,专项 Agent 处理具体任务。
# voice_agent_service.py
# 运行前:pip install strands-agents boto3
# 需要 AWS 凭证配置好,且 Bedrock 区域支持 Nova Sonic
from strands.agent import Agent
from strands.handlers.audio_handler import AudioHandler
from strands.multiagent import Orchestrator, SpecialistAgent
from strands.session import SessionManager
import boto3
import json
# --- 1. 定义专项 Agent ---
order_agent = SpecialistAgent(
name="order_lookup",
model_id="us.amazon.nova-micro-v1:0", # 用快模型做简单查询
description="查询用户订单状态、物流信息",
tools=[{
"name": "lookup_order",
"description": "根据订单号查询订单状态",
"parameters": {
"order_id": {"type": "string", "description": "订单号"}
},
# 实际项目中这里接你的订单 API
"handler": lambda params: {
"order_id": params["order_id"],
"status": "已发货",
"estimated_delivery": "2025-01-20"
}
}]
)
refund_agent = SpecialistAgent(
name="refund_processor",
model_id="us.amazon.nova-micro-v1:0",
description="处理退款申请,查询退款政策",
tools=[{
"name": "check_refund_policy",
"description": "查询商品退款政策",
"parameters": {
"product_category": {"type": "string", "description": "商品类别"}
},
"handler": lambda params: {
"category": params["product_category"],
"policy": "7天无理由退货",
"refund_time": "3-5个工作日"
}
}]
)
# --- 2. Orchestrator 路由逻辑 ---
orchestrator = Orchestrator(
name="voice_router",
model_id="us.amazon.nova-pro-v1:0", # 路由需要更强的理解能力
specialists=[order_agent, refund_agent],
# 路由 prompt:快速判断意图,不要做多余推理
system_prompt="""你是语音客服路由器。用户说的话会被实时转文本。
你的唯一任务是判断用户意图并分派给专项Agent。
可选Agent:
- order_lookup: 查订单、物流
- refund_processor: 退款、退货政策
只输出JSON: {"agent": "agent_name", "params": {...}}
不要解释,不要闲聊,直接输出JSON。"""
)
# --- 3. 会话分段管理 ---
session_mgr = SessionManager(
max_segment_turns=5, # 每5轮对话切一段
summary_model="us.amazon.nova-micro-v1:0", # 段间摘要用快模型
context_window_tokens=4000 # 每段上下文限制
)
# --- 4. 启动双向音频流服务 ---
def handle_voice_call(audio_stream_in, audio_stream_out):
"""处理一次语音通话的双向流"""
audio_handler = AudioHandler(
model_id="us.amazon.nova-sonic-v1:0", # Nova Sonic 处理ASR+TTS
input_stream=audio_stream_in,
output_stream=audio_stream_out
)
session = session_mgr.create_session()
for transcript_chunk in audio_handler.transcribe_stream():
# 流式拿到用户文本片段
session.add_user_input(transcript_chunk.text)
# 检查是否需要切段
if session.should_segment():
summary = session.summarize_current_segment()
session.start_new_segment(summary)
# 等用户说完一句话再路由(避免半句触发Agent)
if transcript_chunk.is_complete:
# 路由到专项Agent
route_result = orchestrator.route(transcript_chunk.text)
if route_result["agent"] == "none":
# 不需要工具,直接让 Nova Sonic 生成回复
response_text = audio_handler.generate_response(
transcript_chunk.text,
session.get_context()
)
else:
# 调用专项Agent获取结果
specialist = orchestrator.get_specialist(route_result["agent"])
result = specialist.execute(route_result["params"])
# 把结果喂给 Nova Sonic 生成语音回复
response_text = audio_handler.generate_response(
f"根据查询结果:{json.dumps(result)},请用自然语言回复用户",
session.get_context()
)
session.add_agent_response(response_text)
# --- 5. 本地测试入口 ---
if __name__ == "__main__":
# 生产环境用 WebSocket 或 gRPC 接真实音频流
# 这里用文件模拟测试
print("语音Agent服务已配置完成")
print(f"专项Agents: {order_agent.name}, {refund_agent.name}")
print(f"Orchestrator: {orchestrator.name}")
print(f"会话分段: 每{session_mgr.max_segment_turns}轮切一段")
# 模拟一次路由
test_route = orchestrator.route("我的订单123456发货了吗")
print(f"路由结果: {json.dumps(test_route, ensure_ascii=False)}")
运行前需要改的地方:
- AWS 凭证:确保
boto3能访问 Bedrock,Region 选us-east-1。 - 工具 handler:示例里的
lookup_order和check_refund_policy是 mock 函数,替换成你自己的 API 调用。 - 模型 ID:根据你 Bedrock 的可用模型调整,
nova-micro延迟最低适合简单任务,nova-pro理解力更强适合路由。 - 音频流:生产环境需要接 WebSocket/电话系统的真实音频流,本地测试可以用录音文件。
延迟优化的几个实操细节
流式 TTS 不要等完整文本。Nova Sonic 支持流式音频输出——LLM 掤出前几个 token 就可以开始转语音,不需要等整段回复生成完。在 BidiAgent 的配置里确保 stream_tts=True。
Orchestrator 的 prompt 要极短。路由 Agent 的 system prompt 越短,首 token 越快。上面示例里要求它只输出 JSON、不解释,就是为了把推理时间压到最低。实测一个 50 词的 system prompt 比 500 词的快 200-400ms。
会话分段时摘要要精简。段间传递的摘要不是完整对话记录,而是"用户身份+当前意图+关键数据"的结构化摘要。用 nova-micro 生成摘要比用 nova-pro 快一倍,摘要质量对后续段的影响远小于完整上下文溢出的影响。
工具调用做超时熔断。语音场景里工具调用超过 2 秒就应该熔断,先给用户一个"正在查询,请稍等"的语音反馈,而不是让用户在沉默中等待。示例里可以加:
import asyncio
async def call_tool_with_timeout(tool_fn, params, timeout_sec=2.0):
try:
result = await asyncio.wait_for(tool_fn(params), timeout=timeout_sec)
return result
except asyncio.TimeoutError:
return {"error": "timeout", "message": "查询超时,请稍后再试"}
选哪种模式:决策清单
| 条件 | 推荐模式 |
|---|---|
| 工具调用 ≤ 2 个,对话 ≤ 10 轮 | 单 Agent |
| 工具调用 3+ 个,不同任务延迟要求不同 | 多 Agent |
| 对话经常超过 20 轮,或需要长时间挂起 | 会话分段 + 多 Agent |
| 用户量 > 1000 并发通话 | 会话分段(降低单次推理成本) |
实际生产中,多 Agent + 会话分段是最常见的组合。Orchestrator 做路由,专项 Agent 处理任务,会话分段控制上下文长度——三者配合才能在复杂业务和低延迟之间找到平衡点。
语音 Agent 的工程难点不在"让模型说话",而在"让模型在 500ms 内开始说话,同时还能查订单、算退款、记住上下文"。Nova Sonic 解决了 ASR/TTS 的流式问题,Bedrock AgentCore 解决了会话和工具管理的基础设施,Strands BidiAgent 解决了双向流的编排——但架构选择和延迟优化,仍然需要你自己根据业务场景做决策。