[本地语音降噪]:完成本地语音链路和噪音过滤,包含降噪模型、本地ASR和实时字幕稳定策略
This commit is contained in:
@@ -31,12 +31,14 @@ run-live
|
||||
-> SoundDeviceAudioTransport
|
||||
-> SherpaOnnxKeywordWakeWordProvider(local models/wake)
|
||||
-> AcknowledgeStage(local "我在")
|
||||
-> CaptureStage(primary speaker endpoint by default)
|
||||
-> CloudAsrSttProvider or SherpaOnnxSttProvider
|
||||
-> CaptureStage(raw mic frames)
|
||||
-> AudioPreprocessStage(local GTCRN denoise, capture only)
|
||||
-> PrimarySpeakerEndpoint/VAD(denoised frames)
|
||||
-> SherpaOnnxSttProvider(local CTC partial/final by default)
|
||||
-> DialogStage(session memory)
|
||||
-> ConversationContext
|
||||
-> OpenAICompatibleLlmProvider
|
||||
-> CloudTtsProvider or MacSayTtsProvider
|
||||
-> MacSayTtsProvider
|
||||
-> speaker playback
|
||||
```
|
||||
|
||||
@@ -51,11 +53,12 @@ run-live
|
||||
7. On wake hit, reset wake stream and VAD recorder.
|
||||
8. Record user utterance with VAD. The default provider is `hybrid`: project-local `sherpa-onnx` VAD remains the primary detector, and an energy threshold fallback prevents low microphone gain from being treated as no speech.
|
||||
9. When `OWNER_ENDPOINT_MODE=primary_speaker`, build a temporary per-turn speaker profile from the first valid user speech frames and end the capture when the primary speaker is absent for `OWNER_SPEAKER_ABSENT_MS`.
|
||||
10. Transcribe user utterance.
|
||||
11. Emit transcript event to terminal.
|
||||
12. Append user text and call LLM.
|
||||
13. Synthesize/play reply.
|
||||
14. Append assistant reply and return to standby.
|
||||
10. When `OWNER_NOISE_FILTER_ENABLED=1`, pass formal user utterance frames through the local GTCRN denoiser before VAD, realtime STT, and final STT segment assembly.
|
||||
11. Transcribe user utterance with the configured STT provider. The default is local CTC STT; cloud STT remains an explicit compatibility option.
|
||||
12. Emit transcript event to terminal.
|
||||
13. Append final user text and call LLM.
|
||||
14. Synthesize/play reply with local TTS by default.
|
||||
15. Append assistant reply and return to standby.
|
||||
|
||||
## Interfaces
|
||||
|
||||
@@ -88,6 +91,29 @@ class PipelineEventBus:
|
||||
|
||||
Required event types are `pipeline_started`, `wake_listening`, `wake_detected`, `ack_started`, `capture_started`, `speech_started`, `speech_ended`, `stt_started`, `transcript_final`, `llm_started`, `tts_started`, `playback_finished`, `standby_resumed`, and `stage_error`.
|
||||
|
||||
### `AudioPreprocessor`
|
||||
|
||||
```python
|
||||
class AudioPreprocessor:
|
||||
def load(self) -> None: ...
|
||||
def reset(self) -> None: ...
|
||||
def process_frame(self, frame: AudioFrame) -> AudioFrame: ...
|
||||
def flush(self) -> list[AudioFrame]: ...
|
||||
```
|
||||
|
||||
`NoopAudioPreprocessor` preserves tests and disabled configurations. `SherpaOnnxDenoiserPreprocessor` uses `sherpa_onnx.OnlineSpeechDenoiser` with an `OfflineSpeechDenoiserGtcrnModelConfig` pointing to `models/denoise/gtcrn_simple.onnx`. It converts int16 PCM to float32, calls `run(samples, sample_rate)`, converts returned `DenoisedAudio.samples` back to int16 PCM, and adds diagnostic metadata without storing audio.
|
||||
|
||||
The first version applies preprocessing only in capture. Wake listening remains raw by default because wake KWS models can be sensitive to spectral changes introduced by denoising. `OWNER_WAKE_DENOISE_ENABLED=1` reserves an explicit future switch for wake preprocessing.
|
||||
|
||||
### Local CTC STT
|
||||
|
||||
`SherpaOnnxSttProvider` reads `providers.stt.type` from `models/manifest.json`:
|
||||
|
||||
1. `sherpa-onnx-streaming-transducer`: existing `tokens`/`encoder`/`decoder`/`joiner` files and `OnlineRecognizer.from_transducer`.
|
||||
2. `sherpa-onnx-streaming-zipformer2-ctc`: new default `tokens`/`model` files and `OnlineRecognizer.from_zipformer2_ctc`.
|
||||
|
||||
Realtime partial and final transcript use the same local recognizer when `OWNER_SPEECH_PROVIDER=local`. Partial output is filtered before emitting `transcript_partial`: texts with fewer than two meaningful characters, duplicate texts, and short regressions from the last displayed text are ignored. The final transcript remains the only user text appended to `ConversationContext`.
|
||||
|
||||
### `TurnController`
|
||||
|
||||
```python
|
||||
@@ -136,7 +162,11 @@ models/
|
||||
vad/
|
||||
silero_vad.onnx
|
||||
stt/
|
||||
sherpa-onnx-streaming-zipformer-zh-14M-2023-02-23/
|
||||
sherpa-onnx-streaming-zipformer-ctc-zh-int8-2025-06-30/
|
||||
tokens.txt
|
||||
model.int8.onnx
|
||||
denoise/
|
||||
gtcrn_simple.onnx
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
@@ -152,6 +182,10 @@ models/
|
||||
9. Playback drain misconfiguration: negative `OWNER_POST_PLAYBACK_DRAIN_MS` remains invalid; non-zero values are treated as explicit user tuning rather than default behavior.
|
||||
10. Speaker profile threshold misconfiguration: non-positive `OWNER_SPEAKER_PROFILE_MIN_MS` fails config validation.
|
||||
11. Realtime STT failure: startup model缺失按 `model-check` 暴露;capture 中 partial session 失败不得把 partial 文本写入上下文。
|
||||
12. Denoiser missing model: `model-check` reports `denoise/gtcrn_simple.onnx` and live startup fails before microphone listening.
|
||||
13. Denoiser runtime failure: the current turn emits `stage_error` and recovers to standby without invoking final STT or LLM.
|
||||
14. CTC manifest mismatch: missing `model` or `tokens` files report `STT_MODEL_MISSING`; transducer manifests remain supported for existing local setups.
|
||||
15. Partial text noise: single-character or transient partial output is ignored rather than displayed as terminal feedback.
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
@@ -168,6 +202,11 @@ models/
|
||||
11. Transport batching test validates that queued SoundDevice frames are returned together.
|
||||
12. Partial transcript event test validates that realtime text appears after speech start and before final transcript.
|
||||
13. Context isolation test validates partial transcript does not enter LLM messages.
|
||||
14. Denoiser manifest/model-check test validates the required GTCRN file is present.
|
||||
15. Fake denoiser test validates VAD, partial STT, and final STT receive denoised frames from the same processed stream.
|
||||
16. Local speech provider test validates `OWNER_SPEECH_PROVIDER=local` does not construct cloud ASR/TTS providers.
|
||||
17. Partial filter test validates single-character and short transient results are not emitted.
|
||||
18. CTC STT loading test validates manifest type selects `from_zipformer2_ctc` and does not require transducer encoder/decoder/joiner files.
|
||||
|
||||
## Migration
|
||||
|
||||
@@ -179,3 +218,11 @@ python3.11 scripts/download_speech_models.py --dir models
|
||||
```
|
||||
|
||||
Existing `.env` remains valid because new wake keys have defaults.
|
||||
For the local voice-chain revision, users should also ensure:
|
||||
|
||||
```dotenv
|
||||
OWNER_SPEECH_PROVIDER=local
|
||||
OWNER_NOISE_FILTER_ENABLED=1
|
||||
OWNER_WAKE_DENOISE_ENABLED=0
|
||||
OWNER_NOISE_FILTER_PROVIDER=sherpa_onnx_gtcrn
|
||||
```
|
||||
|
||||
@@ -52,6 +52,10 @@
|
||||
10. 实时消费问题:`SoundDeviceAudioTransport.read_frames()` 每次只返回一个队列帧,在音频回调批量积压时会增加 pipeline 对真实麦克风流的追帧成本。
|
||||
11. 画像门槛问题:`PrimarySpeakerVadRecorder._profile_ready()` 把主说话人画像就绪阈值绑定到 `OWNER_VAD_MIN_DURATION_MS`,默认至少等待 250 ms 后主说话人端点才参与结束判断,短句用户会被迫等普通 VAD 静音或重复说话。
|
||||
12. 实时转写问题:当前终端只在整段录音结束并完成 final STT 后显示“转写结果”,用户说话期间看不到任何文字反馈,无法判断系统是否已经听到并识别当前句子。
|
||||
13. 实时字幕质量问题:真人日志中 `实时转写:家`、`实时转写:家确` 来自旧本地 14M streaming STT partial,最终云端 ASR 虽然较准,但用户看到的实时反馈会被单字噪声和短暂跳变污染。
|
||||
14. 语音链路一致性问题:当前 `OWNER_SPEECH_PROVIDER=cloud` 时 final STT/TTS 走云端,而 partial 走本地模型;同一轮对话里 partial 和 final 来自不同模型,容易出现“实时字幕和最终转写明显冲突”的体验。
|
||||
15. 降噪缺失问题:正式问题录音直接把原始麦克风帧送入 VAD、partial STT 和 final STT,背景噪声会同时影响端点、实时字幕和最终识别。
|
||||
16. 本地模型落后问题:默认 STT 仍是 2023 年 14M 小模型,适合最小验收但不适合作为默认实时字幕质量基线;应升级为 sherpa-onnx 官方 2025 中文 CTC int8 模型。
|
||||
|
||||
## 详细需求
|
||||
|
||||
@@ -78,6 +82,15 @@
|
||||
19. 录音期间 SHALL 支持 partial transcript 事件;当本地 streaming STT 产生新的中间文本时,终端 SHALL 立即显示 `实时转写:<文本>`。
|
||||
20. partial transcript SHALL 只作为用户可见反馈,不得直接写入对话上下文;LLM 输入仍以最终 `transcript_final` 文本为准。
|
||||
21. 当 `OWNER_SPEECH_PROVIDER=cloud` 时,partial transcript SHALL 使用本地 streaming STT,避免对云端 ASR 进行高频请求。
|
||||
22. 下一版默认语音链路 SHALL 改为“除 LLM 外全本地”:wake、VAD、STT、partial transcript、TTS、噪音过滤均在本机执行;LLM 仍走配置中的云端 OpenAI-compatible endpoint。
|
||||
23. `.env.example` 和代码默认 SHALL 将 `OWNER_SPEECH_PROVIDER` 设为 `local`,从而默认 final STT 使用本地 sherpa-onnx 模型,TTS 使用 macOS 本地 `say/afplay` provider。
|
||||
24. 模型 manifest SHALL 默认使用 sherpa-onnx 官方 2025 中文 Zipformer2 CTC int8 模型,关键文件为 `tokens.txt` 和 `model.int8.onnx`,不再以旧 14M transducer 作为默认 STT 模型。
|
||||
25. 模型 manifest SHALL 新增 GTCRN/sherpa-onnx speech denoiser 模型 `denoise/gtcrn_simple.onnx`,`download_speech_models.py` 和 `model-check` 必须把它列为 required file。
|
||||
26. Pipeline SHALL 新增 `AudioPreprocessStage`。默认 `OWNER_NOISE_FILTER_ENABLED=1` 时,唤醒后的正式问题采集必须先对音频帧降噪,再把同一份降噪后帧送入 VAD、实时 partial STT 和 final STT segment。
|
||||
27. wake 阶段默认 SHALL 继续使用原始音频帧,避免 denoiser 改变 KWS 特征;仅当用户显式设置 `OWNER_WAKE_DENOISE_ENABLED=1` 时才允许对 wake 帧预处理。
|
||||
28. 降噪 provider 失败 SHALL 作为结构化可恢复错误进入 `stage_error -> recovering -> standby`,不得把未经标记的半处理音频写入对话上下文。
|
||||
29. partial transcript SHALL 增加稳定过滤:不得显示单个中文/英文有效字符,不得重复显示同一文本,不得把极短的瞬态跳变作为终端实时字幕输出。
|
||||
30. final transcript SHALL 是唯一进入 LLM 的用户文本;降噪帧、partial 文本、denoiser metadata 和 ASR raw metadata 均不得进入 `ConversationContext`。
|
||||
|
||||
### 非功能需求
|
||||
|
||||
@@ -88,6 +101,9 @@
|
||||
5. 测试性:自动化测试必须能注入 fake wake provider,不依赖真实麦克风或真实 KWS 模型。
|
||||
6. 架构可观测性:所有用户可见状态必须来自 pipeline event bus,终端 reporter 和后续 GUI 只消费事件,不直接嵌入 stage 逻辑。
|
||||
7. 端点性能:默认配置下,主说话人音色消失后 300 ms 左右应结束采集,并进入 STT;最大录音时长仍作为兜底。
|
||||
8. 实时字幕质量:本地 partial 默认不显示 1 个有效字符以内的文本,避免 `家`、`嗯`、`啊` 这类背景噪声触发可见字幕;最终字幕仍由 final STT 决定。
|
||||
9. 语音隐私:除 LLM 请求文本外,正式问题原始音频、降噪音频、VAD 特征和临时音色画像均不得上传云端、不得落盘。
|
||||
10. 降噪性能:GTCRN online denoiser 只能在 capture 阶段运行,目标是不明显拖慢端点;如果 denoiser 不可用,启动/模型检查必须明确失败,而不是静默回退为无降噪。
|
||||
|
||||
### 边缘案例
|
||||
|
||||
@@ -100,6 +116,11 @@
|
||||
7. 播放回声误触发:播放期间仍遵循既有音频反馈抑制要求,不能把 TTS 当作新 wake。
|
||||
8. 背景噪声拖尾:用户停止说话后若仍有非主说话人或噪声,主说话人端点必须允许结束录音。
|
||||
9. 音色画像不足:如果开头音频太短或能量不足,采集阶段必须回退到普通 VAD 静音端点,不能卡死。
|
||||
10. 降噪模型缺失:`model-check` 必须报告 `denoise/gtcrn_simple.onnx` 缺失;`run-live` 启动时不得进入“看似可用但未降噪”的状态。
|
||||
11. 降噪运行时异常:如果 GTCRN provider 在某帧处理失败,当前 turn 必须结构化报错并恢复待机;不得把可能损坏的片段送入 STT 或 LLM。
|
||||
12. partial 单字误识别:本地 streaming STT 输出 `家`、`确`、`a` 等单字时,终端不得显示为 `实时转写`。
|
||||
13. partial 瞬态跳变:本地 streaming STT 从“你是谁”短暂跳到“加”再回到“你是谁”时,不得显示中间极短跳变。
|
||||
14. CTC 模型缺旧 transducer 文件:当 manifest 类型为 `sherpa-onnx-streaming-zipformer2-ctc` 时,不应再要求 encoder/decoder/joiner 文件存在。
|
||||
|
||||
### 输入输出规格
|
||||
|
||||
@@ -120,6 +141,10 @@
|
||||
13. `OWNER_POST_PLAYBACK_DRAIN_MS=0`:ACK 或 TTS 播放完成后只 flush 已积压输入,不额外读取并丢弃新音频。
|
||||
14. `OWNER_SPEAKER_PROFILE_MIN_MS=120`:主说话人画像参与端点判断的最低有效语音长度。
|
||||
15. `OWNER_REALTIME_TRANSCRIPT_ENABLED=1`:启用录音期间本地 streaming STT 中间结果显示。
|
||||
16. `OWNER_SPEECH_PROVIDER=local`:默认本地 STT/TTS,云端只保留 LLM。
|
||||
17. `OWNER_NOISE_FILTER_ENABLED=1`:正式问题阶段默认启用本地降噪。
|
||||
18. `OWNER_WAKE_DENOISE_ENABLED=0`:wake 阶段默认不启用降噪。
|
||||
19. `OWNER_NOISE_FILTER_PROVIDER=sherpa_onnx_gtcrn`:第一版本地降噪 provider。
|
||||
|
||||
终端输出:
|
||||
|
||||
@@ -147,12 +172,15 @@ SoundDeviceAudioTransport
|
||||
-> LocalWakeWordProvider(sherpa-onnx KeywordSpotter, models/wake, keywords.txt)
|
||||
-> wake_hit
|
||||
-> AcknowledgeStage("我在")
|
||||
-> CaptureStage(primary speaker endpoint, user utterance only)
|
||||
-> SttProvider(cloud or local, configured by OWNER_SPEECH_PROVIDER)
|
||||
-> CaptureStage(raw user utterance only)
|
||||
-> AudioPreprocessStage(sherpa-onnx GTCRN denoise, capture only)
|
||||
-> PrimarySpeakerEndpoint + VAD(denoised frames)
|
||||
-> RealtimeSttProvider(local CTC, denoised frames, stable partial filter)
|
||||
-> SttProvider(local CTC by default, denoised final segment)
|
||||
-> PipelineEvent(transcript_final)
|
||||
-> ConversationContext(temporary process history)
|
||||
-> Cloud LLM
|
||||
-> TTS
|
||||
-> Local TTS(MacSayTtsProvider)
|
||||
-> Speaker
|
||||
-> standby
|
||||
```
|
||||
@@ -168,6 +196,9 @@ SoundDeviceAudioTransport
|
||||
7. Runtime 将 `user_text` 追加到临时上下文并调用 LLM。
|
||||
8. TTS 播放后追加 assistant 历史并恢复待机。
|
||||
9. 所有 stage 同步发出 pipeline events;终端 reporter 只把事件映射为中文文案。
|
||||
10. 正式问题 capture 收到原始麦克风帧后,先调用 `AudioPreprocessor.process_frame(frame)` 得到降噪帧。
|
||||
11. VAD、主说话人端点、partial streaming STT 和 final STT segment builder 必须使用同一份降噪帧,保证用户看到的 realtime partial 和最终转写来自一致音频来源。
|
||||
12. wake listening 默认使用原始帧;后续仅在 `OWNER_WAKE_DENOISE_ENABLED=1` 时把同一预处理接口接到 wake 阶段。
|
||||
|
||||
### 接口定义
|
||||
|
||||
@@ -192,6 +223,28 @@ TurnController.run_turn(turn_id: int) -> TurnResult
|
||||
VoiceAssistantPipeline.run(once: bool = False, max_turns: int | None = None) -> RuntimeSummary
|
||||
```
|
||||
|
||||
```text
|
||||
AudioPreprocessor.load() -> None
|
||||
AudioPreprocessor.reset() -> None
|
||||
AudioPreprocessor.process_frame(frame: AudioFrame) -> AudioFrame
|
||||
AudioPreprocessor.flush() -> list[AudioFrame]
|
||||
```
|
||||
|
||||
```text
|
||||
SherpaOnnxDenoiserPreprocessor(
|
||||
models_dir: Path,
|
||||
provider: "sherpa_onnx_gtcrn",
|
||||
enabled: bool,
|
||||
sherpa_module: object | None = None,
|
||||
)
|
||||
```
|
||||
|
||||
```text
|
||||
SherpaOnnxSttProvider.load()
|
||||
manifest type "sherpa-onnx-streaming-transducer" -> OnlineRecognizer.from_transducer(...)
|
||||
manifest type "sherpa-onnx-streaming-zipformer2-ctc" -> OnlineRecognizer.from_zipformer2_ctc(tokens, model, ...)
|
||||
```
|
||||
|
||||
```text
|
||||
SherpaOnnxKeywordWakeWordProvider(
|
||||
models_dir: Path,
|
||||
@@ -228,6 +281,9 @@ standby
|
||||
7. 批量读帧:真实 SoundDevice 输入在拿到首帧后立即 drain 当前队列中所有可用帧并返回给 pipeline,使 wake、capture 和 VAD 能在同一个循环内处理积压帧。
|
||||
8. 快速主说话人结束:画像就绪最低语音长度由 `OWNER_SPEAKER_PROFILE_MIN_MS` 控制,默认 120 ms;一旦画像就绪,主说话人缺席计时达到 `OWNER_SPEAKER_ABSENT_MS` 即结束,不再叠加 `OWNER_VAD_MIN_DURATION_MS`。
|
||||
9. 实时转写显示:CaptureStage 在 `speech_started` 后把已录入的帧同时送入本地 streaming STT session;每当 partial 文本变化时发出 `transcript_partial`,终端显示 `实时转写:<文本>`;最终段落仍交给 configured STT provider 生成 `transcript_final`。
|
||||
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 的文本直接忽略;若新文本比上一次已显示文本短且不构成稳定前缀推进,也忽略,避免把瞬时噪声显示给用户。
|
||||
|
||||
### 数据库/状态管理变更
|
||||
|
||||
@@ -246,6 +302,9 @@ standby
|
||||
2. 新增 KWS 模型约 15 MB,下载到 ignored `models/wake/`。
|
||||
3. 不新增 Python 运行依赖。
|
||||
4. `model-check` 变严格:缺 wake 模型会失败。
|
||||
5. 默认 STT 模型升级为 2025 中文 CTC int8,模型体积和加载时间高于旧 14M 小模型,但换来更稳定的本地 partial/final 一致性。
|
||||
6. 新增 GTCRN denoiser 单文件模型,下载到 ignored `models/denoise/gtcrn_simple.onnx`。
|
||||
7. 继续使用已安装的 `sherpa-onnx` 包;不新增 Python 依赖,不接入云 ASR/TTS 作为默认路径。
|
||||
|
||||
## 风险与权衡
|
||||
|
||||
@@ -253,7 +312,7 @@ standby
|
||||
| --- | --- | --- | --- |
|
||||
| KWS 关键词拼音格式不匹配模型 | 中 | 高 | 默认写入 sherpa 示例格式;保留 keywords 文件可编辑;测试下载后用真实模型加载;README 标明关键词文件位置 |
|
||||
| KWS 模型误唤醒或漏唤醒 | 中 | 中 | 暴露 threshold/score 配置;保留状态输出;后续可替换 KWS Provider |
|
||||
| 唤醒应答期间用户抢说被缓冲清理吞掉 | 中 | 高 | 终端提示顺序改为“唤醒命中 -> 应答中 -> 请说出问题 -> 录音中”,用户只在应答完成后收到提问提示;默认播放后排水从 250 ms 降为 50 ms |
|
||||
| 唤醒应答期间用户抢说被缓冲清理吞掉 | 中 | 高 | 终端提示顺序改为“唤醒命中 -> 应答中 -> 请说出问题 -> 录音中”,用户只在应答完成后收到提问提示;默认 `OWNER_POST_PLAYBACK_DRAIN_MS=0`,不再额外读取并丢弃播放后的新音频 |
|
||||
| 本地 VAD 对真实麦克风音量过保守 | 中 | 高 | 默认 VAD provider 改为 `hybrid`,本地模型判断和能量阈值兜底任一命中即认为有语音;保留 `local` 和 `energy` 可配置回退 |
|
||||
| 能量兜底让录音无法及时结束 | 高 | 高 | 将能量兜底限制为“开始录音辅助”,结束录音优先使用主说话人音色消失和本地 VAD 静音 |
|
||||
| ACK 后额外丢弃音频截断首句 | 高 | 高 | 默认 `OWNER_POST_PLAYBACK_DRAIN_MS=0`;播放结束后只 flush 已积压输入;新增首句保留回归测试 |
|
||||
@@ -268,6 +327,11 @@ standby
|
||||
| 终端转写不是逐字流式 | 中 | 低 | 第一版至少在 LLM 前即时显示最终 STT 文本;后续可接入本地 streaming STT partial |
|
||||
| 模型下载网络失败 | 中 | 中 | 下载脚本保留重试;`model-check` 给明确缺失文件 |
|
||||
| 误提交模型或 key | 低 | 高 | `.gitignore`、security-check、提交前 `git status --short` |
|
||||
| 降噪模型 API 与当前 sherpa-onnx 版本不一致 | 中 | 高 | 用本机 `sherpa_onnx 1.13.3` introspection 确认 `OnlineSpeechDenoiser` 构造;新增 fake module 单测;model-check 尝试加载 provider |
|
||||
| GTCRN 对 wake 特征造成误伤 | 中 | 中 | wake 默认不走降噪,仅正式问题阶段降噪;保留 `OWNER_WAKE_DENOISE_ENABLED=1` 作为显式实验开关 |
|
||||
| CTC 模型比旧 14M 模型更大导致下载慢 | 中 | 中 | 仍放 `models/` 并跳过已存在文件;README 明确首次下载较久;下载脚本保留重试 |
|
||||
| partial 过滤过严导致实时字幕少显示 | 中 | 低 | 过滤只影响用户可见 partial,不影响 final STT 和 LLM;后续可配置更细阈值 |
|
||||
| 降噪失败后用户无法继续本轮 | 低 | 中 | 当前 turn 结构化失败并恢复待机,避免错误音频进入 LLM;下一轮可继续唤醒 |
|
||||
|
||||
## 任务分解
|
||||
|
||||
@@ -322,6 +386,17 @@ standby
|
||||
- [ ] 7.5 更新 README、`.env.example`、本地 `.env` 非密钥配置;前置条件:7.2 至 7.4 完成;验收标准:运行说明匹配新 pipeline;测试要点:`--show-config` 不泄露 key;优先级:P0;预计:30 分钟。
|
||||
- [ ] 7.6 验证并提交“Pipeline 文档验收”模块;前置条件:7.1 至 7.5 完成;验收标准:compileall、unittest、security-check、model-check、device-check、OpenSpec strict 全通过;优先级:P0;预计:30 分钟。
|
||||
|
||||
### 10. 本地语音链路、高质量实时转写与噪音过滤
|
||||
|
||||
- [ ] 10.1 更新 OpenSpec 描述本地语音链路和降噪阶段;前置条件:真人日志确认旧本地 14M partial 会产生 `家/家确` 等噪声误识别;验收标准:proposal/design/spec/tasks 明确 wake/VAD/STT/partial/TTS/denoise 全本地、LLM 云端、正式问题阶段默认降噪;测试要点:OpenSpec strict;优先级:P0;预计:45 分钟。
|
||||
- [ ] 10.2 升级 speech model manifest 和下载脚本;前置条件:确认 sherpa-onnx 官方 2025 中文 CTC int8 模型和 GTCRN denoiser URL;验收标准:`models/manifest.json` 默认 STT 指向 CTC `model.int8.onnx`,新增 `denoise/gtcrn_simple.onnx` required file;测试要点:manifest、required files、下载脚本单测/真实下载;优先级:P0;预计:60 分钟。
|
||||
- [ ] 10.3 实现 `AudioPreprocessStage`;前置条件:10.2 完成;验收标准:新增可注入的 no-op 和 sherpa-onnx GTCRN online denoiser provider,正式问题帧在进入 VAD、partial STT、final STT 前共用同一份降噪后音频;测试要点:fake denoiser 验证 capture/STT 收到降噪后帧;优先级:P0;预计:60 分钟。
|
||||
- [ ] 10.4 扩展本地 STT Provider 支持 CTC 模型和 partial 稳定过滤;前置条件:10.2 完成;验收标准:`SherpaOnnxSttProvider` 可按 manifest 加载 transducer 或 zipformer2 CTC,partial 不显示单字噪声和短暂跳变;测试要点:fake sherpa CTC、single-char partial filter、transient partial filter;优先级:P0;预计:60 分钟。
|
||||
- [ ] 10.5 修改 runtime 默认本地语音链路;前置条件:10.3 至 10.4 完成;验收标准:默认 `OWNER_SPEECH_PROVIDER=local`,TTS 使用 `MacSayTtsProvider`,云端只用于 LLM,`--show-config` 显示噪音过滤配置且不泄露 key;测试要点:build runtime provider 类型、cloud ASR/TTS 不被调用;优先级:P0;预计:45 分钟。
|
||||
- [ ] 10.6 更新 README、`.env.example` 和本地 `.env` 非密钥配置;前置条件:10.5 完成;验收标准:中文运行说明包含本地语音默认值、降噪开关、模型下载、model-check、run-live 验收;测试要点:命令可复制,`.env` 不进入提交;优先级:P0;预计:35 分钟。
|
||||
- [ ] 10.7 下载/校验新本地模型并执行门禁;前置条件:10.2 至 10.6 完成;验收标准:下载脚本、compileall、unittest、security-check、model-check、device-check、OpenSpec strict、git diff check 通过或记录真实设备失败原因;测试要点:输出不泄露 key,模型文件不进 Git;优先级:P0;预计:60 分钟。
|
||||
- [ ] 10.8 提交“本地语音降噪”模块;前置条件:10.7 门禁通过;验收标准:中文 commit 信息为 `[本地语音降噪]:完成本地语音链路和噪音过滤,包含降噪模型、本地ASR和实时字幕稳定策略`,提交后除本地 `.env` 非提交修改外无未提交源码/文档中间状态;优先级:P0;预计:10 分钟。
|
||||
|
||||
## Spec Deltas
|
||||
|
||||
### 新增能力
|
||||
@@ -340,6 +415,7 @@ standby
|
||||
8. `Primary speaker endpointing`:新增本轮临时主说话人音色消失结束录音要求。
|
||||
9. `Low latency capture and first utterance preservation`:新增 ACK 后不额外丢弃正式问题、批量读帧、独立画像就绪阈值和快速主说话人端点要求。
|
||||
10. `Realtime partial transcript output`:新增录音期间 partial transcript 事件、终端显示和上下文隔离要求。
|
||||
11. `Local voice chain and noise filtering`:新增本地 STT/TTS 默认、GTCRN 降噪、CTC 模型、partial 稳定过滤和音频不上传要求。
|
||||
|
||||
### 删除项
|
||||
|
||||
@@ -359,6 +435,7 @@ standby
|
||||
6. M6:Stage 化 pipeline、事件总线、TurnController、主说话人端点和文档验收分模块提交。
|
||||
7. M7:低延迟端点与首句保留修正完成后提交,保留真人 `run-live` 验收任务,不在用户确认前归档。
|
||||
8. M8:录音期间实时转写显示完成后提交,保留真人 `run-live` 验收任务,不在用户确认前归档。
|
||||
9. M9:本地语音链路、高质量实时字幕和正式问题降噪完成后提交,继续保留真人 `run-live` 验收任务,不在用户确认前归档。
|
||||
|
||||
估时:
|
||||
|
||||
|
||||
+44
-3
@@ -64,6 +64,47 @@ The live runtime SHALL display recognized user utterance text in the terminal bo
|
||||
- **WHEN** STT returns empty text, punctuation-only text, or an invalid transcript
|
||||
- **THEN** the runtime SHALL NOT emit a misleading transcript as valid user input and SHALL recover to standby without invoking the LLM
|
||||
|
||||
### Requirement: Local voice chain and noise filtering
|
||||
The live runtime SHALL run wake, VAD, STT, realtime transcript, TTS, and capture-stage noise filtering locally by default, while continuing to send only the final user text and session history to the configured cloud LLM.
|
||||
|
||||
#### Scenario: Default speech provider is local
|
||||
- **WHEN** the user starts `run-live` without overriding `OWNER_SPEECH_PROVIDER`
|
||||
- **THEN** the runtime SHALL use local sherpa-onnx STT and local macOS TTS
|
||||
- **AND** it SHALL NOT call cloud ASR or cloud TTS providers during the turn
|
||||
|
||||
#### Scenario: Capture audio is denoised before downstream stages
|
||||
- **WHEN** `OWNER_NOISE_FILTER_ENABLED=1` and the user speaks after wake acknowledgement
|
||||
- **THEN** the capture stage SHALL pass microphone frames through the configured local denoiser before VAD analysis, realtime partial STT, and final STT segment assembly
|
||||
- **AND** all three downstream consumers SHALL observe the same denoised frame content for a given captured frame
|
||||
|
||||
#### Scenario: Wake remains raw by default
|
||||
- **WHEN** `OWNER_WAKE_DENOISE_ENABLED` is unset or `0`
|
||||
- **THEN** wake word detection SHALL receive raw microphone frames rather than denoised frames
|
||||
|
||||
#### Scenario: Denoiser model is missing
|
||||
- **WHEN** `models/denoise/gtcrn_simple.onnx` is absent
|
||||
- **THEN** `model-check` SHALL fail with a structured missing model error
|
||||
- **AND** `run-live` SHALL NOT enter live microphone listening as if noise filtering were available
|
||||
|
||||
#### Scenario: Denoiser runtime fails
|
||||
- **WHEN** the local denoiser raises an error while processing a formal user utterance frame
|
||||
- **THEN** the current turn SHALL emit a stage error and recover to standby
|
||||
- **AND** the runtime SHALL NOT invoke final STT, LLM, or TTS for that failed turn
|
||||
|
||||
#### Scenario: Local ASR uses the 2025 CTC model by default
|
||||
- **WHEN** the default manifest is written by `download_speech_models.py`
|
||||
- **THEN** the STT provider SHALL be declared as `sherpa-onnx-streaming-zipformer2-ctc`
|
||||
- **AND** required STT files SHALL be `tokens.txt` and `model.int8.onnx`, not transducer encoder/decoder/joiner files
|
||||
|
||||
#### Scenario: Realtime partial filters noise
|
||||
- **WHEN** local streaming STT emits a single meaningful character or a short transient regression
|
||||
- **THEN** the terminal reporter SHALL NOT display that text as `实时转写`
|
||||
- **AND** final STT SHALL remain responsible for the text sent to the LLM
|
||||
|
||||
#### Scenario: Audio privacy is preserved
|
||||
- **WHEN** local noise filtering and local STT run during capture
|
||||
- **THEN** raw PCM, denoised PCM, VAD features, partial transcript metadata, and temporary speaker profile data SHALL remain process-local and SHALL NOT be sent to the cloud LLM
|
||||
|
||||
### 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.
|
||||
|
||||
@@ -120,14 +161,14 @@ The system SHALL provide project-local speech model preparation and diagnostics
|
||||
|
||||
#### Scenario: Models are downloaded
|
||||
- **WHEN** the user runs `python3.11 scripts/download_speech_models.py --dir models`
|
||||
- **THEN** the script SHALL create or update a project-local model directory with the files required by the configured wake, VAD, and STT providers
|
||||
- **THEN** the script SHALL create or update a project-local model directory with the files required by the configured wake, VAD, STT, and denoiser providers
|
||||
|
||||
#### Scenario: Model check succeeds
|
||||
- **WHEN** required wake, VAD, STT dependencies and model files are available
|
||||
- **WHEN** required wake, VAD, STT, denoiser dependencies and model files are available
|
||||
- **THEN** `PYTHONPATH=src python3.11 -m owner_voice_pet model-check` SHALL exit successfully and report the model directory and checked providers
|
||||
|
||||
#### Scenario: Model check fails
|
||||
- **WHEN** `sherpa-onnx` is unavailable, a wake model file is missing, a VAD/STT model file is missing, or a model cannot be loaded
|
||||
- **WHEN** `sherpa-onnx` is unavailable, a wake model file is missing, a VAD/STT/denoiser model file is missing, or a model cannot be loaded
|
||||
- **THEN** `model-check` SHALL fail with a structured model error and SHALL NOT start live microphone listening
|
||||
|
||||
### Requirement: Live repeat voice runtime
|
||||
|
||||
@@ -70,3 +70,14 @@
|
||||
- [x] 9.3 实现本地 streaming STT partial provider;前置条件:本地 STT 模型已由 `model-check` 覆盖;验收标准:`SherpaOnnxSttProvider` 支持 streaming session,cloud final ASR 模式下仍使用本地 streaming STT 做 partial;测试要点:fake session 与 metadata partial 单测;优先级:P0;预计:60 分钟。
|
||||
- [x] 9.4 接入 `VoiceAssistantPipeline` 捕获循环;前置条件:9.2 至 9.3 完成;验收标准:用户说话期间持续 feed 已开始录音的帧,partial 在 `speech_started` 后、`transcript_final` 前输出;测试要点:live runtime partial 顺序测试;优先级:P0;预计:45 分钟。
|
||||
- [x] 9.5 补充配置、README、门禁并提交;前置条件:9.1 至 9.4 完成;验收标准:新增 `OWNER_REALTIME_TRANSCRIPT_ENABLED=1`,compileall、unittest、security-check、model-check、device-check、OpenSpec strict 通过后中文 commit;优先级:P0;预计:45 分钟。
|
||||
|
||||
## 10. 本地语音链路、高质量实时转写与噪音过滤
|
||||
|
||||
- [x] 10.1 更新 OpenSpec 描述本地语音链路和降噪阶段;前置条件:真人日志确认旧本地 14M partial 会产生 `家/家确` 等噪声误识别;验收标准:proposal/design/spec/tasks 明确 wake/VAD/STT/partial/TTS/denoise 全本地、LLM 云端、正式问题阶段默认降噪;测试要点:OpenSpec strict;优先级:P0;预计:45 分钟。
|
||||
- [x] 10.2 升级 speech model manifest 和下载脚本;前置条件:确认 sherpa-onnx 官方 2025 中文 CTC int8 模型和 GTCRN denoiser URL;验收标准:`models/manifest.json` 默认 STT 指向 CTC `model.int8.onnx`,新增 `denoise/gtcrn_simple.onnx` required file;测试要点:manifest、required files、下载脚本单测/真实下载;优先级:P0;预计:60 分钟。
|
||||
- [x] 10.3 实现 `AudioPreprocessStage`;前置条件:10.2 完成;验收标准:新增可注入的 no-op 和 sherpa-onnx GTCRN online denoiser provider,正式问题帧在进入 VAD、partial STT、final STT 前共用同一份降噪后音频;测试要点:fake denoiser 验证 capture/STT 收到降噪后帧;优先级:P0;预计:60 分钟。
|
||||
- [x] 10.4 扩展本地 STT Provider 支持 CTC 模型和 partial 稳定过滤;前置条件:10.2 完成;验收标准:`SherpaOnnxSttProvider` 可按 manifest 加载 transducer 或 zipformer2 CTC,partial 不显示单字噪声和短暂跳变;测试要点:fake sherpa CTC、single-char partial filter、transient partial filter;优先级:P0;预计:60 分钟。
|
||||
- [x] 10.5 修改 runtime 默认本地语音链路;前置条件:10.3 至 10.4 完成;验收标准:默认 `OWNER_SPEECH_PROVIDER=local`,TTS 使用 `MacSayTtsProvider`,云端只用于 LLM,`--show-config` 显示噪音过滤配置且不泄露 key;测试要点:build runtime provider 类型、cloud ASR/TTS 不被调用;优先级:P0;预计:45 分钟。
|
||||
- [x] 10.6 更新 README、`.env.example` 和本地 `.env` 非密钥配置;前置条件:10.5 完成;验收标准:中文运行说明包含本地语音默认值、降噪开关、模型下载、model-check、run-live 验收;测试要点:命令可复制,`.env` 不进入提交;优先级:P0;预计:35 分钟。
|
||||
- [x] 10.7 下载/校验新本地模型并执行门禁;前置条件:10.2 至 10.6 完成;验收标准:下载脚本、compileall、unittest、security-check、model-check、device-check、OpenSpec strict、git diff check 通过或记录真实设备失败原因;测试要点:输出不泄露 key,模型文件不进 Git;优先级:P0;预计:60 分钟。
|
||||
- [x] 10.8 提交“本地语音降噪”模块;前置条件:10.7 门禁通过;验收标准:中文 commit 信息为 `[本地语音降噪]:完成本地语音链路和噪音过滤,包含降噪模型、本地ASR和实时字幕稳定策略`,提交后除本地 `.env` 非提交修改外无未提交源码/文档中间状态;优先级:P0;预计:10 分钟。
|
||||
|
||||
Reference in New Issue
Block a user