CLI: --until-enter und optionale LLM-Korrektur (--llm-correct)

- --until-enter: Aufnahme mit beliebig langen Pausen, Enter beendet, Ausgabe als ein Text
- stt_llm.py: Nachkorrektur per lokalem LLM (Standard gemma4:12b über Ollama nativ mit
  kleinem Kontext; OpenAI-kompatible Server möglich), strenger Prompt mit Beispielen,
  Ähnlichkeitsprüfung und Fehler-Fallback auf den Rohtext
- Output-Klasse in der CLI: Korrektur in eigenem Thread, Reihenfolge bleibt erhalten
- Tests für die LLM-Korrektur (13 Unit, 1 Integration)
- Doku: Bedienungsanleitung, README, Entwicklerdoku (inkl. Modellvergleich), CLAUDE.md

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-09-14 00:48:02 +02:00
commit 6ebea4b745

View file

@ -89,7 +89,7 @@ mit. Ohne vorhandene Installation lädt `pip` alles herunter.
```bash
.venv/bin/python -m pytest
# … 36 passed
# … 49 passed
```
> Beim **ersten Start des Dienstes** wird das Whisper-Modell aus dem Internet geladen
@ -189,6 +189,17 @@ Unterstützt: WAV, MP3, OGG, FLAC, M4A, WebM und praktisch alle anderen Audiofor
Nach **„Bereit — sprich.“** einfach sprechen und kurz Pause machen. Jeder Satz erscheint als eigene
Zeile. Beenden mit `Strg+C`.
### Mit Pausen sprechen, selbst beenden
```bash
.venv/bin/python stt_cli.py --until-enter -v
```
Die Aufnahme läuft, bis **Enter** gedrückt wird — Denk- und Sprechpausen beliebiger Länge beenden sie
nicht. Mit `-v` erscheinen die erkannten Abschnitte schon während des Sprechens (mit `+` markiert).
Nach Enter wird der letzte Abschnitt fertig transkribiert und der **gesamte Text als eine Zeile**
ausgegeben. Mit `--timeout SEK` endet die Aufnahme spätestens nach SEK Sekunden.
```
Mikrofon: pulse → alsa_input.usb-Seeed_Studio_reSpeaker_XVF3800_… (16000 Hz, 2 Kanäle, Kanal 1)
Bereit — sprich. (Strg+C beendet)
@ -207,6 +218,7 @@ Schalte das Licht im Wohnzimmer ein.
| `-l`, `--language` | Sprache: `de`, `en`, `fr` … oder `auto` (automatisch erkennen) |
| `--prompt "…"` | Eigennamen/Fachbegriffe vorgeben, z. B. `--prompt "Bredelar, Marsberg"` |
| `--once` | nach dem ersten erkannten Satz beenden |
| `--until-enter` | Aufnahme mit **beliebig langen Pausen**, bis Enter gedrückt wird; Ergebnis als **ein** Text |
| `--timeout SEK` | Mikrofon nach SEK Sekunden beenden |
| `--silence-ms MS` | Pausenlänge, die einen Satz beendet (Standard 700; für Denkpausen 1200–2000) |
| `-d`, `--device` | Mikrofon wählen: Nummer oder Namensteil, z. B. `-d respeaker` |
@ -217,6 +229,9 @@ Schalte das Licht im Wohnzimmer ein.
| `--model NAME` | nur mit `--local`: Whisper-Modell, z. B. `small`, `large-v3-turbo` |
| `--compute-device` | nur mit `--local`: `auto`, `cuda` oder `cpu` |
| `--http-timeout SEK` | max. Wartezeit bei Dateien (Standard 300) |
| `--llm-correct` | Ergebnis zusätzlich von einem lokalen LLM korrigieren lassen, siehe [LLM-Korrektur](#llm-korrektur-optional) |
| `--llm-model NAME` | LLM für die Korrektur (Standard `gemma4:12b`) |
| `--llm-url URL` | LLM-Server (Standard Ollama `http://127.0.0.1:11434`; OpenAI-kompatibel mit `…/v1`) |
### Rückgabewerte (für Skripte)
@ -239,10 +254,48 @@ for f in aufnahmen/*.mp3; do
.venv/bin/python stt_cli.py "$f" > "${f%.mp3}.txt"
done
# Längere Notiz mit Pausen sprechen, Enter beendet, Text in Variable
notiz=$(.venv/bin/python stt_cli.py --until-enter)
# Diktat mit Denkpausen fortlaufend in eine Datei schreiben
.venv/bin/python stt_cli.py --silence-ms 1500 >> notizen.txt
```
### LLM-Korrektur (optional)
Mit `--llm-correct` wird der erkannte Text zusätzlich von einem **lokalen Sprachmodell** (Standard:
`gemma4:12b` über Ollama) geprüft. Es korrigiert nur offensichtliche Erkennungsfehler — falsch
verstandene Wörter, Rechtschreibung, Groß-/Kleinschreibung, Satzzeichen — und lässt Bedeutung, Zahlen,
Namen und Verneinungen unangetastet.
```bash
.venv/bin/python stt_cli.py --until-enter --llm-correct -v
.venv/bin/python stt_cli.py notiz.mp3 --llm-correct --json
```
```
LLM: korrigiert (1.21s)
roh: Heute ist ein schöner Tag. Die Lonne scheint und die Rögel singen im Garten.
Heute ist ein schöner Tag. Die Sonne scheint und die Vögel singen im Garten.
```
**Sicherungen:** Weicht die Antwort des LLM zu stark vom erkannten Text ab (z. B. weil es etwas erfindet
oder einer „Anweisung“ im Diktat folgt), wird sie verworfen und der **Rohtext** ausgegeben. Ist das LLM
nicht erreichbar, erscheint eine Meldung und ebenfalls der Rohtext. Mit `--json` stehen Rohtext
(`raw_text`) und Details (`llm`: Status, Dauer, Modell) immer mit im Ergebnis.
**Wann sinnvoll?** Für Notizen, Diktate und längere Texte. Für kurze Befehle („Licht an“) nicht nötig —
Whisper erkennt sie meist fehlerfrei, und die Korrektur kostet rund 1 Sekunde pro Text.
**Voraussetzungen und Hinweise:**
- Ollama läuft und das Modell ist installiert: `ollama pull gemma4:12b`.
- Das **erste Laden** dauert je nach Rechner rund eine Minute; im Mikrofon-Modus beginnt es schon beim
Start im Hintergrund. Danach bleibt das Modell 30 Minuten geladen (`STT_LLM_KEEP_ALIVE`).
- Das Modell belegt dabei ca. 10 GB Grafikspeicher.
- Anderes Modell oder anderer Server: `--llm-model mistral-nemo` bzw.
`--llm-url http://127.0.0.1:8011/v1` (llama.cpp, LM Studio …).
### Ohne laufenden Dienst
```bash
@ -356,6 +409,18 @@ Der Dienst wird über **Umgebungsvariablen** eingestellt:
| `STT_DOWNLOAD_ROOT` | – | eigener Ordner für Modelle (sonst Hugging-Face-Cache) |
| `CUDA_VISIBLE_DEVICES` | – | Grafikkarte festlegen (UUID aus `nvidia-smi -L`) |
**LLM-Korrektur** (nur `stt_cli.py --llm-correct`):
| Variable | Standard | Bedeutung |
|---|---|---|
| `STT_LLM_MODEL` | `gemma4:12b` | Modell für die Korrektur |
| `STT_LLM_URL` | `http://127.0.0.1:11434` | Ollama; OpenAI-kompatible Server mit `…/v1` |
| `STT_LLM_API` | `auto` | `auto`, `ollama` oder `openai` |
| `STT_LLM_KEEP_ALIVE` | `30m` | nur Ollama: wie lange das Modell nach Gebrauch geladen bleibt |
| `STT_LLM_NUM_CTX` | `4096` | nur Ollama: Kontextgröße (klein halten, sonst läuft das Modell auf der CPU) |
| `STT_LLM_TIMEOUT` | `120` | max. Wartezeit in Sekunden (inkl. Laden) |
| `STT_LLM_MIN_SIMILARITY` | `0.75` | Mindestähnlichkeit zum Rohtext, sonst wird die Korrektur verworfen |
---
## 10. Welches Whisper-Modell?
@ -395,7 +460,7 @@ erprobt. Die übrigen Angaben sind Richtwerte. Jedes Modell wird beim ersten Geb
| `/health` zeigt `"device":"cpu"`, obwohl eine GPU da ist | GPU belegt/zu wenig Speicher/CUDA-Problem | Log lesen (`Laden auf cuda fehlgeschlagen: …`), `nvidia-smi` prüfen, andere Karte per `CUDA_VISIBLE_DEVICES` |
| `/health` zeigt `error` | Modell nicht ladbar | Meldung im Feld `error`; Modellname und Speicherplatz prüfen |
| Nie „Sprache erkannt“ | falsches/stummes Mikrofon | [Abschnitt 6](#6-das-richtige-mikrofon); Pegel in den Toneinstellungen prüfen |
| Sätze werden mittendrin geteilt | Pausen zu lang für die Einstellung | `--silence-ms 1200` |
| Sätze werden mittendrin geteilt / Aufnahme endet bei Pausen | Pausen zu lang für die Einstellung | `--silence-ms 1200` oder `--until-enter` (Enter beendet) |
| Text kommt erst sehr spät | Pausenerkennung sehr lang eingestellt oder CPU-Betrieb | `--silence-ms` verringern; GPU nutzen |
| Wörter/Namen falsch | Aussprache, unbekannte Namen | `--prompt` mit Namen; größeres Modell; deutlicher sprechen |
| „Untertitel im Auftrag des ZDF“ o. Ä. | Whisper-Halluzination bei Stille | wird normalerweise gefiltert; tritt es trotzdem auf, bitte Beispiel melden |
@ -403,6 +468,10 @@ erprobt. Die übrigen Angaben sind Richtwerte. Jedes Modell wird beim ersten Geb
| `Warteschlange voll` / HTTP 503 | zu viele Aufträge gleichzeitig | kurz warten; `STT_MAX_QUEUE` erhöhen |
| `Address already in use` beim Start | Port 9998/10300 belegt (zweiter Dienst läuft) | anderen Dienst beenden (`systemctl --user stop stt-service`) |
| Home Assistant findet den Dienst nicht | Firewall, falsche IP, Dienst aus | `curl http://<IP>:9998/health` vom HA-Rechner; [Abschnitt 8](#8-andere-geräte-im-netzwerk) |
| `LLM: Fehler, Rohtext behalten — ConnectionError` | Ollama läuft nicht / falsche `--llm-url` | `systemctl status ollama`; `curl http://127.0.0.1:11434/api/version` |
| `LLM: verworfen, Rohtext behalten` | LLM-Antwort wich zu stark ab | gewollte Sicherung; bei Bedarf anderes Modell probieren |
| LLM-Korrektur dauert beim ersten Mal ~1 Minute | Modell wird geladen | normal; danach ~1 s pro Text |
| LLM-Korrektur dauert immer mehrere Sekunden | Modell läuft auf der CPU (zu großer Kontext/kein GPU-Speicher) | `ollama ps` prüfen: `PROCESSOR` sollte `100% GPU` zeigen |
| `Warnung: … Audioblöcke verworfen` | Netzwerk/Dienst zu langsam für das Mikrofon | Verbindung prüfen; lokal testen |
**Log ansehen:** manuell gestartet → im Terminal; als systemd-Dienst →
@ -414,6 +483,8 @@ erprobt. Die übrigen Angaben sind Richtwerte. Jedes Modell wird beim ersten Geb
- Die gesamte Spracherkennung läuft **lokal**. Audio und Text verlassen den Rechner nicht.
- Internetzugriff nur beim **ersten Herunterladen** eines Modells (Hugging Face).
- Die optionale LLM-Korrektur nutzt ein **lokales** LLM (Ollama). Nur wer `--llm-url` auf einen fremden
Server setzt, schickt Text dorthin.
- Der Dienst **speichert keine Aufnahmen** und keine Texte auf der Festplatte.
- **Ausnahme Log:** Bei Home-Assistant-Anfragen (Wyoming) wird der erkannte Text im Log protokolliert
(Stufe `INFO`). Wer das nicht möchte: `STT_LOG_LEVEL=WARNING`.