)
给 AI Agent 打电话基于 OpenAI Agents SDK 与 Twilio 的实时语音通话 Agentcall-my-agent 实战拆解【免费下载链接】agentsBuild and deploy AI Agents on Cloudflare项目地址: https://gitcode.com/GitHub_Trending/agents1/agents导读call-my-agent是当前仓库中一个完整可运行的最小示例它展示如何把一个基于 OpenAI Agents SDK 构建的 RealtimeAgent接入 Twilio 电话线路部署在 Cloudflare WorkersAgents 框架上让用户打一个电话就能与 AI 对话并在浏览器中实时看到通话转录文本。读完本文你将掌握 Agent 与 Twilio 媒体流Media StreamsWebSocket 对接的完整链路、TwiML 入站呼叫配置、Durable Object 中的实时会话管理以及前端实时转录面板的实现方式。一、项目概览一条电话 → AI的实时语音链路该示例的源码结构如下相对仓库根目录README.md —— 项目说明一句话做一个可以接电话的 Agent由 OpenAI Agents SDK 和 Twilio 驱动src/server.ts —— Worker 入口包含 TwiML 响应与 AgentDurable Object实现src/client.tsx —— React 前端展示通话状态与实时转录wrangler.jsonc —— Cloudflare Worker / Durable Object 配置package.json —— 开发、构建、部署脚本vite.config.ts —— Vite cloudflare/vite-plugin配置从代码结构看整条链路由三个环节组成入站呼叫Twilio 在接到来电时向 Worker 的/incoming-call端点发送 POST 请求Worker 返回一段 TwiML要求 Twilio 将通话媒体流转发到一个 WebSocket 地址.../agents/my-agent/123/media-stream。实时会话WebSocket 连接被routeAgentRequest路由到MyAgent一个 Durable Object的onConnect在media-stream路径下创建RealtimeAgent与RealtimeSession并通过TwilioRealtimeTransportLayer把电话音频与 OpenAI Realtime API 双向对接。实时转录会话的history_updated事件把对话历史写入 Agent 状态前端通过useAgent订阅状态实时渲染通话转录。二、服务端核心MyAgent 与 Twilio 实时传输层整个服务端逻辑集中在 src/server.ts。MyAgent继承自agents包提供的Agent基类本质上是一个由 Wrangler 配置的 Durable Object详见下文 wrangler 配置。export class MyAgent extends Agent { // dont use hibernation, the dependencies will manually add their own handlers async onConnect(connection: Connection, ctx: ConnectionContext) { if (ctx.request.url.includes(media-stream)) { // ...实时会话初始化 } } onMessage() {} // just a blank, the transport layer will add its own handlers onClose(connection: Connection) { connection.close(); } }这里有几个值得注意的实现细节关闭休眠hibernation注释明确指出不能使用 Durable Object 的休眠特性因为 Twilio 传输层会自行注册事件处理器onMessage()被留空正是传输层自己加 handler的体现。若启用休眠连接可能被挂起导致媒体流中断。按 URL 分流onConnect中通过ctx.request.url.includes(media-stream)判断该连接是否为电话媒体流。这保证了同一个 Agent 既可以处理媒体流 WebSocket也可以承载前端的普通 WebSocket 连接前端useAgent也连到同一个 Agent。2.1 RealtimeAgent定义接电话的 AI行为const agent new RealtimeAgent({ instructions: You are a helpful assistant that starts every conversation with a creative greeting., name: Triage Agent });RealtimeAgent来自openai/agents/realtime是 OpenAI Agents SDK 的实时语音Agent 形态。instructions定义系统提示词这里要求助手每次都以一句有创意的问候开场name用于标识。你可以在此处扩展更复杂的工具调用或语境设定这一层与普通文本 Agent 的配置方式一致。2.2 TwilioRealtimeTransportLayer音频的接线员const twilioTransportLayer new TwilioRealtimeTransportLayer({ twilioWebSocket: connection }); const session new RealtimeSession(agent, { transport: twilioTransportLayer }); await session.connect({ apiKey: process.env.OPENAI_API_KEY as string });TwilioRealtimeTransportLayer来自openai/agents-extensions接收 Twilio 的 WebSocket 连接connection即上一步由 Agents 框架路由进来的媒体流连接负责把电话一端的音频帧翻译成 Realtime Session 能理解的消息。RealtimeSession将RealtimeAgent与传输层绑定在一起形成一条电话 ↔ 传输层 ↔ OpenAI Realtime API的闭环。session.connect({ apiKey })使用OPENAI_API_KEY环境变量建立与 OpenAI Realtime API 的连接。因此在本地运行或部署时必须在环境变量或 Cloudflare Worker 的 Secrets中配置OPENAI_API_KEY。2.3 会话历史把对话沉淀到状态里session.on(history_updated, (history) { this.setState({ history }); });每当通话内容更新就通过this.setState({ history })把historyRealtimeItem[]写入 Agent 状态。这一步是前后端联动的关键前端正是通过读取这份状态来实现实时转录效果的见第四节。三、入站电话接入/incoming-call 与 TwiMLWorker 的fetch处理器在收到POST /incoming-call时返回一段 TwiML 指令告诉 Twilio 如何接管这通电话if (path /incoming-call request.method POST) { const twimlResponse ?xml version1.0 encodingUTF-8? Response SayO.K. you can start talking!/Say Connect Stream urlwss://call-my-agent.threepointone.workers.dev/agents/my-agent/123/media-stream / /Connect /Response.trim(); return new Response(twimlResponse, { headers: { Content-Type: text/xml } }); }对 TwiML 的拆解Say让 Twilio 先用文本转语音TTS播报一句O.K. you can start talking!给来电者一个清晰的开始提示。ConnectStream urlwss://...这是核心指令。Twilio 会把通话双方的音频流转发到指定的 WebSocket 地址——即上面MyAgent.onConnect处理的media-stream端点。响应头Content-Type: text/xml是 Twilio 识别 TwiML 所必需的。注意示例中的wss://call-my-agent.threepointone.workers.dev/...是示例部署时的占位地址。实际使用时应替换为你自己部署后的 Worker 域名。该 WebSocket 路径由 Agents 框架的路由约定生成其my-agent与123分别对应 Agent 标识与连接名与前端useAgent的agent: my-agent、name: 123一一对应见下节。当请求不是/incoming-call时fetch会把请求交给routeAgentRequest处理后者负责把/agents/...路径的 WebSocket / HTTP 请求路由到对应的 Agent Durable Object 实例没有命中任何路由时返回 404return ( (await routeAgentRequest(request, env, { cors: true })) || new Response(Not found, { status: 404 }) );{ cors: true }开启跨域支持使浏览器前端可以直接连接 Worker。Twilio 侧配置依据代码行为推断要让来电真正触达/incoming-call需要在 Twilio 控制台购买一个电话号码并把该号码的Voice → A Call Comes InWebhook 指向https://你的Worker域名/incoming-call方法选择POST。随后 Twilio 会按上述流程工作。四、前端通话状态与实时转录面板src/client.tsx 实现了一个仿通话界面的 React 页面通过agents/react的useAgent钩子订阅 Agent 状态useAgent{ history: RealtimeItem[] }({ agent: my-agent, name: 123, onStateUpdate(newState) { setState(newState); if (newState.history newState.history.length 0) { setCallStatus(connected); } } });agent/name与 TwiML 中 WebSocket URL 的my-agent/123保持一致前端建立的连接会路由到同一个 Agent Durable Object 实例。onStateUpdate在服务端setState({ history })后被触发前端据此刷新转录内容并在一开始有历史记录时把通话状态切换为connected。页面主体是一个通话面板头部显示Live Call Transcription标题、通话时长计时器callDuration每 1 秒递增和状态指示灯Connecting / Connected / Disconnected中间区域按history渲染消息气泡——用户role: user与助手role: assistant分列两侧每一条消息根据status显示✓completed/●in_progress同时展示打字指示动画底部是静音、挂断、扬声器三个装饰性控制按钮。未接到来电时界面显示Waiting for call to begin...的脉冲动画。转录内容来自message.content?.[0]?.transcript即 OpenAI Realtime 会话返回的文本转录字段代码中为音频内容保留了audio字段类型来自 OpenAI SDK 未导出的内部类型以any标注并显式屏蔽了 lint 检查。五、部署配置Wrangler、Durable Object 与迁移wrangler.jsonc 定义了 Worker 的运行环境{ compatibility_date: 2026-06-11, compatibility_flags: [nodejs_compat], durable_objects: { bindings: [ { class_name: MyAgent, name: MyAgent } ] }, main: src/server.ts, migrations: [ { new_sqlite_classes: [MyAgent], tag: v1 } ], name: call-my-agent }要点说明compatibility_flags: [nodejs_compat]启用 Node.js 兼容层openai/agents-extensions与openai/agents/realtime依赖的 Node 风格 API如Buffer、events等依赖该标志才能正常工作属必选项。durable_objects绑定 new_sqlite_classes迁移MyAgent被声明为一个基于 SQLite 存储的 Durable Object 类。Agents 框架在此基础上提供 WebSocket 会话、状态同步setState能力迁移标签v1是首次部署时必须执行的版本标记后续新增类需追加新迁移条目。main: src/server.tsWorker 入口导出的默认对象需满足ExportedHandlerEnv约束源码中已用satisfies校验。Env类型由wrangler types自动生成见 env.d.ts其中MyAgent: DurableObjectNamespace...的类型绑定与 wrangler 配置一一对应。六、本地开发与部署一条命令从 dev 到上线package.json 提供了三个脚本命令脚本内容用途npm run startvite dev本地开发启动 Vite 开发服务器含 Cloudflare 模拟运行时npm run deployvite build wrangler deploy构建前端资源并部署 Workernpm run typeswrangler types env.d.ts --include-runtime false依据 wrangler 配置重新生成Env类型典型工作流安装依赖后在环境变量或.dev.vars中配置OPENAI_API_KEY执行npm run start启动本地开发环境先在本地验证 Agent 与前端页面在 Twilio 控制台把电话号码的 Webhook 指向本地隧道如wrangler dev输出的地址或线上地址的/incoming-call确认一切正常后执行npm run deploy发布到 Cloudflare并把 Twilio Webhook 更新为线上域名。七、扩展方向与实现要点小结从该示例出发可以做如下扩展均可在这个最小闭环上叠加自定义系统提示词与工具调用RealtimeAgent的instructions与工具配置决定了接电话的 AI的个性与能力可替换为客服、导购、预约等场景话术呼叫方识别与多会话隔离Durable Object 以name维度隔离实例可在onConnect中根据呼叫方号码或会话 ID 动态路由到不同 Agent 实例转录持久化history_updated中目前只是setState可进一步写入 SQLite 或下游存储状态机扩展前端把callStatus绑定到history非空这一简单判据实际场景可结合 Twilio 回调事件如呼叫结束细化状态流转。总结这个示例最核心的工程要点媒体流对接靠 WebSocketTwiML 中ConnectStream指定 WebSocket 地址Worker 侧由routeAgentRequest路由到 Durable Object传输层解耦TwilioRealtimeTransportLayer屏蔽了 Twilio 媒体协议的细节RealtimeSession只关心与 OpenAI Realtime API 的对话前后端共享 Agent 状态setStateuseAgent的组合让通话与实时转录天然同步部署前提明确nodejs_compat兼容标志、Durable Object 迁移、OPENAI_API_KEY环境变量三者缺一不可。参照 openai-sdk/call-my-agent 的完整源码即可搭建出属于自己的可以打电话的 AI Agent。【免费下载链接】agentsBuild and deploy AI Agents on Cloudflare项目地址: https://gitcode.com/GitHub_Trending/agents1/agents创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考