AWS · ML 博客

使用 Amazon Bedrock AgentCore 和 Amazon Nova 2 Sonic 构建餐厅电话 AI 接待员

Building a restaurant telephony AI host with Amazon Bedrock AgentCore and Amazon Nova 2 Sonic

二〇二六年七月十六日 · 英文原文

本文介绍了一个基于Amazon Bedrock AgentCore和Amazon Nova 2 Sonic构建的餐厅语音下单系统,该系统可自动接听电话并完成从问候到确认的完整订单流程。系统分为电话层、agent层和后端层,通过Model Context Protocol (MCP)连接。使用AWS CDK部署,包括Amazon Chime SDK Voice Connector处理电话、AWS Fargate上的SIP网关、AgentCore Runtime托管对话逻辑、Amazon DynamoDB存储数据。系统在电话振铃期间预热agent会话,避免静音,并通过电话号码哈希识别来电者。

每家餐厅每月平均漏接150通电话,其中约60%是顾客试图下单或订座。这些电话大多在晚餐时段打来,恰逢领位员安排客人入座、服务员翻台,电话便成了被遗忘的角落。让员工离开岗位去接听并不能解决问题,只会让两边的体验都变差。添加应用或网站能帮助偏好在线下单的顾客,但对只想打电话的人来说毫无帮助。本文将介绍如何构建一个语音下单系统,该系统能接听电话并从问候到确认完成整个订单流程。系统使用 Amazon Bedrock AgentCore 来托管和运行 agent,使用 Amazon Nova 2 Sonic 进行实时语音交互,并通过 Model Context Protocol (MCP) 连接到餐厅后端。本指南涵盖使用 AWS Cloud Development Kit (AWS CDK) 部署完整技术栈,以及通过 Amazon Elastic Container Service (Amazon ECS) 和 AWS Fargate 上的 Session Initiation Protocol (SIP) 网关将电话接入 agent。系统还会在电话振铃期间预热 agent 会话,确保来电者永远不会听到静音。

解决方案概览

该系统分为三层。电话层处理与电话相关的特定问题。音频通过电话网络而非浏览器传入,系统通过电话号码而非登录信息识别来电者。SIP 网关通过签名的 WebSocket 连接将音频流传输到 agent 层,agent 在此与 Amazon Nova 2 Sonic 进行对话。Agent 通过 MCP 工具访问后端层。后端存储菜单、购物车、订单和门店信息。保持各层分离意味着下单逻辑独立于调用它的渠道。新的渠道(如移动应用或自助终端)可以连接到同一个 agent,而无需重写后端。由于 MCP 是将 agent 连接到外部工具的开放标准,后端可以在不触及 agent 的情况下进行更改。Agent 支持文本和音频作为输入和输出,在单个双向流中处理转录、轮流发言和打断。

该解决方案部署以下组件:

架构图

下图展示了解决方案,分为四个部分。餐厅的后端基础设施首先在 A 部分部署。Amazon DynamoDB 存储客户、订单、菜单、购物车和门店数据,Amazon Location Service 处理地址和路线。AWS Lambda 运行业务逻辑,Amazon API Gateway 通过 IAM 授权将其对外暴露。资源按依赖顺序部署。

B 部分创建 AgentCore Gateway,设置其 IAM 权限,并配置网关将后端端点暴露为 agent 可访问的 MCP 工具。这是将 agent 与后端解耦的层。没有它,添加或更改工具将需要重新部署 agent 本身。

C 部分配置 agent。它创建 Amazon ECR 仓库,并使用 Amazon S3 和 AWS CodeBuild 构建并推送容器镜像。然后部署 AgentCore Runtime。它还部署了让 agent 个性化每次通话的辅助组件,包括一个 prompt-renderer Lambda 函数和一组 AWS Systems Manager Parameter Store 条目,这些条目保存 prompt 模板和用于来电者识别的密钥。

D 部分配置电话路径。它设置 Amazon Chime SDK Voice Connector 和一个免费电话号码、一个决定如何处理来电的 SIP Media Application Lambda、一个共享的 Amazon Virtual Private Cloud (Amazon VPC),以及 SIP 网关 (drachtio-server),该网关在 AWS Fargate 上的 Amazon ECS 上运行,位于网络负载均衡器后面。该网关桥接 Chime SDK Voice Connector 和 AgentCore Runtime 之间的通话。

上图中带编号的标注追踪了解决方案的端到端流程:

  1. 客户或从其他线路转接的电话拨打由 Amazon Chime SDK 配置的电话号码。
  2. Amazon Chime SDK 接听电话并调用 AWS Lambda 来建立桥接。
  3. Lambda 创建一个会话标识符,并打开到 AgentCore Runtime 的连接以预热 microVM,从而避免媒体流开始时出现冷启动。
  4. 在 Lambda 成功响应后,Amazon Chime SDK 通过 TCP 端口 5060 发送 SIP invite 来对网络负载均衡器发起桥接操作。
  5. 在 AWS Fargate 上运行的 SIP 服务接受 invite,并在同一容器的 Real-time Transport Protocol (RTP) 服务中分配一个空闲端口,以在分配的公共 IP 地址上接收媒体。
  6. RTP 服务通过 UDP 端口从 Voice Connector 接收媒体,并连接到 AgentCore Runtime WebSocket 开始媒体转换,使用 SIP Media Application handler 创建的相同会话标识符。
  7. AgentCore Runtime 根据会话标识符和客户的 Amazon DynamoDB 记录,调用 AWS Lambda 函数来构建存储在 AWS Systems Manager Parameter Store 中的系统 prompt。
  8. AgentCore Runtime 使用 Amazon Nova 2 Sonic 创建一个会话,并根据系统 prompt 指令通过已建立的连接问候客户。
  9. AgentCore Runtime 使用 MCP 协议从 AgentCore Gateway 列出并调用可用工具。
  10. AWS CDK 将解决方案部署到 Amazon S3,触发 AWS CodeBuild 构建容器镜像并将其存储在 Amazon ECR 中。AgentCore Runtime 和 AWS Fargate 使用这些镜像来部署 agent 以及 SIP 和 RTP 服务器。
  11. Amazon CloudWatch 提供跨所有服务的集中监控、日志记录和告警,所有静态数据都使用 AWS Key Management Service (AWS KMS) 加密。

标注 1-9 发生在单次通话期间,标注 10 涵盖了解决方案如何部署 SIP 服务器和 AgentCore Runtime,标注 11 涵盖了如何对其进行监控和保护。下一节将搁置部署和运维,更贴近通话本身。

来电流程

本节从来电者角度追踪一次通话,从第一次振铃到语音回复。这与上述架构图中的标注 1-9 是相同的运行时路径。下图以序列形式展示,以便更直观地看到事件顺序。

上图中带编号的步骤对应通话的以下阶段:

  1. 来电者拨打免费电话号码,Amazon Chime SDK Voice Connector 接听。
  2. Voice Connector 调用 SIP Media Application Lambda,该 Lambda 为通话计算一个会话标识符。
  3. Lambda 向 AgentCore Runtime 发送预热请求,以便 agent 在电话仍在振铃时准备其会话。
  4. Lambda 告诉 Voice Connector 将通话桥接到 AWS Fargate 上 Amazon ECS 的 SIP 网关,并传递会话标识符。
  5. SIP 网关使用相同的会话标识符打开一个 SigV4 签名的 WebSocket 连接到 AgentCore Runtime。
  6. 通话连接到已预热的会话,来电者音频流向 agent,agent 的音频流回给来电者。
  7. Agent 使用 Amazon Nova 2 Sonic 进行对话,并在需要菜单、购物车、订单或门店数据时通过 AgentCore Gateway 调用后端工具。

前提条件

开始之前,请确认您已具备以下条件:

使用 AWS CDK 部署解决方案

完整解决方案可在 GitHub 上的示例仓库中找到。克隆仓库并进入项目目录。

git clone https://github.com/aws-samples/sample-restaurant-telephony-ai-host-using-amazon-bedrock-agentcore-nova-sonic.git
cd sample-restaurant-telephony-ai-host-using-amazon-bedrock-agentcore-nova-sonic

运行预检脚本。它会确认 Node.js、AWS CLI、git、AWS CDK 引导和 Amazon Bedrock 模型访问权限是否都已就绪,并报告任何缺失项。

./scripts/preflight-check.sh

然后使用部署前缀运行部署脚本。该前缀会添加到每个资源名称中,您可以使用它在同一账户中多次部署解决方案。

./scripts/deploy-all.sh --deploymentPrefix qsr-tel

该脚本按顺序部署每个 AWS CDK 堆栈,并将一个堆栈的输出传递给后续堆栈。它首先构建后端,然后在后端 API 前面添加 AgentCore Gateway。接下来,它使用 AWS CodeBuild 构建并推送 agent 容器镜像,并在 AgentCore Runtime 上部署 agent。最后,它在 AWS Fargate 上的 Amazon ECS 上启动 SIP 网关,并配置 Amazon Chime SDK Voice Connector 和免费电话号码。它还会填充示例菜单和门店数据,以便完成后您可以立即下真实订单。首次构建 agent 容器需要几分钟时间,因此在 AWS CodeBuild 工作时,运行会暂停。

脚本完成后,会打印要拨打的号码:

Your telephony agent is live at +1XXXXXXXXXX. Dial to test.

SIP 网关的工作原理

电话层只有一个任务:将电话通话转换为 agent 可以读写的数据流。这很重要,因为电话网络和 agent 使用不同的协议。电话通过 UDP 以 RTP 数据包形式传输音频。Agent 期望一个承载 Amazon Nova 2 Sonic 能理解格式帧的 WebSocket。如果没有中间的转换层,agent 将需要了解 SIP、编解码器协商和网络级媒体路由,所有这些都会将其耦合到单一渠道。

两个组件处理这种转换。Amazon Chime SDK Voice Connector 接受来电并调用 SIP Media Application Lambda。该 Lambda 负责引导通话。它返回一个指令,将通话桥接到 SIP 网关,并携带会话标识符,以便下一跳知道此通话属于哪个会话。

SIP 网关在 AWS Fargate 上的 Amazon ECS 上运行。它使用 drachtio-server 进行 SIP 信令,并带有一个用于音频的 Node.js 桥接器。网关接听通话,并在电话网络使用的音频格式和 Amazon Nova 2 Sonic 期望的格式之间进行转换。为了实现高可用性 (HA),它跨两个可用区 (AZ) 作为两个任务运行,这提供了冗余,并可以发布通话计数指标到 Amazon CloudWatch 以进行扩展。通话的信令通过网络负载均衡器传递,但音频本身直接在 Voice Connector 和 Fargate 任务之间流动,这使负载均衡器远离媒体路径。

在电话振铃时预热 agent

语音通话对静音毫不宽容。如果来电者接通后几秒钟内听不到任何声音,即使系统正在工作,通话也会感觉中断。启动通话的慢速部分是一次性设置。Agent 必须为此来电者解析系统 prompt,打开 Amazon Nova 2 Sonic 流,并发现可用的 MCP 工具。SIP Media Application Lambda 不会让来电者等待这些完成,而是在电话仍在振铃时启动它。

一旦通话到达,Lambda 就会向 AgentCore Runtime 发送一个带有通话会话标识符的预热请求。AgentCore Runtime 在振铃窗口期间分配一个 microVM 并运行设置。片刻之后,当 SIP 网关使用相同的会话标识符打开其 WebSocket 连接时,AgentCore Runtime 会将其路由到那个已经预热的 microVM,agent 就可以开始说话了。会话标识符是连接这两个请求的关键。Lambda 从通话本身计算它,因此预热请求和后来的音频连接解析到同一个 microVM,无需跟踪任何额外状态。下图显示了这如何与振铃窗口重叠,以便在通话接通时 agent 已准备就绪。

存储菜单、购物车和订单

五个 Amazon DynamoDB 表覆盖了下单工作流程。Customers 表存储个人资料,包括姓名、电话和忠诚度信息,agent 使用这些信息来识别回头客。Orders 表存储订单历史以及取货门店。Menu 表存储菜品、价格和可用性,这些可能因门店而异。Carts 表存储进行中的购物车,并使用生存时间值,以便废弃的购物车自行清理。Locations 表存储餐厅详细信息,如坐标、营业时间和税率,agent 用于计算总额和提供建议。DynamoDB 按需容量随流量扩展,因此无需管理吞吐量。

查找取货门店

Amazon Location Service 帮助来电者无需输入任何内容即可找到方便的取货点。电话来电者没有浏览器可以共享位置,因此 agent 会询问邮政编码或交叉路口,并使用 Amazon Location Service 将其转换为坐标。从那里,后端可以使用这些坐标做几件事。它可以找到最近的餐厅,按驾车时间而非直线距离排序,以便优先考虑来电者路线上的短途绕行,或者对特定地址进行地理编码。这使得 agent 可以说出一些来电者可以采取行动的内容,例如“最近的门店在主街,大约五分钟车程”,而不是读回一个内部代码。

使用 Amazon Bedrock AgentCore 和 Amazon Nova 2 Sonic 处理语音

Agent 在 AgentCore Runtime 上运行。每个通话在其自己的 microVM 中运行,因此一个来电者的会话不会影响另一个。AgentCore Runtime 处理扩展,并提供双向传输音频的 WebSocket 连接。在 agent 内部,agent 框架定义了系统 prompt、它可以调用的工具以及对话流程。

Amazon Nova 2 Sonic 在通话中执行语音工作。它能识别各种口音的语音,并处理电话线路带来的不同音频质量。它以低延迟双向流式传输音频,并异步调用工具而不会中断对话(请参阅 Amazon Nova Sonic 中的异步工具调用),因此来电者不会在等待数据获取时被晾在一边。它还能处理打断,因此来电者可以像人与人通话那样打断 agent。

有一个细节是电话通话特有的。后端查询可能需要几秒钟,而 Amazon Nova 2 Sonic 会结束在服务器端空闲时间过长的会话。为了在缓慢的工具调用期间保持会话打开,agent 会以短间隔发送一个静音帧。会话保持活动状态,来电者听不到任何异常。

使用 MCP 将 agent 连接到后端工具

Agent 从不直接调用后端 Lambda 函数。AgentCore Gateway 位于中间,将后端端点呈现为 agent 发现并按名称调用的 MCP 工具,涵盖菜单查询、购物车操作、下单、客户和订单历史、地理编码以及门店搜索。这意味着 agent 不需要知道给定操作背后是哪个 Lambda 函数,或者如何对其进行身份验证。这一层是保持设计松散耦合的关键。

当 agent 调用诸如 PlaceOrder 之类的工具时,网关会将其转换为对 Amazon API Gateway 的 REST 请求,后者将其路由到匹配的 AWS Lambda 函数。由于 agent 与命名工具对话,而不是与特定函数对话,因此您可以更改后端处理程序或添加工具,而无需更改 agent。同一个后端也可以服务于其他渠道,因为它们都针对相同的工具和数据下单。下图显示了单个工具调用如何从 agent 传递到后端。

无需登录即可识别来电者

电话来电者不会登录,因此系统使用来电者的电话号码作为身份基础。系统使用 AWS Systems Manager Parameter Store 中保存的密钥值对号码进行哈希处理,结果成为会话标识符。原始电话号码不会出现在日志或会话状态中。这种哈希处理方法有助于满足个人身份信息 (PII) 处理要求,确保敏感的来电者信息永远不会以明文形式存储或传输。

如果该标识符与已知客户匹配,agent 会以姓名问候来电者,并可以提供他们上次的订单。如果不匹配,来电者以访客身份下单,订单仍然会被放置和存储。每个订单都会记录其来源渠道以及来电者是否为访客,因此您以后可以识别电话订单。这可以在不要求登录的情况下识别回头客,但这并非身份验证,电话号码哈希不应被视为来电者身份的证明。任何可以访问同一部电话的人都可以以该身份下单。需要验证身份的生产部署可以添加一个步骤,例如在 agent 打开来电者的账户历史之前,通过短信发送一次性密码。

下单演示

拨打部署输出中的号码。Agent 会问候您并询问您想要什么。您可以自然说话,询问菜单,提供取货的邮政编码,并确认订单,全部通过语音完成。在来电者说话时,agent 会在后台调用后端工具,因此在数据加载时不会有停顿。以下视频展示了一个从问候到确认的典型订单。

通话结束后,您可以在 Amazon CloudWatch Logs 中追踪完整路径。SIP 网关日志显示通话是如何建立并桥接到 agent 的。Agent 日志显示对话过程中每个 agent 事件和工具调用。订单确认后,它会连同总额和预计准备时间一起存入 DynamoDB Orders 表。

成本

您需要为系统使用的 AWS 服务付费。免费电话每分钟费用和始终运行的 Fargate 任务是最大的两项支出。如果符合您的需求,您可以降低两者。本地直拨号码每分钟费用低于免费电话号码,并且在了解流量后,在非营业时间缩减 Fargate 任务可以降低计算费用。请查看每个服务的定价页面以了解当前费率,并在 AWS Cost Explorer 中设置预算以跟踪支出。

清理

为避免持续产生费用,请在完成后移除资源。始终运行的 Fargate 任务和免费电话号码无论是否有来电都会持续产生费用。清理脚本按相反顺序删除堆栈,以便每个堆栈在其依赖的堆栈之前被移除。

# 预览将要删除的内容,但不实际删除任何东西。
./scripts/cleanup-all.sh --dry-run

# 删除部署创建的所有堆栈。
./scripts/cleanup-all.sh

清理是破坏性的。它会释放免费电话号码,删除 Amazon DynamoDB 中的订单历史,删除 AWS Systems Manager Parameter Store 中的密钥,并删除 Amazon ECR 中的容器镜像。请先备份您想要保留的任何内容。脚本完成后,在 AWS CloudFormation 控制台中确认堆栈已消失(请参阅在 AWS Management Console 上查看 AWS CloudFormation 堆栈数据和资源),并在 Amazon Chime SDK 控制台中确认免费电话号码已释放(请参阅在 Amazon Chime SDK 中管理电话号码)。

结论

在本文中,我们从头到尾介绍了电话语音下单系统,从部署到完成订单。该架构使电话层、agent 层和后端层保持独立,以便每个部分可以独立发展。这种分离使得系统易于操作,因为您可以更新菜单数据、更换新的语音模型或添加渠道,而无需协调整个技术栈的更改。

对于餐厅而言,这意味着电话订单可以顺畅流入,无需将员工从柜台前调离。来电者会立即得到问候,而不是等待音乐,agent 会大声确认每件商品以减少错误,并且系统可以在高峰时段处理订单而无需增加人手。由于 agent 通过 MCP 工具连接到后端,您可以通过注册新工具来添加预订或积分等功能,而无需更改 agent 或电话层。

要开始使用,请克隆 GitHub 上的示例仓库并运行预检脚本以确认您的环境已准备就绪。然后,将 DynamoDB 表定义中的种子数据替换为您自己的菜单项、门店和定价。更新 Parameter Store 条目中的系统 prompt 模板,以反映您餐厅的名称、问候风格和任何下单规则。然后运行部署脚本,让系统在您自己的免费电话号码上上线。

关于作者

Sergio Barraza Sergio 是 AWS 的高级技术客户经理,帮助客户设计和优化云解决方案。他在软件开发领域拥有超过 25 年的经验,指导客户采用 AWS 服务。工作之余,Sergio 会弹吉他、钢琴和打鼓,并练习咏春拳。

Salman Ahmed Salman 是 AWS 的高级技术客户经理,专门帮助客户设计、实施和优化其 AWS 环境。他将深厚的网络专业知识与探索新兴技术的热情相结合,帮助组织最大化其云投资的价值。工作之余,他喜欢摄影、旅行和观看他最喜欢的运动队比赛。

Ravi Kumar Ravi 是 AWS Enterprise Support 的高级技术客户经理,帮助旅游和酒店行业的客户运营其云环境。他拥有超过 20 年的 IT 经验,并探索生成式 AI 在云计算中的应用。工作之余,Ravi 喜欢绘画、板球和去新地方旅行。

译自 AWS · ML 博客 · 录于 二〇二六年七月十六日