[真实链路自测]:完成完整流程自测和ACK缓冲修正,包含真实Provider验收、播放回声清理和回归测试

This commit is contained in:
mkbk
2026-06-17 23:47:21 +08:00
parent b3566c2424
commit b2822dc1af
10 changed files with 121 additions and 7 deletions
@@ -238,6 +238,31 @@ The generated sequence intentionally contains:
The transport is bounded: if frames are exhausted unexpectedly, it raises a structured validation error instead of letting the pipeline loop forever. `--write-fixture` writes the exact simulated microphone frames as JSONL, and `--fixture` replays them for repeatable debugging.
## Real Provider Fixture Acceptance and Playback Drain
The simulated microphone command validates pipeline ordering with fake providers, but it does not prove that real local models and real playback can run together. The next acceptance layer uses generated 16 kHz PCM utterances as microphone input while keeping the provider chain real:
```text
generated wake/question PCM
-> MemoryAudioTransport(flush preserves prefilled fixture frames)
-> SherpaOnnxKeywordWakeWordProvider(real KWS)
-> HybridVadProvider(real sherpa VAD + energy fallback)
-> SherpaOnnxDenoiserPreprocessor(real GTCRN)
-> PrimarySpeakerVadRecorder
-> SherpaOnnxSttProvider(real local CTC partial/final)
-> OpenAICompatibleLlmProvider(real cloud LLM from .env)
-> MacSayTtsProvider(real macOS TTS)
-> SoundDeviceAudioTransport.play_pcm(real speaker smoke)
```
Playback drain semantics are intentionally split by transport type:
1. `SoundDeviceAudioTransport.flush_input()` clears the realtime callback queue that accumulated during ACK/TTS playback.
2. `MemoryAudioTransport` defaults to the same destructive flush behavior for direct unit tests.
3. Simulated and fixture-driven live tests set `flush_clears_input=False` because their queued frames represent future time-ordered microphone input, not already accumulated realtime echo.
`_drain_input_after_playback()` therefore always calls `flush_input()` once after actual playback. If `OWNER_POST_PLAYBACK_DRAIN_MS=0`, it returns immediately and does not perform any timed read/drop loop. If the value is positive, the positive window is treated as an explicit user tuning and the runtime reads and discards only that configured duration before a final flush. When `OWNER_WAKE_ACK_TEXT` is empty and no ACK playback occurs, the runtime skips playback drain entirely.
## Migration
No database migration. Users should run:
@@ -57,6 +57,7 @@
15. 降噪缺失问题:正式问题录音直接把原始麦克风帧送入 VAD、partial STT 和 final STT,背景噪声会同时影响端点、实时字幕和最终识别。
16. 本地模型落后问题:默认 STT 仍是 2023 年 14M 小模型,适合最小验收但不适合作为默认实时字幕质量基线;应升级为 sherpa-onnx 官方 2025 中文 CTC int8 模型。
17. 自测闭环问题:现有 `acceptance` 只覆盖旧单轮 pipeline,不能证明当前 `VoiceAssistantPipeline` 在“模拟麦克风 -> wake -> capture -> partial -> final -> LLM -> TTS -> standby -> 第二轮”路径上完整正常;真人测试前缺少可重复的自动调试入口。
18. ACK 缓冲实现偏差:文档要求 `OWNER_POST_PLAYBACK_DRAIN_MS=0` 时仍应清理播放期间已积压的输入队列,但当前实现 `<=0` 直接返回;真实麦克风上可能把“我在”播放回声或旧缓冲送入正式问题 VAD/STT,模拟预排帧又不能直接用真实 flush 语义清空。
## 详细需求
@@ -97,6 +98,10 @@
33. 模拟麦克风输入 SHALL 包含 wake 帧、正式问题主说话人帧、短噪声 partial、背景噪声/非主说话人帧和第二轮重复唤醒帧。
34. 模拟 transport SHALL 有边界保护:如果帧提前耗尽,命令必须结构化失败并退出,不能无限等待。
35. `simulate-live` SHALL 支持写入和回放 JSONL fixture,便于后续持续复现同一组模拟麦克风输入。
36. ACK/TTS 播放完成后的输入清理 SHALL 在 `OWNER_POST_PLAYBACK_DRAIN_MS=0` 时仍执行一次 `flush_input()`,清掉播放期间已进入真实输入队列的回声或旧帧,但不得再主动读取并丢弃新音频。
37. 当 ACK 文本为空且没有实际播放行为时,runtime SHALL NOT 执行 post-playback drain,避免无播放场景误清正式问题开头。
38. 模拟麦克风和 fixture replay 场景 SHALL 能保留预排的未来输入帧,并仍记录 flush 调用次数,用于验证真实队列清理动作不吞掉测试中尚未“发生”的后续用户语音。
39. 完整链路自测 SHALL 尽量使用真实本地 provider 和真实云端 LLM:真实 KWS、VAD、降噪、STT、LLM、TTS 和播放;无法自动使用真人麦克风时,允许用生成的 16 kHz PCM 音频帧替代物理麦克风输入。
### 非功能需求
@@ -127,6 +132,8 @@
12. partial 单字误识别:本地 streaming STT 输出 `家``确``a` 等单字时,终端不得显示为 `实时转写`
13. partial 瞬态跳变:本地 streaming STT 从“你是谁”短暂跳到“加”再回到“你是谁”时,不得显示中间极短跳变。
14. CTC 模型缺旧 transducer 文件:当 manifest 类型为 `sherpa-onnx-streaming-zipformer2-ctc` 时,不应再要求 encoder/decoder/joiner 文件存在。
15. ACK 禁用:当 `OWNER_WAKE_ACK_TEXT=` 时,系统不得因为“播放后清理”逻辑而清空唤醒后的正式问题帧。
16. 模拟预排帧:当 `MemoryAudioTransport` 用于模拟完整 turn 序列时,flush 调用不得把未来 turn 的预排帧删除,否则模拟验收会掩盖真实 pipeline 顺序。
### 输入输出规格
@@ -290,6 +297,8 @@ standby
10. 捕获阶段降噪:`AudioPreprocessStage` 将 int16 PCM 转为 float32,调用 `sherpa_onnx.OnlineSpeechDenoiser.run(samples, sample_rate)`,把返回的 `DenoisedAudio.samples` 转回 int16 PCM,并在 metadata 中加入 `denoised=True``noise_filter_provider=sherpa_onnx_gtcrn`
11. CTC STT 加载:manifest `stt.type=sherpa-onnx-streaming-zipformer2-ctc` 时,只校验 `tokens``model`,调用 `OnlineRecognizer.from_zipformer2_ctc`;旧 manifest 仍兼容 transducer 路径。
12. partial 稳定过滤:实时字幕会先去除空白和标点,计算有效字符数;有效字符数小于 2 的文本直接忽略;若新文本比上一次已显示文本短且不构成稳定前缀推进,也忽略,避免把瞬时噪声显示给用户。
13. 零时长播放后清理:`_drain_input_after_playback()` 先执行一次 `transport.flush_input()`;若 `post_playback_drain_ms <= 0` 立即返回;若配置为正数,才按用户显式配置继续 read/drop drain window 并最终再次 flush。
14. 模拟 flush 隔离:`MemoryAudioTransport` 默认保持 flush 清空语义;模拟 live pipeline 显式设置 `flush_clears_input=False`,只记录 flush 调用,不删除预排帧,避免将真实时间队列与离线 fixture 序列混淆。
### 数据库/状态管理变更
@@ -341,6 +350,7 @@ standby
| 模拟验收与真实麦克风仍有差异 | 中 | 中 | 明确模拟验收用于自动调试 live pipeline 状态机和事件顺序;仍保留 4.3 真人 `run-live` 验收,不在用户确认前归档 |
| 模拟帧耗尽导致测试卡死 | 中 | 高 | 使用 bounded simulated transport,空读超过阈值直接抛结构化错误并让命令返回非 0 |
| 模拟 provider 掩盖真实模型加载问题 | 中 | 中 | 模拟命令只验证 pipeline;模型加载仍由 `model-check` 真实加载 KWS/VAD/STT/denoiser 覆盖 |
| `0ms` drain 不执行 flush 导致播放回声污染正式问题 | 中 | 高 | `_drain_input_after_playback()` 无论 drain window 是否为 0 都先 flush 一次真实输入队列;新增回归测试验证 flush 发生且首句不丢 |
## 任务分解
@@ -414,6 +424,14 @@ standby
- [ ] 11.4 补充 README 和自动化测试;前置条件:11.3 完成;验收标准:README 包含模拟验收命令,测试验证两轮闭环、partial 噪声过滤、第二轮临时上下文、fixture 写入/回放;测试要点:unittest;优先级:P0;预计:35 分钟。
- [ ] 11.5 执行全量门禁并提交“模拟麦克风验收”模块;前置条件:11.1 至 11.4 完成;验收标准:`simulate-live --turns 2`、compileall、unittest、security-check、model-check、device-check、OpenSpec strict、git diff check 通过后中文 commit;优先级:P0;预计:45 分钟。
### 12. 真实完整链路自测与 ACK 缓冲修正
- [ ] 12.1 更新 OpenSpec 描述真实完整链路自测和 ACK 零时长 flush 偏差;前置条件:模拟验收已提交且本机模型齐全;验收标准:proposal/design/spec/tasks/spec delta 覆盖真实 provider 验收、0ms flush、模拟预排帧隔离和 ACK 禁用边缘案例;测试要点:OpenSpec strict;优先级:P0;预计:35 分钟。
- [ ] 12.2 修正 `VoiceAssistantPipeline` 和兼容 `LiveVoiceRuntime` 播放后输入清理;前置条件:12.1 完成;验收标准:ACK/TTS 播放后即使 `OWNER_POST_PLAYBACK_DRAIN_MS=0` 也执行一次 flush,ACK 文本为空时不执行 post-playback drain;测试要点:零时长 flush 单测、ACK 禁用不清空单测;优先级:P0;预计:45 分钟。
- [ ] 12.3 调整 `MemoryAudioTransport` 和模拟验收;前置条件:12.2 完成;验收标准:默认 flush 仍清空输入,模拟 live 显式保留预排帧并记录 flush 调用次数;测试要点:transport flush 双语义测试、两轮 simulated live 回归;优先级:P0;预计:35 分钟。
- [ ] 12.4 执行真实 provider 脚本化闭环验收;前置条件:本地模型、`.env` LLM key、macOS TTS/播放可用;验收标准:生成 wake/question 音频帧驱动真实 KWS、VAD、denoiser、本地 STT、云端 LLM、本地 TTS、播放,两轮上下文可引用第一轮历史;测试要点:输出不泄露 key,模型文件不进 Git;优先级:P0;预计:60 分钟。
- [ ] 12.5 执行全量门禁并提交“真实链路自测”模块;前置条件:12.1 至 12.4 完成;验收标准:compileall、unittest、simulate-live、security-check、model-check、device-check、OpenSpec strict、git diff check 全通过后中文 commit;优先级:P0;预计:45 分钟。
## Spec Deltas
### 新增能力
@@ -434,6 +452,7 @@ standby
10. `Realtime partial transcript output`:新增录音期间 partial transcript 事件、终端显示和上下文隔离要求。
11. `Local voice chain and noise filtering`:新增本地 STT/TTS 默认、GTCRN 降噪、CTC 模型、partial 稳定过滤和音频不上传要求。
12. `Simulated microphone live acceptance`:新增不依赖真人麦克风的 live pipeline 模拟验收,覆盖两轮重复对话、短噪声过滤、背景噪声端点、fixture 写入/回放和空帧防卡死。
13. `Real provider live fixture acceptance`:新增真实 provider 脚本化闭环验收和 ACK/TTS 播放后 0ms flush 语义,确保真实本地模型、云端 LLM、本地 TTS/播放在同一 pipeline 中可连续运行。
### 删除项
@@ -455,6 +474,7 @@ standby
8. M8:录音期间实时转写显示完成后提交,保留真人 `run-live` 验收任务,不在用户确认前归档。
9. M9:本地语音链路、高质量实时字幕和正式问题降噪完成后提交,继续保留真人 `run-live` 验收任务,不在用户确认前归档。
10. M10:模拟麦克风自动验收完成后提交,作为真人验收前的可重复自测入口;仍不归档变更,直到真实 `run-live` 行为由用户确认。
11. M11:真实 provider 脚本化闭环和 ACK 缓冲修正完成后提交;仍保留用户真人 `run-live` 体验验收,不在用户确认前归档。
估时:
@@ -130,6 +130,31 @@ The system SHALL provide a deterministic simulated microphone acceptance command
- **WHEN** the simulated microphone transport runs out of frames before the expected turn completes
- **THEN** the command SHALL fail with a structured error instead of waiting indefinitely
### Requirement: Real provider live fixture acceptance
The system SHALL support scripted full-chain acceptance that drives the live pipeline with generated microphone PCM while using real local speech providers, the configured cloud LLM, and local TTS/playback capability.
#### Scenario: Real provider fixture completes one turn
- **WHEN** generated wake and formal question PCM frames are passed through the live pipeline
- **THEN** the pipeline SHALL use the real local KWS, local VAD, local denoiser, local STT, configured cloud LLM, local TTS, and playback provider path to complete a wake-to-standby turn
#### Scenario: Real provider fixture completes repeated turns with temporary context
- **WHEN** two generated wake/question turns run in one process and the first turn gives a user fact
- **THEN** the second LLM request SHALL include the first turn's user and assistant history from process-local memory
- **AND** no history SHALL be read from disk or persisted after the process exits
#### Scenario: Zero playback drain still flushes queued echo
- **WHEN** ACK or TTS playback completes and `OWNER_POST_PLAYBACK_DRAIN_MS=0`
- **THEN** the runtime SHALL call `flush_input()` once to clear already queued playback echo
- **AND** it SHALL NOT perform an additional timed microphone read-and-discard loop
#### Scenario: No acknowledgement playback is configured
- **WHEN** `OWNER_WAKE_ACK_TEXT` is empty and no wake acknowledgement audio is played
- **THEN** the runtime SHALL NOT run post-playback drain before capture starts
#### Scenario: Fixture transport preserves future frames
- **WHEN** a memory or fixture transport represents pre-generated future microphone frames
- **THEN** tests SHALL be able to configure flush to record the cleanup call without deleting those future frames
### Requirement: Wake acknowledgement before recording
The live runtime SHALL provide an audible local acknowledgement after local wake detection and before it starts recording the user's formal question.
@@ -89,3 +89,11 @@
- [x] 11.3 新增 `owner_voice_pet simulate-live` CLI;前置条件:11.2 完成;验收标准:默认两轮模拟,输出 JSON checks,可写入/回放 JSONL fixture;测试要点:CLI 单测和真实命令;优先级:P0;预计:40 分钟。
- [x] 11.4 补充 README 和自动化测试;前置条件:11.3 完成;验收标准:README 包含模拟验收命令,测试验证两轮闭环、partial 噪声过滤、第二轮临时上下文、fixture 写入/回放;测试要点:unittest;优先级:P0;预计:35 分钟。
- [x] 11.5 执行全量门禁并提交“模拟麦克风验收”模块;前置条件:11.1 至 11.4 完成;验收标准:`simulate-live --turns 2`、compileall、unittest、security-check、model-check、device-check、OpenSpec strict、git diff check 通过后中文 commit;优先级:P0;预计:45 分钟。
## 12. 真实完整链路自测与 ACK 缓冲修正
- [x] 12.1 更新 OpenSpec 描述真实完整链路自测和 ACK 零时长 flush 偏差;前置条件:模拟验收已提交且本机模型齐全;验收标准:proposal/design/spec/tasks/spec delta 覆盖真实 provider 验收、0ms flush、模拟预排帧隔离和 ACK 禁用边缘案例;测试要点:OpenSpec strict;优先级:P0;预计:35 分钟。
- [x] 12.2 修正 `VoiceAssistantPipeline` 和兼容 `LiveVoiceRuntime` 播放后输入清理;前置条件:12.1 完成;验收标准:ACK/TTS 播放后即使 `OWNER_POST_PLAYBACK_DRAIN_MS=0` 也执行一次 flush,ACK 文本为空时不执行 post-playback drain;测试要点:零时长 flush 单测、ACK 禁用不清空单测;优先级:P0;预计:45 分钟。
- [x] 12.3 调整 `MemoryAudioTransport` 和模拟验收;前置条件:12.2 完成;验收标准:默认 flush 仍清空输入,模拟 live 显式保留预排帧并记录 flush 调用次数;测试要点:transport flush 双语义测试、两轮 simulated live 回归;优先级:P0;预计:35 分钟。
- [x] 12.4 执行真实 provider 脚本化闭环验收;前置条件:本地模型、`.env` LLM key、macOS TTS/播放可用;验收标准:生成 wake/question 音频帧驱动真实 KWS、VAD、denoiser、本地 STT、云端 LLM、本地 TTS、播放,两轮上下文可引用第一轮历史;测试要点:输出不泄露 key,模型文件不进 Git;优先级:P0;预计:60 分钟。
- [x] 12.5 执行全量门禁并提交“真实链路自测”模块;前置条件:12.1 至 12.4 完成;验收标准:compileall、unittest、simulate-live、security-check、model-check、device-check、OpenSpec strict、git diff check 全通过后中文 commit;优先级:P0;预计:45 分钟。