关于 voice-agent
voice-agent 是用于构建 Zavu 电话语音 Agent 的技能,面向需要自动接听、调用代码处理任务并转接人工的团队。它涵盖语音配置、工具、部署和通话管理。
主要功能
- 01配置语音 Agent 与通话行为
- 02通过工具执行自定义逻辑
- 03设置人工转接和语音信箱
- 04使用 CLI 部署并管理通话
- 05测试提示词与工具处理器
适用场景
- 01构建自动接听来电的 Agent
- 02让 Agent 查询空闲时段并预约
- 03在需要时将来电转给人工
- 04排查通话内容与执行结果
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ zavudev/ zavu-skills --skill voice-agent安装后在 Claude Code 中输入/voice-agent确认已加载。
详细介绍
语音 Agent
语音 Agent 会接听电话、进行对话、在需要执行操作时调用你的代码,并在适当时转接给人工。它与消息 Agent 是同一个 Agent 对象,只是多了一个 voice 块。
Zavu 负责运行整个流程:语音识别、Agent 的模型、语音合成和打断处理。你只需提供提示词和技能。
**先了解这一点:**语音是工具实际运行的渠道。在纯文本场景中,模型根本不会获得工具(参见 ai-agent)。如果你的 Agent 必须执行操作,语音和工作流是目前可行的方式。
发送方与账户(一段话说明)
Sender 是你作为 Zavu-Sender 传入的 API 句柄;账户(WhatsApp Business Account、Facebook Page、Telegram bot、电话号码)是它路由到的连接,也是计费对象。Sender 免费。在控制面板中连接账户时会自动创建对应的 Sender;使用 GET /v1/senders 查找,并以其 channels 数组为准,了解它可以发送什么。完整模型请参阅 channel-setup 技能。
最快路径:使用工厂 Agent
Zavu 提供可运行的语音 Agent。在 zavu.dev 上与其中一个对话,然后获取其代码,得到的就是同一个 Agent。
npx zavudev agents catalog # what's available
npx zavudev agents pull kepler --dir kepler # scaffold it locally
# booking agents: add --calendar calcom for a working Cal.com client
cd kepler
npm install # types + local runs
npx zavudev senders list # find your sender id
npx zavudev fn secrets set SENDER_ID <senderId>
npx zavudev deploy
npx zavudev agents list # confirm what actually landed
一个 Agent 可以接听多个号码。通过 id 将它们连接起来:
npx zavudev agents senders connect --agent <agentId> --sender <senderId>
agents pull 会生成一个真实的 TypeScript 项目:包含 Agent 及其技能的 index.ts、tsconfig.json,以及 @zavudev/functions 的本地替代实现,以确保项目能在你的机器上通过类型检查并运行。你可以自由编辑——文件归你所有,npx zavudev deploy 会根据其中的声明进行同步。
在代码中声明语音 Agent
import { defineAgent, defineTool } from "@zavudev/functions"
defineAgent({
senderId: process.env.SENDER_ID!,
name: "Kepler",
provider: "zavu",
model: "openai/gpt-5.6-luna",
channels: ["voice", "whatsapp"],
voice: {
enabled: true,
model: "openai/gpt-4o",
greeting: "Hi, I'm Kepler. What day would you like to book?",
// Per-language greeting, used when the caller's language differs.
greetings: { es: "Hola, soy Kepler. ¿Qué día querés reservar?" },
// language: "es", // omit to follow the caller's language
interruptible: true, // caller can talk over the agent
maxCallDurationMinutes: 12,
voiceSpeed: 1.15,
},
prompt: `# Personality
You book meetings. One idea per turn.
# Environment
You are on a phone call. They can hear you, they cannot see a screen.
Never read a URL or a code block aloud.`,
})
voice 块
| 字段 | 作用 |
|---|---|
enabled | Agent 是否接听电话。必填。false 表示不接听该号码的来电,并拒绝呼出电话。 |
greeting | 电话接通时说的第一句话。省略则让来电者先说。 |
greetings | 按语言设置问候语,使用语言标签作为键:{ es: "Hola…" }。 |
language | BCP-47 标签(en、es、pt-BR)。省略则跟随来电者的语言。 |
model | 驱动对话的模型。可以与文本模型不同。 |
ttsProvider / ttsVoiceId | 用于语音播报的合成语音。省略则使用默认的中性语音。 |
sttProvider / sttModel | 语音识别。省略则使用默认设置。 |
voiceSpeed | 0.5–1.5。仅适用于支持速率控制的语音。 |
interruptible | 插话打断。true(默认值)允许来电者打断 Agent。 |
maxCallDurationMinutes | 通话时长上限,1–120。达到上限时通话结束。 |
maxIdleSeconds | Agent 挂断前的静默时长。5–300。 |
voicemailAction | 接听机器接听时执行 hangup(默认值)或 leave_message。 |
voicemailMessage | 当 voicemailAction 为 leave_message 时播报的内容。未设置时使用 greeting。 |
transferPhoneNumber | 用于转接人工的 E.164 号码。设置后会为 Agent 提供转接工具。 |
channels 和 voice 是分开的:channels: ["voice"] 将电话路由到此 Agent,voice.enabled 配置其接听电话时的行为。**两者都需要设置。**路由到语音渠道但没有 voice 块的 Agent 无法正常接听。
人工转接
核心承诺是“接听、解决问题,并转接给人工”。转接由 transferPhoneNumber 实现:设置此项后,Agent 就会获得一个可自行决定是否使用的转接工具。在提示词中说明何时使用该工具。
voice: {
enabled: true,
transferPhoneNumber: "+14155551234",
},
prompt: `Transfer to a human when the caller asks for one, is upset,
or asks something about their account that you cannot answer.`,
无需编写后端即可预约
预约会议的语音 Agent 完全不需要代码:在仪表板中(Agent、工具、库)添加 check_availability
和 book_meeting,并在同一处连接 Cal.com 或 Google 日历。Zavu 托管这两项技能。
如果 Agent 唯一需要做的就是读取日历并添加日程,就用这种方式,而不是搭建预约函数。如果预约逻辑由你负责,比如先筛选、转接给不同的主持人,或还要写入你自己的系统,则使用
npx zavudev agents pull kepler --calendar calcom。
Agent 可调用的技能
工具在语音通话中运行。在 Agent 旁边声明它们;它们会在你的函数中、Zavu Cloud 内执行。
defineTool({
name: "check_availability",
description: "Find open meeting slots. Call before offering a time.",
parameters: {
type: "object",
properties: {
preferred_time: { type: "string", description: "What the caller asked for" },
},
required: ["preferred_time"],
},
handler: async (args, ctx) => {
const res = await fetch(`https://api.example.com/slots?q=${args.preferred_time}`)
if (!res.ok) return { ok: false, reason: "lookup_failed", slots: [] }
return { ok: true, slots: (await res.json()).slots }
},
})
无需部署即可在本地运行:
npx zavudev fn invoke --tool check_availability --args '{"preferred_time":"tomorrow 3pm"}'
绝不要返回虚假的成功结果。 如果处理程序什么也没做,却返回 { booked: true, confirmationCode: "ABC-123" },Agent 就会告诉真实来电者会议已经安排妥当。返回 { ok: false, reason: "not_configured" }
并让提示词处理此情况——Agent 可以告知对方目前无法预约,这种情况可以恢复。虚假的确认则不行。
发起和检查通话
npx zavudev calls create --to +14155551234 # outbound. COSTS MONEY.
npx zavudev calls list --status completed
npx zavudev calls get <callId> # includes the transcript
npx zavudev calls hangup <callId>
calls get 会逐轮打印对话,包括工具调用。这是 Agent 实际说过什么的唯一记录,也是通话出错时首先要查看的地方。
从 SDK 调用(@zavudev/sdk 0.56.0+;较早版本没有 calls 资源——请直接调用 REST 端点):
const { call } = await zavu.calls.create({
to: "+14155551234",
greeting: "Hi, this is Acme calling about your appointment.",
language: "es-ES", // or "auto" to follow the caller
metadata: { campaign: "reminders" },
})
花钱之前先测试
没有电话或浏览器,就无法听到 Agent 的声音。但你可以免费测试音频之前的所有环节:
# The agent's brain: prompt, model, knowledge base. Nothing is delivered.
npx zavudev agents test --agent <agentId> --message "do you have anything Tuesday?"
# A skill's handler, locally.
npx zavudev fn invoke --tool check_availability --args '{"preferred_time":"Tuesday"}'
agents test 运行的是文本流程,因此不会测试工具调用,即使语音通话会调用工具也是如此——适用时它会发出警告。用它测试提示词,用
fn invoke 测试处理程序。
要实际听到它,你需要一个电话号码并进行真实通话。无法免费收听 Agent:agents test 仅支持文本,并且每次运行时都会说明这一点。通话前的所有内容都可以验证(配置、提示词、处理程序);音频本身则无法验证。承诺交付日期前,请预留这部分时间。
接听来电需要拥有一个号码:
npx zavudev phone-numbers search --country US
不要只根据 capabilities 列做判断。该字段由运营商报告,且曾出现过两种方向的错误:可以拨出和接听电话的号码显示为 ["sms"],而列出 voice 的号码却无法接通。通过 Zavu 购买的每个号码,购买时都已配置为可通话。要真正确认是否符合要求,应检查发送方自己的通道列表:
npx zavudev senders get <senderId> # channels includes "voice" once it can call
要求与费用
- 发送方 Agent 需要设置
voice.enabled: true,否则POST /v1/calls会返回400 "The sender's agent does not have voice enabled"。 - 测试模式 API 密钥不可用。
- 接听来电需要具备
voice功能的电话号码。 - 费用按接通分钟数计费,另加电信费用,从预付余额中扣除。通话开始时会预留一笔短时费用;通话结束时则按实际时长收费。
402表示余额不足。
Webhook 事件
每个语音事件都包含 callId、direction、from、to、status、
durationSeconds、endReason 和 transcriptAvailable。
| 事件 | 触发时机 |
|---|---|
call.initiated | 发起外呼,或收到来电。status: ringing |
call.answered | 已接通,Agent 开始工作。status: in_progress |
call.completed | 对话结束后通话结束 |
call.failed | 忙线、无人接听、已取消或发生错误 |
npx zavudev senders update <senderId> \
--webhook-url https://api.example.com/hooks/zavu \
--webhook-events call.completed,call.failed
出错时
-
Agent 会回答,但内容毫无用处。 检查知识库。如果提示词要求“只陈述检索结果”,却没有附加任何文档,Agent 就会编造答案,而不是拒绝回答。使用
agents test验证——它会报告使用了多少个知识片段。 -
电话响了,但 Agent 没有接听。 先检查发送方:
npx zavudev senders get <senderId> # channels must be non-emptychannels数组为空,表示发送方未连接到任何内容,无法通过任何渠道发送或接收,包括语音渠道。这就是人们容易忽略的原因:Agent、voice.enabled和号码看起来都正确,但实际问题就在这里。关联一个由你的项目持有的号码,并启用语音:npx zavudev phone-numbers update <phoneNumberId> --sender <senderId> npx zavudev senders update <senderId> --enable-voice只有当
channels包含voice后,才值得检查voice.enabled和 Agent 本身是否已启用。 -
设置的字段没有效果。 只有支持速率控制的语音才会应用
voiceSpeed。greetings需要使用与来电者实际所说语言相匹配的语言标签。 -
部署提示已同步,但没有任何变化。 查看 ✓ 上方的内容,不要相信退出代码。
npx zavudev deploy会在成功提示行之前打印警告,但即使 Agent 无法访问,它仍会以 0 退出——例如,同一个发送方上有两个 Agent,但只有一个会应答。如果显示绿色勾号,只能说明部署已运行,并不代表你的 Agent 一定能被联系到。如果要以此作为 CI 的门槛,请检查警告行,而不是退出状态。
语音提示词
语音经不起冗长表达。重要事项如下,按优先级排列:
- 每轮只表达一个观点。 最多两句话。听众无法快速浏览。
- 先给出答案,再补充细节。
- 绝不要朗读 URL、代码或 JSON。 说明它的作用以及所在位置即可。
- 逐个字符读出数字和代码。
- 预料转录可能会弄错名称,包括你自家产品的名称。告诉 Agent 名称可能听起来像什么,并让它默认这是在说你。
- 说明哪些内容不在范围内,并在相关问题出现时用一句话说明该怎么做。