系统告诉你的「完成」,和世界真的发生变化,是两件不同的事。这一集讲同一家族的三种失败,播完了但没播出来。
代码认为自己把活干完了,日志正常,连播放结束事件都老实触发了,而另一端一个比特的声音都没有。
共同性格是不报错,日志正常,让人先去怀疑硬件和别人。
答案文本切句,并行合成,多段连续无缝播放,全程定向到同一张卡。
必须显式指定的原因是,默认设备上坐着的是面试官通道。指错或没指定成,答案会跑到面试官那条路上,那边的人听得一清二楚,而本该听到答案的平台什么都收不到。
| 方式 | 做法 | 特点 |
|---|---|---|
| data 形式 | 把整段字节编码成文本,直接拼进地址 | 字符串极长,抄一份多占一份地方 |
| blob 形式 | 先申请一块临时空间放字节,拿到临时地址 | 短,用完必须显式释放 |
普通页面里两者常被当作等价互换,也确实表现一致。但这是一个只在自动化环境现身的坑,而自动化环境恰恰最缺人耳朵。
现象是,文本正常、段数正常、每段都触发了播放结束事件、调用方认为播完了,而同时测着的目标线电平恒为零。
症状与虚拟线坏了一模一样,于是第一反应全跑到硬件那边去查驱动、查格式、查端点,全部白查。
当场能排除硬件的依据是,那几根虚拟线在此前一天刚做完完整验收,证据目录里明确写着有信号,面试官线 0.1905、答案线 0.1892。同样的音频、同样的播放代码,只是换了内容,方向就该换。
代码留了一段长注释写死结论,必须用 blob 形式,不能用 data 形式。诚实边界是,机制层面属于推理而非读源码得出,且由于错误与结束走同一分支,当时其实分不清它是真播完还是走了错误分支。
停止播报时只调用暂停,音频既没播完也没出错,于是结束与出错两个事件都不会再触发,外层的等待永远等不到回音。
后果链条:监听循环永久卡住,表示正在说话的标志恒为真,后面排队的所有句子一句都不再播。日志上文字照常进来、照常切句、照常入队,一切正常,只是没有一句说出口。平台随后必然问出那句话。
修法只有一行却很关键,停止时除了暂停,还要把等待着的那个兑现函数调用掉。
通用规则:取消语义必须包含兑现,而不只是包含终止。暂停某个异步操作时先问,谁在等它的结果,我要不要替它把结果交出去。
实测有一段 5.57 秒的音频,47 秒才报告结束。症状与上一种完全一样,外层被永久挂住。
对策是逐段看门狗,超时值按实测时长乘以三再加十秒计算。到点未结束就暂停该段、标记停滞、跳过剩余段落交给调用方记账。
取舍在于,剩余句子是被放弃而不是重试,因为被拖死的代价大于少说几句。另外要把被打断与被判停滞区分开,前者是外部主动、属正常交互,后者是内部发现的异常,二者的改进方向完全不同。
AGENTS.md 陷阱节(data 形式静默不出声、停止播报必须连带兑现);autobot/lib/assistant.js 的 playSegments 与 stopPlayback 实现及其注释。
实测锚点:2026-10-07 data 形式静默不出声;sim7 / sim8 场次卡在等待;某段 5.57 秒音频 47 秒才结束。