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

12 KiB

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.