1. 为什么我要在本地复现 Event Guest Confirmation 这个用例
openclaw 的官方用例里,Event Guest Confirmation 是我认为最适合拿来练手的一个。它做的事情很具体:读取一份嘉宾名单,逐个拨打电话,确认对方是否出席,收集饮食禁忌、同行人数这类备注,最后汇总成一张清单。听起来像是一个小型呼叫中心,但实际拆开看,它只依赖三个外部能力——Twilio 负责把电话打出去,OpenAI 负责让通话里的 AI 听懂人话并自然回应,ngrok 负责把 Twilio 的回调请求转发到你本地的服务。
我一开始以为这个链路会很复杂,毕竟涉及电话网络和实时语音。但真正跑通之后发现,核心配置就那么几项,难点反而在于几个容易填错的地方:Twilio 的 webhook 地址到底填哪个、ngrok 每次重启后地址变了怎么办、OpenAI 的 Realtime 模型和普通 Chat 模型在配置上的区别。这篇笔记就围绕这些实际问题展开,把 Event Guest Confirmation 用例在本地复现一遍。
适合读这篇的人:已经在用 openclaw 做自动化、想试试语音外呼场景的开发者;或者你手上有一批需要电话确认的名单,想看看能不能用 AI 分担一部分重复劳动。不需要你有 Twilio 或 OpenAI 的深度使用经验,但需要你能看懂环境变量和 JSON 配置。
整个流程我拆成六块:先讲清楚这个用例里 Twilio 和 OpenAI 各自负责什么,再准备 TaoToken 的 API 访问,然后给出可复制的配置片段,接着用一次模拟请求验证链路,再列出我踩过的报错和排查方法,最后说清楚后续怎么继续用。
2. SuperCall 流程里 Twilio 与 OpenAI 的分工拆解
先把 SuperCall 的调用链画清楚,不然后面配置的时候容易把参数填错位置。
当 openclaw 决定给某位嘉宾打电话时,它调用的是 SuperCall 插件。SuperCall 内部做两件事:第一,通过 Twilio 的 REST API 发起一个外呼请求,告诉 Twilio「拨打这个号码,接通后把音频流指向我的 webhook 地址」;第二,Twilio 接通电话后,会把通话的音频流通过 WebSocket 推送到你配置的回调地址。这个回调地址就是 ngrok 暴露出来的本地服务。
OpenAI 在这一步的角色是「实时语音大脑」。SuperCall 把 Twilio 送来的音频流转发给 OpenAI 的 Realtime API,OpenAI 返回合成的语音,SuperCall 再把这股语音流送回 Twilio,最终播放给电话那头的人。所以 OpenAI 的 API Key 和模型 ID 是配在 SuperCall 这一侧的,不是配在 Twilio 控制台里。
这里有一个很容易混淆的点:Twilio 控制台里也有「TwiML」和「webhook」的配置项,但 Event Guest Confirmation 用例走的是 SuperCall 主动外呼的模式,Twilio 号码的 Voice webhook 不需要你手动填。真正需要填 webhook 地址的地方在 SuperCall 的配置文件里,它告诉 Twilio「接通后往哪里推流」。我一开始在 Twilio 控制台里找了半天,后来才发现方向反了。
再解释一下为什么这个用例强调用 SuperCall 而不是内置的 voice_call。SuperCall 是一个独立的语音智能体,通话中的 AI 只能看到你给它的角色名、目标和开场白,碰不到你的 openclaw 网关、文件系统和其他工具。对于嘉宾确认这种场景,这意味着电话那头的人没法通过对话诱导 AI 去读你的本地文件或者调用其他工具。每次通话都是一个干净的沙盒,打完就重置,不会把上一位嘉宾的信息带到下一通电话里。
Twilio 这边你需要的是一组 Account SID 和 Auth Token,以及一个已经购买、具备外呼能力的电话号码。OpenAI 这边你需要一个能访问 Realtime API 的 Key。ngrok 则是把本地的 SuperCall 服务端口暴露到公网,让 Twilio 能回调进来。三者缺一不可,但配置的归属要分清楚:Twilio 凭证和号码给 SuperCall 用来发起外呼,OpenAI Key 给 SuperCall 用来驱动语音,ngrok 地址给 SuperCall 用来接收 Twilio 的音频流。
理解了这层分工,后面填配置的时候就不会把 OpenAI 的 Key 填到 Twilio 那一栏,也不会把 ngrok 地址填到 Twilio 控制台的 Voice URL 里。
3. 可复制的环境变量与 SuperCall 配置片段
这一节给出我实际跑通时用的配置。路径和字段名以 SuperCall 的 README 为准,我把自己验证过的版本贴出来,你可以直接对照修改。
先准备环境变量。我习惯把敏感信息放在一个.env文件里,SuperCall 启动时会读取。文件放在 openclaw 的工作目录下,内容如下:
# Twilio 凭证 TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx TWILIO_AUTH_TOKEN=your_auth_token_here TWILIO_PHONE_NUMBER=+15550001111 # OpenAI Realtime OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx OPENAI_REALTIME_MODEL=gpt-4o-realtime-preview # ngrok 暴露出来的公网地址,每次重启 ngrok 后要更新 SUPERCALL_PUBLIC_URL=https://xxxx-xx-xx-xx-xx.ngrok-free.app # 本地服务监听端口 SUPERCALL_PORT=8787这里SUPERCALL_PUBLIC_URL是重点。ngrok 免费版每次重启会分配一个新的随机域名,所以这个值不是固定的。我的做法是先把 ngrok 跑起来,拿到地址后再填进.env,然后启动 SuperCall。如果你用的是付费版固定域名,那就可以写死。
接下来是 SuperCall 的配置文件。openclaw 的插件配置一般放在~/.openclaw/config.json或者项目目录下的openclaw.config.json,具体位置取决于你的安装方式。我用的是项目级配置,片段如下:
{ "plugins": { "supercall": { "enabled": true, "twilio": { "accountSid": "${TWILIO_ACCOUNT_SID}", "authToken": "${TWILIO_AUTH_TOKEN}", "fromNumber": "${TWILIO_PHONE_NUMBER}" }, "openai": { "apiKey": "${OPENAI_API_KEY}", "model": "${OPENAI_REALTIME_MODEL}", "voice": "alloy" }, "server": { "port": 8787, "publicUrl": "${SUPERCALL_PUBLIC_URL}" }, "hooks": { "enabled": true } } } }hooks.enabled这一项要确认是true。Event Guest Confirmation 用例依赖 hooks 来在每次通话结束后记录结果,如果这里是false,通话能打通,但汇总环节拿不到数据。
如果你用的是 TOML 格式的配置,等价写法是:
[plugins.supercall] enabled = true [plugins.supercall.twilio] accountSid = "${TWILIO_ACCOUNT_SID}" authToken = "${TWILIO_AUTH_TOKEN}" fromNumber = "${TWILIO_PHONE_NUMBER}" [plugins.supercall.openai] apiKey = "${OPENAI_API_KEY}" model = "${OPENAI_REALTIME_MODEL}" voice = "alloy" [plugins.supercall.server] port = 8787 publicUrl = "${SUPERCALL_PUBLIC_URL}" [plugins.supercall.hooks] enabled = true配置里三个关键字段再强调一遍:fromNumber必须是 Twilio 里已经验证过、能外呼的号码;model要填 Realtime 系列,填成普通的gpt-4o会在建立 WebSocket 时失败;publicUrl必须和 ngrok 当前地址完全一致,包括https://前缀。
ngrok 的启动命令:
ngrok http 8787启动后终端会显示一个Forwarding地址,把它复制到.env的SUPERCALL_PUBLIC_URL里。注意 ngrok 免费版会在浏览器访问时弹一个确认页,但 Twilio 的服务器回调不受这个影响,因为 Twilio 走的是 API 请求,不是浏览器。
配置改完后重启 openclaw,让插件重新加载。可以用下面的命令确认 SuperCall 是否注册成功:
openclaw plugins list输出里应该能看到@xonder/supercall且状态为enabled。如果没看到,检查插件是否安装:
openclaw plugins install @xonder/supercall安装完成后再次列出插件,确认版本号。我遇到过一次插件装了但没启用的情况,原因是配置文件里enabled写成了字符串"true"而不是布尔值true,JSON 解析后插件被当成禁用。这种细节很容易忽略。
4. 用一次模拟来宾确认请求验证链路
配置就绪后,不要直接拿真实嘉宾名单开跑。先用一个模拟请求验证整条链路:Twilio 能不能拨出去、OpenAI 能不能接上、ngrok 能不能收到回调、hooks 能不能记录结果。
我用的验证方式是给自己或同事的号码打一通测试电话。在 openclaw 的对话界面里输入下面这段提示:
我需要你为我的活动确认出席情况。以下是详细信息: 活动:链路测试 日期:本周六下午 4 点 地点:测试地址 这是我的测试名单: - 测试来宾: +15551234567 对于这位来宾,使用 supercall 给他打电话。使用角色"Jamie,活动协调员"。 每个通话的目标是确认是否出席,并记录任何备注。 每次通话后,记录结果。所有通话完成后,给我一份摘要。把+15551234567换成你自己的号码。openclaw 收到请求后会调用 SuperCall,SuperCall 通过 Twilio 发起外呼。你的手机会响,接起来后应该能听到 AI 用「Jamie」的身份打招呼,确认活动信息,然后询问你是否出席。
通话过程中,观察几个信号。第一,电话接通后有没有明显的延迟。如果延迟超过三秒,通常是 OpenAI Realtime 的连接没建立好,检查OPENAI_API_KEY和model字段。第二,AI 的回应是否自然。如果它反复说同一句话或者答非所问,可能是voice参数或者角色上下文没传对。第三,挂断后 openclaw 有没有输出通话记录。
通话结束后,检查 SuperCall 的日志目录:
ls -la ~/clawd/supercall-logs应该能看到一个以时间戳命名的文件,里面是这次通话的转录稿。打开看看 AI 和你的对话是否被完整记录。如果目录是空的,说明 hooks 没生效,回到配置里确认hooks.enabled为true,并且 openclaw 重启过。
再检查 ngrok 的终端窗口。通话期间应该能看到 Twilio 发来的 POST 请求和 WebSocket 升级请求。如果 ngrok 窗口里什么都没有,说明 Twilio 根本没回调过来,问题出在publicUrl配置或者 Twilio 号码的外呼权限上。
验证通过的标准是:电话打通、AI 正常对话、转录稿生成、ngrok 有请求记录。这四点都满足,就可以把真实嘉宾名单替换进去。我建议第一次跑真实名单时只放两到三位,确认角色语气和开场白符合你的预期,再扩展到完整名单。
真实名单的提示词和测试版结构一样,只是把名单换成多行:
这是我的嘉宾名单: - Sarah Johnson: +15551234567 - Mike Chen: +15559876543 - Rachel Torres: +15555551234openclaw 会逐个拨打,每通电话结束后记录结果。你可以随时问「现在进度如何」来查看状态。全部完成后,它会输出一份汇总,列出谁确认、谁谢绝、谁没接、以及各自的备注。
5. 常见报错排查:401、local proxy failed 与 choices 读取失败
这一节列我实际遇到过的报错,以及对应的排查路径。这些报错在 SuperCall 的日志和 openclaw 的输出里都能看到,关键是知道往哪个方向查。
401 Unauthorized。这个报错通常来自 OpenAI 或 Twilio 的 API 调用。如果出现在通话建立阶段,先检查OPENAI_API_KEY是否有效、有没有过期、余额是否充足。如果出现在外呼发起阶段,检查TWILIO_ACCOUNT_SID和TWILIO_AUTH_TOKEN是否匹配。我遇到过一次 401 是因为.env文件里的 Key 复制时带了空格,去掉后恢复正常。另外注意,OpenAI 的 Realtime API 和普通 Chat API 用的是同一个 Key,但权限范围可能不同,确认你的 Key 没有被限制到只允许某些模型。
local proxy failed。这个报错一般出现在 SuperCall 尝试连接 OpenAI Realtime 的 WebSocket 时。原因可能是网络环境导致 WebSocket 握手失败,或者model字段填了一个不支持 Realtime 的模型名。检查OPENAI_REALTIME_MODEL是否写成gpt-4o-realtime-preview这类 Realtime 专用模型。如果网络环境本身有限制,这个报错会反复出现,需要确认你的运行环境能正常访问 OpenAI 的 API 端点。
reading choices 失败。这个报错说明 SuperCall 在解析 OpenAI 返回的响应时,期望的choices字段不存在。常见原因是把 Realtime 模型和普通 Chat 模型的响应格式搞混了。Realtime API 返回的是流式音频事件,不是标准的choices结构。如果你在配置里误填了gpt-4o而不是gpt-4o-realtime-preview,就会看到这个报错。回到配置里把model改对即可。
OAuth 相关报错。如果你在 openclaw 里配置了 OAuth 认证的模型访问方式,而 SuperCall 走的是 API Key 直连,两者可能冲突。检查 openclaw 的全局认证配置,确认 SuperCall 使用的是独立的 API Key,而不是继承网关的 OAuth 凭证。我建议给 SuperCall 单独配一套 Key,避免和网关的认证方式混在一起。
Twilio 报错 21211 或 21608。21211 表示To号码格式不对,检查嘉宾号码是否带了国家码,比如+1开头。21608 表示From号码没有外呼权限,去 Twilio 控制台确认这个号码的 Voice 功能已开启,并且账户余额足够。
ngrok 地址失效。如果你重启了 ngrok 但没更新.env里的SUPERCALL_PUBLIC_URL,Twilio 的回调会打到旧地址,表现为电话接通后没有声音或者立即挂断。每次重启 ngrok 后,把新地址更新到配置里,然后重启 SuperCall。
排查的时候有一个通用方法:先看 ngrok 窗口有没有请求进来。有请求,说明 Twilio 回调正常,问题在 SuperCall 或 OpenAI 侧;没请求,说明问题在 Twilio 外呼或publicUrl配置。这个二分法能帮你快速缩小范围。
6. 把 Event Guest Confirmation 接到你的日常流程里
链路跑通之后,这个用例可以怎么继续用,取决于你的实际场景。如果你只是偶尔做一次活动确认,那每次手动改名单、跑一遍就行。如果你需要反复用,可以把嘉宾名单做成一个文件,让 openclaw 从文件读取,避免每次粘贴。
SuperCall 的转录稿存在~/clawd/supercall-logs,这些文件可以拿来做后续分析。比如统计确认率、提取饮食禁忌关键词、生成一份给餐饮供应商的备注清单。openclaw 本身能读这些文件,你可以直接让它处理。
关于费用,Twilio 按分钟计费,OpenAI Realtime 按音频时长计费。跑之前先在 Twilio 控制台设置消费限额,避免名单太大导致意外支出。我一般会先估算总通话时长,再决定要不要分批跑。
如果你需要更稳定的 API 访问和模型调用,可以用 TaoToken 的 API 服务来统一管理 Key 和额度。它的接入文档在 https://taotoken.net/api ,API Keys 管理页面在 https://taotoken.net/api-keys ,模型对话调试在 https://taotoken.net/chat 。对于需要长期跑编码或 Agent 任务的场景,Coding Plan 页面 https://taotoken.net/coding-plan 有更详细的方案说明。
回到这个用例本身,我最后想说的是:Event Guest Confirmation 的价值不在于「AI 帮你打电话」这个噱头,而在于它把 Twilio 的通信能力、OpenAI 的实时语音能力和 openclaw 的编排能力串成了一条可复制的链路。你理解了这条链路,就能把它改造成其他外呼场景,比如预约提醒、满意度回访、简单的信息收集。配置里的三个关键点——Twilio 号码权限、OpenAI Realtime 模型 ID、ngrok 公网地址——是每次部署都要确认的,记住它们,下次换场景时能省不少时间。