mock(开发用,无网络)、plivo(Voice API + XML 转接 +
GetInput 语音)、telnyx(Call Control v2)、twilio(Programmable Voice +
Media Streams)。
Voice Call 插件运行在 Gateway 网关进程内部。如果你使用
远程 Gateway 网关,请在运行 Gateway 网关的机器上安装并配置该插件,
然后重启 Gateway 网关以加载它。
快速开始
配置提供商和 webhook
在
plugins.entries.voice-call.config 下设置配置(参见下方
配置)。至少需要:provider、提供商
凭证、fromNumber,以及一个可公开访问的 webhook URL。配置
如果enabled: true 但所选提供商缺少凭证,Gateway 网关
启动时会记录一条设置未完成警告,包含缺失键名,并跳过
启动运行时。命令、RPC 调用和智能体工具在使用时仍会返回
精确缺失的配置。
语音通话凭证接受 SecretRefs。
plugins.entries.voice-call.config.twilio.authToken、plugins.entries.voice-call.config.realtime.providers.*.apiKey、plugins.entries.voice-call.config.streaming.providers.*.apiKey 和 plugins.entries.voice-call.config.tts.providers.*.apiKey 会通过标准 SecretRef surface 解析;参见 SecretRef 凭证 surface。配置参考
上面未展示的plugins.entries.voice-call.config 下的顶层键:
| 键 | 默认值 | 说明 |
|---|---|---|
enabled | false | 总开关。 |
inboundPolicy | "disabled" | disabled | allowlist | pairing | open。参见 入站通话。 |
allowFrom | [] | 用于 inboundPolicy: "allowlist" 的 E.164 允许列表。 |
maxDurationSeconds | 300 | 每通电话的硬性时长上限,无论是否已接听都会强制执行。 |
staleCallReaperSeconds | 120 | 参见 过期通话清理器。0 会禁用它。 |
silenceTimeoutMs | 800 | 经典(非实时)流程的语音结束静音检测。 |
transcriptTimeoutMs | 180000 | 在放弃一个轮次前,等待来电者转录文本的最长时间。 |
ringTimeoutMs | 30000 | 出站通话的响铃超时。 |
maxConcurrentCalls | 1 | 超过此限制的出站通话会被拒绝。 |
outbound.notifyHangupDelaySec | 3 | 通知模式下,TTS 后等待自动挂断的秒数。 |
skipSignatureVerification | false | 仅用于本地测试;切勿在生产环境启用。 |
store | 未设置 | 覆盖默认的 ~/.openclaw/voice-calls 通话日志路径。 |
agentId | "main" | 用于生成响应和会话存储的智能体。 |
responseModel | 未设置 | 覆盖经典(非实时)响应的默认模型。 |
responseSystemPrompt | 已生成 | 用于经典响应的自定义系统提示词。 |
responseTimeoutMs | 30000 | 经典响应生成的超时时间(毫秒)。 |
提供商暴露和安全说明
提供商暴露和安全说明
- Twilio、Telnyx 和 Plivo 都需要一个可公开访问的 webhook URL。
mock是本地开发提供商(不发起网络调用)。- Telnyx 需要
telnyx.publicKey(或TELNYX_PUBLIC_KEY),除非skipSignatureVerification为 true。 skipSignatureVerification仅用于本地测试。- 在 ngrok 免费层上,将
publicUrl设置为精确的 ngrok URL;签名验证始终会强制执行。 tunnel.allowNgrokFreeTierLoopbackBypass: true仅在tunnel.provider="ngrok"且serve.bind为 loopback(ngrok 本地代理)时,允许带无效签名的 Twilio webhook。仅限本地开发。- Ngrok 免费层 URL 可能变化,或添加中间页行为;如果
publicUrl漂移,Twilio 签名会失败。生产环境:优先使用稳定域名或 Tailscale funnel。
流式连接上限
流式连接上限
streaming.preStartTimeoutMs(默认5000)会关闭从未发送有效start帧的套接字。streaming.maxPendingConnections(默认32)限制未认证的预启动套接字总数。streaming.maxPendingConnectionsPerIp(默认4)限制每个源 IP 的未认证预启动套接字数。streaming.maxConnections(默认128)限制所有打开的媒体流套接字(待处理 + 活跃)。
旧版配置迁移
旧版配置迁移
配置解析会自动规范化这些旧版键,并记录一条
指明替代路径的警告;该兼容层会在未来版本
(
2026.6.0)中移除,因此请运行 openclaw doctor --fix 将已提交的
配置重写为规范形态:provider: "log"→provider: "mock"twilio.from→fromNumberstreaming.sttProvider→streaming.providerstreaming.openaiApiKey→streaming.providers.openai.apiKeystreaming.sttModel→streaming.providers.openai.modelstreaming.silenceDurationMs→streaming.providers.openai.silenceDurationMsstreaming.vadThreshold→streaming.providers.openai.vadThresholdrealtime.agentContext.includeSystemPrompt已移除(实时上下文现在使用生成的智能体提示词)
会话范围
默认情况下,Voice Call 使用sessionScope: "per-phone",因此来自
同一来电者的重复通话会保留对话记忆。当每个运营商通话都应以
全新上下文开始时,请设置 sessionScope: "per-call",例如前台接待、
预约、IVR,或 Google Meet 桥接流程,其中同一电话号码可能
代表不同会议。
Voice Call 会将生成的会话键存储在配置的智能体命名空间下
(agent:<agentId>:voice:*)。原始显式集成键会解析到同一
命名空间:规范的 agent:<configuredAgentId>:* 键会保留该
所有者,并遵循核心 session.mainKey/全局范围别名;外部或
格式错误的 agent:* 输入会作为不透明键限定在配置的
智能体下;global 和 unknown 会保留为全局哨兵值。
实时语音对话
realtime 为实时通话音频选择全双工实时语音提供商。
它独立于 streaming,后者只会将音频转发到实时
转录提供商。
当前运行时行为:
- Twilio 和 Telnyx 支持
realtime.enabled。 realtime.provider是可选的。如果未设置,Voice Call 会使用第一个已注册的实时语音提供商。- 内置实时语音提供商:Google Gemini Live(
google)和 OpenAI(openai),由各自的提供商插件注册。 - 提供商拥有的原始配置位于
realtime.providers.<providerId>下。 - Voice Call 默认公开共享的
openclaw_agent_consult实时工具。当来电者要求更深入的推理、当前信息或常规 OpenClaw 工具时,实时模型可以调用它。 realtime.consultPolicy可选择性地添加指导,说明实时模型应在何时调用openclaw_agent_consult。realtime.agentContext.enabled默认关闭。启用后,Voice Call 会在会话设置时将有界的智能体身份和选定的工作区文件胶囊注入实时提供商指令。realtime.fastContext.enabled默认关闭。启用后,Voice Call 会先为 consult 问题搜索已索引的记忆/会话上下文,并在realtime.fastContext.timeoutMs内将这些片段返回给实时模型;只有当realtime.fastContext.fallbackToConsult为 true 时,才会回退到完整的 consult 智能体。- 如果
realtime.provider指向未注册的提供商,或根本没有注册任何实时语音提供商,Voice Call 会记录警告并跳过实时媒体,而不是让整个插件失败。 - 当
realtime.enabled为 true 时,inboundPolicy不得为"disabled";validateProviderConfig会拒绝这种组合。 - consult 会话键会在可用时复用已存储的呼叫会话,然后回退到配置的
sessionScope(默认per-phone,隔离呼叫为per-call)。
工具策略
realtime.toolPolicy 控制 consult 运行:
| 策略 | 行为 |
|---|---|
safe-read-only | 公开 consult 工具,并将常规智能体限制为 read、web_search、web_fetch、x_search、memory_search 和 memory_get。 |
owner | 公开 consult 工具,并允许常规智能体使用正常的智能体工具策略。 |
none | 不公开 consult 工具。自定义 realtime.tools 仍会透传给实时提供商。 |
realtime.consultPolicy 仅控制实时模型指令:
| 策略 | 指导 |
|---|---|
auto | 保留默认提示词,并让提供商决定何时调用 consult 工具。 |
substantive | 直接回答简单的对话衔接内容,并在涉及事实、记忆、工具或上下文之前进行 consult。 |
always | 在每个实质性回答前进行 consult。 |
智能体语音上下文
当语音桥接应听起来像已配置的 OpenClaw 智能体,但又不想在普通轮次中支付完整智能体 consult 往返成本时,启用realtime.agentContext。上下文胶囊会在创建实时会话时添加一次,因此不会增加每轮延迟。对 openclaw_agent_consult 的调用仍会运行完整的 OpenClaw 智能体,并应当用于工具工作、当前信息、记忆查找或工作区状态。
实时提供商示例
- Google Gemini Live
- OpenAI
默认值:API key 来自
realtime.providers.google.apiKey、GEMINI_API_KEY
或 GOOGLE_API_KEY;模型为 gemini-2.5-flash-native-audio-preview-12-2025;
语音为 Kore。sessionResumption 和 contextWindowCompression 默认开启,
以支持更长且可重连的呼叫。使用 silenceDurationMs、
startSensitivity 和 endSensitivity 调整电话音频上更快的轮次切换。流式转录
streaming 为实时呼叫音频选择实时转录提供商。
当前运行时行为:
streaming.provider是可选的。如果未设置,Voice Call 会使用第一个已注册的实时转录提供商。- 内置实时转录提供商:Deepgram(
deepgram)、ElevenLabs(elevenlabs)、Mistral(mistral)、OpenAI(openai)和 xAI(xai),由各自的提供商插件注册。 - 提供商拥有的原始配置位于
streaming.providers.<providerId>下。 - Twilio 发送已接受的流
start消息后,Voice Call 会立即注册该流,在提供商连接期间通过转录提供商排队入站媒体,并且只在实时转录就绪后开始初始问候语。 - 如果
streaming.provider指向未注册的提供商,或没有注册任何提供商,Voice Call 会记录警告并跳过媒体流式传输,而不是让整个插件失败。
流式提供商示例
- OpenAI
- xAI
默认值:API key 为
streaming.providers.openai.apiKey 或
OPENAI_API_KEY;模型为 gpt-4o-transcribe;silenceDurationMs: 800;
vadThreshold: 0.5。呼叫 TTS
Voice Call 使用核心messages.tts 配置为呼叫提供流式语音。
你可以在插件配置下用相同形状覆盖它,它会与 messages.tts 深度合并。
- 插件配置中的旧版
tts.<provider>键(openai、elevenlabs、microsoft、edge)会由openclaw doctor --fix修复;提交的配置应使用tts.providers.<provider>。 - 启用 Twilio 媒体流式传输时,会使用核心 TTS;否则呼叫会回退到提供商原生语音。
- 如果 Twilio 媒体流已处于活动状态,Voice Call 不会回退到 TwiML
<Say>。如果在该状态下电话 TTS 不可用,播放请求会失败,而不是混合两条播放路径。 - 当电话 TTS 回退到次要提供商时,Voice Call 会记录一条包含提供商链(
from、to、attempts)的警告以便调试。 - 当 Twilio 插话或流拆除清空待处理 TTS 队列时,已排队的播放请求会完成结算,而不是让等待播放完成的呼叫者一直挂起。
TTS 示例
- Core TTS only
- Override to ElevenLabs (calls only)
- OpenAI model override (deep-merge)
入站呼叫
入站策略默认为disabled。要启用入站呼叫,请设置:
responseModel、responseSystemPrompt 和 responseTimeoutMs 调优。
按号码路由
当一个 Voice Call 插件接收多个电话号码的来电,并且每个号码都应像不同线路一样工作时,使用numbers。例如,一个号码可以使用随和的个人助手,而另一个号码使用商务人设、不同的响应智能体和不同的 TTS 语音。
路由根据提供商给出的被叫 To 号码选择。键必须是 E.164 号码。来电到达时,Voice Call 会解析一次匹配路由,将匹配到的路由存储到通话记录中,并在问候语、经典自动响应路径、实时咨询路径和 TTS 播放中复用该有效配置。如果没有匹配的路由,则使用全局 Voice Call 配置。外呼不使用 numbers;发起通话时请显式传入外呼目标、消息和会话。
路由覆盖目前支持:
inboundGreetingttsagentIdresponseModelresponseSystemPromptresponseTimeoutMs
tts 路由值会深度合并到全局 Voice Call tts 配置之上,因此通常只需覆盖提供商语音:
语音输出契约
对于自动响应,Voice Call 会向系统提示追加严格的语音输出契约,要求返回{"spoken":"..."} JSON 回复。Voice Call 会以防御方式提取语音文本:
- 忽略标记为推理/错误内容的载荷。
- 解析直接 JSON、围栏 JSON,或内联
"spoken"键。 - 回退到纯文本,并移除可能是规划/元信息开头的段落。
对话启动行为
对于外呼conversation 通话,首条消息处理与实时播放状态绑定:
- 仅当初始问候语正在主动播放时,才会抑制插话队列清空和自动响应。
- 如果初始播放失败,通话会回到
listening,并且初始消息会保留在队列中以便重试。 - Twilio 流式传输的初始播放会在流连接时开始,不增加额外延迟。
- 插话会中止活动播放,并清除已排队但尚未播放的 Twilio TTS 条目。被清除的条目会解析为已跳过,因此后续响应逻辑可以继续,而无需等待永远不会播放的音频。
- 实时语音对话使用实时流自己的开场轮次。Voice Call 不会为该初始消息发布旧版
<Say>TwiML 更新,因此外呼<Connect><Stream>会话会保持附加状态。
Twilio 流断开宽限期
当 Twilio 媒体流断开时,Voice Call 会等待 2000 ms 后再自动结束通话:- 如果流在该窗口内重新连接,则取消自动结束。
- 如果宽限期后没有流重新注册,则结束通话,防止活动通话卡住。
过期通话清理器
使用staleCallReaperSeconds(默认 120)结束从未接听且从未进入实时对话状态的通话,例如提供商从不投递终止 Webhook 的通知模式通话。将其设为 0 可禁用。
清理器每 30 秒运行一次,并且只会结束没有 answeredAt 时间戳、且尚未处于终止或实时(speaking/listening)状态的通话,因此已接听的对话永远不会被该定时器清理;maxDurationSeconds(默认 300)是单独的上限,用于结束运行过久的已接听通话。
对于运营商可能较慢投递振铃/接听 Webhook 的通知式流程,请将 staleCallReaperSeconds 提高到默认值以上,避免缓慢但正常的通话被过早清理;120-300 秒是合理的生产范围。
Webhook 安全
当代理或隧道位于 Gateway 网关前面时,该插件会重建用于签名验证的公共 URL。这些选项控制信任哪些转发头:允许列表中的转发头主机。
在没有允许列表的情况下信任转发头。
仅当请求远端 IP 与列表匹配时才信任转发头。
- Twilio、Telnyx 和 Plivo 已启用 Webhook 重放保护。被重放的有效 Webhook 请求会被确认,但会跳过副作用。
- Twilio 对话轮次在
<Gather>回调中包含逐轮令牌,因此过期/重放的语音回调不能满足较新的待处理转录轮次。 - 当缺少提供商要求的签名头时,未经认证的 Webhook 请求会在读取正文之前被拒绝。
- voice-call Webhook 使用共享的认证前正文读取配置(最大 64 KB 正文、5 秒读取超时),并在签名验证前应用按键的在途上限(默认每个键 8 个并发请求)。
CLI
voicecall 命令会委托给 Gateway 网关拥有的 voice-call 运行时,因此 CLI 不会绑定第二个 Webhook 服务器。如果无法访问 Gateway 网关,这些命令会回退到独立 CLI 运行时。
latency 会从默认 voice-call 存储路径读取 calls.jsonl。使用 --file <path> 指向不同日志,并使用 --last <n> 将分析限制为最后 N 条记录(默认 200)。输出包括轮次延迟和监听等待时间的 min/max/avg、p50 和 p95。
智能体工具
工具名称:voice_call。
| 操作 | 参数 |
|---|---|
initiate_call | message, to?, mode?, dtmfSequence? |
continue_call | callId, message |
speak_to_user | callId, message |
send_dtmf | callId, digits |
end_call | callId |
get_status | callId |
Gateway 网关 RPC
| 方法 | 参数 | 说明 |
|---|---|---|
voicecall.initiate | to?, message, mode?, sessionKey?, requesterSessionKey? | 省略 to 时回退到 toNumber 配置。 |
voicecall.start | to, message?, mode?, dtmfSequence?, sessionKey? | 与 initiate 相同,但也接受连接前 dtmfSequence。 |
voicecall.continue | callId, message | 阻塞直到该轮次解析完成;返回转录。 |
voicecall.continue.start | callId, message | 异步变体:立即返回 operationId。 |
voicecall.continue.result | operationId | 轮询待处理的 voicecall.continue.start 操作以获取其结果。 |
voicecall.speak | callId, message | 不等待即说话;当 realtime.enabled 时使用实时桥接。 |
voicecall.dtmf | callId, digits | |
voicecall.end | callId | |
voicecall.status | callId? | 省略 callId 可列出所有活动通话。 |
dtmfSequence 仅在 mode: "conversation" 下有效;如果通知模式通话在连接后需要数字,应在通话存在后使用 voicecall.dtmf。
故障排查
设置无法暴露 Webhook
从运行 Gateway 网关的同一环境中运行设置:twilio、telnyx 和 plivo,webhook-exposure 必须为绿色。已配置的 publicUrl 如果指向本地或私有网络空间,仍会失败,因为运营商无法回拨这些地址。不要将 localhost、127.0.0.1、0.0.0.0、10.x、172.16.x-172.31.x、192.168.x、169.254.x、fc00::/7、fd00::/8 或其他运营商级 NAT 范围用作 publicUrl。
Twilio 通知模式外呼会在创建通话请求中直接发送初始 <Say> TwiML,因此第一条语音消息不依赖 Twilio 获取 Webhook TwiML。状态回调、对话通话、连接前 DTMF、实时流和连接后通话控制仍然需要公共 Webhook。
使用一个公共暴露路径:
--yes,否则 voicecall smoke 是一次空运行。
提供商凭证失败
检查所选提供商和必需的凭证字段:- Twilio:
twilio.accountSid、twilio.authToken和fromNumber,或TWILIO_ACCOUNT_SID、TWILIO_AUTH_TOKEN和TWILIO_FROM_NUMBER。 - Telnyx:
telnyx.apiKey、telnyx.connectionId、telnyx.publicKey和fromNumber,或TELNYX_API_KEY、TELNYX_CONNECTION_ID和TELNYX_PUBLIC_KEY。 - Plivo:
plivo.authId、plivo.authToken和fromNumber,或PLIVO_AUTH_ID和PLIVO_AUTH_TOKEN。
呼叫开始,但提供商 Webhook 未到达
确认提供商控制台指向准确的公共 Webhook URL:publicUrl指向的路径与serve.path不同。- 隧道 URL 在 Gateway 网关启动后发生了变化。
- 代理转发了请求,但剥离或重写了 host/proto 标头。
- 防火墙或 DNS 将公共主机名路由到了 Gateway 网关以外的位置。
- Gateway 网关重启时未启用 Voice Call 插件。
webhookSecurity.allowedHosts 设置为公共主机名,或对已知代理地址使用
webhookSecurity.trustedProxyIPs。仅当代理边界
在你的控制之下时,才使用 webhookSecurity.trustForwardingHeaders。
签名验证失败
提供商签名会根据 OpenClaw 从传入请求重构出的公共 URL 进行检查。如果签名失败:- 确认提供商 Webhook URL 与
publicUrl完全匹配,包括方案、主机和路径。 - 对于 ngrok 免费层级 URL,当隧道主机名变化时更新
publicUrl。 - 确保代理保留原始 host 和 proto 标头,或配置
webhookSecurity.allowedHosts。 - 不要在本地测试以外启用
skipSignatureVerification。
Google Meet Twilio 加入失败
Google Meet 使用此插件进行 Twilio 拨入加入。首先验证 Voice Call:--dtmf-sequence。电话呼叫可能正常,
但会议会拒绝或忽略不正确的 DTMF 序列。
Google Meet 通过 voicecall.start 使用
预连接 DTMF 序列启动 Twilio 电话分支。由 PIN 派生的序列包含 Google Meet
插件的 voiceCall.dtmfDelayMs(默认 12000 ms)作为前置 Twilio
等待数字,因为 Meet 拨入提示可能会延迟到达。然后 Voice Call
会在请求开场问候之前重定向回实时处理。
使用 openclaw logs --follow 查看实时阶段跟踪。正常的 Twilio Meet
加入会按此顺序记录日志:
- Google Meet 将 Twilio 加入委托给 Voice Call。
- Voice Call 存储预连接 DTMF TwiML。
- Twilio 初始 TwiML 在实时处理之前被使用并提供。
- Voice Call 为 Twilio 呼叫提供实时 TwiML。
- Google Meet 在 DTMF 后延迟之后通过
voicecall.speak请求开场语音。
openclaw voicecall tail 仍会显示持久化的呼叫记录;它对
呼叫状态和转录很有用,但并非每个 Webhook/实时转换
都会出现在那里。
实时呼叫没有语音
确认只启用了一种音频模式:realtime.enabled 和
streaming.enabled 不能同时为 true。
对于实时 Twilio/Telnyx 呼叫,还要验证:
- 已加载并注册一个实时提供商插件。
realtime.provider未设置,或命名了一个已注册的提供商。- 提供商 API key 对 Gateway 网关进程可用。
openclaw logs --follow显示已提供实时 TwiML、实时桥已启动,并且初始问候已入队。