机器人接入连续语音后,端云架构要改什么

摘要:连续语音进入机器人后,声音停止不等于播放、任务或物理动作同时停止。本文把系统拆成媒体、交互控制、物理动作三个平面,用五类身份、播放进度代理、动作准入和渐进迁移步骤重新建立端云一致性,并明确 IMPLEMENTED、VERIFIED 与 UNKNOWN 的证据边界。

关键词:机器人语音、WebRTC、端云协同、播放确认、动作安全、Voice Agent

连续语音进入机器人后,声音停止不等于动作停止

一、先拆三个平面:媒体、交互控制、物理动作

媒体、交互控制与物理动作必须分别收敛

1. 媒体面回答“声音在哪里”

媒体面包括采集、AEC/NS、编码、RTP、jitter buffer、解码、播放队列和扬声器。它关心帧、序号、时间戳、丢包、缓冲和实际播放位置。

OpenAI 当前 Voice Agents 文档把实时语音分成 speech-to-speech 与 chained pipeline 两条架构,并明确 WebRTC 或 WebSocket 可以承载实时会话。[S1] Realtime VAD 还会产生 speech_startedspeech_stopped 等语音活动事件。[S2] 这些公共能力可以帮助建立输入和响应链路,但它们不知道一段音频是否已经穿过机器人端的 jitter buffer,更不知道扬声器是否真的发声。

官方公共接口能证明的范围与不能代替的机器人动作真值

OpenAI Voice Agents 官方文档中的 speech-to-speech 与 chained 架构入口

图:OpenAI Voice Agents 官方文档聚焦裁片(检查于 2026-08-24)。它证明公共语音架构入口,不证明机器人端已经实现播放确认或动作停止。查看官方页面

OpenAI Realtime VAD 官方文档中的 speech_started 与 speech_stopped 事件

图:OpenAI Realtime VAD 官方文档聚焦裁片(检查于 2026-08-24)。它证明公共语音边界事件存在,不证明机器人场景的回声、旁人声与动作错误代价已经被解决。查看官方页面

OpenAI Realtime WebRTC 官方文档中的媒体轨与 DataChannel 事件路径

图:OpenAI Realtime WebRTC 官方文档聚焦裁片(检查于 2026-08-24)。它证明媒体轨与事件通道的公共接口边界,不证明当前机器人链路已经通过真实 TURN 与设备 E2E。查看官方页面

因此服务端的 sent_at 不是用户听见的时间,客户端的 received_at 也不是播放完成。机器人端必须提供软件层的播放进度代理,例如:

{
  "type": "playback.progress",
  "session_id": "sess_42",
  "response_id": "resp_18",
  "playback_id": "pb_18",
  "received_until_ms": 1840,
  "buffered_until_ms": 1520,
  "played_until_ms": 960,
  "device_monotonic_ns": 938472993822
}

played_until_ms 仍只是播放器软件上报的进度代理,不是声学传感器对“用户确实听到”的证明。但它比服务端“已发送”更接近历史修复和停止判断所需的真值。

2. 交互控制面回答“哪件事仍然有效”

交互控制面维护输入、话权、响应、后台任务、工具调用和历史。它不直接搬运每一帧音频,而是决定哪些事件属于同一意图,哪个 response 可以继续,哪个 task 已过期,哪个工具结果还能进入当前会话。

传统回合系统常用一个 round_id 包办所有关系。连续语音需要把它拆成至少五类身份:

身份 代表什么 不能被什么替代
session_id 一次持续会话 不能当作逐轮 trace
utterance_id 一段候选输入语音 不能等同完整意图
response_id 一次可撤销输出 不能用当前 session 代替
playback_id 一次客户端播放实例 不能用 response 已发送代替
task_id / action_id 后台工作或物理动作 不能用工具名或当前轮次代替

五类身份连接端云真值,session 不能替代逐次身份

此外还需要逐次追踪用的 trace_idsession_id 可以跨很多轮存在,不能拿它替代真正的 per-turn trace。否则一次会话里的两个并发 task、两次 response 和一次重连会被挤进同一条日志,问题无法还原。

3. 物理动作面回答“机器人实际做了什么”

物理动作与播放不同。音频通常可以停止后丢弃;机器人动作可能已经改变世界:底盘已经移动,机械臂已经抓取,门锁已经打开,消息已经发送。

因此模型产生一个工具调用,不应直接等于设备执行。更安全的动作生命周期是:

proposed → authorized → dispatched → acknowledged → running
         → completed | cancelled | failed | compensation_required
  • proposed:模型或规则提出候选动作。
  • authorized:权限、上下文、参数、风险与确认条件通过。
  • dispatched:命令已发给设备或业务系统。
  • acknowledged:对端确认收到,并不等于完成。
  • completed:以设备传感器、控制器状态或业务回执确认结果。
  • cancelled:只对仍可取消的阶段成立。
  • compensation_required:副作用已发生,不能假装回滚成功。

机器人动作从授权、派发到执行和补偿的独立生命周期

这套动作状态不是 OpenAI API 字段,也不是 GPT-Live 私有协议,而是机器人系统必须自己承担的工程责任。Realtime 工具接口可以产生调用并接收应用回注的结果,[S4] 但设备权限、急停、幂等和物理完成仍在应用与设备侧。

二、端云职责不能按语言划分,要按时间与真值划分

“Go 做实时、Python 做 AI”是有用起点,却不够精确。真正稳定的边界应回答:哪一层最先看到事件,哪一层拥有不可替代的真值,哪一层可以在断网时独立保护设备。

Rust Client:拥有设备附近的低延迟真值

端侧最接近麦克风、扬声器和设备控制器,应承担:

  • 音频采集、AEC/NS、编码与本地缓冲;
  • 唤醒、按键、急停和可选的低延迟本地 VAD/KWS;
  • 播放队列、playback_id、实际软件播放进度和队列清空;
  • 网络中断时的本地安全动作;
  • 设备动作回执与传感器状态上报;
  • 单调时钟与端侧事件序号。

端侧不应自行决定复杂语义,但必须可以先执行安全优先的本地动作。例如用户在机器人靠近台阶时说“停”,系统不应等云端完成语义推理才停止驱动。可以先本地减速或急停,再让云端判断这句话是否还要改变会话与任务状态。

Go Realtime Edge:拥有连接、媒体边界和背压真值

Go Gateway 适合维护 WebRTC 连接、ICE/STUN/TURN、RTP/DataChannel、会话注册、媒体桥接、背压和事件转发。当前项目代码已经存在这些职责以及播放回报、旧播放不得影响新播放等测试证据。

连续语音改造后,Go 层还应:

  • 校验 session_idconnection_id、序列号和协议版本;
  • 将 RTP 媒体与 DataChannel 控制事件绑定到同一会话;
  • 对重复/迟到的 playback_interrupted 做身份隔离;
  • 记录直连与 TURN 路径、排队、丢包和桥接耗时;
  • 在 Python 编排暂时不可用时维持连接与明确降级;
  • 对控制事件设定小而可解释的队列,不能用无限缓冲掩盖阻塞。

Go 层不应因为“更快”而吞并所有业务状态。它需要保护媒体和连接时序,但任务版本、权限、业务补偿和复杂对话策略仍属于更高层。

Python AI Orchestrator:拥有模型、任务与业务编排真值

Python 层适合连接 STT、LLM、MCP、TTS、后台任务与业务工具,并维护:

  • ASR partial/final 与稳定前缀;
  • turn.action 决策及其证据;
  • response_id、TTS 生成和取消传播;
  • task_idgoal_version、截止时间和结果新鲜度;
  • 工具参数、权限请求、幂等键和补偿信息;
  • 对话历史按实际播放进度修复;
  • 模型路线和降级策略。

是否把 Python Gateway 全部重写成 Go,不应由“架构统一”决定,而应由 trace 证明:连接建立、序列化、队列、事件循环、GC 或取消长尾确实是主要瓶颈,而且重写能改善该瓶颈而不破坏模型与业务迭代速度。否则大规模重写只会把连续交互迁移变成语言迁移项目。

三、五条关键一致性链必须端到端闭合

1. 输入链:音频活动不是最终意图

端侧 VAD 可以快速发现声音并发出 audio_start,云端 ASR 和语义判断再决定这是有效打断、附和、旁人对话还是噪声。Realtime VAD 公共文档允许 server_vadsemantic_vad,并可配置是否自动创建或打断 response。[S2] 这证明接口路径存在,不代表机器人场景的错误代价已经被模型处理。

对机器人动作而言,输入需要两阶段处理:

local safety signal → immediate safe action
cloud semantic decision → conversation/task/action reconciliation

本地安全信号可以减速、静音或暂停;云端决定是否取消当前 response、后台 task 和业务动作。两阶段结果都必须进入同一 trace。

2. 响应链:生成、传输、缓冲和播放不是同一状态

一个 response 至少会经过:

created → generating → chunk_sent → chunk_received
        → buffered → playing → completed
        ↘ cancel_requested → generation_stopped
          → queue_purged → playback_stopped

取消成功不能只看 generation_stopped。旧 chunk 可能仍在网络或客户端队列。服务端应等待与同一 playback_id 绑定的 queue_purged / playback_stopped,并忽略旧播放实例迟到的完成事件。

3. 历史链:只保留用户实际获得的语义

如果回答只播放到 960ms,服务端不能把完整文本都当成用户已经听见,也不能粗暴删除整条回答。可以用 TTS 的文本—音频时间对齐,将 played_until_ms 映射到最后一个稳定语义边界;映射不可靠时,下一轮显式澄清,而不是伪造精确历史。

这里的核心不是追求毫秒级完美,而是记录不确定性:

{
  "response_id": "resp_18",
  "played_until_ms": 960,
  "semantic_commit": "partial",
  "last_committed_span": "span_04",
  "alignment_confidence": 0.81
}

4. 任务链:旧结果不能覆盖新目标

后台任务创建时应冻结 goal_version、输入快照和交付条件。用户中途补充、改口或取消时,不一定需要立即杀死所有计算,但返回结果必须重新经过准入:

task completed
  → goal_version still current?
  → deadline still valid?
  → side effect already happened?
  → result still relevant?
  → user currently interruptible?
  → deliver / summarize / store / discard

后台任务生命周期不在本文重复展开;机器人迁移需要把目标版本、截止时间和结果准入接进端云事件面,并把物理动作副作用纳入判断。

5. 动作链:取消语音不等于取消设备

动作命令必须携带独立 action_id、幂等键、风险级别、授权依据、设备目标和超时。取消 response 时,系统需要查询动作处于哪个阶段:

  • 尚未授权:直接拒绝。
  • 已授权未派发:撤销授权。
  • 已派发未执行:发送设备取消并等待 ACK。
  • 正在执行:进入受控停止或安全姿态。
  • 已完成:不能写“取消成功”,只能评估补偿。

这也是为什么机器人连续语音必须增加动作面。纯聊天系统可以把错误回答当成内容问题;机器人系统会把错误意图变成物理结果。

四、AEC 优先级高,但不能写成一句“打开即可”

机器人扬声器和麦克风相对位置固定,TTS 播放时输入链仍然开启,残余回声可能被 VAD、ASR 或 KWS 识别成用户声音,造成自我打断。更强的 VAD 无法替代声学回声消除,因为 VAD 只判断是否像语音,不知道这段语音来自用户还是扬声器参考信号。

但“AEC 优先”不等于在配置里打开一个布尔值。上线前至少要确认:

  • 播放参考信号是否在正确采样率、正确通道和正确时间进入 AEC;
  • 设备播放、采集、重采样和网络时钟是否导致漂移;
  • 双讲时用户语音是否被过度抑制;
  • 音量、距离、房间反射和机器人电机噪声变化时是否仍稳定;
  • AEC 发散后系统是继续误打断、临时降级还是重新收敛;
  • 直连、TURN 和弱网条件是否改变控制事件与媒体的相对到达顺序。

可观测指标可以包括 ERLE、残余回声能量、双讲检测、播放期间 VAD 触发率、ASR 自回声片段率和误取消率。具体阈值必须来自目标设备实验,不能从通用文章抄一个数字。

五、渐进迁移:每一步都能停止、回退和解释

六步迁移路线:先闭合真值,再比较模型路线

第 0 步:冻结当前链路和证据边界

记录实际部署 commit、Rust/Go/Python 版本、协议版本、模型配置、ICE 路径和设备型号。把当前能力标为 IMPLEMENTEDVERIFIEDUNKNOWN。没有这一步,后续体验变化无法归因。

第 1 步:统一五类身份和逐轮 trace

先在端、Go、Python 和设备事件中贯通 session_idutterance_idresponse_idplayback_idtask_id/action_id,并增加真正的 per-turn trace_id。旧字段可以兼容,但不得继续把 session 当 trace。

第 2 步:完成可取消播放闭环

先不改变模型。让每个音频 chunk 绑定 response/playback,端侧支持清队列与播放停止回报,服务端拒绝旧播放迟到事件。只有这一阶段稳定,后续自然打断才有安全基础。

第 3 步:接入 AEC 与双阶段输入判断

端侧负责快速声学事件和安全优先动作,云端负责语义动作分类。分别记录本地检测、云端确认和最终状态收敛时间。误判要能恢复,不得用“全部打断”掩盖策略缺失。

第 4 步:把任务和工具接入版本、权限与副作用协议

后台 task 携带目标版本和交付条件;机器人 action 使用 propose/authorize/dispatch/ack/complete。对不可逆操作增加显式确认或更保守策略。

第 5 步:在相同控制面下 A/B 模型路线

增强级联与原生 speech-to-speech 都输出相同的 turn.action、response、task 和 action 事件。这样模型切换才是可归因实验,而不是连媒体、协议和业务逻辑一起重写。

第 6 步:扩展到真实设备与弱网场景

先单设备、单动作、局域网,再到 TURN、网络切换、重连、多任务和长时间会话。每次只扩大一个风险维度,并保留上一阶段回退路线。

六、上线证据矩阵:什么可以说,什么仍然不能说

IMPLEMENTED、VERIFIED 与 UNKNOWN 必须分开记录

能力 IMPLEMENTED 证据 VERIFIED 证据 仍需 UNKNOWN/E2E
WebRTC 上下行 Rust/Go 代码与配置 本地桥接/协议测试 生产 ICE/TURN 与真实设备稳定性
播放身份 round_id / playback_id 路径 旧播放不影响新播放的测试 扬声器真实停止长尾
播放回报 complete/interrupted 事件 Go/Python 转发测试 进度代理与声学输出误差
输入打断 DataChannel interrupt 路径 本地事件测试 回声、旁人声、双讲误判
机器人动作 工具或控制接口 协议/模拟器测试 真实设备 ACK、急停与完成回执
端云取消 局部取消路径 组件测试 模型、TTS、网络、播放、动作全链收敛

这张表的用途不是保守措辞,而是决定下一项测试。IMPLEMENTED 说明代码路径存在;VERIFIED 说明某个明确环境下的证据通过;UNKNOWN 说明不能据此宣传生产能力。单测、preflight、上传 ACK、mock 或本地 publish 都不能自动升级成真实硬件 E2E。

七、三个常见但代价很高的错误方向

错误一:先把 Python 全部重写成 Go

如果 trace 没有证明 Python 是排队、取消或延迟长尾的主要来源,重写只会增加协议迁移、回归与模型接入成本。先把事件和时间线补齐,瓶颈会自己暴露。

错误二:把 VAD 当成话权与急停系统

VAD 能发现声音或估计话语结束,不能完成语义动作分类,也不能替代本地安全回路。高风险动作必须有独立确认、权限与设备停止路径。

错误三:把模型事件当作设备真值

模型取消不代表 TTS 队列清空,TTS 停止不代表扬声器停止,工具返回 success 不代表机器人完成。每层都要由最接近真实状态的一方签字。

八、什么时候应该保留更简单的轮次式系统

连续语音不是所有机器人场景的默认答案。以下情况保留按键说话、唤醒后单轮或明确确认,可能更安全:

  • 指令短、歧义代价高;
  • 环境回声和噪声尚未被测量;
  • 设备没有可靠取消或动作回执;
  • 网络不稳定且端侧安全能力不足;
  • 用户更需要可预测性而非自然重叠对话;
  • 当前级联系统已经满足任务成功率和延迟目标。

真正成熟的迁移允许团队停在更简单的一层。目标不是获得“全双工”标签,而是让系统在自己声称的能力范围内正确、可观测、可回退。

结语:把连续语音当成一致性工程

机器人接入连续语音后,原有 Rust Client、WebRTC、Go Gateway、Python Orchestrator 和模型服务大部分都可以保留。最先要改的不是语言,而是关系:哪个输入触发哪个 response,哪段音频真正播放,哪个任务仍然新鲜,哪个动作已经越过可撤销边界。

媒体面、交互控制面和物理动作面必须分别收敛,再通过稳定身份与事件连接。端侧拥有播放与设备附近真值,实时网关拥有连接和媒体边界,AI 编排层拥有模型、任务与业务状态,设备控制器拥有动作完成真值。任何一层都不能替其他层签字。

当这些关系建立之后,团队才有资格比较增强级联和原生音频模型,才知道一次“自然打断”究竟是模型变强、AEC 稳定、播放器可取消,还是动作协议终于正确。否则,更快的模型只会更快地放大旧架构里的竞态。

参考资料

  1. OpenAI, Voice agents: https://developers.openai.com/api/docs/guides/voice-agents
  2. OpenAI, Voice activity detection: https://developers.openai.com/api/docs/guides/realtime-vad
  3. OpenAI, Realtime API with WebRTC: https://developers.openai.com/api/docs/guides/realtime-webrtc
  4. OpenAI, Realtime with tools: https://developers.openai.com/api/docs/guides/realtime-mcp
  5. OpenAI, Realtime conversations: https://developers.openai.com/api/docs/guides/realtime-conversations
  6. W3C, WebRTC 1.0: https://www.w3.org/TR/webrtc/
  7. IETF, RFC 6716 — Definition of the Opus Audio Codec: https://www.rfc-editor.org/rfc/rfc6716
  8. 当前项目只读证据:dify_stream_test/README.mdCLAUDE.md、Go Gateway 播放/会话/桥接代码与测试、Python Gateway 会话和内部语音协议测试(访问 2026-08-09)
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐