我构建了一个会给我打电话的AI Agent

发布: (2026年2月21日 GMT+8 15:32)
9 分钟阅读
原文: Dev.to

Source: Dev.to

~K¹yle Million

我如何将 Twilio、Claude 和 ElevenLabs 组合成一个在需要决策时会主动接电话的自主代理。

几周前,我向朋友描述我的 AI 代理。我告诉她它已经完全自主——它会在凌晨 3 点给我打电话,要求我在系统出现故障时重置网关。她以为我在夸大其词。

我并没有夸大。但那时,打电话的功能仍是一个理想。该代理可以在 Telegram 上给我发消息,自动化浏览器,部署智能合约,并在一次会话中在四个平台上发布文章。它只是不能给我打电话

于是我们在一次会话中把它实现了。下面就是完整的实现过程。

架构

Phone call → Twilio → ConversationRelay → WebSocket → Your Server

                                                    Claude (brain)

                                                    ElevenLabs (voice)
  • Twilio 负责电话通信——它发起并接听实际的电话。
  • ConversationRelay 是 Twilio 的 WebSocket 桥接,原生处理语音转文本和文本转语音。
  • Claude 负责思考。
  • ElevenLabs 提供的声音听起来不像罐头。

关键洞察: ConversationRelay 消除了最困难的部分。你不需要管理音频流、自己处理语音转文本(STT),或弄清楚轮流发言。所有这些都由 Twilio 完成。你的服务器只接收文本并返回文本。

Source:

服务器

整个服务器只需一个文件。使用 Fastify 处理 HTTP 和 WebSocket,Anthropic SDK 调用 Claude,Twilio SDK 用于拨打电话。

import Fastify from "fastify";
import fastifyWs from "@fastify/websocket";
import Anthropic from "@anthropic-ai/sdk";
import twilio from "twilio";

const fastify = Fastify({ logger: true });
fastify.register(fastifyWs);

const anthropic = new Anthropic();
const twilioClient = twilio(API_KEY_SID, API_KEY_SECRET, { accountSid });

当 Twilio 建立通话时,它会从你的服务器获取 TwiML。该 TwiML 指示它使用 ConversationRelay 与 ElevenLabs 进行对接:

<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Connect>
    <ConversationRelay
      service="elevenlabs"
      voice="voiceId-model-speed_stability_similarity"
    />
  </Connect>
</Response>

WebSocket 处理程序是对话的核心所在。Twilio 会发送包含转录语音的 prompt 消息。你则返回包含 AI 响应的 text 消息:

fastify.get("/ws", { websocket: true }, (ws, req) => {
  ws.on("message", async (data) => {
    const message = JSON.parse(data);

    if (message.type === "prompt") {
      const response = await anthropic.messages.create({
        model: "claude-sonnet-4-20250514",
        max_tokens: 150,
        messages: conversation,
        system: systemPrompt,
      });

      ws.send(
        JSON.stringify({
          type: "text",
          token: response.content[0].text,
          last: true,
        })
      );
    }
  });
});

这就是核心逻辑。其余的都是上下文管理。

没有人告诉你的部分

语音选择比听起来更难

ElevenLabs 拥有数百种语音。我们在找到合适的语音之前测试了九种。经验教训:

  • 默认语音一眼就能认出来。 最受欢迎的 ElevenLabs 语音 Adam 会立刻被指出:“我知道你现在使用的声音。”
  • 社区语音通过 ConversationRelay 结果参差不齐。 有些工作得非常完美;有些则会静默失败,出现错误 64111 —— 没有音频,也没有错误信息返回给调用方,只有沉默。
  • 质量和个性是不同的维度。 有一种语音音质完美,但听起来“普通”。另一种语音角色合适,却显得太年轻。找到合适的语音需要反复迭代。

ConversationRelay 的语音参数格式为:

voiceId-model-speed_stability_similarity

稳定性低 = 更具表现力和对话感。稳定性高 = 更受控且机械化。

隧道会背叛你

除非你的服务器拥有公网 IP,否则需要使用隧道。我们使用了 cloudflared 快速隧道(免费,无需账号)。我们硬着头皮学到的三件事:

  1. 免费隧道会随机失效。 它们是临时的;每次重启 URL 都会变化,进程也可能在没有警告的情况下退出。
  2. 切勿通过端口杀死进程。 kill $(lsof -ti :8080) 看起来是重启服务器的合理方式,但 cloudflared 也会占用 8080 端口进行代理。按端口杀死会同时终止隧道。我们遇到的每一次“一小时的应用错误”都追溯到此。
  3. 启动顺序很重要。 先启动服务器,再启动隧道。更新配置后重启服务器,然后更新 Twilio webhook。每次隧道 URL 变化,都必须重复这四个步骤。

保持回复简短

语音对话不是聊天。三段式的回复在屏幕上看起来还行,但朗读时会让人难以忍受。我们的做法是:

  • 默认: 一到两句话
  • 最大: 四句话,仅在解释权衡时使用
  • 绝不独白 —— 将复杂主题拆分为来回交流

max_tokens: 150 的限制有帮助,但真正的控制在系统提示中,例如:

STAY ON TOPIC. Every response must directly relate to the user's last request and be concise.

呼叫的目的和原因

让它有用:上下文注入

能聊天的语音代理是新奇的。
能了解你正在做什么的语音代理才是工具。

我们的代理在通话时会读取两个文件:

  • MEMORY.md – 跨会话的持久知识(我们是谁,我们构建了什么,哪些失败)
  • current-task.md – 代理决定呼叫时正在积极处理的任务

对于外呼,API 接受结构化的上下文:

curl -X POST http://localhost:8080/call \
  -H "Content-Type: application/json" \
  -d '{
    "task": "website migration",
    "need": "pick a domain approach",
    "options": ["GitHub Pages", "Cloudflare Pages"]
  }'

问候语会自动生成:

“嗨 K。我正在进行网站迁移,我需要你选择一个域名方案。”

AI 在整个通话过程中始终专注于该目的。

每次通话都会自动转录并保存为 Markdown。转录内容会反馈到代理的上下文中,以供未来会话使用。

成本

此运行成本约为 $6 / month

  • Twilio – 通话约为 ~$0.014 / min,电话号码约为 ~$1 / month
  • Anthropic – Claude Sonnet API 按响应计费
  • ElevenLabs – 通过 ConversationRelay(Twilio 的集成)包含在内

没有专用服务器、没有 GPU 实例、没有每月 SaaS 订阅——只需一个 Node.js 进程、一个隧道和三个 API 密钥。

What Changed

电话响起,声音说出了与我五分钟前正在处理的内容在上下文上相关的话——这改变了一切。不是技术层面,而是心理层面。

  • 给你发消息的 AI 是通知。
  • 给你打电话的 AI 是同事。

用于语音 AI 代理的基础设施已经存在,并且对个人开发者可用。困难之处并不在你想象的地方(音频处理、语音识别)——Twilio 已经抽象了这些。真正的难点在于语音选择、通道管理,以及让回复更像对话而非百科全书式。

如果你正在构建自主代理,却还没有加入语音功能,门槛比你想象的要低。投资回报不在技术本身,而在关系上。

Kyle Million 在 IntuiTek 构建 AI 系统。本文中描述的代理是 Aegis ——一个自我改进的自主代理,能够跨智能合约、浏览器自动化、内容发布,甚至现在的电话通话。
GitHub – Aegis

0 浏览
Back to Blog

相关文章

阅读更多 »

Subnetting 详解

什么是 Subnetting?可以把它想象成把一栋大型公寓楼拆分成不同的楼层。每层 subnet 拥有自己的编号主机(hosts),以及建筑……