feat(voice): improve Romanian STT — hallucination gate + finetuned model
Gemma 4 cloud audio was infeasible (31b-cloud has no audio; E4B broken upstream, no deploy host), so improve faster-whisper instead. - Pin temperature=0.0 to disable the fallback ladder that re-decoded unclear audio up to 6x (source of the 16-24s latency outliers); reject hallucinated segments via avg_logprob/compression_ratio in the new pure _filter_segments. - Adopt mikr/whisper-small-ro-cv11 (CT2 int8) via configurable voice.stt_model: spike showed WER 24%->10%, numbers fixed at source, +0.33s p50 (in budget). - Add tools/voice_stt_mine.py (log mining) + tools/voice_stt_spike.py (model eval with diacritic scoring) + tests for the gate and miner. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -51,3 +51,17 @@ Lecții capturate din corectările lui Marius. Citește acest fișier la începu
|
||||
**Greșeala:** Am editat index.json direct, cu o schemă diferită față de ce produce update_notes_index.py.
|
||||
**Regula:** Niciodată nu scriei manual în `memory/kb/index.json`. Fluxul corect: (1) creezi fișierul `.md` în `memory/kb/<categorie>/`, (2) rulezi `python3 tools/update_notes_index.py`. Dacă ai nevoie să salvezi o notiță din Facebook/video, folosești `scripts/transcribe_video.sh <URL> <lang> --save-kb` care face totul corect.
|
||||
**Când se aplică:** Orice salvare de notiță în KB (Facebook, YouTube, coaching, insights, orice). Dacă ești tentat să `json.dump` în index.json — stop, rulează scriptul.
|
||||
|
||||
## Verifică că modelul/tool-ul numit chiar are capabilitatea ÎNAINTE de a planifica în jurul lui
|
||||
**Data:** 2026-06-27
|
||||
**Context:** Marius a cerut să folosesc `gemma4:31b-cloud` (Ollama) pentru decodare audio ca alternativă la Whisper. Am verificat pe pagina oficială Ollama: variantele cloud (31b) suportă doar Text+Image — audio există DOAR pe E2B/E4B (edge, local), iar acela e stricat de o regresie upstream deschisă (issue #16584). Premisa cererii era infezabilă.
|
||||
**Greșeala (evitată):** Dacă planificam direct integrarea fără să verific pagina modelului, scriam cod de cablare Ollama audio care n-ar fi funcționat niciodată. Search-ul generic spunea „Gemma 4 are audio" — adevărat la nivel de familie, fals pentru modelul cloud specific cerut.
|
||||
**Regula:** Când userul numește un model/serviciu specific pentru o capabilitate (audio, vision, tool-use, context lung), verifică pagina/docs ACELUI model exact înainte de a planifica. Capabilitățile diferă per variantă (cloud vs edge, sizes). Fetch pagina oficială, nu te baza pe search agregat la nivel de familie.
|
||||
**Când se aplică:** Orice task care pornește de la „folosește modelul X pentru Y". Confirmă X→Y pe sursa primară înainte de plan mode.
|
||||
|
||||
## Corecția post-STT de text e cosmetică dacă consumatorul e un LLM — fixează la sursă (model), nu cu dicționar
|
||||
**Data:** 2026-06-27
|
||||
**Context:** Plan inițial pentru curățarea transcrierii Whisper avea 4 piese, inclusiv dicționar de restaurare diacritice + canonicalizare wake-word. Două review-uri independente (/autoplan CEO+Eng) au arătat: textul transcris merge la Claude, care citește română fără diacritice perfect; NU există wake-word gate în cod (`on_segment_done` dispatch necondiționat); singurul consumator precis (`detect_voice_change`) e deja fuzz-hardenat. Un spike a confirmat că modelul RO-finetuned (`mikr/whisper-small-ro-cv11`) înjumătățește WER (24%→10%) și fixează numerele la SURSĂ, +0.33s latență.
|
||||
**Greșeala (evitată):** Construirea unui strat de corecție hand-curat (întreținere perpetuă, risc de regresie pe cuvinte ambigue) când fix-ul real era un model finetuned cu același cost de inferență.
|
||||
**Regula:** Înainte de a peticit output-ul unui model ML cu post-procesare rule-based, întreabă: (1) cine e CONSUMATORUL textului? (un LLM tolerează erori; un parser regex nu); (2) există un model finetuned care fixează la sursă cu același cost? Spike-uiește modelul ÎNAINTE de a scrie straturi de corecție. Verifică unde merge output-ul prin cod, nu presupune un gate care „pare" că există.
|
||||
**Când se aplică:** Orice îmbunătățire de calitate STT/OCR/ML output. Tool de spike: `tools/voice_stt_spike.py`.
|
||||
|
||||
61
tasks/voice-stt-quality.md
Normal file
61
tasks/voice-stt-quality.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Voice STT Quality — îmbunătățiri Whisper
|
||||
|
||||
Branch: `voice/stt-quality`. Origine: cererea de a folosi Gemma 4 cloud pentru audio
|
||||
(infezabil — `gemma4:31b-cloud` n-are audio, E4B e stricat upstream, fără host de deploy).
|
||||
Pivot la îmbunătățirea Whisper, validat prin `/autoplan` (CEO + Eng review).
|
||||
|
||||
## Ce s-a livrat
|
||||
|
||||
### 1. Gate rejection halucinații (cost zero latență) — `src/voice/pipeline.py`
|
||||
- `model.transcribe(..., temperature=0.0)` — **dezactivează scara de fallback** a faster-whisper.
|
||||
Codul vechi nu pasa `temperature`, deci folosea implicit `[0.0..1.0]` (6 pași) care re-decoda
|
||||
segmentul pe audio prost → exact sursa latențelor de 24.4s / 16.7s din `voice_stt_log.jsonl`.
|
||||
- `_filter_segments()` — funcție pură nouă care dropează segmentele cu `no_speech_prob` mare,
|
||||
`avg_logprob < -1.0` (decoder nesigur) sau `compression_ratio > 2.4` (buclă/gunoi). Zero
|
||||
re-decodare. Prinde „Care pune o zana judiciul tugea" / „Acest lucru a fost foarte mult".
|
||||
- `hotwords += Bitcoin`. `initial_prompt` neatins (evită taxa de latență pe fiecare enunț).
|
||||
- Teste: `tests/test_voice_pipeline_filter.py` (8 cazuri).
|
||||
|
||||
### 2. Unealtă de mining — `tools/voice_stt_mine.py`
|
||||
- CLI read-only peste `voice_stt_log.jsonl`: frecvențe token, tokeni rari (candidați
|
||||
hotwords/corecții), candidați diacritice lipsă, rânduri suspecte de halucinație.
|
||||
- Tolerează rânduri fără `text_corrected` (citește `text`). Teste: `tests/test_voice_stt_mine.py` (13).
|
||||
|
||||
### 3. Spike model RO-finetuned (D1) — `tools/voice_stt_spike.py`
|
||||
Compară modele faster-whisper pe audio RO sintetizat (Supertonic) cu ground-truth diacritizat.
|
||||
|
||||
**Rezultat (threads=4, beam=5):**
|
||||
|
||||
| Model | p50 | p95 | WER | Diacritice |
|
||||
|-------|-----|-----|-----|-----------|
|
||||
| `small` (baseline) | 2.59s | 3.04s | 24.2% | 12/20 |
|
||||
| **`mikr/whisper-small-ro-cv11`** (CT2 int8) | 2.92s | 3.25s | **10.5%** | **17/20** |
|
||||
|
||||
- WER se înjumătățește; diacritice 60%→85%; numere PERFECTE (baseline: „120 si 3 delei"
|
||||
→ finetuned: „o sută douăzeci și trei de lei"). Cost: +0.33s p50 (în bugetul 1.5-3s).
|
||||
- Modelul CT2: `~/.cache/echo-ct2/whisper-small-ro-cv11-int8` (234M int8).
|
||||
|
||||
### 4. Model STT configurabil — `src/voice/pipeline.py::_get_whisper_model`
|
||||
- Citește `voice.stt_model` din config (default `"small"`). Adopția finetuned = flip config,
|
||||
nu cod. **Default rămâne `small`** până la decizia de adopție.
|
||||
|
||||
## Cum adopți modelul finetuned (când decizi)
|
||||
```bash
|
||||
# config.json → "voice": { ..., "stt_model": "/home/moltbot/.cache/echo-ct2/whisper-small-ro-cv11-int8" }
|
||||
systemctl --user restart echo-core # reload model
|
||||
```
|
||||
Re-rulează spike-ul oricând: `python3 tools/voice_stt_spike.py --models "small,<path>" --threads 4`
|
||||
|
||||
## Decizii autoplan respinse (din review)
|
||||
- ❌ `temperature=[0.0..0.6]` fallback → regresie latență pe worst-case. Înlocuit cu rejection.
|
||||
- ❌ `canonicalize_wakeword` → **nu există wake gate** în cod (verificat); ar fi spart `detect_voice_change`.
|
||||
- ❌ Dicționar diacritice pe calea Claude → Claude citește română stâlcită OK; finetuned-ul rezolvă la sursă.
|
||||
- ⏸ `correct_vocab` / `src/voice/stt_correct.py` → deferate (21 mostre = anecdotă; mining adună întâi date).
|
||||
|
||||
## Note de mediu
|
||||
- `transformers 5.12.1` instalat în `.venv` pentru conversia CT2 (one-time). A downgradat
|
||||
`tokenizers` 0.23.1→0.22.2 (faster-whisper încă OK, pin `<1` respectat). Se poate `pip uninstall
|
||||
transformers` dacă nu mai e nevoie de conversii.
|
||||
- **Pre-existent, neatins de mine:** `tools/tts.py` modificat necommis sparge 2 teste din
|
||||
`test_voice_normalize.py` (truncare 200 cuvinte). Confirmat: cu `tts.py` committed, testele trec.
|
||||
```
|
||||
Reference in New Issue
Block a user