docs: lokales TTS (piper in-process) + Start-Warm-up dokumentieren

README/BEDIENUNGSANLEITUNG/Architektur: piper laeuft in-process (gecachtes
Stimmmodell, kein Subprozess pro Satz), In-Process-Resampling, lokale Modelle
werden beim Start vorgeladen. .[local] installiert jetzt faster-whisper UND
piper-tts. Latenz-Hinweis erster Ton 5,8 s -> ~1,6 s.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-06-18 08:32:05 +02:00
commit 1899663308
3 changed files with 18 additions and 10 deletions

View file

@ -237,19 +237,20 @@ python scripts/voice_loop.py --recorder arecord --device plughw:5,0 --session hy
--llm-provider local-openai-compatible \ --llm-provider local-openai-compatible \
--tts-provider openrouter --tts-provider openrouter
``` ```
Erster Turn ist langsamer (Whisper-Modell lädt; das LLM-Modell ist nach `make llm-up` Die lokalen Modelle (faster-whisper, piper) werden **beim Serverstart vorgeladen**
bereits geladen), danach zügig. STT-Modell (Warm-up im Hintergrund) — der erste Turn ist daher nicht mehr spürbar langsamer. STT-Modell
und Gerät steuern `FASTER_WHISPER_MODEL`/`FASTER_WHISPER_DEVICE` in `.env`. Satzweises und Gerät steuern `FASTER_WHISPER_MODEL`/`FASTER_WHISPER_DEVICE` in `.env`. Satzweises
Vorlesen ist Standard (früher Ton); `--stream-text` zeigt den Text live dazu. Vorlesen ist Standard (früher Ton); `--stream-text` zeigt den Text live dazu.
**Voll-lokal-Beispiel** (STT + LLM + TTS **alles lokal** — kein API-Geld, maximaler **Voll-lokal-Beispiel** (STT + LLM + TTS **alles lokal** — kein API-Geld, maximaler
Datenschutz). TTS läuft hier über **piper** (lokales, CPU-freundliches Neural-TTS): Datenschutz). TTS läuft hier über **piper** (lokales Neural-TTS, **in-process**: das
Stimmmodell wird einmal geladen und gecacht, kein Subprozess-Start pro Satz):
```bash ```bash
# einmalig: lokales STT installieren # einmalig: lokales STT + TTS installieren (faster-whisper + piper-tts)
pip install -e .[local] pip install -e .[local]
# lokalen llama.cpp-Server starten (großes, unzensiertes Modell): # lokalen llama.cpp-Server starten (großes, unzensiertes Modell):
make llm-up # mit make llm-status auf "HTTP OK" warten make llm-up # mit make llm-status auf "HTTP OK" warten
# piper-Binary + Stimme bereitstellen: die Stimm-Dateien (<name>.onnx + <name>.onnx.json) # piper-Stimme bereitstellen: die Stimm-Dateien (<name>.onnx + <name>.onnx.json)
# liegen im PIPER_VOICES_DIR (Default ~/.local/share/piper/voices). Deutsche Stimmen z. B. # liegen im PIPER_VOICES_DIR (Default ~/.local/share/piper/voices). Deutsche Stimmen z. B.
# von huggingface 'rhasspy/piper-voices' (de_DE-thorsten-high, de_DE-kerstin-low). # von huggingface 'rhasspy/piper-voices' (de_DE-thorsten-high, de_DE-kerstin-low).
# Verfügbare Stimmen prüfen: ls ~/.local/share/piper/voices/*.onnx # Verfügbare Stimmen prüfen: ls ~/.local/share/piper/voices/*.onnx

View file

@ -198,8 +198,9 @@ In-Memory-Metriken `/api/metrics`), Tageskontingent pro Nutzer und heuristische
Notfall-Eskalation**; automatisierte Tests. Notfall-Eskalation**; automatisierte Tests.
**Echtes lokales STT & TTS:** `faster-whisper` (optionale Dependency `.[local]`, **Echtes lokales STT & TTS:** `faster-whisper` (optionale Dependency `.[local]`,
CTranslate2) transkribiert real; `piper` (Binary + Stimmmodell, ffmpeg-Resampling CTranslate2) transkribiert real; `piper` (in-process via piper-Python-API, Stimmmodell
auf 24000 Hz) synthetisiert real. Damit ist sowohl ein Hybrid „STT+LLM lokal, TTS einmal geladen + gecacht, In-Process-Resampling auf 24000 Hz) synthetisiert real. Lokale
Modelle werden beim Serverstart vorgeladen (Warm-up). Damit ist sowohl ein Hybrid „STT+LLM lokal, TTS
remote" als auch eine **voll-lokale** Konstellation möglich (live verifiziert). remote" als auch eine **voll-lokale** Konstellation möglich (live verifiziert).
**Platzhalter (Gerüst):** Audio-Endpunkte (`local-default`, `bluetooth`, **Platzhalter (Gerüst):** Audio-Endpunkte (`local-default`, `bluetooth`,
@ -218,7 +219,7 @@ Reihenfolge der Weiterentwicklung:
4. **(weitgehend erledigt)** Echtzeit: WebSocket-Streaming-Chat (`/ws/chat`), **Token-Level-LLM-Streaming (SSE, `stream:true`)**, **Audio-Streaming (chunked TTS satzweise, `audio_stream:true`)**, **Audio-Eingang (`/ws/voice`)**, **Barge-in/Turn-Manager (`interrupt` bricht laufende Antwort ab)** und **VAD-Aeusserungserkennung (energie-basiert, opt-in)** sind umgesetzt. Offen: **echte partielle Live-Transkripte (Streaming-STT-Dienst, wortweise)** und **WebRTC (aiortc)** — beide brauchen schwere Abhaengigkeiten/Dienste. Heute laeuft STT pro Aeusserung. 4. **(weitgehend erledigt)** Echtzeit: WebSocket-Streaming-Chat (`/ws/chat`), **Token-Level-LLM-Streaming (SSE, `stream:true`)**, **Audio-Streaming (chunked TTS satzweise, `audio_stream:true`)**, **Audio-Eingang (`/ws/voice`)**, **Barge-in/Turn-Manager (`interrupt` bricht laufende Antwort ab)** und **VAD-Aeusserungserkennung (energie-basiert, opt-in)** sind umgesetzt. Offen: **echte partielle Live-Transkripte (Streaming-STT-Dienst, wortweise)** und **WebRTC (aiortc)** — beide brauchen schwere Abhaengigkeiten/Dienste. Heute laeuft STT pro Aeusserung.
5. **(weitgehend erledigt)** Resilienz: Fallback-Ketten je Modul (`*_FALLBACK`, Provider faellt aus → naechster) und In-Memory-Metriken (`/api/metrics`: Request/Latenz, Pipeline-Stufen, Fallback/Fehler; JSON + Prometheus). Offen: verteiltes Tracing, Alerting. 5. **(weitgehend erledigt)** Resilienz: Fallback-Ketten je Modul (`*_FALLBACK`, Provider faellt aus → naechster) und In-Memory-Metriken (`/api/metrics`: Request/Latenz, Pipeline-Stufen, Fallback/Fehler; JSON + Prometheus). Offen: verteiltes Tracing, Alerting.
6. **(weitgehend erledigt)** Betrieb: Tageskontingent pro Nutzer (`DAILY_REQUEST_LIMIT`, 429) und **zweistufige** Notfall-Eskalation: (1) schnelle Stichwort-Heuristik im Hot-Path (0 Latenz) + (2) **LLM-Klassifikation** (`app/safety/llm_classifier.py`) als Hintergrund-Task, der laeuft, wenn die Heuristik nichts fand — faengt verpasste Formulierungen (z. B. metaphorisch geaeusserte Suizidalitaet, Schlaganfall-Symptome ohne Stichwort) mit Konfidenz-Schwelle, ohne die Antwortlatenz zu erhoehen. Eskalation jeweils -> Log + Metrik (`source`: keyword/llm) + optionaler Webhook + Event. Offen: Telefon-/Angehoerigen-Integration, Abrechnung. 6. **(weitgehend erledigt)** Betrieb: Tageskontingent pro Nutzer (`DAILY_REQUEST_LIMIT`, 429) und **zweistufige** Notfall-Eskalation: (1) schnelle Stichwort-Heuristik im Hot-Path (0 Latenz) + (2) **LLM-Klassifikation** (`app/safety/llm_classifier.py`) als Hintergrund-Task, der laeuft, wenn die Heuristik nichts fand — faengt verpasste Formulierungen (z. B. metaphorisch geaeusserte Suizidalitaet, Schlaganfall-Symptome ohne Stichwort) mit Konfidenz-Schwelle, ohne die Antwortlatenz zu erhoehen. Eskalation jeweils -> Log + Metrik (`source`: keyword/llm) + optionaler Webhook + Event. Offen: Telefon-/Angehoerigen-Integration, Abrechnung.
7. **(weitgehend erledigt)** Lokale Provider: STT via `faster-whisper` (`.[local]`) und **TTS via `piper`** (lokales Neural-TTS, ffmpeg-Resampling auf 24000 Hz) sind echt — eine **voll-lokale Konstellation** (STT+LLM+TTS lokal, keine API-Kosten, max. Datenschutz) ist damit möglich. Offen: `chatterbox`-TTS (noch Stub), höhere Sprachqualität als piper. 7. **(weitgehend erledigt)** Lokale Provider: STT via `faster-whisper` (`.[local]`) und **TTS via `piper`** (lokales Neural-TTS, **in-process** mit gecachtem Stimmmodell, In-Process-Resampling auf 24000 Hz; lokale Modelle werden beim Start vorgeladen) sind echt — eine **voll-lokale Konstellation** (STT+LLM+TTS lokal, keine API-Kosten, max. Datenschutz) ist damit möglich. Offen: `chatterbox`-TTS (noch Stub), höhere Sprachqualität als piper.
8. **TransportRouter** als eigene lokal/remote-Achse aktivieren; echte Geräte-Endpunkte (PipeWire/Bluetooth) — heute OS-Ebene. 8. **TransportRouter** als eigene lokal/remote-Achse aktivieren; echte Geräte-Endpunkte (PipeWire/Bluetooth) — heute OS-Ebene.
**Datenschutz (querschnittlich, ab sofort mitdenken):** Senioren-Sprachdaten sind **Datenschutz (querschnittlich, ab sofort mitdenken):** Senioren-Sprachdaten sind

View file

@ -148,6 +148,12 @@ Markdown/Emojis — schlecht zum Vorlesen und spürbar träge. Der Provider
Modell `base`. Auf einer RTX 3090 lohnt `FASTER_WHISPER_DEVICE=cuda` + Modell `base`. Auf einer RTX 3090 lohnt `FASTER_WHISPER_DEVICE=cuda` +
`FASTER_WHISPER_COMPUTE_TYPE=float16`; das verkürzt die Transkriptionszeit pro Turn. `FASTER_WHISPER_COMPUTE_TYPE=float16`; das verkürzt die Transkriptionszeit pro Turn.
**Dritter Hebel — lokales TTS (piper):** piper läuft **in-process** über die piper-Python-API
(im `.[local]`-Extra). Das Stimmmodell wird **einmal** geladen und prozessweit gecacht —
früher startete piper als Subprozess **pro Satz** und zahlte jedes Mal ~2 s Modell-Ladezeit.
Zusätzlich werden lokale Modelle **beim Serverstart vorgeladen** (Warm-up), sodass auch der
erste Nutzer keinen Kaltstart spürt. Messung (lokales Setup): erster Ton **5,8 s → ~1,6 s**.
### Komplett lokal: Profil `local-dev` ### Komplett lokal: Profil `local-dev`
`VA_PROFILE=local-dev` betreibt **alle** KI-Module ohne Cloud. Die Route löst auf zu: `VA_PROFILE=local-dev` betreibt **alle** KI-Module ohne Cloud. Die Route löst auf zu:
@ -160,8 +166,8 @@ Modell `base`. Auf einer RTX 3090 lohnt `FASTER_WHISPER_DEVICE=cuda` +
**Voraussetzungen:** **Voraussetzungen:**
- **LLM:** llama.cpp-Container läuft (`make llm-up`) - **LLM:** llama.cpp-Container läuft (`make llm-up`)
- **STT:** faster-whisper installiert (`pip install -e .[local]`) - **STT + TTS:** `pip install -e .[local]` (installiert faster-whisper **und** piper-tts);
- **TTS:** piper-Binary + Stimme vorhanden (siehe `PIPER_*` in `.env.example`) ein piper-Stimmmodell (`<name>.onnx` + `.onnx.json`) im `PIPER_VOICES_DIR` (siehe `PIPER_*`)
**Start (Reihenfolge):** **Start (Reihenfolge):**