Appearance
本页是 OpenClaw Voice Call 插件的完整参考,解决从零配置到生产环境故障的一切问题。插件支持 Twilio、Telnyx、Plivo 三大语音平台,以及本地 mock 测试。覆盖安装步骤、配置文件结构(含 streaming/realtime/TTS 子配置)、呼入白名单、每号码路由、Webhook 安全加固、CLI 命令(openclaw voicecall)和 Agent 工具(voice_call)的用法。故障排查部分集中处理 Webhook 暴露失败、签名验证出错、呼叫无语音等常见场景。
OpenClaw Voice Call 插件配置与排查完整指南
通过插件为 OpenClaw 添加电话通话能力,支持外拨通知、多轮对话、全双工实时语音、流式转录,以及基于白名单的呼入通话。
当前支持的语音提供商:
twilio(Programmable Voice + Media Streams)telnyx(Call Control v2)plivo(Voice API + XML transfer + GetInput speech)mock(开发/无网络测试)
重要提示: Voice Call 插件运行在 Gateway 进程内部。如果你使用远程 Gateway,请在运行 Gateway 的机器上安装和配置插件,然后重启 Gateway 加载。
快速开始
第一步:安装插件
bash
# 从 npm 安装(推荐)
openclaw plugins install @openclaw/voice-call
# 或从本地目录安装(开发用)
PLUGIN_SRC=./path/to/local/voice-call-plugin
openclaw plugins install "$PLUGIN_SRC"
cd "$PLUGIN_SRC" && pnpm install安装后 必须重启 Gateway,插件才能生效。
第二步:配置提供商和 Webhook
在 plugins.entries.voice-call.config 下设置。至少需要:provider、提供商凭证、fromNumber、一个公网可访问的 Webhook URL。详见下文 配置 章节。
第三步:验证配置
bash
openclaw voicecall setup该命令会检查插件是否启用、提供商凭证是否有效、Webhook 是否公网可达,并且只会检查是否同时启用了 streaming 和 realtime(两者不能并存)。建议首次验证时直接用 openclaw voicecall setup,脚本中可使用 --json 参数。
bash
# 输出 JSON 格式结果(适合脚本解析)
openclaw voicecall setup --json第四步:烟雾测试
bash
# 干运行(默认不会实际拨号)
openclaw voicecall smoke
openclaw voicecall smoke --to "+15555550123"
# 添加 --yes 参数后才会实际拨出一通简短的通知电话
openclaw voicecall smoke --to "+15555550123" --yes注意: 对于 Twilio、Telnyx、Plivo,设置必须解析为 公网 Webhook URL。如果 publicUrl、隧道 URL、Tailscale URL 或 serve 兜底地址解析到环回地址或私有网络空间,openclaw voicecall setup 会直接失败,不会启动一个无法接收运营商 Webhook 的运行时。
配置
如果 enabled: true 但所选提供商缺少凭证,Gateway 启动时会记录一条“设置不完整”的警告(包含缺失的键名),并跳过该提供商的运行时。此时 CLI 命令、RPC 调用和 Agent 工具在被使用时仍会返回具体的缺失项信息。
凭证支持 SecretRef: 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 表面解析;详见 SecretRef 凭证表面。
json5
{
plugins: {
entries: {
"voice-call": {
enabled: true,
config: {
provider: "twilio", // 或 "telnyx" | "plivo" | "mock"
fromNumber: "+15550001234", // Twilio 也可用 TWILIO_FROM_NUMBER 环境变量
toNumber: "+15550005678",
sessionScope: "per-phone", // per-phone | per-call
numbers: {
"+15550009999": {
inboundGreeting: "Silver Fox Cards, how can I help?",
responseSystemPrompt: "You are a concise baseball card specialist.",
tts: {
providers: {
openai: { voice: "alloy" },
},
},
},
},
twilio: {
accountSid: "ACxxxxxxxx",
authToken: "...",
},
telnyx: {
apiKey: "...",
connectionId: "...",
// Telnyx webhook 公钥(Base64;也可通过 TELNYX_PUBLIC_KEY 环境变量设置)
publicKey: "...",
},
plivo: {
authId: "MAxxxxxxxxxxxxxxxxxxxx",
authToken: "...",
},
// Webhook 服务器
serve: {
port: 3334,
path: "/voice/webhook",
},
// Webhook 安全(隧道/代理场景强烈推荐)
webhookSecurity: {
allowedHosts: ["voice.example.com"],
trustedProxyIPs: ["100.64.0.1"],
},
// 公网暴露方式(三选一)
// publicUrl: "https://example.ngrok.app/voice/webhook",
// tunnel: { provider: "ngrok" },
// tailscale: { mode: "funnel", path: "/voice/webhook" },
outbound: {
defaultMode: "notify", // notify | conversation
},
streaming: { enabled: true /* 详见流式转录 */ },
realtime: { enabled: false /* 详见实时语音对话 */ },
},
},
},
},
}提供商暴露与安全说明
- Twilio、Telnyx、Plivo 都需要一个 公网可访问 的 Webhook URL。
mock是本地开发提供商,不会发起网络调用。- Telnyx 必须提供
telnyx.publicKey(或TELNYX_PUBLIC_KEY环境变量),除非skipSignatureVerification设为true。 skipSignatureVerification仅限本地测试使用。- 使用 ngrok 免费版时,设置
publicUrl为精确的 ngrok URL;签名验证始终强制执行。 tunnel.allowNgrokFreeTierLoopbackBypass: true允许 Twilio 带无效签名的 Webhook,仅当tunnel.provider="ngrok"且serve.bind为环回地址(ngrok 本地代理)时生效。仅限本地开发。- Ngrok 免费版 URL 会变化或出现中间页面;若
publicUrl漂移,Twilio 签名验证会失败。生产环境建议使用固定域名或 Tailscale Funnel。
流媒体连接限制
streaming.preStartTimeoutMs:关闭从未发送有效start帧的套接字。streaming.maxPendingConnections:限制未认证的 pre-start 套接字总数。streaming.maxPendingConnectionsPerIp:限制每个源 IP 的未认证 pre-start 套接字。streaming.maxConnections:限制打开的媒体流套接字总数(pending + active)。
旧配置键迁移
旧配置若使用 provider: "log"、twilio.from 或旧版 streaming.* OpenAI 键,可以运行 openclaw doctor --fix 自动修复。运行时回退仍然接受旧的 voice-call 键,但迁移路径是 openclaw doctor --fix,兼容层是临时的。
自动迁移的流式键:
streaming.sttProvider→streaming.providerstreaming.openaiApiKey→streaming.providers.openai.apiKeystreaming.sttModel→streaming.providers.openai.modelstreaming.silenceDurationMs→streaming.providers.openai.silenceDurationMsstreaming.vadThreshold→streaming.providers.openai.vadThreshold
会话作用域
默认 sessionScope: "per-phone",同一来电号码的重复通话会保留对话记忆。设置为 sessionScope: "per-call" 时,每次运营商通话都从头开始新上下文,适用于接待、预订、IVR、Google Meet 桥接等场景(同一号码可能代表不同会议)。
实时语音对话
realtime 选择一个全双工实时语音提供商处理实时通话音频。它与 streaming 互斥:streaming 仅将音频转发给实时转录提供商。
重要: realtime.enabled 和 streaming.enabled 不能同时为 true,每个通话只能选一种音频模式。
当前运行时行为:
realtime.enabled支持 Twilio Media Streams。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 在会话建立时,将绑定的 Agent 标识、系统提示覆盖和选定的工作区文件胶囊注入到实时提供商指令中。realtime.fastContext.enabled默认关闭。启用后 Voice Call 先搜索索引记忆/会话上下文中与咨询问题相关的内容,在realtime.fastContext.timeoutMs内返回这些片段;仅当realtime.fastContext.fallbackToConsult为true时才回退到完整查询 Agent。- 如果
realtime.provider指向未注册的提供商,或没有任何实时语音提供商被注册,Voice Call 会记录警告并跳过实时媒体,不会导致整个插件失败。 - 咨询会话键复用已存储的通话会话(如果可用),否则回退到配置的
sessionScope(默认为per-phone,或per-call用于隔离通话)。
工具策略
realtime.toolPolicy 控制咨询运行的权限:
| 策略 | 行为 |
|---|---|
safe-read-only | 暴露咨询工具,并将常规 Agent 限制为 read、web_search、web_fetch、x_search、memory_search、memory_get |
owner | 暴露咨询工具,让常规 Agent 使用正常的 Agent 工具策略 |
none | 不暴露咨询工具。自定义 realtime.tools 仍然传递给实时提供商 |
realtime.consultPolicy 仅控制实时模型的指令:
| 策略 | 指导 |
|---|---|
auto | 使用默认提示,让提供商自行决定何时调用咨询工具 |
substantive | 简单的对话衔接直接回答,但在涉及事实、记忆、工具或上下文时先咨询 |
always | 每个实质性回答前都先咨询 |
Agent 语音上下文
当语音桥接应听起来像配置的 OpenClaw Agent 但又不想在每个普通对话轮次都支付一次完整的 Agent 咨询往返时,启用 realtime.agentContext。上下文胶囊在实时会话创建时仅添加一次,因此不会增加每轮延迟。对 openclaw_agent_consult 的调用仍然运行完整的 OpenClaw Agent,用于工具工作、最新信息、记忆查找或工作区状态。
json5
{
plugins: {
entries: {
"voice-call": {
config: {
agentId: "main",
realtime: {
enabled: true,
provider: "google",
toolPolicy: "safe-read-only",
consultPolicy: "substantive",
agentContext: {
enabled: true,
maxChars: 6000,
includeIdentity: true,
includeSystemPrompt: true,
includeWorkspaceFiles: true,
files: ["SOUL.md", "IDENTITY.md", "USER.md"],
},
},
},
},
},
},
}实时提供商示例
Google Gemini Live:
默认值:API 密钥来自 realtime.providers.google.apiKey、GEMINI_API_KEY 或 GOOGLE_GENERATIVE_AI_API_KEY;模型 gemini-2.5-flash-native-audio-preview-12-2025;语音 Kore。sessionResumption 和 contextWindowCompression 默认开启以支持更长的可重连接通话。使用 silenceDurationMs、startSensitivity、endSensitivity 调整电话音频上的更快轮转。
json5
{
plugins: {
entries: {
"voice-call": {
config: {
provider: "twilio",
inboundPolicy: "allowlist",
allowFrom: ["+15550005678"],
realtime: {
enabled: true,
provider: "google",
instructions: "Speak briefly. Call openclaw_agent_consult before using deeper tools.",
toolPolicy: "safe-read-only",
consultPolicy: "substantive",
consultThinkingLevel: "low",
consultFastMode: true,
agentContext: { enabled: true },
providers: {
google: {
apiKey: "${GEMINI_API_KEY}",
model: "gemini-2.5-flash-native-audio-preview-12-2025",
voice: "Kore",
silenceDurationMs: 500,
startSensitivity: "high",
},
},
},
},
},
},
},
}OpenAI:
json5
{
plugins: {
entries: {
"voice-call": {
config: {
realtime: {
enabled: true,
provider: "openai",
providers: {
openai: { apiKey: "${OPENAI_API_KEY}" },
},
},
},
},
},
},
}相关提供商文档:Google provider 和 OpenAI provider。
流式转录
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:
默认值:API 密钥 streaming.providers.openai.apiKey 或 OPENAI_API_KEY;模型 gpt-4o-transcribe;silenceDurationMs: 800;vadThreshold: 0.5。
json5
{
plugins: {
entries: {
"voice-call": {
config: {
streaming: {
enabled: true,
provider: "openai",
streamPath: "/voice/stream",
providers: {
openai: {
apiKey: "sk-...", // 如果已设置 OPENAI_API_KEY 则可选
model: "gpt-4o-transcribe",
silenceDurationMs: 800,
vadThreshold: 0.5,
},
},
},
},
},
},
},
}xAI:
默认值:API 密钥 streaming.providers.xai.apiKey 或 XAI_API_KEY;端点 wss://api.x.ai/v1/stt;编码 mulaw;采样率 8000;endpointingMs: 800;interimResults: true。
json5
{
plugins: {
entries: {
"voice-call": {
config: {
streaming: {
enabled: true,
provider: "xai",
streamPath: "/voice/stream",
providers: {
xai: {
apiKey: "${XAI_API_KEY}", // 如果已设置 XAI_API_KEY 则可选
endpointingMs: 800,
language: "en",
},
},
},
},
},
},
},
}通话 TTS 配置
Voice Call 使用核心 messages.tts 配置进行通话中的流式语音合成。你也可以在插件配置中覆盖它(使用与 messages.tts 相同的结构,深度合并)。
json5
{
tts: {
provider: "elevenlabs",
providers: {
elevenlabs: {
voiceId: "pMsXgVXv3BLzUgSXRplE",
modelId: "eleven_multilingual_v2",
},
},
},
}重要:Microsoft 语音在通话中被忽略。 电话音频需要 PCM,当前 Microsoft 传输不暴露电话 PCM 输出。
行为说明:
- 插件配置中位于
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 barge-in 或流拆除清空待处理 TTS 队列时,已排队的播放请求会静默解决,不会让等待播放完成的调用方挂起。
TTS 示例
仅核心 TTS:
json5
{
messages: {
tts: {
provider: "openai",
providers: {
openai: { voice: "alloy" },
},
},
},
}覆盖为 ElevenLabs(仅用于通话):
json5
{
plugins: {
entries: {
"voice-call": {
config: {
tts: {
provider: "elevenlabs",
providers: {
elevenlabs: {
apiKey: "elevenlabs_key",
voiceId: "pMsXgVXv3BLzUgSXRplE",
modelId: "eleven_multilingual_v2",
},
},
},
},
},
},
},
}OpenAI 模型覆盖(深度合并):
json5
{
plugins: {
entries: {
"voice-call": {
config: {
tts: {
providers: {
openai: {
model: "gpt-4o-mini-tts",
voice: "marin",
},
},
},
},
},
},
},
}呼入通话
呼入策略默认为 disabled。启用呼入通话:
json5
{
inboundPolicy: "allowlist",
allowFrom: ["+15550001234"],
inboundGreeting: "Hello! How can I help?",
}重要: inboundPolicy: "allowlist" 是低保证的来电号码过滤。插件将提供商提供的 From 值规范化后与 allowFrom 比对。Webhook 验证可以认证提供商交付和负载完整性,但 不能证明 PSTN/VoIP 主叫号码的所有权。将 allowFrom 视为来电号码过滤,而非强身份认证。
自动回复使用 Agent 系统,可用以下键调整:responseModel、responseSystemPrompt、responseTimeoutMs。
每号码路由
使用 numbers 配置段:当一个 Voice Call 插件为多个电话号码接收来电,且每个号码应表现不同线路时。例如,一个号码可以使用随和的个人助手,另一个使用商务角色、不同的响应 Agent 和不同的 TTS 语音。
路由根据提供商提供的被叫 To 号码选择。键必须是 E.164 格式(如 +15550001111)。来电到达时,Voice Call 解析匹配的路由一次,存储该匹配路由到通话记录中,然后在整个问候、经典自动回复路径、实时咨询路径和 TTS 播放中复用该有效配置。如果没有匹配的路由,则使用全局 Voice Call 配置。出站通话不使用 numbers;出站通话需显式传递目标、消息和会话。
当前支持的路由覆盖键:
inboundGreetingttsagentIdresponseModelresponseSystemPromptresponseTimeoutMs
tts 路由值会与全局 Voice Call 的 tts 配置深度合并,因此通常只需覆盖提供商语音:
json5
{
inboundGreeting: "Hello from the main line.",
responseSystemPrompt: "You are the default voice assistant.",
tts: {
provider: "openai",
providers: {
openai: { voice: "coral" },
},
},
numbers: {
"+15550001111": {
inboundGreeting: "Silver Fox Cards, how can I help?",
responseSystemPrompt: "You are a concise baseball card specialist.",
tts: {
providers: {
openai: { voice: "alloy" },
},
},
},
},
}语音输出契约
对于自动回复,Voice Call 会在系统提示中附加一个严格的语音输出契约:
text
{"spoken":"..."}Voice Call 防御性地提取语音文本:
- 忽略标记为推理/错误内容的数据。
- 解析直接 JSON、fenced JSON 或内联
"spoken"键。 - 回退到纯文本,并移除可能的计划/元引导段落。
这样可以确保语音播报聚焦于面向呼叫者的文本,避免将计划文本泄露到音频中。
会话启动行为
对于出站 conversation 通话,首条消息处理与实时播放状态绑定:
- 只有在初始问候语 正在播放 时,才会抑制 barge-in 队列清空和自动回复。
- 如果初始播放失败,通话会返回
listening状态,初始消息保留在队列中等待重试。 - Twilio Streaming 的初始播放在 stream 连接时立即开始,无需额外延迟。
- Barge-in 会中止当前播放并清空已排队但尚未播放的 Twilio TTS 条目。清空的条目会以“已跳过”状态解决,因此后续响应逻辑可以继续,而无需等待永远不会播放的音频。
- 实时语音对话使用实时流自身的开场轮次。Voice Call 不会 为该初始消息发布传统的
<Say>TwiML 更新,因此出站<Connect><Stream>会话保持附加。
Twilio 流断开宽限期
当 Twilio 媒体流断开时,Voice Call 会等待 2000 ms,然后自动结束通话:
- 如果流在此窗口内重新连接,自动结束被取消。
- 如果宽限期过后仍无流重新注册,通话将被结束,以防止卡住的活跃通话。
失效通话清理器
使用 staleCallReaperSeconds 结束那些从未收到终止 Webhook 的通话(例如通知模式通话一直未完成)。默认值 0(禁用)。
推荐设置:
- 生产环境: notify 风格的通话设为
120–300秒。 - 保持此值 大于
maxDurationSeconds,以便正常通话能正常完成。好的起点是maxDurationSeconds + 30–60秒。
json5
{
plugins: {
entries: {
"voice-call": {
config: {
maxDurationSeconds: 300,
staleCallReaperSeconds: 360,
},
},
},
},
}Webhook 安全
当代理或隧道位于 Gateway 前面时,插件会重建公网 URL 以进行签名验证。以下选项控制哪些转发头被信任:
webhookSecurity.allowedHosts: string[]:从转发头中白名单允许的主机。webhookSecurity.trustForwardingHeaders: boolean:不使用白名单直接信任转发头。webhookSecurity.trustedProxyIPs: string[]:仅当请求远程 IP 匹配列表时信任转发头。
其他保护措施:
- Webhook 重放保护:已为 Twilio 和 Plivo 启用。重放的有效 Webhook 请求会被确认但跳过副作用。
- Twilio 对话轮次在
<Gather>回调中包含每轮令牌,因此旧的/重放的语音回调无法满足新的待处理转录轮次。 - 未认证的 Webhook 请求在读取请求体之前就会被拒绝(提供商必需的签名头缺失时)。
- Voice Call 的 Webhook 使用共享的预认证体配置文件(64 KB / 5 秒),加上每个 IP 的 in-flight 上限(在签名验证之前)。
示例(使用固定公网域名):
json5
{
plugins: {
entries: {
"voice-call": {
config: {
publicUrl: "https://voice.example.com/voice/webhook",
webhookSecurity: {
allowedHosts: ["voice.example.com"],
},
},
},
},
},
}CLI 命令
bash
openclaw voicecall call --to "+15555550123" --message "Hello from OpenClaw"
openclaw voicecall start --to "+15555550123" # call 的别名
openclaw voicecall continue --call-id <id> --message "Any questions?"
openclaw voicecall speak --call-id <id> --message "One moment"
openclaw voicecall dtmf --call-id <id> --digits "ww123456#"
openclaw voicecall end --call-id <id>
openclaw voicecall status --call-id <id>
openclaw voicecall tail
openclaw voicecall latency # 从日志汇总通话轮次延迟
openclaw voicecall expose --mode funnelGateway 运行中的情况下,操作性的 voicecall 命令会委托给 Gateway 拥有的 voice-call 运行时,因此 CLI 不会绑定第二个 Webhook 服务器。如果没有 Gateway 可达,命令会回退到独立的 CLI 运行时。
latency 从默认的 voice-call 存储路径读取 calls.jsonl。使用 --file <path> 指定其他日志文件,--last <n> 限制分析最近 N 条记录(默认 200)。输出包含通话轮次延迟和等待时间(listen-wait)的 p50/p90/p99。
Agent 工具
工具名称: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 |
本仓库附带一份匹配的技能文档 skills/voice-call/SKILL.md。
Gateway RPC
| 方法 | 参数 |
|---|---|
voicecall.initiate | to?、message、mode?、dtmfSequence? |
voicecall.continue | callId、message |
voicecall.speak | callId、message |
voicecall.dtmf | callId、digits |
voicecall.end | callId |
voicecall.status | callId |
dtmfSequence 仅在 mode: "conversation" 时有效。通知模式通话应在通话建立后使用 voicecall.dtmf 发送后续 DTMF 信号。
故障排查
设置时 Webhook 暴露检查失败
在与 Gateway 相同的环境中运行 setup:
bash
openclaw voicecall setup
openclaw voicecall setup --json对于 twilio、telnyx、plivo,webhook-exposure 检查必须为绿色。配置了 publicUrl 但指向本地或私有网络地址时仍然会失败,因为运营商无法回调到这些地址。不要在 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。
Twilio 通知模式出站通话会在创建通话的请求中直接发送初始 <Say> TwiML,因此首条播报消息不依赖于 Twilio 获取 Webhook TwiML。但公网 Webhook 对于状态回调、对话模式通话、预连接 DTMF、实时流和连接后通话控制仍然是必需的。
使用一种公网暴露方式:
json5
{
plugins: {
entries: {
"voice-call": {
config: {
publicUrl: "https://voice.example.com/voice/webhook",
// 或
tunnel: { provider: "ngrok" },
// 或
tailscale: { mode: "funnel", path: "/voice/webhook" },
},
},
},
},
}更改配置后,重启或重新加载 Gateway,然后运行:
bash
openclaw voicecall setup
openclaw voicecall smokevoicecall smoke 默认是干运行,除非传入 --yes 参数。
提供商凭据验证失败
检查所选提供商的必填字段:
- Twilio:
twilio.accountSid、twilio.authToken、fromNumber,或者环境变量TWILIO_ACCOUNT_SID、TWILIO_AUTH_TOKEN、TWILIO_FROM_NUMBER。 - Telnyx:
telnyx.apiKey、telnyx.connectionId、telnyx.publicKey、fromNumber。 - Plivo:
plivo.authId、plivo.authToken、fromNumber。
凭据必须存在于 Gateway 主机上。仅编辑本地 shell 配置文件不会影响已运行的 Gateway,直到重启或重新加载环境。
通话已发起但提供商 Webhook 没有送达
确认提供商控制台中配置的 Webhook URL 与 publicUrl 完全一致,包括路径:
text
https://voice.example.com/voice/webhook然后检查运行时状态:
bash
openclaw voicecall status --call-id <id>
openclaw voicecall tail
openclaw logs --follow常见原因:
publicUrl指向的路径与serve.path不同。- 隧道 URL 在 Gateway 启动后发生了变化。
- 代理转发请求但删除了或重写了 host/proto 头。
- 防火墙或 DNS 将公网主机名路由到了 Gateway 以外的地址。
- Gateway 重启后未启用 Voice Call 插件。
当反向代理或隧道位于 Gateway 前面时,设置 webhookSecurity.allowedHosts 为公网主机名,或使用 webhookSecurity.trustedProxyIPs 指定已知代理地址。仅在你完全控制代理边界时才使用 webhookSecurity.trustForwardingHeaders。
签名验证失败
提供商签名会对照 OpenClaw 从入站请求重建的公网 URL 进行校验。如果签名失败:
- 确认提供商的 Webhook URL 与
publicUrl完全一致,包括协议、主机和路径。 - 使用 ngrok 免费版时,隧道主机名变化后更新
publicUrl。 - 确保代理保留了原始 host 和 proto 头,或配置
webhookSecurity.allowedHosts。 - 不要在本地测试之外启用
skipSignatureVerification。
Google Meet 与 Twilio 的接入失败
Google Meet 通过此插件实现 Twilio 拨入接入。首先验证 Voice Call 本身:
bash
openclaw voicecall setup
openclaw voicecall smoke --to "+15555550123"然后显式验证 Google Meet 传输:
bash
openclaw googlemeet setup --transport twilio如果 Voice Call 正常但 Meet 参与者从未加入,请检查 Meet 拨入号码、PIN 和 --dtmf-sequence。电话通话正常,但会议可能拒绝或忽略错误的 DTMF 序列。
Google Meet 通过 voicecall.start 启动 Twilio 电话腿,并携带预连接 DTMF 序列。PIN 衍生的序列包含 Google Meet 插件的 voiceCall.dtmfDelayMs 作为前导 Twilio 等待数字。默认值是 12 秒,因为 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 在 post-DTMF 延迟后通过
voicecall.speak请求介绍语音。
openclaw voicecall tail 仍然显示持久化的通话记录,对于通话状态和转录很有用,但并非每个 webhook/实时变迁都会出现在那里。
实时通话没有声音
确认只有一个音频模式启用:realtime.enabled 和 streaming.enabled 不能同时为 true。
对于实时 Twilio 通话,还需验证:
- 实时提供商插件已加载并注册。
realtime.provider未设置或指向已注册的提供商。- 提供商 API 密钥在 Gateway 进程中可用。
openclaw logs --follow显示已提供实时 TwiML、实时桥接已启动、初始问候已排队。
相关资源
常见问题
Webhook 暴露检查一直失败,但我的服务器能在公网访问
可能是 publicUrl 配置了私有网络地址。即使你本机可以访问,运营商(Twilio/Telnyx/Plivo)无法回调到 localhost、127.0.0.1、192.168.x 等地址。使用 ngrok、Tailscale Funnel 或固定公网域名。运行 openclaw voicecall setup 后会告诉你具体是哪个地址有问题。
通话已经拨出,但对方听不到声音,TTS 不生效
首先检查你是否同时启用了 streaming 和 realtime,两者不能共存。如果使用 Twilio Media Streams,Voice Call 会使用核心 TTS 而非 TwiML <Say>。确保 TTS 提供商配置正确,且不是 Microsoft(电话模式不支持)。运行 openclaw voicecall setup 和 openclaw logs --follow 查看实时阶段日志。
呼入电话从白名单号码打来却总是被拒绝,怎么办?
检查 inboundPolicy 是否已设为 "allowlist"(默认是 "disabled")。然后确认 allowFrom 中的号码格式与提供商传入的 From 完全一致,建议使用 E.164 格式(如 +15550001234)。可以在 openclaw voicecall tail 中查看来电的 raw 号码。如果格式不匹配(例如缺少国家代码前导加号),插件会拒绝。