TTS 与呼入通话
电话文本转语音、入站策略和按号码路由、语音输出契约、会话启动以及过期呼叫清理器。属于 Voice call plugin 指南的一部分。
电话 TTS¶
Voice Call 使用核心 tts 配置在呼叫中流式传输语音。你可以在插件配置下使用相同结构覆盖它——它会与 tts 深度合并。
{
tts: {
provider: "elevenlabs",
providers: {
elevenlabs: {
speakerVoiceId: "pMsXgVXv3BLzUgSXRplE",
modelId: "eleven_multilingual_v2",
},
},
},
}
Warning
Microsoft speech 在语音呼叫中会被忽略。 电话合成需要一个实现电话目标输出的提供商;Microsoft speech 提供商未实现,因此呼叫会跳过它,并尝试回退链中的其他提供商。
行为说明:
- 插件配置中的旧版
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 队列时,已排队的播放请求会结束,而不是让等待播放完成的呼叫方挂起。
- 恢复的呼叫方语音会丢弃仍在生成的较早自动回复。Twilio 流式处理会对语音开始和部分转录做出反应;运营商 webhook 呼叫会在新的语音事件到达时做出反应。显式语音请求仍然可用,已接受的代理工作可以完成,而无需说出过时的回复。
TTS 示例¶
入站呼叫¶
入站策略默认为 disabled。要启用入站呼叫,请设置:
{
inboundPolicy: "allowlist",
allowFrom: ["+15550001234"],
inboundGreeting: "Hello! How can I help?",
}
Warning
inboundPolicy: "allowlist" 是低保证的来电号码筛选。插件会规范化提供商提供的 From 值,并将其与 allowFrom 比较。Webhook 验证会认证提供商投递和负载完整性,但它不能证明 PSTN/VoIP 来电号码所有权。请将 allowFrom 视为来电号码筛选,而不是强来电者身份。
自动回复使用代理系统。可通过 responseModel、responseSystemPrompt 和 responseTimeoutMs 进行调整。
按号码路由¶
当一个 Voice Call 插件接收多个电话号码的呼叫,并且每个号码都应表现得像一条不同的线路时,使用 numbers。例如,一个号码可以使用随意的个人助理,而另一个号码可以使用商务人格、不同的响应代理和不同的 TTS 语音。
路由根据提供商提供的被叫 To 号码选择。键必须是 E.164 号码。当呼叫到达时,Voice Call 会解析一次匹配的路由,将匹配的路由存储在呼叫记录上,并在问候语、经典自动回复路径、实时咨询路径和 TTS 播放中复用该有效配置。如果没有匹配的路由,则使用全局 Voice Call 配置。出站呼叫不使用 numbers;在发起呼叫时,请显式传入出站目标、消息和会话。
路由覆盖仅支持以下字段:
inboundGreetingttsagentIdresponseModelresponseSystemPromptresponseTimeoutMs
路由中的 tts 值会深度合并到全局 Voice Call tts 配置之上,因此通常只需覆盖提供商语音:
{
inboundGreeting: "Hello from the main line.",
responseSystemPrompt: "You are the default voice assistant.",
tts: {
provider: "openai",
providers: {
openai: { speakerVoice: "coral" },
},
},
numbers: {
"+15550001111": {
inboundGreeting: "Silver Fox Cards, how can I help?",
responseSystemPrompt: "You are a concise baseball card specialist.",
tts: {
providers: {
openai: { speakerVoice: "alloy" },
},
},
},
},
}
语音输出契约¶
对于自动回复,Voice Call 会在系统提示中附加一个严格的语音输出契约,要求返回 {"spoken":"..."} JSON 回复。Voice Call 会防御性地提取语音文本:
- 忽略标记为推理/错误内容的负载。
- 解析直接 JSON、围栏 JSON 或内联
"spoken"键。 - 回退到纯文本,并移除可能的规划/元信息引导段落。
这使语音播放专注于面向呼叫方的文本,并避免将规划文本泄漏到音频中。
对话启动行为¶
对于出站 conversation 调用,首条消息处理与实时播放状态绑定:
- Barge-in 队列清除和自动响应仅在初始问候语正在播放时被抑制。
- 如果初始播放失败,通话会返回
listening,初始消息仍保留在队列中等待重试。 - Twilio 流式传输的初始播放会在流连接时开始,无额外延迟。
- Barge-in 会中止当前播放,并清除已排队但尚未播放的 Twilio TTS 条目。被清除的条目会解析为 skipped,因此后续响应逻辑可以继续,而无需等待永远不会播放的音频。
- 实时语音对话使用实时流自身的开场轮次。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 秒是合理的生产范围。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw