mcpField
V
voice-agent

voice-agent

github.com/zavudevAgent 工作流190 次安装1 天前更新
代码仓库

关于 voice-agent

voice-agent 是用于构建 Zavu 电话语音 Agent 的技能,面向需要自动接听、调用代码处理任务并转接人工的团队。它涵盖语音配置、工具、部署和通话管理。

主要功能

  1. 01配置语音 Agent 与通话行为
  2. 02通过工具执行自定义逻辑
  3. 03设置人工转接和语音信箱
  4. 04使用 CLI 部署并管理通话
  5. 05测试提示词与工具处理器

适用场景

  1. 01构建自动接听来电的 Agent
  2. 02让 Agent 查询空闲时段并预约
  3. 03在需要时将来电转给人工
  4. 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 块
字段作用
enabledAgent 是否接听电话。必填。false 表示不接听该号码的来电,并拒绝呼出电话。
greeting电话接通时说的第一句话。省略则让来电者先说。
greetings按语言设置问候语,使用语言标签作为键:{ es: "Hola…" }。
languageBCP-47 标签(en、es、pt-BR)。省略则跟随来电者的语言。
model驱动对话的模型。可以与文本模型不同。
ttsProvider / ttsVoiceId用于语音播报的合成语音。省略则使用默认的中性语音。
sttProvider / sttModel语音识别。省略则使用默认设置。
voiceSpeed0.5–1.5。仅适用于支持速率控制的语音。
interruptible插话打断。true(默认值)允许来电者打断 Agent。
maxCallDurationMinutes通话时长上限,1–120。达到上限时通话结束。
maxIdleSecondsAgent 挂断前的静默时长。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-empty
    

    channels 数组为空,表示发送方未连接到任何内容,无法通过任何渠道发送或接收,包括语音渠道。这就是人们容易忽略的原因: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 的门槛,请检查警告行,而不是退出状态。

语音提示词

语音经不起冗长表达。重要事项如下,按优先级排列:

  1. 每轮只表达一个观点。 最多两句话。听众无法快速浏览。
  2. 先给出答案,再补充细节。
  3. 绝不要朗读 URL、代码或 JSON。 说明它的作用以及所在位置即可。
  4. 逐个字符读出数字和代码。
  5. 预料转录可能会弄错名称,包括你自家产品的名称。告诉 Agent 名称可能听起来像什么,并让它默认这是在说你。
  6. 说明哪些内容不在范围内,并在相关问题出现时用一句话说明该怎么做。

评论

支持 Markdown