01 · Decision
可用的全双工,是一条跨层闭环
系统既要阻止机器人自己的声音触发 VAD,也要在双讲时保住用户插话;触发之后还要停止真实播放、拒绝旧 generation,并让状态可以恢复和审计。
02 · Evidence boundary
哪些已证实,哪些仍然未知
本地代码已确认
Go/Java 网关使用 generation 清理旧 TTS;Go 测试本轮通过。现有 Go、Java、media-core 路径均未发现 AEC、render reference、残余回声或 double-talk 判定。
合成实验已确认
参考感知门控能在固定声学路径里抑制回声自触发,并以 20ms 检测代价换取更好的有效插话识别;晚 ACK 和 ACK 缺失需要本地先静音。
真实环境仍未知
手机、免提、蓝牙、PSTN、房间混响、真人低声、电视人声、真实 ASR 和声卡缓冲均未测试;ERLE、MOS 与实际停声分位数不能从本实验推断。
03 · Measurement contract
取消事件不等于扬声器已经停声
现有 Duplex Trace 的 120ms 来自固定脚本。生产测量必须把声学起点、检测、控制、播放和确认放到同一 turn 与 generation 中。
现有演示:speech_start 13460ms → barge_in 13560ms → tts_cancel 13580ms。这三个值是 simulate_call.py 的剧本,不是真实电话采样。
04 · Local audit
当前链路解决了“打断后”,还没解决“为何打断”
| 资产 | 已具备 | 缺口 | 证据判断 |
|---|---|---|---|
| Speech Gateway · Go | 上下行独立循环;AdvanceGeneration 清旧队列、拒绝旧帧 | 无播放参考、AEC、ERLE、double-talk | 测试本轮通过 |
| Speech Gateway · Java | generation 推进、provider/Agent 通知 | 入站 PCM 原样送 ASR/EOU;无 AEC | 历史 8 / 8 |
| media-core | RTP、G.711、jitter、VAD 插件接口 | echo cancellation 仅为 MGCP 参数枚举,没有实现 | 代码可见 |
| duplex-trace | speech_start / barge_in / cancel 事件轨道 | 演示硬编码;首事件配对;无声学停声与 correlation ID | 不是实测 |
| smart-turn / Soniox | 孤立语音 VAD/EOU 或 endpointing 管线 | 无同时 TTS、回声、双讲;Soniox 还需要密钥 | 本轮跳过 |
05 · Deterministic lab
固定 8kHz、20ms 帧的故障注入
所有信号、噪声和场景参数都确定;实验可逐字节重跑。它验证控制策略,不模拟真实音素、说话人或房间。
Naive energy gate
- 不使用 AEC,直接看麦克风 RMS
- 一帧过阈值即触发 barge-in
- 等待远端 ACK 后才停止播放
- 不区分回声、真人、噪声和短附和
Reference-aware guarded
- 配置路径的两抽头线性抵消
- 参考相干度 veto + 残余 RMS
- 连续两帧才确认打断
- 本地立即静音,远端 ACK 单独确认
06 · Results
速度只有和正确性一起看才有意义
策略总览
停止 ACK 的反例
900ms ACK 永久缺失时,Naive 从用户起声到原 TTS 自然结束Guarded 在同一场景 40ms 后模拟停声,同时把状态保留为 pending_confirmation。本地静音解决用户听感,远端 ACK 解决状态确认,两者不能合并成一个事件。
| 代表场景 | Naive | Guarded | 解释 |
|---|---|---|---|
| 正常 echo-only | 40ms 被自身回声误触发 | 不打断;ERLE 40.8387dB | 直接能量门无法区分来源 |
| 参考错位 40ms | 误打断 | 无误打断;ERLE −4.4419dB | 控制通过不等于 AEC 质量好 |
| 清晰 double-talk | 真人前已被回声误触发 | 20ms 确认;40ms 模拟停声 | 必须用声学真值判断有效触发 |
| 低声 double-talk | 真人前误触发 | 漏检 | 固定残余阈值的 recall 边界 |
| 参考缺失 + double-talk | 真人前误触发 | fail-closed 漏检 | 参考健康度必须可观测 |
| ACK 延迟 120ms | 120ms 模拟停声 | 40ms 模拟停声 | 本地先静音隔离远端延迟 |
| ACK 永久缺失 | 900ms | 40ms + pending | 听感和确认状态分开 |
07 · Metric semantics
ERLE 只在远端单讲段计算
Echo-only
报告 ERL、ERLE、残余回声、收敛和回声自触发。双讲段不混入 ERLE。
AECDouble-talk
报告近端语音衰减、CER/WER、失真、插话 precision/recall 与首字损失。
USER SPEECHControl
报告声学起点→打断、打断→停播请求、用户起点→实际停声、ACK。
LATENCYState
报告旧音频泄漏、未播放内容截断、迟到结果拒绝和假打断恢复。
CONSISTENCY08 · Architecture actions
本地链路需要补的最小闭环
拿到同源参考
render reference 与实际播放共享采样时钟,保存 pre/post AEC 和设备 route。
分开声学事件
activity、near speech、confirmed barge-in 不再用同一布尔量。
本地先停声
播放器立即静音;远端 cancel 与 ACK 继续异步完成。
绑定 ID 与时钟
turn_id + generation_id + playback_id 配对多次打断和迟到结果。
09 · Real-world gate
升级成“真实全双工结论”还差什么
| 工况 | 组合 | 必须采集 | 指标 |
|---|---|---|---|
| 远端单讲 | 音量、房间、route、回声路径变化 | render、pre/post AEC、实际出声 | ERL/ERLE、残余、收敛、自触发 |
| 近端单讲 | 口音、低声、距离、噪声 | 干净刺激、post-AEC、ASR | 语音保持、CER/WER、VAD 延迟 |
| 双讲 | 100/300/800/1500ms 插话、纠正、附和 | 双通道真值、事件、实际停声 | precision/recall、p50/p95/p99、首字损失 |
| 路径变化 | 移动、遮挡、免提/听筒/USB/蓝牙切换 | route、参考健康度、设备时钟 | 重收敛时间与最差分位 |
| 故障 | 参考丢帧、漂移、CPU、抖动、双重 AEC | buffer、乱序、旧 generation | 降级、恢复、旧结果拒绝 |
本轮已完成
- 无密钥确定性基准
- 本地网关代码审计
- 指标口径与反例
- 可复算 raw/summary
下一阶段需授权
- 麦克风与 loopback 录制
- 真实免提 DUT
- 真人双讲语料同意
- SIP/PSTN 与播放器日志
本轮没有声称
- 真实设备 ERLE
- 真人误/漏打断率
- 真实电话停声时延
- AEC/VAD 厂商排名
10 · Sources
标准、官方资料与本地证据
echoCancellation constraint · 请求启用不构成算法或效果保证。
Digital network echo cancellers · 收敛、路径变化、双讲与非线性测试框架。
Hands-free terminals · 声学回声、单讲与双讲传输特性。
One-way transmission time · 传输时延与交互质量。
论文 · echo impairment 的主观质量代理及适用限制。
众包语音质量评价 · 听者、环境、陷阱样本和统计质量控制。
Adaptive handling · Options · 真打断、backchannel、假打断与恢复。
Frames · Smart Turn · 中断系统事件与 EOU 的边界。
Interruption and truncation · VAD · 播放侧取消与状态截断。
本地 session、pipeline、generation 与相关测试;核对日期 2026-08-31。
本地 MGCP Local Connection Option、VAD 接口与媒体路径代码审计;未发现 AEC 实现。
本地 README、simulate_call.py 与 app.js;确认当前延迟为脚本演示且首事件配对。
本轮 16 场景、32 次运行、22 项测试、raw/summary 复算与确定性哈希。