wiki/concepts/Commcoach-Voice-Recording-Streaming-Konzept.md
2026-03-16 22:55:50 +01:00

334 lines
12 KiB
Markdown

# CommCoach Voice Recording Streaming Konzept
## Ausgangslage
Die aktuelle CommCoach-Spracheingabe basiert auf Browser `SpeechRecognition` (`webkitSpeechRecognition`).
Auf Mobile-Browsern wird die Aufnahme typischerweise alle ca. 5-7 Sekunden automatisch beendet und neu gestartet.
Das ist kein sauber behebbarer App-Bug, sondern eine Browser/API-Limitation.
## Zielbild
Die Spracheingabe soll auf Mobile und Desktop stabil laufen, ohne erzwungene 5-Sekunden-Resets, ohne Textverlust zwischen Restart-Gaps und ohne MSFT-Abhängigkeiten.
## Antwort auf die Kernfragen
### 1) Ist Option 3 ein genereller Umbau oder nur Mobile?
Beides ist möglich:
- **Variante A (Mobile-only Umbau):**
Neue Audio-Streaming-Pipeline nur auf Mobile aktivieren, Desktop bleibt vorerst bei Browser STT.
- **Variante B (Genereller Umbau):**
Neue Pipeline für alle Clients (Mobile + Desktop), einheitliches Verhalten und weniger Wartung.
Empfehlung: **Variante B**, da nur ein Stack gepflegt werden muss und CommCoach-Logik konsistent bleibt.
### 2) Kann das komplett in unserer Plattform umgesetzt werden?
Ja. Vollständig in eigener Plattform ist möglich.
Notwendig sind:
- Eigene UI- und Gateway-Implementierung (WebSocket Streaming)
- Eigener STT-Service (self-hosted), z. B. `faster-whisper`
- Optional eigene VAD (Voice Activity Detection), z. B. `webrtcvad`
Nicht zwingend notwendig:
- Externe SaaS-STT Provider
- MSFT/Azure Speech
## Zielarchitektur (MSFT-frei)
### UI (Frontend CommCoach)
- Mikrofonaufnahme via `MediaStream` + `AudioWorklet` oder `MediaRecorder`
- Chunking (z. B. 100-250 ms)
- Streaming per WebSocket an Gateway
- Lokaler Live-Preview-Text kommt nicht mehr aus Browser-STT, sondern aus Server-Interims
- Bestehende State-Machine bleibt als Steuerlogik erhalten (`idle`, `listening`, `botSpeaking`, `interrupted`, `muted` orthogonal)
### Gateway
- Neuer Endpoint, z. B. `ws /api/feature/commcoach/{instanceId}/stt/stream`
- Sessiongebundene Stream-Verarbeitung
- Weiterleitung der Audio-Chunks an STT-Worker
- Rückkanal von Interims + Final-Text zum UI
- Serverseitige Segmentierung (Silence / VAD) statt Browser `onend`
### STT-Service (self-hosted)
- `faster-whisper` als Runtime (GPU empfohlen, CPU möglich)
- Sprachen konfigurierbar (z. B. `de`, `en`)
- Ausgabe:
- `interim` Events (optional, je nach Latenzbudget)
- `final` Segmente
- Zeitmarken für Debug und Nachvollziehbarkeit
### Optional: VAD-Service
- Entweder im Gateway oder STT-Worker
- Trigger für Segment-Ende statt fester 1s-Timer
- Stabiler als browserseitige `onspeechstart/onspeechend`
## Ist-Analyse der bestehenden Codebase (UI + Gateway)
### Frontend (`frontend_nyla`)
- `src/pages/views/commcoach/useVoiceController.ts`
- Nutzt Browser `SpeechRecognition` direkt.
- Auto-Restart bei `onend` mit `REC_AUTORESTART_DELAY_MS = 300`.
- `SILENCE_TIMEOUT_MS = 1000` finalisiert User-Text.
- Mobile-Problem entsteht hier durch Browser-`onend`-Zwang (alle ~5-7s).
- `src/pages/views/commcoach/CommcoachDossierView.tsx`
- Bindet Voice-State-Machine an UI/TTS.
- `voice.liveTranscript` wird als Live-User-Bubble angezeigt.
- Debuglog existiert bereits via `window.__dlog`.
- `src/hooks/useCommcoach.ts`
- Text läuft über `sendMessageStreamApi` (SSE).
- Audio läuft derzeit nur als One-Shot Blob über `sendAudioStreamApi` (SSE).
- Kein echtes bidirektionales Low-Latency Audio-Streaming.
- `src/api/commcoachApi.ts`
- Bestehende Endpunkte sind HTTP + SSE.
- Es gibt noch keinen WebSocket-Endpunkt für chunkweises Audio.
### Gateway (`gateway`)
- `modules/features/commcoach/routeFeatureCommcoach.py`
- `POST .../message/stream` (SSE) für Text.
- `POST .../audio/stream` (SSE) für Audio-One-Shot.
- Audio-Endpoint liest `await request.body()` komplett und startet danach STT.
- `modules/features/commcoach/serviceCommcoach.py`
- `processAudioMessage(...)`: STT auf gesamtem Blob, danach `processMessage(...)`.
- Keine Segment-/Chunk-Logik für laufende Erkennung.
- `modules/interfaces/interfaceVoiceObjects.py`
- Kapselt STT/TTS aktuell über Google Connector.
- API ist bereits zentralisiert und damit gut erweiterbar.
- `modules/connectors/connectorVoiceGoogle.py`
- Verwendet `recognize` auf Einzeldateien (nicht Streaming).
- Damit strukturell ungeeignet für kontinuierliche Mobile-Spracheingabe.
## Implementierung im bestehenden System (konkret)
### Ziel: bestehende CommCoach-Pipeline beibehalten, nur STT-Eingang ersetzen
Unverändert bleiben:
- Session- und Message-Processing in `serviceCommcoach.py` (`processMessage`, Events, TTS).
- UI-State-Machine-Fachlogik (`idle`, `listening`, `botSpeaking`, `interrupted`, `muted`).
Ersetzt wird:
- Browser-STT (`SpeechRecognition`) durch Streaming-STT Provider.
- One-Shot Audio-Upload durch WebSocket-Audio-Stream.
### A) Frontend Änderungen
1. Neue API-Funktion in `src/api/commcoachApi.ts`
- `openSttStreamApi(instanceId, sessionId, handlers, options)` via WebSocket.
- Handler: `onStatus`, `onInterim`, `onFinal`, `onError`, `onClose`.
2. Neues Hook `src/hooks/useAudioStreamTranscription.ts`
- Mikrofon aufnehmen (`MediaStream` + `AudioWorklet` oder `MediaRecorder`).
- Audio in 100-250ms Chunks an WS senden.
- Event-Rückkanal verarbeiten (interim/final/status/error).
- Reconnect-Mechanismus für Mobile-Netzwechsel.
3. `src/pages/views/commcoach/useVoiceController.ts` refactor
- Provider-Interface einführen:
- `browserSpeech` (legacy)
- `streamedStt` (neu)
- `transcriptPartsRef` und `liveTranscript` aus Serverevents speisen.
- `SILENCE_TIMEOUT_MS` nur noch als optionales Guard-Rail, nicht als Kernsegmentierung.
4. `src/pages/views/commcoach/CommcoachDossierView.tsx`
- Keine Verhaltensänderung in Tabs/State nötig.
- Nur Provider initialisieren und vorhandene Debuganzeige um STT-WS Events erweitern.
### B) Gateway Änderungen
1. Neue WS-Route in `modules/features/commcoach/routeFeatureCommcoach.py`
- Beispiel: `ws /api/commcoach/{instanceId}/sessions/{sessionId}/stt/stream`
- Ownership-/Session-Checks analog zu `message/stream`.
- WebSocket akzeptieren, Audio-Chunks entgegennehmen, Events zurücksenden.
2. Neuer Service `modules/features/commcoach/serviceCommcoachSttStream.py`
- Sessiongebundene Streamverwaltung.
- Chunk-Buffer, VAD/Silence-Regeln, Segmentbildung.
- Für jedes finale Segment: `processMessage(sessionId, contextId, finalText, interface)` aufrufen.
3. Anpassung `modules/features/commcoach/serviceCommcoach.py`
- `processAudioMessage` bleibt für Legacy/Upload kompatibel.
- Neue Streaming-Nutzung erfolgt über den neuen STT-Stream-Service.
4. Erweiterung `modules/interfaces/interfaceVoiceObjects.py`
- Neue Methoden ergänzen:
- `startSttStream(...)`
- `pushSttAudioChunk(...)`
- `stopSttStream(...)`
- Bestehende Methoden (`speechToText`, `textToSpeech`) unverändert lassen.
### C) STT Worker (self-hosted)
1. Neuer Connector (z. B.) `modules/connectors/connectorVoiceWhisper.py`
- `faster-whisper` Integration.
- Modell, Sprache, VAD-Parameter konfigurierbar.
2. Optional separater Worker-Prozess
- Entkoppelt Gateway-Latenz von STT-Rechenlast.
- Kommunikation intern über Queue/IPC oder internen WS.
## WS Event Contract (verbindlich)
### Client -> Server
- `open`
- `{ "type": "open", "sessionId": "...", "language": "de-DE", "codec": "pcm16" }`
- `audio`
- `{ "type": "audio", "seq": 123, "chunk": "<base64>" }`
- `commit`
- `{ "type": "commit", "reason": "silence|manual|stateChange" }`
- `close`
- `{ "type": "close" }`
### Server -> Client
- `status`
- `{ "type": "status", "label": "Sprache wird erkannt..." }`
- `interim`
- `{ "type": "interim", "text": "...", "segmentId": "..." }`
- `final`
- `{ "type": "final", "text": "...", "segmentId": "...", "confidence": 0.0 }`
- `ack`
- `{ "type": "ack", "seq": 123 }`
- `error`
- `{ "type": "error", "code": "stt_failed", "message": "..." }`
- `closed`
- `{ "type": "closed", "reason": "client|server|timeout" }`
## Migrationsstrategie (ohne doppelte Logikfalle)
1. Feature-Flag `commcoachVoiceProvider` auf Instanzebene
- Werte: `browserSpeech` | `streamedStt`.
2. Rolloutpfad
- Schritt 1: intern auf Mobile aktivieren.
- Schritt 2: nach Stabilisierung global aktivieren.
- Schritt 3: Browser-STT-Code entfernen.
3. Expliziter Fehlerpfad
- Kein stiller Fallback auf Browser-STT, wenn `streamedStt` aktiv ist.
- Fehler wird sichtbar im UI (Banner + Debuglog).
## Konkrete Taskliste pro Repository
### Repo `frontend_nyla`
1. `commcoachApi.ts`: `openSttStreamApi` ergänzen.
2. Neues Hook `useAudioStreamTranscription.ts` implementieren.
3. `useVoiceController.ts`: Provider-Abstraktion + streamed Provider integrieren.
4. `CommcoachDossierView.tsx`: Provider-Init + Debug-Events anzeigen.
5. Tests:
- unit: Segment-Assembler
- integration: state transitions bei interim/final/error
### Repo `gateway`
1. WS-Route für STT-Stream in `routeFeatureCommcoach.py`.
2. Neuer Stream-Service `serviceCommcoachSttStream.py`.
3. `interfaceVoiceObjects.py` um Streaming-Methoden erweitern.
4. Neuer Whisper-Connector `connectorVoiceWhisper.py`.
5. Telemetrie + Logs:
- stream open/close
- first interim latency
- first final latency
- reconnect count
## Variante A vs. Variante B
| Kriterium | Variante A: Nur Mobile | Variante B: Alle Plattformen |
|---|---|---|
| Time-to-first-fix | schneller | mittel |
| Komplexität gesamt | höher langfristig (2 Stacks) | niedriger langfristig (1 Stack) |
| UX-Konsistenz | unterschiedlich je Device | einheitlich |
| Wartung | doppelt | einfach |
| Risiko | mittelhoch (Divergenz) | mittel (einmaliger Umbau) |
**Empfehlung:** Variante B.
## Implementierungsplan
### Phase 1: Infrastruktur und Prototyp
1. STT-Worker als eigener Service im Gateway-Umfeld bereitstellen
2. Streaming-Protokoll definieren (Events: `audio`, `interim`, `final`, `status`, `error`, `close`)
3. WS-Route im Gateway für CommCoach implementieren
4. Test-Client mit Beispielaudio aufbauen (ohne UI) zur Last-/Latenzprüfung
### Phase 2: Frontend Integration
1. Neues Modul `useAudioStreamTranscription` einführen
2. `useVoiceController` auf Provider-Abstraktion umstellen:
- Provider `browserSpeech` (bestehend)
- Provider `streamedStt` (neu)
3. Transcript-Handling auf Serverevents umstellen:
- Interim in `liveTranscript`
- Final in `transcriptParts`
4. State-Transitions unverändert belassen, nur STT-Quelle ersetzen
### Phase 3: Segmentierung und Qualität
1. Serverseitige VAD aktivieren
2. Segment-Ende sauber in `onMessage` überführen
3. Doppelungen/Fragmentverluste mit Session-IDs und Segment-Counter verhindern
4. Mobile Netzwechsel/WS-Reconnect robust behandeln
### Phase 4: Rollout
1. Feature-Flag: zuerst intern, dann Pilotmandanten
2. Optionale Stufen:
- Stufe 1: Nur Mobile
- Stufe 2: Alle Plattformen
3. Browser-STT nach Stabilitätsphase vollständig entfernen
## Technische Leitplanken
- Keine stillen Fallbacks, die Fehler verdecken
- Explizite Fehlerzustände im UI (Mikrofon, Netzwerk, STT nicht verfügbar)
- Klares Telemetrie-Set:
- Time to first interim
- Time to first final
- Segment-Länge
- Abbruchgründe
- WS-Reconnect-Rate
## Drittkomponenten
### Minimal erforderlich
- Python Packages (self-hosted):
- `faster-whisper`
- optional `webrtcvad`
- Audio-Decode (`ffmpeg` Laufzeit)
### Nicht erforderlich
- MSFT/Azure Speech
- Externe STT-SaaS
## Risiken und Gegenmassnahmen
- **GPU-Kapazität fehlt:** zunächst kleines Modell und Queueing, später GPU-Skalierung
- **Latenz zu hoch:** Chunk-Grösse reduzieren, VAD feinjustieren, Modellwahl anpassen
- **Mobile Netz instabil:** robustes Reconnect-Handling + Segment-ACKs
- **Drift zwischen UI und Backend:** eindeutige Stream-/Segment-IDs und Idempotenzregeln
## Fazit
Der Umbau ist vollständig in der eigenen Plattform machbar und löst das Mobile-5-Sekunden-Problem an der Wurzel.
Für Wartbarkeit und konsistentes Verhalten ist ein **genereller Umbau (Variante B)** sinnvoll.
Ein stufenweiser Rollout mit Feature-Flag minimiert Risiko.