Echtzeit-Sprachpipeline: von Rohaudio zu KI
Wichtige Punkte: Eine Echtzeit-Sprachpipeline zu erstellen bedeutet, Deepgram STT (Streaming mit VAD), ein LLM über OpenRouter (Function Calling) und OpenAI TTS zu verketten — alles verbunden über einen authentifizierten JWT WebSocket. Dieser Artikel beschreibt jeden Schritt, die Latenzen, die Fallbacks und die Bugs, die mich schlaflose Nächte gekostet haben.
Das Herzstück von TAMSIV ist die Stimme. Kein Gadget, kein versteckter Mikrofon-Button in einer Ecke. Die Stimme IST die Hauptschnittstelle. Du drückst, du sprichst, die KI versteht und führt aus. Aber eine Echtzeit-Sprachpipeline alleine zu bauen, bedeutet, in eine Welt einzutreten, in der jede Millisekunde zählt und alles jederzeit kaputtgehen kann.
Nach drei Wochen intensiver Entwicklung und einer unvernünftigen Menge Kaffee habe ich eine Pipeline, die in 1,5 bis 3 Sekunden antwortet. Hier ist, wie es funktioniert, Stück für Stück.
Wie funktioniert die Architektur der Sprachpipeline?
Die komplette Pipeline in einer Zeile:
Audio PCM 16kHz mono → WebSocket (JWT) → Deepgram Live STT (VAD) → OpenRouter LLM → Function Calling → OpenAI TTS → Sprachantwort
Sechs Schritte, sechs potenzielle Fehlerquellen. Jeder hat seine Einschränkungen, seine Latenzen und seine Fallstricke. Alles muss in weniger als 3 Sekunden ablaufen, damit die Erfahrung flüssig ist. Darüber hinaus denkt der Benutzer, dass die App abgestürzt ist.
Lass uns jeden Schritt aufschlüsseln.
Warum ist WebSocket für Echtzeit-Audio unerlässlich?
Klassisches HTTP funktioniert nicht für Audio-Streaming. Du müsstest die gesamte Aufnahme in einem Block senden, auf die Verarbeitung warten und dann die Antwort erhalten. Die Latenz wäre inakzeptabel.
Der WebSocket ermöglicht einen kontinuierlichen bidirektionalen Fluss: Das Telefon sendet Audio-Chunks, während der Benutzer spricht, und das Backend kann mit der Verarbeitung beginnen, noch bevor der Benutzer fertig ist.
Die JWT-Authentifizierung
Jede WebSocket-Verbindung wird über ein Supabase JWT-Token authentifiziert:
ws://backend:3001?token=eyJhbGciOiJIUzI1NiIs...
Das Token ist bei der Verbindung gültig. Wenn das Token während eines Gesprächs abläuft (Supabase-Tokens laufen nach 1 Stunde ab), erkennt der Client die Trennung und verbindet sich automatisch mit einem frischen Token neu. Ich musste diesen Fall explizit behandeln — anfangs stürzten lange Gespräche mysteriös ab.
Die Sicherheit des WebSockets wird im Artikel über Sicherheitsaudit und Rate Limiting detailliert beschrieben.
Wie verwaltet Deepgram Speech-to-Text im Streaming?
Das Audio muss im Format PCM 16 Bit, 16 kHz, Mono vorliegen. Das Telefon nimmt das Audio in diesem Format auf und sendet rohe binäre Chunks über den WebSocket. Keine Komprimierung, keine Kodierung — rohes PCM ist das schnellste Format zur Verarbeitung.
Deepgram empfängt diese Chunks und transkribiert sie im Streaming. Aber die wahre Magie ist die VAD (Voice Activity Detection).
Warum VAD alles verändert?
Ohne VAD müsste man clientseitig ein Stille-Timeout implementieren: Wenn der Benutzer X Sekunden lang nicht spricht, wird davon ausgegangen, dass er fertig ist. Das Problem:
- Zu kurz (1s): Du unterbrichst den Benutzer, der zwischen zwei Sätzen nachdenkt.
- Zu lang (3s): Die App lahmt, der Benutzer wartet.
- Variabel je nach Benutzer: Manche sprechen schnell, andere lassen sich Zeit.
Die VAD von Deepgram erkennt mit bemerkenswerter Präzision, wann der Benutzer aufgehört hat zu sprechen. Sie analysiert das Audiosignal in Echtzeit und sendet ein speech_final-Ereignis, wenn sie sicher ist, dass der Benutzer fertig ist. Das dauert etwa 200 ms nach dem Ende des Sprechens.
Die Zwischen- vs. Endergebnisse
Deepgram sendet zwei Arten von Ergebnissen:
is_final: false— Zwischenergebnisse, instabil. Das erkannte Wort kann sich ändern, wenn der Kontext reicher wird.is_final: true— Bestätigte Ergebnisse. Der Text wird sich nicht mehr ändern.
Die Falle: Zwischenergebnisse sammeln, um eine Echtzeit-Vorschau anzuzeigen (gut für die UX), während nur die Endergebnisse an das LLM übermittelt werden (gut für die Qualität). Ich zeige Zwischenergebnisse in Grau und Endergebnisse in Weiß an — der Benutzer sieht, wie seine Stimme direkt in Text umgewandelt wird.
Für den Vergleich zwischen nativem STT (kostenlos, auf dem Gerät) und Deepgram Cloud (präziser), lies meinen detaillierten Artikel natives STT vs. Deepgram.
Wie orchestriert das LLM Aktionen über Function Calling?
Die vollständige Transkription geht an OpenRouter mit Function Calling. OpenRouter ist ein Router, der Zugang zu über 400 LLM-Modellen mit automatischem Fallback bietet — wenn das Hauptmodell ausfällt, übernimmt ein Fallback innerhalb weniger Sekunden.
Das LLM empfängt die Transkription und muss die Absicht des Benutzers verstehen. Es verfügt über 7 Funktionen:
create_task— Eine Aufgabe erstellenupdate_task— Eine bestehende Aufgabe änderncreate_memo— Ein Memo erstellenupdate_memo— Ein bestehendes Memo änderncreate_calendar_event— Ein Kalenderereignis erstellenask_clarification— Eine Klärung vom Benutzer anfordernend_conversation— Das Gespräch beenden
Das LLM analysiert den Satz ("Erinnere mich daran, morgen um 10 Uhr Brot zu kaufen"), identifiziert die Aktion (create_task), extrahiert die Parameter (Titel, Datum, Uhrzeit) und gibt einen strukturierten Funktionsaufruf zurück. Das Backend führt die Aktion aus und sendet das Ergebnis an das Frontend zurück.
Das Muster PendingCreation ist hier entscheidend: Das Backend erstellt eine Vorschau des Elements, und der Benutzer kann vor dem endgültigen Speichern in der Datenbank validieren, bearbeiten oder abbrechen. Keine bösen Überraschungen.
Die LLM-Latenz
Das LLM macht den Großteil der Latenz aus: zwischen 800 ms und 2 Sekunden, je nach Modell und Komplexität der Anfrage. Hier macht die Wahl des Modells über OpenRouter den Unterschied — ein schnelles, aber weniger präzises Modell vs. ein langsames, aber zuverlässigeres Modell. TAMSIV verwendet ein konfigurierbares Modell mit automatischem Fallback, wenn das Hauptmodell zu langsam oder nicht verfügbar ist.
Wie generiert OpenAI TTS die Sprachantwort?
Sobald die Aktion ausgeführt wurde, generiert das Backend eine Textantwort ("Notiert! Ich habe die Aufgabe 'Brot kaufen' für morgen um 10 Uhr erstellt"). Dieser Text wird an OpenAI TTS mit der Stimme nova gesendet.
Das Audio wird zurückgestreamt über denselben WebSocket. Das Frontend beginnt mit der Wiedergabe der ersten Audio-Chunks, ohne auf die vollständige Antwort zu warten. Das reduziert die wahrgenommene Latenz um etwa 500 ms — der Benutzer hört den Anfang der Antwort, während das Ende noch generiert wird.
Zur Sprachpersonalisierung und zur Wahl der TTS-Stimme spreche ich im Artikel über Sprachpersonalisierung.
Was sind die drei WebSocket-Modi von TAMSIV?
Im Laufe der Entwicklung haben sich drei WebSocket-Modi herausgebildet:
- LiveWebSocketServer (Standard) — natives STT auf dem Gerät + Deepgram-Fallback, LLM-Orchestrierung, OpenAI TTS. Dies ist der Standardmodus, der wirtschaftlichste.
- RealtimeWebSocketServer — OpenAI Realtime API, bidirektional, geringe Latenz. Kostspieliger, aber flüssiger für lange Gespräche.
- WebSocketServer — Batch STT/TTS, Legacy-Modus. Wird für Fälle verwendet, in denen Streaming nicht erforderlich ist.
Der Modus ist auf der Admin-Seite über die Tabelle app_config in Supabase wählbar. Dies ermöglicht den Wechsel zwischen den Modi, ohne eine neue Version der App bereitzustellen.
Wie geht man mit Fehlern in einer Echtzeit-Pipeline um?
Die goldene Regel: Alles kann jederzeit fehlschlagen. Deepgram kann ausfallen. OpenRouter kann ein Timeout haben. OpenAI TTS kann einen 429-Fehler zurückgeben. Die WebSocket-Verbindung kann mitten im Gespräch abbrechen.
Hier sind die Resilienzmechanismen:
- Intelligente Retries: Jeder Schritt hat eine konfigurierte Anzahl von Retries mit exponentiellem Backoff. Keine Retries in einer Endlosschleife.
- Circuit Breaker: Wenn ein Dienst zu oft fehlschlägt, wird er für X Sekunden nicht mehr aufgerufen, um eine Überlastung eines bereits in Schwierigkeiten befindlichen Dienstes zu vermeiden.
- Fallbacks in jedem Schritt: Natives STT, wenn Deepgram ausgefallen ist, alternatives LLM-Modell über OpenRouter, Textantwort, wenn TTS fehlschlägt.
- AlertService: Jeder Fallback löst eine E-Mail-Benachrichtigung (über Resend) + Supabase-Log aus. Ich weiß in Echtzeit, wenn etwas degradiert wird.
Jede Zeile der Fehlerbehandlung stellt einen in der Produktion erlebten Fehler dar. Die Pipeline ist heute robust, aber es waren Dutzende von Debugging-Sitzungen erforderlich, um dorthin zu gelangen.
Wie hoch ist das Latenzbudget für jeden Schritt?
Aufschlüsselung einer vollständigen Sprachinteraktion:
- Audioaufnahme + WebSocket-Versand: ~50ms (vernachlässigbar)
- Deepgram STT + VAD: ~200-400ms nach Sprechende
- OpenRouter LLM (Function Calling): ~800ms-2000ms (variabel)
- OpenAI TTS (erster Chunk): ~300-500ms
Gesamt: 1,3 bis 3 Sekunden. Ziel ist es, bei 90% der Interaktionen unter 2 Sekunden zu bleiben. Darüber hinaus wird die Erfahrung frustrierend.
Streaming TTS ist der beste Hebel: Der Benutzer hört den Anfang der Antwort nach durchschnittlich ~1,5 Sekunden, auch wenn die vollständige Generierung 3 Sekunden dauert. Die wahrgenommene Latenz ist viel geringer als die tatsächliche Latenz.
Welche Lehren kannst du für den Aufbau deiner eigenen Sprachpipeline ziehen?
Nach drei Wochen intensiver Entwicklung würde ich Folgendes empfehlen:
- Beginne mit dem WebSocket: Er ist das Rückgrat. Wenn der WebSocket solide ist, fügt sich der Rest zusammen.
- Verwende die VAD des STT-Anbieters: Implementiere deine eigene Stilleerkennung nicht — das ist ein Komplexitätsgrab für ein minderwertiges Ergebnis.
- Streame alles: STT im Streaming, TTS im Streaming. Jede gesparte Millisekunde verbessert die UX.
- Plane Fallbacks vom ersten Tag an: Nicht im Modus "Ich werde später sehen". Jeder Schritt muss einen Plan B haben.
- Messe die Latenz in der Produktion: Lokale Benchmarks sind irreführend. Die tatsächliche Latenz hängt vom Netzwerk, der Serverlast und dem geografischen Standort ab. Das Admin-Dashboard war dafür unerlässlich.
FAQ
Warum Deepgram statt Google Speech-to-Text oder AWS Transcribe?
Deepgram bietet das beste Qualitäts-/Latenzverhältnis für Streaming. Google STT ist ausgezeichnet, aber teurer und langsamer im Streaming-Modus. AWS Transcribe ist robust, aber die WebSocket-Integration ist komplexer. Deepgram hat auch eine integrierte VAD, was den Code enorm vereinfacht.
Funktioniert die Pipeline im Offline-Modus?
Das native STT des Geräts funktioniert offline. Aber das LLM und TTS benötigen eine Internetverbindung. Im Offline-Modus ermöglicht TAMSIV die klassische Texteingabe und reiht Sprachanfragen ein, bis die Verbindung wiederhergestellt ist.
Wie viel kostet eine vollständige Sprachinteraktion?
Mit nativem STT (kostenlos), einem kostengünstigen LLM über OpenRouter (~$0.001-0.01) und OpenAI TTS (~$0.015/1000 Zeichen) kostet eine Interaktion zwischen $0.01 und $0.03. Die Kostendetails findest du im Rückblickartikel zu den 650 Commits.
Kann OpenAI TTS durch eine Open-Source-Lösung ersetzt werden?
Technisch ja. Projekte wie Coqui TTS oder Bark liefern gute Ergebnisse. Aber die Qualität der "Nova"-Stimme von OpenAI ist immer noch überlegen, und das Streaming wird besser unterstützt. Für ein Produktionsprojekt rechtfertigt sich der zusätzliche Preis von OpenAI TTS ($15/Million Zeichen) durch die Qualität.
Wie geht man mit mehreren Sprachen in der Sprachpipeline um?
Deepgram unterstützt die automatische Spracherkennung. Das LLM über OpenRouter ist von Natur aus mehrsprachig. Das TTS von OpenAI generiert Audio in der Sprache des bereitgestellten Textes. TAMSIV unterstützt 6 Sprachen (FR, EN, DE, ES, IT, PT) ohne spezifische Konfiguration pro Sprache in der Pipeline. Details zur Internationalisierung findest du im Artikel über i18n in 6 Sprachen.