用 Amazon Nova Sonic 构建可扩展语音 Agent:多 Agent、工具调用与会话分段实战

2026-05-19 28 预计阅读时间:1 分钟
来源:aws.amazon.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:13 分钟

语音 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)}")

运行前需要改的地方:

  1. AWS 凭证:确保 boto3 能访问 Bedrock,Region 选 us-east-1
  2. 工具 handler:示例里的 lookup_ordercheck_refund_policy 是 mock 函数,替换成你自己的 API 调用。
  3. 模型 ID:根据你 Bedrock 的可用模型调整,nova-micro 延迟最低适合简单任务,nova-pro 理解力更强适合路由。
  4. 音频流:生产环境需要接 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 解决了双向流的编排——但架构选择和延迟优化,仍然需要你自己根据业务场景做决策。


相关推荐