Skip to main content
代表项目:端侧语音 Agent 智控平台。 语音会话的难点不在「接一个识别 API」,而在:连续状态、低延迟体感、打断一致性。下面把头牌里的走读再挖一层。

连接上挂着什么

一条 WebSocket ≈ 一台设备的会话世界:当前配置装配出的模块组合、是否已绑定、工具是否在跑、这一轮生成是否已被用户作废。 文本帧按类型分流(握手、监听、打断、IoT、MCP、视觉等),比在连接主逻辑里堆分支好维护。新能力优先加处理单元或可替换模块,而不是继续加长主文件。

经典路径 vs 端到端

经典路径:音频 → 语音活动检测 → 识别成字 → 模型(可带工具)→ 合成语音播回去。每段可换实现,排障也相对直观。 端到端路径:更多能力收进语音模型侧,靠桥接层处理打断、半双工、工具生命周期等。 两条路若同时抢麦克风和会话状态,会出现「谁在播、谁在听」说不清。所以同一连接只激活一条主路径。产品上可以切换,测试上要当两条回归矩阵。

为什么打断是硬问题

用户说停的时候,系统里可能同时有:
  • 还在识别的尾巴
  • 还在生成的模型输出
  • 还在推的合成音频
  • 还在更新的字幕
  • 还在跑的长耗时工具
只停采集,旧轮次仍会往外冒——就是幽灵播报。做法是给轮次打标记:新一轮开始或用户打断后,旧标记的输出一律丢弃。工具则要有明确的开始/结束;打断时补结束,避免 UI 一直转圈。 这类问题复现看运气,所以日志必须能回答:这一通会话用了哪套模块、卡在听/识/想/说哪一段、哪一轮被作废。

别把事件循环堵死

实时收发包跑在异步循环上。若把重初始化、重计算直接塞进循环,表现就是「突然谁也不理」。 常见纪律:
  • 阻塞活丢线程池
  • 配置/模块初始化可在后台做,完成前不放行业务语音
  • 记忆预热等非关键路径不挡首轮
首包体感差时,先查有没有东西堵在循环上,再怀疑「是不是模型太慢」。

工具挂在会话上

「能说话」到「能办事」,靠的是会话里可调用的工具目录:服务端插件、MCP、设备侧能力等统一分发。知识检索也是工具的一种——短超时,失败返回可理解信息,避免模型空转重试烧环。 部分工具还要和播报编排配合(例如结果口播与模型口头前缀别打架)。细节因工具而异,原则是:工具生命周期对用户可见、可打断、可收尾。

还没完美的地方

  • 没有对外可引用的统一延迟看板;优化仍靠小样本真实会话听体感、看分段日志
  • 双路径切换后的测试成本明显上升
  • 极端网络抖动下,仍要靠日志对齐「作废是否生效」

相关