# 自适应对话接场器 > 状态:v0.11 模型生成式动态接场已实现;真实提供商延迟与自然度仍需真机持续调优。 ## 问题 Harness 的深度推理和工具调用可能需要数秒到数十秒。完全沉默会让实时语音像“卡住”,但再启动一个能自由回答的子 Agent 又会产生两套结论、抢话、上下文污染和额外成本。 目标不是制造更多内容,而是像人类对话一样管理发言权:脑子在后台处理,嘴巴先自然承接;结果一到,嘴巴在合适的语音边界顺畅转入答案。简单问题必须走直达路径,不能为判断复杂度额外增加延迟。 ## 决策 采用一个受限的 **Conversation Floor Manager(对话接场器)**,而不是第二个负责解题的 Agent。 - Harness 是唯一有权推理、调用工具、形成结论并写入正式对话历史的“脑子”。 - 接场器通过当前语音供应商的独立轻量文本请求生成短暂口语承接,不调用业务工具、不猜测结果、不形成新的用户轮次。 - 是否接场主要由**实际等待时间和 Harness 真实事件**决定,复杂度预测只作辅助信号。 - Harness 的首个可播报摘要先到时,直接取消接场计时器,简单问题零额外话术、零额外等待。 这相当于综合用户提出的两个方案:保留浅/深判断的收益,但不用一个前置分类器阻塞所有请求;接场器始终待命,却只在真实延迟越过阈值后开口。 ## 双通道 ```text 用户提交 ───────────────→ Harness(唯一推理与工具通道) │ │ └→ 800ms 响应窗口 ├→ reasoning/tool/progress 事件 │ └→ 最终正文第一自然段 └→ Floor Manager ───────────────→ TTS 播放队列 ``` 两条通道共享同一个 `turnId` 和取消令牌。Harness 没有 `ephemeral-voice` 事件;接场只存在于插件本地 TTS 队列,不作为 assistant 正式消息写入 Harness 历史。 ## 状态机 ```text IDLE └─ user_commit → WAITING_FOR_RESULT WAITING_FOR_RESULT ├─ result_before_threshold → SPEAKING_RESULT ├─ timer_threshold → SPEAKING_ACK └─ user_cancel → CANCELLED SPEAKING_ACK ├─ summary_ready → HANDOFF_AT_BOUNDARY ├─ ack_finished → WAITING_SILENTLY └─ user_barge_in → INTERRUPTED WAITING_SILENTLY ├─ verified_progress_after_4s → SPEAKING_PROGRESS ├─ summary_ready → SPEAKING_RESULT └─ user_barge_in → INTERRUPTED HANDOFF_AT_BOUNDARY └─ ack_sentence_boundary → SPEAKING_RESULT SPEAKING_RESULT ├─ audio_finished → IDLE └─ user_barge_in → INTERRUPTED ``` 任何旧 `turnId` 的回调都必须丢弃,不能覆盖新一轮或播放过期音频。 ## 话术策略 ### 第一次承接 在 650–900ms 后仍没有可播报结果时,只说一句,目标时长 0.6–1.4 秒: - 从用户原话中只提取一个经过清洗、长度受限的主题,例如“深圳明天天气”或“这两个训练方案”。 - 由任务类型、主题、当前阶段和本轮已经说过的话组合句子;同一轮不能重复同一句。 - 查找、比较、操作、分析采用不同动词和句式,但不额外调用一个会回答问题的模型。 不能说尚未发生或无法验证的状态,例如“已经查到了”“正在分析天气”,除非 Harness 已发布对应的真实事件。 避免反复使用“呃”“我想想”。语气词只用于偶发的韵律连接,不单独充当内容,也不连续播放。 ### 真实进度 只有结果仍未返回且出现可验证事件时,才进入对应进度阶段: - 收到工具开始事件:延迟 3.5 秒后,基于原始主题组合一条“正在取数据/等待工具返回”的话。 - 收到 LLM retry:延迟后说明“这一步正在重试”,不把错误、参数或内部轨迹读出来。 - 长等待:再过约 7 秒仍无正文时,可组合一条不同的陪伴式衔接;每轮总计最多三条。 简单问题若在首次阈值前出结果,一句接场都不说。禁止靠闲聊无限填充,也禁止从 tool payload、reasoning 或密钥形文本中构造话题。 ### 结果衔接 结果到达时不直接砍断一个汉语句子。TTS 调度器等待最近的安全边界(句号、问号、逗号后的短停顿),最长不超过 280ms,然后选用与上一句话匹配的连接: - `我先看看。` → `找到了,……` - `我帮你对比一下。` → `对比下来,……` - `行,我来处理。` → `好了,……` 连接词只由状态和已说话术决定,不让模型重新总结结果,从而避免多一次模型往返。 ## 调度与缓存 - Harness 请求在用户提交时立即开始,接场生成不得位于关键路径上。 - 最终正文第一段的首句在后台缓冲;若接场正在播,进入同一个单写者队列,紧随承接句播放。 - 若结果在接场音频真正开始前到达,取消接场并直接播结果。 - TTS 使用单一播放器和单写者队列,任何时刻只能有一个音频生产者拥有发言权。 - 接场请求与 Harness 在 t=0 并行启动;只发送 Host 二次清洗后的短主题、阶段枚举和此前安全接场句。它不进入 Harness 模型平面或会话历史。健康路径使用模型生成句;失败才使用最短中性兜底。 ## 插话和当前输入框语义 - 用户近讲达到现有持续时长阈值后,立即停止接场或结果 TTS。 - 新 ASR 文本仍只追加到 Harness 原生输入框,不自动创建第二个推理任务。 - 当前 Harness 任务默认继续运行;只有明确的“停止/取消/算了”才发送取消。 - 用户点击发送时,把输入框内已经累积的所有句子作为一个新轮次提交,不能跳过旧草稿。 ## 验收指标 - 简单问题:接场触发率低于 10%,不增加首个结果音频延迟。 - 深任务:用户提交到第一段自然音频的 P95 小于 1 秒。 - 错误进度陈述率为 0;没有真实事件就不声称具体进度。 - 每轮接场最多 1 次、进度最多 1 次。 - 接场转结果之间的静音 P95 小于 350ms,句中硬切率低于 1%。 - 任意压力测试中,同一时刻最多一个 TTS 写者;旧轮音频播放率为 0。 ## 实现顺序 1. ✅ 已实现 `FloorManager`、单写者 TTS 队列、可配置 800ms 竞速、LLM retry 与旧轮丢弃。 2. ✅ 已接入 Harness 的 turn/tool/retry 事件,并将重置原因明确传给接场器。 3. ✅ 已加入任务主题、意图、阶段与去重驱动的动态组合,不引入第二个回答模型。 4. 待真机采集等待分布、接场触发率和用户打断数据后,调整三个阈值而不改变单说话者架构。 这个顺序先解决抢话与衔接,再追求“更像真人”,避免把新的并发 Agent 变成新的混乱来源。