> ## Documentation Index
> Fetch the complete documentation index at: https://dingguoliang.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 会话与打断：让语音链路听得懂、停得住

> 对话走读加深、双路径互斥、轮次作废、线程边界与工具收尾。

代表项目：[端侧语音 Agent 智控平台](/projects/voice-agent-platform)。

语音会话的难点不在「接一个识别 API」，而在：**连续状态、低延迟体感、打断一致性**。下面把头牌里的走读再挖一层。

***

## 连接上挂着什么

一条 WebSocket ≈ 一台设备的会话世界：当前配置装配出的模块组合、是否已绑定、工具是否在跑、这一轮生成是否已被用户作废。

文本帧按类型分流（握手、监听、打断、IoT、MCP、视觉等），比在连接主逻辑里堆分支好维护。新能力优先加处理单元或可替换模块，而不是继续加长主文件。

***

## 经典路径 vs 端到端

**经典路径**：音频 → 语音活动检测 → 识别成字 → 模型（可带工具）→ 合成语音播回去。每段可换实现，排障也相对直观。

**端到端路径**：更多能力收进语音模型侧，靠桥接层处理打断、半双工、工具生命周期等。

两条路若同时抢麦克风和会话状态，会出现「谁在播、谁在听」说不清。所以同一连接只激活一条主路径。产品上可以切换，测试上要当两条回归矩阵。

***

## 为什么打断是硬问题

用户说停的时候，系统里可能同时有：

* 还在识别的尾巴
* 还在生成的模型输出
* 还在推的合成音频
* 还在更新的字幕
* 还在跑的长耗时工具

只停采集，旧轮次仍会往外冒——就是幽灵播报。做法是给轮次打标记：新一轮开始或用户打断后，旧标记的输出一律丢弃。工具则要有明确的开始/结束；打断时补结束，避免 UI 一直转圈。

这类问题复现看运气，所以日志必须能回答：这一通会话用了哪套模块、卡在听/识/想/说哪一段、哪一轮被作废。

***

## 别把事件循环堵死

实时收发包跑在异步循环上。若把重初始化、重计算直接塞进循环，表现就是「突然谁也不理」。

常见纪律：

* 阻塞活丢线程池
* 配置/模块初始化可在后台做，完成前不放行业务语音
* 记忆预热等非关键路径不挡首轮

首包体感差时，先查有没有东西堵在循环上，再怀疑「是不是模型太慢」。

***

## 工具挂在会话上

「能说话」到「能办事」，靠的是会话里可调用的工具目录：服务端插件、MCP、设备侧能力等统一分发。知识检索也是工具的一种——短超时，失败返回可理解信息，避免模型空转重试烧环。

部分工具还要和播报编排配合（例如结果口播与模型口头前缀别打架）。细节因工具而异，原则是：**工具生命周期对用户可见、可打断、可收尾**。

***

## 还没完美的地方

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

***

## 相关

* [配置与控制面](/notes/config-control-plane)
* [经验总结](/notes/lessons-learned)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.