Azure Communication Services
通过 ACS Call Automation,让用户拨打由 ElevenLabs 智能体接听的电话号码。
概述
此方案会为智能体分配一个 电话号码。呼叫者拨打该号码后,Azure Communication Services(ACS)通过双向媒体流接听,一个轻量桥接服务则使用标准智能体 WebSocket 协议,在 ACS 和 ElevenLabs 智能体之间转发 PCM 音频。这是联络中心 / IVR 模式,与 SIP 中继部署的结构相同,只是由 ACS 作为运营商。
它还可通过两种方式连接到 Teams:拥有 Calling Plan 的 Teams 用户可直接拨打 ACS 号码;或者,你可以通过 Teams Phone Extensibility 将该号码接入 Teams,使呼叫 Teams 资源账户的电话路由到 ACS。
ACS 仅在有限的 国家/地区提供 PSTN 号码。 如果所在地区无法提供号码,请改用支持 SIP 中继的 SIP 服务商,或使用 Graph 呼叫 机器人。
工作原理
两端均使用 PCM 16 kHz 单声道音频(智能体输入/输出格式为 pcm_16000),因此可作为 base64 直接传递,无需重采样。
桥接服务公开以下路由:
要求
- 一个付费 Azure 订阅(MCA / EA / Pay-As-You-Go)——免费 / 试用 / 赞助订阅无法购买号码。
- 一个 Azure Communication Services 资源。
- 用于桥接服务且具有公共 WebSocket 的 HTTPS 主机(Azure Container Apps、App Service 或 VM)。
- 一个 ElevenLabs 智能体,两端均设为 PCM 16000 Hz:在 Voice 选项卡设置 TTS 输出格式,在 Advanced 选项卡设置用户输入音频格式。
权限和角色
在 Contributor 权限下(而非 Owner),az containerapp up 无法创建托管标识的 ACR 拉取角色分配。
请启用注册表管理员用户并改为附加它,参见步骤 2 中的警告。
步骤 1 — 配置 ACS 资源和号码
在该资源中购买号码(Portal → ACS 资源 → Phone numbers → Get,或使用 phone-numbers SDK)。对于接听电话的智能体,具备呼入通话功能的号码即可;如果还要使用 /api/outboundCall,请添加呼出功能。

要通过 CLI 验证(需要 az extension add --name communication),并获取桥接服务以 ACS_CONNECTION_STRING 使用的连接字符串:
步骤 2 — 部署桥接服务
桥接服务是一个使用 azure-communication-callautomation 的小型 Flask + flask-sock 应用。以下是呼入流程的核心部分:
在 /ws 套接字上双向转发 PCM16:将 ACS AudioData 帧作为 {"user_audio_chunk": "<base64>"} 转发给 ElevenLabs,并将智能体音频作为 {"Kind":"AudioData","AudioData":{"Data":"<base64>"},"StopAudio":null} 发回。ACS 发送的第一帧是 AudioMetadata(协商后的格式)——记录它并忽略即可。ElevenLabs 端使用标准智能体 WebSocket 协议。
ACS 每个方向使用不同的 JSON 大小写:它发送的入站帧使用 camelCase(kind、
audioData.data),而它期望的出站帧使用 PascalCase(Kind、AudioData.Data、
StopAudio)。请区分这两种大小写——以下转发逻辑与之保持一致。
此转发逻辑有意保持精简。生产环境中,请添加日志记录、重连和优雅终止。 完整消息参考请参阅 WebSocket 文档。
EL_WS 连接到公开智能体。对于私有智能体,请让桥接服务在服务器端请求短期有效的
签名 URL:使用 API 密钥调用 GET /v1/convai/conversation/get-signed-url?agent_id=...,
然后连接到返回的 URL。对于数据
驻留,请将 ELEVENLABS_ORIGIN 设为相应的
驻留主机(wss://api.eu.residency.elevenlabs.io、.in. 或 .sg.);签名 URL 请求使用对应的 https:// 主机。
部署到 Azure Container Apps 并获取公共 FQDN:
然后在应用中设置 BRIDGE_PUBLIC_HOST=$FQDN,并将 ACS 连接字符串设为密钥。
在 Contributor 权限下(而非 Owner),az containerapp up 无法创建托管标识的 ACR
拉取角色。请启用注册表管理员用户(az acr update --admin-enabled true),使用
az containerapp registry set 附加它,然后运行 az containerapp update --image ...。
步骤 3 — 将 IncomingCall 路由到桥接服务
在 ACS 资源上创建一个 Event Grid 订阅,将 IncomingCall POST 到桥接服务。桥接服务的验证握手(如上所示)会自动完成订阅。
订阅会显示在 ACS 资源的 Events 面板下:

拨打该号码——智能体会接听。
连接到 Teams
- 直接拨号: 拥有 Teams Phone + Calling Plan 的 Teams 用户可像拨打任何外部号码一样拨打 ACS 号码。
- Teams 资源账户(TPE): 使用 Teams Phone Extensibility 将 Teams 资源账户绑定到 ACS 资源,使呼叫资源账户的电话触发相同的
IncomingCall→ 桥接服务流程。
通话结束
当智能体结束对话时(例如调用其 End Call 工具),ElevenLabs 会关闭 WebSocket。请挂断 ACS 通话链路,以免呼叫者仍停留在无响应线路上:
热转接给人工客服
ElevenLabs 原生转接工具仅适用于 ElevenLabs 管理电话服务的情况,因此这里智能体会触发一个自定义客户端工具(例如 transfer_to_human)。桥接服务通过 add_participant 将人工客服加入当前通话(热转接),而不是进行盲转:
ACS 会向 /api/callbacks 发送 AddParticipantSucceeded / AddParticipantFailed 回调。向智能体返回 client_tool_result,以便它说出交接提示。有关智能体端配置,请参阅系统工具。
在工具触发的瞬间设置转接保护(调用 add_participant 之前),否则快速关闭的 EL
WebSocket 可能会与挂断操作竞争,在人工客服加入前断开通话。
故障排除
桥接服务未收到 IncomingCall
确认 Event Grid 订阅已完成配置(provisioningState: Succeeded),且桥接服务的
/api/incomingCall 已返回验证回显。确认号码具有呼入通话功能,并且与订阅所在的 ACS 资源相同。在订阅的
Filters 选项卡中,事件类型必须包含 Incoming Call:

国际号码出现 CreateCallFailed / AddParticipantFailed
国际号码出现 CreateCallFailed / AddParticipantFailed
ACS 对某些目的地(例如印度)的出站呼叫存在限制或不稳定情况。请使用受支持的目的地,或通过 SIP / Operator 号码接入人工客服线路。桥接服务逻辑不受影响——这是出站线路上的运营商级故障。
音频失真或播放速度异常
两端必须均为 PCM 16 kHz 单声道。将智能体输入/输出格式设为 pcm_16000;桥接服务会从 conversation_initiation_metadata 记录协商后的格式。
无法购买号码 / 所在国家没有可用号码
购买号码需要付费订阅类型(MCA/EA/PAYG)。如果 ACS 不在所在国家提供号码,请改用 SIP 服务商。