给AI Agent加通话能力:CallCow Prompt-to-Call实战

AI Agent 做了那么多事,但一直有个尴尬的短板:只能发消息,不能打电话

你写个客服 Agent,用户问完问题,它说“请稍等,我转人工”——这句话背后就是基础设施没打通。CallCow 最近发布了 Prompt-to-Call API,配合 OpenClaw skill,用一句话就能让 Agent 拨出电话,还能返回结构化结果。我昨天花了一下午跑通了 demo,今天把完整的实战经验写出来。

这东西解决了什么问题?

传统上,给 Agent 加电话功能要干这些事:

  • 买 SIP 线路或 Twilio 号码
  • 配置媒体流、WebSocket、DTMF 识别
  • 写状态机管理通话生命周期
  • 处理录音、转写、摘要

一套下来,小团队至少两周,还得配个懂通信的开发者。CallCow 的做法是把这些都封装成 API,你只需要告诉它“打给谁,说什么”,它帮你执行,然后返回通话记录。

核心思路:Prompt-to-Call 是什么?

官方文档里写的是“A single prompt triggers the entire call workflow”,翻译成人话就是:

你写一段自然语言指令,API 自动生成通话脚本、选择语音、拨号、对话、挂断、返回结果。

比如你写:

“打电话给王经理,确认明天下午3点的会议是否照常,如果他有变动,记录新的时间。”

API 会解析出:

  • 目标号码(从预设联系人或上下文拿)
  • 对话流程(开场→确认→处理异常→结束)
  • 返回字段(确认结果、新时间)

这背后是 GPT 5.4 做语音生成和对话理解,但作为开发者你不需要关心这些,只需要准备一个 prompt 和一个回调 URL。

完整提示词模板(可直接复制)

下面是我测试过的 Python 代码,用 requests 直接调 API,不需要额外 SDK。

python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
import requests
import json

# 配置
API_KEY = "your_callcow_api_key"  # 从 docs.callcow.ai 申请
CALLBACK_URL = "https://your-server.com/webhook/call-result"

# 请求体
payload = {
    "to_number": "+14165551234",  # 目标号码,E.164格式
    "prompt": "你是一个AI客服助手,叫小墨。打电话给用户确认他们是否收到了昨天发出的报价单。如果收到,询问是否有疑问并记录问题;如果没收到,说明会重新发送。语气友好专业,每次回答控制在20字以内。",
    "voice": "natural-female-1",  # 可选:natural-female-1, natural-male-1, expressive-female-1
    "callback_url": CALLBACK_URL,
    "max_duration_seconds": 120,  # 最长通话时间
    "collected_fields": [
        {"name": "received_quote", "type": "boolean", "description": "用户是否收到报价单"},
        {"name": "questions", "type": "string", "description": "用户提出的问题或疑虑"}
    ]
}

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

response = requests.post(
    "https://api.callcow.ai/v1/prompt-to-call",
    headers=headers,
    json=payload
)

if response.status_code == 200:
    result = response.json()
    print("Call initiated:", result["call_id"])
    # 结果会异步发送到 callback_url
else:
    print("Error:", response.text)

效果演示:输入→输出对比

差 Prompt:

“打电话给客户。”

API 会报错或执行得很随机,因为缺少目标、目的、语气。

好 Prompt:

“你是一个牙科诊所的预约提醒助手,叫小雅。打电话给患者李女士(号码已提供),提醒她明天上午10点有洗牙预约。如果她确认,说‘明天见’;如果要改期,询问方便的时间并记录。语气温柔,语速中等。”

回调结果示例(JSON):

json
1 2 3 4 5 6 7 8 9 10 11
{
  "call_id": "call_abc123",
  "status": "completed",
  "duration_seconds": 67,
  "transcript": "小雅:您好,请问是李女士吗?我是牙科诊所的小雅,提醒您明天上午10点有洗牙预约。\n李女士:啊,我明天上午有事,能改到下午吗?\n小雅:好的,我帮您记一下。您方便的时间是下午几点呢?\n李女士:下午3点吧。\n小雅:好的,已经帮您改到明天下午3点。明天见!\n李女士:谢谢,再见。",
  "summary": "成功联系到患者李女士,将原定明天上午10点的洗牙预约改为下午3点。",
  "collected_data": {
    "confirmed": false,
    "new_time": "下午3点"
  }
}

对比很明显:差 prompt 像让实习生做事,好 prompt 像给资深员工分配任务。

为什么这样写有效?

CallCow 的 Prompt-to-Call 本质上是一个任务分解引擎。你给的 prompt 会被:

  1. 解析意图:提取动作(提醒、确认、记录)、实体(时间、人名)、约束(语气、长度)
  2. 生成对话流:自动设计开场→主对话→异常处理→结束的逻辑
  3. 绑定语音行为:控制语速、停顿、重试逻辑(比如用户没听清时重复)

关键在于 collected_fields 参数。很多人写 prompt 时忘了告诉 API 要提取什么数据,结果拿到一坨文本 transcript 还得自己解析。你显式定义字段后,API 会结构化返回,省去后处理。

变体和扩展用法

变体1:集成到 OpenClaw Agent

CallCow 官方发布了 OpenClaw skill,配置很简单:

yaml
1 2 3 4 5 6 7
# agent.yaml
skills:
  - name: callcow_phone
    type: callcow.agent-call
    config:
      api_key: "${CALLOOW_API_KEY}"
      default_voice: "natural-female-1"

然后在 agent 的 prompt 里直接引用:

“用户要求联系技术支持,使用 callcow_phone 技能拨打 1-800-555-0199,prompt 为‘告知用户订单号 #12345 的物流延迟,预计明天送达,致歉并提供补偿方案’。”

变体2:批量外呼

如果你要做批量通知,可以循环调用 API,但注意 CallCow 有并发限制(免费版 5 路,付费版 50 路)。建议用异步队列:

python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
import asyncio
import aiohttp

async def make_call(session, phone, prompt):
    async with session.post(
        "https://api.callcow.ai/v1/prompt-to-call",
        headers=headers,
        json={"to_number": phone, "prompt": prompt, "callback_url": CALLBACK_URL}
    ) as resp:
        return await resp.json()

async def batch_call(contacts):
    async with aiohttp.ClientSession() as session:
        tasks = [make_call(session, c["phone"], c["prompt"]) for c in contacts]
        return await asyncio.gather(*tasks)

变体3:动态 prompt 生成

固定 prompt 不够灵活。你可以让另一个 LLM 根据上下文动态生成 prompt:

python
1 2 3 4
def generate_call_prompt(context):
    base = "你是一个{role}。打电话给{name},目的是{purpose}。"
    base += "语气{style}。如果遇到{exception},则{fallback}。"
    return base.format(**context)

这样每个电话的 prompt 都定制化,准确率更高。

注意事项(我踩过的坑)

  1. 号码格式:必须 E.164 格式(+国家代码+号码),否则 API 返回 400。我一开始用了带空格的号码,浪费了半小时。
  2. prompt 长度:最佳实践是 150-300 字符,太长 GPT 会丢失重点,太短效果随机。
  3. 回调超时:CallCow 默认等 30 秒回调,如果你服务响应慢,可能收不到结果。建议回调 URL 返回 200 后立即处理,或把数据先存队列。
  4. 语音选择natural-female-1 听起来最自然,expressive-* 适合情感场景但偶尔会过度夸张。
  5. 费用:按分钟计费,约 $0.05/分钟(2026年5月价格),比 Twilio 贵但省了开发时间。

值不值得用?

如果你团队有通信背景,自己搭 Twilio + 语音合成可能更便宜。但如果你像我一样,只想让 Agent 快速拥有电话能力,CallCow 的 Prompt-to-Call 是真·开箱即用。尤其是 OpenClaw 集成,改几行配置就能跑,对于 MVP 阶段或内部工具来说,性价比很高。

AI voice agent making a phone call with code snippet overlay

下一步我打算把它接入客服系统,实现“AI 先打电话确认,复杂问题转人工”。如果你也在折腾类似的事,欢迎在评论区交流 prompt 技巧。