ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

TEN Framework 语音助手中枢扩展 main_python:从 Twilio 通话到 ASR/LLM/TTS 的完整编排实践

TEN Framework 语音助手中枢扩展 main_python:从 Twilio 通话到 ASR/LLM/TTS 的完整编排实践 TEN Framework 语音助手中枢扩展 main_python从 Twilio 通话到 ASR/LLM/TTS 的完整编排实践【免费下载链接】ten-frameworkOpen-source framework for conversational voice AI agents项目地址: https://gitcode.com/TEN-framework/ten-framework导读main_python是 TEN Framework 语音助手示例voice-assistant-sip-twilio中的核心控制扩展承担 AI 智能体对话的总指挥角色它接收 ASR 识别结果、驱动 LLM 生成回复、把文本交给 TTS 合成语音并负责用户会话状态管理与跨组件数据路由。本文基于该扩展的源码与配置完整讲解其事件驱动架构、输入输出接口、配置参数、Twilio 媒体流集成原理与音频采样率转换细节读完即可理解并二次开发一套可运行的电话语音助手中枢逻辑。扩展定位为什么需要一个主控制扩展在 TEN Framework 的图graph编排模型中语音助手通常由 STT、LLM、TTS、RTC 等多个扩展协作完成。main_python不是这些能力的实现者而是它们的协调者——正如其文档所述它充当 AI 智能体对话的编排器处理实时语音处理、LLM 交互和 TTS 输出并管理用户会话状态。从源码结构看extension.pyMainControlExtension继承AsyncExtension其核心职责可归纳为生命周期管理在on_init中加载配置并启动内置的 Twilio 呼叫服务器在on_stop中结束所有活动通话并清理资源事件路由把框架下达的Cmd与Data转交给内部Agent事件循环处理音频转发on_audio_frame将 TEN 框架产出的 PCM 音频下发到所有活跃 Twilio 通话的 WebSocket打断Interrupt检测到用户新语音时向 LLM、TTS、RTC 发送 flush 指令实现边说边打断的自然对话体验。在示例图的编排中见 tenapp/property.jsonmain_control节点使用main_pythonaddon与deepgram_asr_python、openai_llm2_python、elevenlabs_tts2_python共同组成voice_assistant图。事件驱动内核Agent 类与事件队列main_python的内部编排基于一个轻量级事件系统核心实现在 agent/agent.py 与 agent/events.py。事件类型AgentEvent事件基类AgentEventBase定义了typecmd/data与name派生出五类事件事件类型触发来源UserJoinedEventcmdon_user_joined用户加入会话UserLeftEventcmdon_user_left用户离开会话ToolRegisterEventcmdtool_register其他扩展注册 LLM 工具ASRResultEventdataasr_resultASR 识别结果含中间/最终结果LLMResponseEventdatallm_responseLLM 流式响应含 message/reasoning双队列消费模型Agent内部维护两条asyncio.Queue_asr_queueASR 结果按序消费由_consume_asr任务派发_llm_queueLLM 流式输出按序消费_consume_llm会把当前事件包装为独立asyncio.Task这样当用户中途说话触发打断时可以取消正在执行的 LLM 处理任务对应flush_llm中_llm_active_task.cancel()的逻辑避免过时回复继续播放。装饰器注册机制扩展在on_init中扫描自身方法凡带有_agent_event_type属性的方法都会通过self.agent.on(event_type, fn)自动注册为事件处理器见 extension.py。注册语法由 agent/decorators.py 提供典型处理器包括_on_asr_result更新session_id、递增turn_id、必要时触发打断、把最终结果送入 LLM、并发送转写文本_on_llm_response将 LLM 增量文本按句子切分后逐句送入 TTS_send_to_tts同时以message/reasoning两种类型发送转写。接口契约输入数据、输出数据与命令README 文档明确给出了该扩展面向框架的接口契约以下是结合源码的完整解读。输入数据ASR ResultData 名为asr_result由Agent.on_data解析{ text: string, final: bool, metadata: { session_id: string } }final为false时表示中间识别结果用于实时字幕为true时表示最终结果会递增turn_id并送入 LLM。注意_on_asr_result中有一个细节当event.final为真或文本长度大于 2 时都会触发_interrupt()这是为了让用户一开口就立刻打断当前播放。LLM Result由LLMExec回调产生type可为message或reasoning{ text: string, end_of_segment: bool }在事件模型中对应LLMResponseEvent的delta增量片段、text累计文本、is_final是否最终与type字段。输出数据Text Data扩展通过_send_transcript把转写文本发送给message_collector字段如下{ text: string, is_final: bool, end_of_segment: bool, stream_id: uint32 }实际发送的负载见 extension.py还包含data_typetranscribe或raw、roleuser/assistant、text_ts毫秒时间戳等字段reasoning 内容会以 JSON 字符串包装后作为raw类型发送。此外扩展还会向 TTS 发送两类数据tts_text_input目标tts携带request_id、text、text_input_end是否本段结束与metadatatts_flush目标tts携带flush_id用于清空 TTS 播放队列。命令Commands输入命令on_user_joined用户加入会话、on_user_left用户离开会话以及tool_register注册 LLM 工具见 agent/agent.py输出命令flush由_interrupt发出目标为agora_rtc_send_cmd(self.ten_env, flush, agora_rtc)用于清空 RTC 音频缓冲实现说话即打断。辅助函数_send_cmd、_send_data定义在 helper.py 中它们通过Loc(, , dest)直接指定图内目标扩展发送省去了手动建立连接的开销注释也提醒这类写法仅适用于该图内部逻辑通用扩展应避免。配置体系从 property.json 到 Pydantic 模型配置参数总览README 中列出的配置只有greeting一项但仓库实际配置远不止于此。扩展的配置由 Pydantic 模型MainControlConfig定义见 config.py在on_init中通过ten_env.get_property_to_json(None)读取运行时属性并校验。参数类型默认值说明greetingstringHello, I am your AI assistant.首通电话接通后播放的问候语twilio_account_sidstringTwilio 账户 SID必填twilio_auth_tokenstringTwilio 认证令牌必填twilio_from_numberstring外呼使用的 Twilio 号码必填twilio_server_portint328000内置服务器端口同时承载 HTTP API 与 WebSockettwilio_public_server_urlstring公网服务器地址不含协议如your-domain.com:8000用于媒体流与 Webhooktwilio_use_httpsbooltrueWebhook 使用 HTTPS 还是 HTTPtwilio_use_wssbooltrue媒体流使用 WSS 还是 WS配置文件 property.json 展示了实际用法——敏感信息全部通过${env:XXX}占位符从环境变量注入{ greeting: Hello, I am your AI assistant., twilio_account_sid: ${env:TWILIO_ACCOUNT_SID}, twilio_auth_token: ${env:TWILIO_AUTH_TOKEN}, twilio_from_number: ${env:TWILIO_FROM_NUMBER}, twilio_server_port: 9000, twilio_public_server_url: ${env:TWILIO_PUBLIC_SERVER_URL}, twilio_use_https: false, twilio_use_wss: false }注意示例的 property.json 中twilio_use_https/twilio_use_wss为false这是配合 ngrok 本地开发的选择——ngrok 负责 SSL 终结本地服务器保持 HTTP/WS若直接部署在带证书的公网服务器上则建议开启。twilio_public_server_url为空时媒体流与状态回调不会启用因此它是媒体流能力的开关。对应的运行时 schema 声明在 manifest.json 的api.property.properties中类型包括 string/int32/bool供 TEN 运行时做配置校验。环境变量准备在示例根目录的.env中配置详见 voice-assistant-sip-twilio/README.md# Twilio必填 TWILIO_ACCOUNT_SIDyour_twilio_account_sid_here TWILIO_AUTH_TOKENyour_twilio_auth_token_here TWILIO_FROM_NUMBER1234567890 TWILIO_PUBLIC_SERVER_URLhttps://your-domain.com # DeepgramSTT必填 DEEPGRAM_API_KEYyour_deepgram_api_key_here # OpenAILLM必填 OPENAI_API_KEYyour_openai_api_key_here OPENAI_MODELgpt-4 # ElevenLabsTTS必填 ELEVENLABS_TTS_KEYyour_elevenlabs_tts_key_here # 可选 WEATHERAPI_API_KEYyour_weather_api_key_here NGROK_AUTHTOKENyour_ngrok_auth_token_here对话工作流从电话接通到语音回复综合 README 的 Workflow 与源码实现一条完整通话的处理链路如下用户加入Twilio 媒体流 WebSocket 建立后服务器收到start事件记录callSid与streamSid随即调用on_websocket_connected——扩展会立即把配置的greeting文本送入 TTS_send_to_tts(greeting_text, True)实现接听即问候。语音上行Twilio 把用户语音以 μ-law 编码的 base64 负载通过 WebSocket 的media事件推送到/media端点TwilioCallServer解码后调用_forward_audio_to_ten见 server.py 与 extension.pybase64.b64decode还原 μ-law 数据audioop.ulaw2lin(mulaw_data, 2)转换为 16-bit PCM构造AudioFrame(pcm_frame)标注 8000 Hz、单声道、2 字节/样本、INTERLEAVE 格式stream_id固定为 54321目的地指向streamid_adapter扩展最终经ten_env.send_audio_frame送入 STT 处理。ASR 与打断STT 返回的asr_result数据触发_on_asr_result一旦检测到用户说话final 或文本超 2 字符立即调用_interrupt()清空句子缓存、flush_llm、向 TTS 发tts_flush、向 RTC 发flush保证用户新输入优先。LLM 处理最终 ASR 文本经agent.queue_llm_input进入 LLM 上下文LLMExec实现在 agent/llm_exec.py。TTS 合成与下行LLM 流式增量经parse_sentences按中英文标点,.。?!切句完整句子即时送入 TTS 以降低首包延迟is_final的剩余片段在结尾补发text_input_endTrue。TTS 生成的 16 kHz PCM 音频帧回到on_audio_frame经send_audio_to_twilio下发。音频下行send_audio_to_twilio中完成16 kHz → 8 kHz降采样与 PCM→μ-law 编码再以{event: media, streamSid: ..., media: {payload: base64}}的格式写入通话对应的 WebSocket见 extension.py。通话结束Twilio 推送stop事件或用户挂断后扩展通过DELETE /api/call/{call_sid}结束通话清理active_call_sessions与音频转储文件。Twilio 媒体流集成内置服务器详解main_python的一大特点是将 Twilio 集成服务器内嵌到扩展进程中_start_server在on_init中启动由 server.py 中的TwilioCallServer基于 FastAPI uvicorn 实现同一端口同时提供 HTTP API 与 WebSocket 媒体流端点方法作用/api/callPOST创建外呼生成含ConnectStream的 TwiML 并调用 Twilio API/api/call/{call_sid}GET查询通话状态/api/call/{call_sid}DELETE结束通话置为 completed/api/callsGET列出所有活跃通话/webhook/statusPOST/GET接收 Twilio 通话状态回调initiated/ringing/answered/completed/api/configGET返回服务器配置与媒体流/Webhook URL/healthGET健康检查/mediaWebSocketTwilio 媒体流端点接收用户语音、下发 TTS 音频关键设计点媒体流地址构造connect.stream(url...)的地址由twilio_use_wss决定协议前缀/media为路径Webhook 地址由twilio_use_https决定路径为/webhook/status。二者均基于twilio_public_server_url拼装。SSL 处理start_server中即使配置了 HTTPS/WSS本地仍以 HTTP 启动 uvicorn注释明确说明ngrok 将负责 SSL 终结——这降低了本地开发门槛。通话状态机active_call_sessions以call_sid为键维护会话包含phone_number、message、status、stream_sid、websocket等字段WebSocketstart事件到达后才绑定流 SID 与 WebSocket 对象。音频转储扩展支持把通话音频以 PCM 文件转储到audio_dump_directory默认/tmp/twilio_audio_dumps便于调试文件名为twilio_audio_{call_sid}_{timestamp}.pcm。音频采样率与编码转换细节Twilio 媒体流固定使用8 kHz、μ-law、单声道而 TEN 图内 TTS如示例中的 ElevenLabspcm_16000输出16 kHz PCM因此扩展承担了双向转换上行Twilio → TENμ-law → PCMaudioop.ulaw2lin保持 8 kHz交由 STT 处理图中 Deepgram 节点sample_rate配置为 8000见 tenapp/property.json。下行TEN → Twilio先降采样再编码_downsample_audio处理 16 kHz → 8 kHz对 16-bit 样本采用每 2 个样本取 1 个的简单抽取decimation其他采样率组合则回退到audioop.ratecv按最大公约数计算转换比audioop.lin2ulaw(downsampled, 2)把 PCM 编码为 μ-lawbase64.b64encode后装入media事件负载发送。这一设计保证了电话侧的语音质量与延迟之间的平衡也让扩展可以直接复用 TEN 生态中标准的 16 kHz TTS 输出。安装、构建、测试与运行通过 TEN 包管理器操作README 给出的标准操作扩展位于示例 tenapp 内命令在对应 tenapp 目录下执行# 安装扩展 ten install main_python # 构建扩展 ten build main_python # 运行测试 ten test main_python运行完整示例安装依赖并启动在示例根目录cd ai_agents/agents/examples/voice-assistant-sip-twilio task install task run本地开发时用 ngrok 暴露端口start-with-ngrok.sh会自动启动 ngrok 并把公网地址用于TWILIO_PUBLIC_SERVER_URL./start-with-ngrok.sh访问入口前端控制台http://localhost:3000支持外呼发起/接听管理见 frontend内置 API/WebSocket 服务器http://localhost:9000TMAN Designer可视化改图http://localhost:49483外呼示例调用扩展内置的 REST APIcurl -X POST http://localhost:9000/api/call \ -H Content-Type: application/json \ -d { phone_number: 1234567890, message: Hello from AI assistant! }依赖版本说明README 中声明的依赖为ten_runtime_python0.10 与ten_ai_base0.6.9而仓库当前 manifest.json 实际声明为ten_runtime_python0.11 与ten_ai_base0.7具体以仓库当前版本为准。代码中大量使用ten_runtime的AsyncExtension、AsyncTenEnv、Cmd、Data、AudioFrame、Loc以及ten_ai_base.types.LLMToolMetadata类型安装时需保证这两个系统包可达。架构小结与扩展点从实现看main_python采用事件驱动 双队列 内置服务器的架构Agent类作为纯事件总线不依赖具体 STT/LLM/TTS 实现MainControlExtension作为运行时驱动对接 TEN 框架与 Twilio 媒体流TwilioCallServer作为通信边界HTTP API WebSocket。三者解耦清晰使得替换 STT/LLM/TTS 提供商只需修改图配置如通过 TMAN Designer 在 http://localhost:49483 调整节点属性无需改动主控制逻辑新增会话事件只需定义新的AgentEvent子类并注册处理器接入其他电话通道如 voice-assistant-sip-plivo、voice-assistant-sip-telnyx可复用同样的主控制编排模式。如果需要了解该示例的完整使用方式环境变量、ngrok 配置、Docker 打包等可继续阅读 voice-assistant-sip-twilio/README.md 与 server/README.md。许可证main_python扩展属于 TEN Framework 的一部分遵循 Apache License 2.0见 LICENSE欢迎在遵循贡献规范的前提下参与改进。【免费下载链接】ten-frameworkOpen-source framework for conversational voice AI agents项目地址: https://gitcode.com/TEN-framework/ten-framework创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表