- 人工智能
- AI Agent
- 多模态
- 语音
- AI 应用
【免费下载链接】ten-framework
Open-source framework for conversational voice AI agents
本篇技术指南围绕 TEN Framework 开源仓库中的stepfun_mllm_python扩展展开,讲解如何将 StepFun(阶跃星辰)新一代多模态实时模型(默认step-1o-audio)以 WebSocket Realtime 协议的方式接入语音 Agent 应用,实现"语音进、语音出"(voice-to-voice)与文本处理的端到端能力。读完本文,你将掌握该扩展的完整配置项语义、manifest 属性契约、数据/命令接口、底层消息协议与源码实现路径,并能够参照仓库中的 stepfun-demo 示例搭建可运行的多模态语音助手。
扩展概览:一个与 OpenAI Realtime 协议对齐的多模态语音扩展
stepfun_mllm_python位于 ai_agents/agents/ten_packages/extension/stepfun_mllm_python 目录,是一个标准的 TEN Framework Python 扩展(extension),版本号为0.2.2。从 extension.py 的类定义可以看到,它继承自AsyncMLLMBaseExtension,并在注释中明确说明其设计目标:
StepFun realtime provider, API-compatible with OpenAIRealtime2Extension: same public methods, same server event mapping → send_server_* APIs
这意味着该扩展在 TEN 生态中扮演的是 MLLM(多模态大模型)Provider 的角色,对外暴露与 OpenAI Realtime 扩展一致的公共方法与服务端事件映射,凡是遵循ten_ai_base中mllm-interface约定的上游节点(如转写模块、主控逻辑),都可以无差别地对接 StepFun。
核心特性
按照 README.md 的说明,该扩展提供三大特性:
- StepFun 多模态集成:支持将 StepFun 多模态模型用于语音到语音(voice to voice)以及文本处理;
- 高度可配置:可通过属性灵活定制 API Key、模型、提示词、temperature 等参数;
- 异步队列处理:基于 asyncio 的实时消息处理,支持任务取消与优先级调度(配合服务端 VAD 实现打断/优先响应)。
API 契约:manifest 属性定义与默认值
该扩展的 API 定义在 manifest.json 的api.property.properties中,默认值则在 property.json 中给出。下表汇总了两者的完整对应关系:
| Property | Type | 默认值 | Description |
|---|---|---|---|
api_key | string | ${env:STEPFUN_API_KEY} | 用于向 StepFun 认证的 API Key |
temperature | float | 0.9 | 采样温度,值越高随机性越强 |
model | string | step-1o-audio | 模型标识符 |
max_tokens | int | 2048 | 生成的最大 token 数 |
system_message | string | (未在 property.json 中设置,见prompt) | 发送给模型的默认系统消息 |
voice | string | linjiajiejie | StepFun 模型说话的语音(如alloy、echo、shimmer等) |
server_vad | bool | true | 是否启用 StepFun 服务端 VAD |
language | string | en | 模型回复使用的语言(如en-US、zh-CN等) |
dump | bool | false | 是否启用音频 dump 用于调试 |
需要注意,README 中的属性表描述的是接口语义,而 manifest.json 中的 schema 是 TEN 运行时实际校验与注入的依据,两者存在轻微差异(如system_message在 schema 中以prompt字段出现)。实际生效的完整配置集合,以源码中StepFunRealtimeConfig数据类为准,详见下文"配置解析"一节。
数据接口(Data Out)
扩展向图(graph)下游节点输出的数据消息:
| Name | Property | Type | Description |
|---|---|---|---|
text_data | text | string | 输出的文本数据 |
命令接口(Command Out)
| Name | Description |
|---|---|
flush | 刷新当前状态后给出响应 |
音频帧接口(Audio Frame In / Out)
| Name | Direction | Description |
|---|---|---|
pcm_frame | In | 语音处理的音频帧输入(用户语音) |
pcm_frame | Out | 语音处理后的音频帧输出(模型语音) |
配置解析:从 property.json 到 StepFunRealtimeConfig
扩展在on_init阶段通过ten_env.get_property_to_json(None)读取全部属性,并用 Pydantic 的model_validate_json校验生成StepFunRealtimeConfig。完整配置字段及其默认值定义在 extension.py:
@dataclass class StepFunRealtimeConfig(BaseModel): base_url: str = "wss://api.stepfun.com" api_key: str = "" path: str = "/v1/realtime" model: str = "step-1o-audio" language: str = "en" prompt: str = "" temperature: float = 0.5 max_tokens: int = 1024 voice: str = "linjiajiejie" server_vad: bool = True audio_out: bool = True sample_rate: int = 24000 # VAD tuning vad_type: Literal["server_vad", "semantic_vad"] = "server_vad" vad_eagerness: Literal["low", "medium", "high", "auto"] = "auto" vad_threshold: float = 0.5 vad_prefix_padding_ms: int = 300 vad_silence_duration_ms: int = 500 dump: bool = False dump_path: str = ""各字段核心语义:
base_url+path:WebSocket 服务地址,默认拼接为wss://api.stepfun.com/v1/realtime,模型名通过查询参数?model=...附加(见 connection.py);sample_rate:输入输出音频采样率,默认 24000 Hz,扩展的input_audio_sample_rate()与synthesize_audio_sample_rate()均直接返回该值(extension.py),即整个图链路统一按 24 kHz PCM 处理;vad_type/vad_eagerness/vad_threshold/vad_prefix_padding_ms/vad_silence_duration_ms:VAD 调优参数。服务端 VAD 模式(server_vad)下,阈值、语音前填充(默认 300ms)与静音判定时长(默认 500ms)会被组装进session.update请求(extension.py);audio_out:是否输出音频模态。为false时session.update会设置modalities = ["text"],实现纯文本对话模式(extension.py);prompt:即 README 中system_message对应的实际实现字段,作为 session 的instructions下发(extension.py);language:通过InputAudioTranscription(language=...)注入输入音频转写的目标语言(extension.py)。
若api_key为空,扩展会在on_init阶段直接抛出ValueError("api_key is required")拒绝启动(extension.py),因此部署时必须提供有效的 StepFun API Key。
依赖与运行环境
manifest.json 声明了两个系统依赖:
ten_runtime_python(版本0.11):TEN 运行时 Python 绑定;ten_ai_base(版本0.7):提供AsyncMLLMBaseExtension基类与MLLMClientMessageItem、MLLMServerFunctionCall、MLLMServerInputTranscript等结构化消息类型。
Python 侧依赖见 requirements.txt 与 pyproject.toml:要求 Python >= 3.10,依赖aiohttp>=3.14.1(WebSocket 客户端)、pydantic>=2.13.4(配置校验)与pydub==0.25.1。
运行机制:WebSocket Realtime 协议与事件循环
扩展的运行核心是 extension.py 中的start_connection()客户端事件循环,整体链路可概括为:
- 建立连接:
RealtimeApiConnection用aiohttp.ClientSession.ws_connect连接base_url + path + ?model=...,并在请求头携带Authorization: Bearer <api_key>(connection.py); - 会话建立:收到
session.created后标记connected=True、缓存session_id,随后发送session.update(注入 prompt、tools、VAD 参数、语音与转写语言),并在session.updated到达后向上游广播mllm_server_session_ready; - 用户语音上行:
send_audio()将收到的AudioFrame二进制按 PCM16 编码为 Base64,封装为input_audio_buffer.append事件发送(connection.py); - 服务端事件分发:
listen()异步迭代 WebSocket 消息,经parse_server_message按type字段反序列化为 30 余种 dataclass 事件(struct.py),再交由match message:模式匹配分发处理; - 输出事件上抛:
- 文本流:
response.text.delta/response.audio_transcript.delta累积为response_transcript,通过send_server_output_text输出mllm_server_output_transcript(final=False),结束事件则补发 final=True; - 音频流:
response.audio.delta中的 Base64 数据解码后经send_server_output_audio_data以 PCM 帧形式输出(extension.py);
- 文本流:
- 打断与 VAD:收到
input_audio_buffer.speech_started且server_vad=true时,发送mllm_server_interrupted中断当前生成,并为未完成的转写追加[interrupted]标记后以 final=True 收尾(extension.py); - 断线重连:事件循环异常退出后调用
_handle_reconnect(),以 1 秒退避延时重新start_connection()(extension.py)。
客户端 → 服务端消息类型
struct.py 定义了完整的客户端上行消息集合,均由ClientToServerMessage派生并自动生成event_id(UUID):
input_audio_buffer.append/commit/clear:音频缓冲区操作;conversation.item.create:创建用户/助手/函数调用输出消息项;response.create:触发模型生成(含cancel_previous取消上一轮生成);session.update:更新会话参数;conversation.item.truncate/delete:截断或删除对话项。
服务端 → 客户端事件分类
下行事件在 struct.py 中按EventType枚举组织,主要分为:
- 会话生命周期:
session.created、session.updated、error; - 输入语音转写:
conversation.item.input_audio_transcription.delta / completed / failed; - 响应流:
response.created / done、response.text.delta / done、response.audio_transcript.delta / done、response.audio.delta / done; - VAD / 轮流:
input_audio_buffer.speech_started / speech_stopped、input_audio_buffer.committed / cleared; - 工具调用:
response.function_call_arguments.delta / done; - 其他:
response.output_item.added / done、rate_limits.updated。
序列化时to_json会剔除值为None的字段,保证上行报文精简(struct.py)。
工具集成:将外部工具接入 StepFun 多模态模型
该扩展完整实现了 MLLM 工具调用链路,README 中注释掉的 Tool Support 特性在源码中已有落地:
- 工具注册:上游节点通过
send_client_register_tool()注册LLMToolMetadata,工具被缓存到available_tools并触发session.update(extension.py); - 会话工具清单:
_update_session()将工具元数据转换为{"type": "function", "name", "description", "parameters"}结构,并根据是否存在工具设置tool_choice为"auto"或"none"(extension.py); - 工具调用回传:收到
response.function_call_arguments.done后,通过send_server_function_call向上游广播mllm_server_function_call(含 call_id、函数名与 JSON 参数); - 结果回填:上游执行完成后调用
send_client_function_call_output(),将结果封装为conversation.item.create(function_call_output类型)回传模型(extension.py)。
以仓库中的 stepfun-demo 为例(property.json),weatherapi_tool_python通过tool_register命令向main_control注册天气工具,main_control再将mllm_server_function_call等消息与 v2v(即 stepfun_mllm_python)相连,形成"模型发起工具调用 → 主控路由 → 工具执行 → 结果回填模型"的闭环。
端到端实战:基于 stepfun-demo 的图编排示例
仓库在 ai_agents/agents/examples/stepfun-demo 提供了完整可运行的示例,其 tenapp/property.json 定义了名为voice_assistant_realtime的预置图(predefined graph)。核心节点与连接如下:
- 音频输入输出:
agora_rtc(声网 RTC 负责采集/播放),通过pcm_frame音频帧与扩展互通; - 流 ID 适配:
streamid_adapter在 RTC 与扩展之间做流标识转换; - 主控:
main_control消费mllm_server_input_transcript、mllm_server_output_transcript、mllm_server_session_ready、mllm_server_interrupted、mllm_server_function_call等数据消息; - MLLM 核心:
v2v节点即stepfun_mllm_python扩展,配置如下(与扩展默认 property.json 完全一致):
{ "api_key": "${env:STEPFUN_API_KEY}", "temperature": 0.9, "model": "step-1o-audio", "max_tokens": 2048, "voice": "linjiajiejie", "language": "en", "server_vad": true, "history": 10, "enable_storage": false, "base_url": "wss://api.stepfun.com" }图中的音频帧连接(property.json)形成两条关键通路:
- 上行:
agora_rtc→streamid_adapter→v2v(用户语音送入 StepFun 模型); - 下行:
v2v→agora_rtc(模型合成语音回放给用户)。
配置中使用${env:STEPFUN_API_KEY}形式从环境变量注入密钥,这是 TEN 生态推荐的密钥管理方式——密钥不出现在配置文件中,且支持${env:VAR|default}的默认值语法。
调试建议
- 开启 verbose 日志:
RealtimeApiConnection的verbose参数开启后,会在日志中打印双向 WebSocket 消息;smart_str会将delta/audio长字段截断到 128 字符,避免日志被音频数据刷屏(connection.py); - 音频 dump:设置
dump: true与dump_path可将输入输出音频落盘,用于排查采集/回放问题; - 观察会话事件:日志中
Session created、Session updated、Resp created、Resp done(含 usage token 统计)等关键节点均打点输出,可作为运行状态的风向标; - 转写失败排查:
conversation.item.input_audio_transcription.failed事件会携带错误详情并以log_warn记录,可据此确认language参数是否与服务端支持的语言一致。
小结
stepfun_mllm_python是一个与 OpenAI Realtime 协议对齐的异步多模态扩展:通过 manifest.json 声明属性契约、property.json 提供默认值、extension.py 承载事件循环与 TEN 消息桥接、realtime/connection.py 与 realtime/struct.py 实现 WebSocket 协议编解码。将其作为图中的一个v2v节点,配合音频帧通路与mllm_server_*数据消息,即可快速构建基于 StepFun 多模态模型的实时语音 Agent,并复用 TEN 生态中现成的 RTC、工具与主控组件。
- 人工智能
- AI Agent
- 多模态
- 语音
- AI 应用
【免费下载链接】ten-framework
Open-source framework for conversational voice AI agents
相关推荐
TEN Framework 集成 StepFun 实时语音模型:stepfun-demo 语音 Agent 演示项目实战指南
TEN Framework 集成 StepFun 实时语音模型:stepfun demo 语音 Agent 演示项目实战指南 本文以 ai_agents/age
人工智能AI Agent多模态语音AI 应用深入解析 TEN Framework 的 main_python 扩展:语音 AI Agent 的中央控制中枢
深入解析 TEN Framework 的 main_python 扩展:语音 AI Agent 的中央控制中枢 导读 : main_python 是 TEN F
人工智能AI Agent多模态语音AI 应用TEN Framework 实战:main_python 扩展如何编排多说话人语音 Agent 会话
TEN Framework 实战:main_python 扩展如何编排多说话人语音 Agent 会话 本篇文章以 TEN Framework 的 speaker
人工智能AI Agent多模态语音AI 应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考