Ihre LLM‑UI wirkt auf dem Büro‑WLAN wahrscheinlich makellos in der Demo. Dann trifft sie auf die Realität: flatternde Mobilfunknetze, Nutzer, die mitten im Stream aktualisieren, Apps, die zwischen Vorder‑ und Hintergrund wechseln, und Menschen, die vom Laptop aufs Handy springen. Plötzlich zahlen Sie doppelt für Output‑Tokens und in den Support‑Tickets steht „es dreht nur noch“.
Server‑Sent Events (SSE) ist weiterhin der praktikabelste Weg, Token‑Streams der meisten Provider auszuliefern. Es harmoniert mit Proxies, funktioniert über HTTP/2 und benötigt kein Full‑Duplex wie WebSockets. Aber out‑of‑the‑box ist SSE fragil. Die gute Nachricht: Man kann es abhärten. Die jüngste Welle an Posts zu wiederaufnehmbaren, abbrechbaren und geräteübergreifenden SSE‑Streams liegt richtig — und es ist Zeit, diesen Rat in einen produktionsreifen Blueprint zu übersetzen, den Sie Ihrem Team geben können.
Die Ausfallmuster, für die Sie zahlen
- Verwaiste Neustarts: Ein Nutzer aktualisiert während der Generierung. Ihr Backend startet einen neuen Provider‑Call. Sie haben die Kosten für dieselbe Anfrage gerade verdoppelt.
- Phantom‑Compute nach Abbruch: Der Nutzer drückt Stopp; Ihre UI schließt die TCP‑Verbindung. Ihr Server propagiert den Abbruch nicht upstream, also generiert der Provider weiter — und rechnet ab.
- Multi‑Device‑Drift: Ein Nutzer öffnet dieselbe Unterhaltung auf Web und Mobile. Beide lösen eine Generierung aus. Sie zahlen doppelt, Inhalte driften auseinander, und Deltas zu versöhnen wird unsauber.
- Middlebox‑Buffering: CDNs und Proxies puffern Ihren Stream „zur Hilfe“. Die Time‑to‑first‑token (TTFT) springt von 300–500 ms auf mehrere Sekunden. Nutzer brechen ab.
- Fortschritt verloren auf wackeligen Netzen: 3–7% mobiler SSE‑Verbindungen fallen in einem 60‑Sekunden‑Fenster aus. Ohne Resumption starten Sie die gesamte Generierung neu.
Wenn Ihr Produkt 300 Mio. Output‑Tokens/Monat bei $15 pro Million bewegt (typisch für GPT‑4o‑Klasse Output), dann sind 8–12% Duplikate/Verschwendung $360–$540/Monat — auf dem Papier klein, aber es korreliert mit Churn und Supportlast. Bei großem Maßstab (Milliarden Tokens oder teurere Modelle) wächst der Betrag schnell. Wichtiger noch: kaputte Streams zerstören Vertrauen.
Entscheidungsrahmen: wann SSE, wann WebSockets, wann WebRTC
- SSE, wenn Sie nur Server→Client‑Token‑Streaming, einfache Reconnect‑Semantik und breite Proxy/CDN‑Kompatibilität brauchen. Die meisten Chat‑artigen LLM‑UIs passen hierher.
- WebSockets, wenn Sie während der Generierung echtes Duplex benötigen (z. B. Echtzeit‑Tool‑Fortschritt, Nutzer‑Unterbrechungen, die über NATs sofort zugestellt werden müssen) und Sie den Edge‑Pfad kontrollieren. Schwieriger im Internet‑Maßstab zu betreiben.
- WebRTC, wenn Sie Audio/Video‑Streams mit niedriger Latenz und NAT‑Traversal machen. Nützlich für gerade eingeführte Sprach‑Features großer Provider, aber Overkill für Text‑Tokens.
Dieser Beitrag setzt SSE voraus. Wenn Sie WebSockets oder WebRTC wählen, gelten die meisten Control‑Plane‑Ideen weiterhin: Stream‑IDs, Sequenznummern, Fan‑out und Cancellation.
Die Architektur, die das Doppeltzahlen verhindert
Lassen Sie Clients nicht direkt Ihren Modell‑Provider ansprechen. Platzieren Sie einen leichtgewichtigen Stream Broker in Ihrem Stack:
- Client öffnet SSE zu Ihrem Edge unter
/v1/streams/:stream_id. - Edge/Ingress terminiert TLS, deaktiviert Buffering und leitet an den Stream Broker weiter (sticky nach
stream_id). - Stream Broker ist ein zustandsloser Worker mit einem kurzlebigen In‑Memory‑Cache (oder Redis/NATS), indiziert nach
stream_id. Er übernimmt drei Aufgaben:- Multiplex/Fan‑out: Läuft für diese
stream_idbereits eine Generierung, hängen Sie den neuen Subscriber an; starten Sie keinen weiteren Provider‑Call. - Resumption: Puffern Sie ein rollierendes Fenster jüngster Deltas mit monoton steigendem
event.id. Bei Reconnect mitLast-Event-IDwerden fehlende Teile nachgespielt. - Cancellation: Bei Nutzer‑Stopp den Upstream‑Provider‑Request über einen abort‑fähigen HTTP‑Client abbrechen. Wenn sich der letzte Subscriber trennt, automatisch abbrechen.
- Multiplex/Fan‑out: Läuft für diese
- Model Worker hält Provider‑Credentials, fordert Streaming‑Completions an und normalisiert provider‑spezifische Deltas auf ein gemeinsames Event‑Format.
Halten Sie den Stream‑Zustand 60–120 Sekunden nach Abschluss/Abbruch im Speicher, um schnelle Gerätewechsel oder Tab‑Refreshes ohne vollständigen Neustart abzufangen.
Stream‑Identität und Sequenzierung
- Leiten Sie
stream_iddeterministisch ausconversation_id+turn_id+ einem 32‑Bit‑epochab. Wenn der Nutzer Prompt oder Systemzustand ändert, erhöhen Sie die Epoch, um veraltete Resumes zu verhindern. - Senden Sie SSE‑
id‑Felder als strikt aufsteigende Ganzzahlen pro Stream, beginnend bei 1. Vermeiden Sie Provider‑Chunk‑IDs; nummerieren Sie auf Ihrer Seite neu. - Senden Sie Events mit Typen:
delta,tool,heartbeat,complete,error. Überladen Siedataniemals mit Steuersignalen.
Client‑Reconnect‑Logik (die einzige, die Sie haben sollten)
- Bei Verbindungsabbruch mit exponentiellem Backoff neu verbinden, gedeckelt auf 2 Sekunden.
Last-Event-IDmit der jüngsten angewendetenidmitsenden. Hat der Broker den Buffer, spielt er nach; andernfalls liefert er410 Gone, um einen vollständigen Neustart zu erzwingen.- Idempotent rendern. Wenn Sie eine alte
idsehen, verwerfen; wenn eine fehlt, nach Reconnect per Query gezielt nachfordern (?from_id=123).
Abbrechbarkeit, die die Rechnung wirklich stoppt
SSE ist einseitig. Das Schließen des Browser‑Tabs garantiert nicht, dass der Provider die Generierung stoppt. Der Abbruch muss serverseitig fließen:
- Stop‑Button → POST /v1/streams/:stream_id/cancel mit einem kurzlebigen, stream‑gebundenen Token (60 s TTL). Verlassen Sie sich nicht allein auf das Erkennen von Verbindungsabbrüchen.
- Broker‑Abbruchverhalten:
- Stream als cancelling markieren; alle Subscriber per
event: errormit einem typisierten Grund (user_cancelled,superseded) informieren. - Den Provider‑HTTP‑Request abbrechen (AbortController oder client‑spezifischer Cancel‑Token). Unterstützt der Provider kein Abort, notfalls den TCP‑Socket schließen.
- Die letzten N Deltas im Speicher halten, damit ein schneller Resume Teilinhalte ohne erneuten Modellaufruf zeigt.
- Stream als cancelling markieren; alle Subscriber per
- Single‑Flight standardmäßig: Pro Unterhaltung genau eine aktive Generierung erzwingen. Ein neuer
startbricht die laufende automatisch mit Grundsupersededab.
In der Praxis reduziert robuste Cancellation die durchschnittlich konsumierten Output‑Tokens pro Gesprächs‑Turn um 15–35%, weil Menschen Antworten gewohnheitsmäßig „kürzen“. Das spart spürbar Geld bei Premium‑Modellen und — noch wichtiger — die UX fühlt sich kontrollierbar an.
Multi‑Device ohne Doppelabrechnung
Ein Nutzerwechsel vom Laptop zum Handy darf keinen neuen Modellaufruf auslösen. Ziel ist: einmal rechnen, vielfach liefern:
- Viele Subscriber an einen Upstream‑Call anhängen. Der Broker behandelt jede Client‑Verbindung als Subscriber. Neue Subscriber erhalten einen schnellen Replay gepufferter Deltas (basierend auf
Last-Event-ID) und danach Live‑Tokens. - Fan‑out auf einem gemeinsamen Event‑Log. Persistieren Sie nur strukturierte Deltas und Metadaten (Token‑Index, Tool‑Calls). Den vollständigen Text erst bei
completespeichern — oder on the fly zusammensetzen. - Gerätekonkurrenz‑Policy durchsetzen. Beispiel: mehrere Leser erlauben, ein Initiator. Neue Initiatoren auf einem anderen Gerät brechen den laufenden Stream automatisch mit
supersededab. - Unbeabsichtigte Duplikate verhindern.
start‑Aktionen pro Unterhaltung 300–500 ms entprellen, um Doppelklicks/Race‑Starts zu vermeiden.
Über Audits, die wir für US‑Consumer‑Apps durchgeführt haben, waren 5–12% der LLM‑Aufrufe durch Tab‑Refreshes oder schnelle Gerätewechsel dupliziert. Ein brokerbasiertes Fan‑out reduziert das auf <2%, und die p95‑Time‑to‑first‑token sinkt typischerweise um 30–50%, sobald das Buffering behoben ist.
Edge‑ und Proxy‑Konfiguration (wo die meisten Teams stolpern)
Middleboxes lieben Buffering. Ihre Aufgabe ist es, Streaming von den HTTP‑Headern bis zum Origin unmissverständlich zu machen. Basiskonfiguration:
- Response‑Header vom Broker:
Content-Type: text/event-stream,Cache-Control: no-cache, no-transform,Connection: keep-alive,X-Accel-Buffering: no(von Nginx beachtet) und alle 10–15 Sekunden einen Heartbeat‑Kommentar: ping\n\nsenden. - Nginx/Envoy: Proxy‑Buffering für
text/event-streamdeaktivieren, kleine Chunks flushen,proxy_read_timeoutfür lange Generierungen erhöhen (z. B. 120 s+). Sicherstellen, dass clientseitig HTTP/2 aktiviert ist; Chunking‑Semantik intakt lassen. - CDN/Edge: Viele CDNs puffern standardmäßig. Funktionen wie „origin streaming“, „no buffering“ nutzen oder Caching für
text/event-streamumgehen.no-transformsetzen, um Inhaltsänderungen zu verhindern. Falls Ihr Edge‑Anbieter echtes Streaming auf Free‑Tiers nicht unterstützt, SSE‑Pfade am Edge umgehen. - Provider‑Verbindungen: Wo unterstützt, HTTP/2 verwenden; kleine Write‑Buffer setzen, um Tokens zügig zu flushen. Einige Provider bündeln Tokens, sofern
Accept: text/event-streamundstream=truenicht exakt gesetzt sind.
Provider‑Eigenheiten, die Sie vereinheitlichen sollten
- Token‑Delta‑Format: Provider unterscheiden zwischen Teil‑ und Volltext‑Frames. Normalisieren Sie auf
event: deltamit{ id, text_delta, tool_calls_delta, usage? }. Text clientseitig zusammensetzen. - Stop‑Gründe: Ein konsistentes Set exponieren:
length,user_cancelled,content_filter,error. Provider‑spezifische Gründe auf Ihre mappen. - Abort‑Semantik: Manche APIs stoppen nur bei TCP‑Close; andere akzeptieren Request‑gebundene Cancellation. Standardisieren über Ihren HTTP‑Client und halten Sie einen Integrationstest vor, der <1 Sekunde Stopp nach Cancel über Provider hinweg verifiziert.
- Usage‑Abrechnung: Verlassen Sie sich nicht nur auf den asynchronen Usage‑Webhook des Providers. Berechnen Sie Ihre eigenen Token‑Zählungen aus den Deltas für Echtzeit‑Metering und Fraud‑Erkennung.
Sicherheit: Scopes an den Stream binden, nicht an den Nutzer
- Stream‑gebundene Tokens: Geben Sie ein JWT aus, das nur für
/v1/streams/:stream_idund/cancelnutzbar ist, TTL ≤ 5 Minuten.conversation_id,epoch, Nutzer‑ID und zulässige Aktionen einbetten. - Keine PII in Logs: Nie
event.dataloggen. Nurid,event, Byte‑Größen und Timings protokollieren. - Provider‑Keys bleiben serverseitig. Clients halten niemals Model‑API‑Keys. Der Broker vermittelt. Das ermöglicht auch zentrale Cancellation und Fan‑out.
- Rate‑Limits pro Unterhaltung: Standardmäßig parallele Generierungen pro Unterhaltung auf 1 begrenzen. Burst‑Policies auf Org‑ oder Projektebene.
Resilienz und Fallbacks
- Resume‑Window‑TTL: Einen 60–120‑Sekunden‑In‑Memory‑Ringbuffer pro Stream halten (~64–256 KB genügen). Ist der Buffer weg,
410 Gonezurückgeben, damit der Client neu startet. - Edge‑Bypass‑Schalter: Per Feature‑Flag einen Pfad, der SSE bei Edge‑Buffering‑Incidents direkt zum Origin routet.
- Provider‑Failover: Mid‑Stream‑Failover ist selten sauber. Bevorzugt: schnelles Fehlschlag‑Erkennen (1–2 Sekunden), abbrechen, dann transparenter Neustart auf einem Secondary‑Provider mit klarem UI‑Hinweis „switched to backup model.“
- Chaos‑Tests im Netzwerk: 3–5% Paketverlust, 250–600 ms variable RTT und zufällige 1–3‑Sekunden‑Disconnects injizieren. Ihr SSE sollte weiterhin lesbar und wiederaufnehmbar bleiben.
Datenmodell: Event‑Sourcing‑Streams schlagen Ad‑hoc‑Strings
Persistieren Sie jeden Stream als Append‑Only‑Log strukturierter Events:
- delta:
{ id, ts, text_delta, tool_delta?, usage_partial? } - heartbeat:
{ id, ts } - complete:
{ id, ts, stop_reason, usage_final } - error:
{ id, ts, code, retriable }
Beim Lesen können Sie den finalen Text wiederaufbauen, Usage präzise auditieren und Analysen zu Unterbrechungsmustern fahren. Und wenn Sie Multi‑Provider‑A/Bs einführen, werden Sie diese Lineage wollen.
Observability: KPIs, die belegen, dass es funktioniert
- TTFT (time‑to‑first‑token): Ziel <600 ms p50, <1,5 s p95 im Breitband; mobil ggf. höher.
- Stream‑Abschlussrate: Anteil der Streams, die in
completeenden vs.error/cancel. Nach Gerät und Netzwerktyp tracken. - Resume‑Erfolgsrate: Anteil der Reconnects, die fehlende Deltas ohne Neustart nachspielen. Ziel >90% innerhalb des Resume‑Windows.
- Duplicate Suppression: Anteil doppelt gestarteter Versuche, die in einen bestehenden Stream multiplexed wurden. Dieser Wert soll hoch sein.
- Cancel‑Propagation‑Zeit: Zeit vom Nutzerklick bis zur Upstream‑Abort‑Bestätigung. Ziel <1 Sekunde.
- Edge‑Buffering‑Vorfälle: Spikes bei TTFT und plötzliche Ausfälle der Heartbeat‑Zustellung zählen; bei Erkennung alarmieren.
Ein Marktplatz‑Produkt, das wir unterstützt haben, senkte die doppelte Token‑Verbrennung um 9,2% und verbesserte die p95‑TTFT um 41%, nachdem es auf einen brokerbasierten SSE‑Pfad mit korrekter Edge‑Konfiguration und Resumption umgestellt hatte.
Rollout‑Plan, den Sie dieses Quartal ausliefern können
Woche 0–2: Den Broker beweisen
- Einen minimalen Stream Broker in Ihrer API‑Schicht einführen. Provider‑Deltas normalisieren, SSE mit
idaussenden und Heartbeats hinzufügen. - End‑to‑End‑Buffering auf einer Canary‑Route deaktivieren. TTFT und Drop‑Raten messen.
/cancelhinzufügen und Abort‑Propagation zu einer Provider‑Integration implementieren.
Woche 3–5: Haltbar machen
- Einen Ringbuffer pro Stream hinzufügen (zuerst In‑Memory; Redis Streams, falls Sie Resumption über Instanzen brauchen).
Last-Event-ID‑Resume und schnellen Replay implementieren. Doppelte Starts entprellen.- Fan‑out zu mehreren Subscribern. Gerätewechsel ohne Doppelabrechnung verifizieren.
Woche 6–8: Leitplanken und Metriken
- Stream‑gebundene JWTs, Rate‑Limits und Single‑Flight pro Unterhaltung.
- Dashboards für TTFT, Resume‑Erfolg, Cancel‑Latenz, Duplicate Suppression.
- Chaos‑Tests mit künstlichem Paketverlust und intermittierenden Disconnects. Fixen, bis die Metriken halten.
Abwägungen — und wann Sie es lassen sollten
- Komplexität: Ein Broker fügt bewegliche Teile hinzu. Ist Ihre App nur intern, auf stabilen Netzen, und die Call‑Volumina sind niedrig, können Sie mit naivem SSE leben.
- Latenz vs. Konsistenz: Replay‑Windows und Normalisierung fügen ein paar Millisekunden hinzu. Für Haltbarkeit lohnend; messen Sie zur Absicherung.
- State‑Management: Deltas zu persistieren erhöht Storage‑ und PII‑Risiken. Kurze TTLs, Verschlüsselung at rest und kein Payload‑Logging.
- Provider‑Zwänge: Bei manchen Anbietern ist das Streaming‑Verhalten inkonsistent. Aggressiv normalisieren und pro Model‑API Vertragstests bauen.
Warum jetzt
Zwei Dinge haben sich 2026 geändert. Erstens wandern LLM‑Funktionen auf Mobile und in Bandbreiten‑arme Kontexte, nicht nur ins Desktop‑Web. Zweitens vergrößern „Sprachintelligenz“ der Modelle und reichere Tool‑Events die Angriffsfläche für Teilzustellung und Abbruch. Sie brauchen nicht mehr Agenten; Sie brauchen Control‑Flow auf der Stream‑Ebene. Wenn Sie das richtig bauen, reduzieren Sie Verschwendung, gewinnen Tempo und — am wichtigsten — Ihre KI‑Features wirken verlässlich.
Wichtigste Takeaways
- Einen Stream Broker zwischen Clients und Provider setzen, um zentral zu multiplexen, wiederaufzunehmen und abzubrechen.
- Deterministische
stream_id+ monoton steigendeevent.idverwenden; mitLast-Event-IDwiederaufnehmen. - Explizites
/cancelimplementieren und Abbruch serverseitig propagieren; nicht auf Socket‑Closes verlassen. - Buffering vom CDN bis zum Origin deaktivieren; Heartbeats und korrekte SSE‑Header senden.
- Einen Upstream‑Run auf mehrere Geräte fan‑outen; doppelte Starts entprellen.
- TTFT, Resume‑Erfolg, Cancel‑Latenz und Duplicate Suppression tracken, um den Impact zu belegen.
- Erwarten Sie 15–35% weniger Output‑Tokens pro Turn und 30–50% schneller wahrgenommene Latenz, wenn es richtig umgesetzt ist.
Author: Diogo Hudson Dias