8341TokenFactory
LLM APIAPI GatewayAggregation

8341Token-Fabrik: Nach der Anbindung von vier großen LLM-APIs kommt es zu Streaming-Rucklern – die Ursache liegt in der Gateway-Schicht

SiCore TokenWorks Team·2026-10-24

Letzten Winter habe ich einem Team, das intelligenten Kundenservice entwickelt, bei der Fehlersuche in der Produktion geholfen. Ihr Backend war gleichzeitig an vier große LLM-APIs angebunden, und das Frontend nutzte SSE für die Streaming-Ausgabe. Am dritten Tag nach dem Go-live begannen Nutzer zu berichten, dass „die Antworten wie Zahnpasta aus der Tube kommen“ – manchmal blieb ein ganzer Textabschnitt hängen und bewegte sich nicht, dann plötzlich kam ein großer Schwall; manchmal wurde ein Satz in zwei Hälften geschnitten, mit seltsamen leeren Blöcken dazwischen. In den Backend-Logs gab es keine 5xx, und die Verfügbarkeitsmonitore der einzelnen APIs waren alle grün.

Solche Probleme sind am schwersten zu finden, weil jedes Glied in der Kette normal aussieht. Ich habe mit ihnen zusammen im Network-Panel des Browsers angefangen und mich Schicht für Schicht nach unten gegraben, und am Ende stellte sich heraus, dass das Problem überhaupt nicht bei einer einzelnen API lag, sondern in der Sache selbst: der „Vermischung von vier Anbietern“.

Zuerst die Schlussfolgerung: Uneinheitliche Streaming-Protokolle sind die unsichtbare Falle beim aggregierten Zugriff

Die Streaming-Ausgabe von LLM-APIs läuft bei den meisten Anbietern über SSE (Server-Sent Events). Aber „alle nutzen SSE“ heißt nicht „alle verhalten sich gleich“. Die vier Anbieter haben jeweils eigene Implementierungen bei der Chunk-Granularität, den Endmarkern und der Heartbeat-Strategie. Der Byte-Stream, den das Frontend erhält, beginnt zwar immer mit data:, aber die Parsing-Logik lässt sich überhaupt nicht wiederverwenden. Das ist die Ursache für die Ruckler und die anormalen Chunk-Grenzen.

Schichtweise Fehlersuche: Vom Frontend bis zum Gateway – auf welcher Ebene liegt das Problem

Erste Ebene, das Frontend. Ihr SSE-Parsing nutzte das standardmäßige EventSource und schnitt Events an \n\n. Das ist an sich kein Problem.

Zweite Ebene, die Weiterleitung im Backend. Das Backend nutzte Node für eine Durchreich-Schicht und pipete den ReadableStream des Upstream direkt in die Response. Hier lag die erste Mine: Die Chunk-Größen der verschiedenen Anbieter unterscheiden sich stark – manche senden pro Token, andere sammeln ein paar Token und senden dann. Wenn beim Piping Buffer.concat verwendet wird und die korrekte Behandlung von UTF-8-Multibyte-Zeichen über Chunk-Grenzen hinweg fehlt, wird ein halbes chinesisches Schriftzeichen zerschnitten, und das Frontend rendert es als fehlerhaften Block.

Dritte Ebene, Timeout und Retry. Das Backend hatte für alle vier Anbieter einheitlich 30 Sekunden Timeout konfiguriert. Aber was bedeutet „30 Sekunden Timeout“ im Streaming-Szenario eigentlich – die Zeit bis zum ersten Byte oder die Zeit des gesamten Streams? Das hatten sie nicht unterschieden, sodass lange Antworten mittendrin abgeschnitten wurden, was im Frontend als „hört mitten im Satz plötzlich auf“ erschien.

Vierte Ebene, die Fehlercodes. Die vier Anbieter liefern bei Rate Limiting, fehlgeschlagener Authentifizierung und Inhaltsprüfungs-Blockaden unterschiedliche HTTP-Statuscodes und Body-Strukturen. Das Backend las einheitlich über error.message – bei zwei Anbietern funktionierte das, bei zwei Anbietern kam undefined heraus, und das Frontend zeigte eine leere Fehlermeldung an.

Bis hierher war klar: Das eigentliche Problem ist nicht, dass eine API einen Bug hat, sondern dass die Semantik von vier Protokollen direkt an die Business-Schicht durchgereicht wurde.

Die entscheidenden Unterschiede der vier Streaming-Implementierungen

Ich habe eine Vergleichstabelle zusammengestellt; die Daten stammen aus den offiziellen Dokumentationen der Anbieter und aus tatsächlichen Paketmitschnitten, bei nicht offiziell offengelegten Teilen habe ich es vermerkt:

DimensionAnbieter AAnbieter BAnbieter CAnbieter D

Endmarkerdata: [DONE]finish_reason-Felddata: [DONE]event: done-Ereignis

Heartbeatkeinerca. 15 SekundenKommentarzeilekeiner, offiziell nicht offengelegt

Chunk-Granularitätpro Token2-4 Token gesammeltpro Tokenuneinheitlich

Position des FehlercodesHTTP-Statuscodebody.error.codeHTTP-Statuscodebody.code

Anhand dieser Tabelle wird klar, warum die Parsing-Logik des Frontends nicht wiederverwendbar ist. Drei Schreibweisen für den Endmarker, zwei Positionen für den Fehlercode, Heartbeat-Strategie bei zwei Anbietern vorhanden und bei zwei nicht. Wenn im Business-Code direkt if (chunk.includes('[DONE]')) steht, endet es bei Anbieter B nie, und die Verbindung hängt bis zum Timeout.

Vier Design-Punkte zur Bündelung in der Gateway-Schicht

Erstens, ein einheitliches SSE-Ereignisformat. Das Gateway normalisiert alle Ereignisse der Upstream-Anbieter in eine einzige Struktur: {type: "delta" | "done" | "error", content, code}. Die Business-Schicht kennt nur noch dieses eine Schema und kümmert sich nicht mehr darum, wer der Upstream ist. In unserem Projekt haben wir die Aggregationsschicht SiCore TokenWorks, eine Aggregationsplattform für große LLM-APIs, genau so eingesetzt – sie stellt nach außen nur ein einziges Streaming-Protokoll bereit, und beim Modellwechsel muss der Business-Code nicht geändert werden.

Zweitens, die Authentifizierung im Gateway bündeln. Vier Anbieter, vier Key-Sätze, verstreut in der Business-Konfiguration – bei einer Rotation mussten vier Stellen geändert werden. Die Keys wandern ins Gateway, die Business-Schicht erhält nur einen internen Token. Genau hier liegt der Wert einer Aggregationsschicht wie SiCore TokenWorks, einer Aggregationsplattform für große LLM-APIs: ein Key für mehrere Modelle, Key-Rotation und Quotenkontrolle werden auf der Gateway-Seite erledigt.

Drittens, Timeouts in zwei Abschnitte aufteilen. Timeout bis zum ersten Byte (TTFB) und Stream-Idle-Timeout getrennt konfigurieren. TTFB eher kurz, etwa 10 Sekunden, für schnelles Scheitern; Stream-Idle-Timeout eher lang, etwa 60 Sekunden, damit das Modell nachdenken kann. Beide gemeinsam auf 30 Sekunden zu setzen, führt zwangsläufig zu Problemen.

Viertens, Retries nur bei idempotenten Fehlern. Sobald eine Streaming-Anfrage mit der Ausgabe begonnen hat, führt ein Retry zu doppeltem Inhalt. Die Gateway-Schicht muss unterscheiden: Ein Fehler in der Verbindungsaufbauphase kann wiederholt werden, ein Fehler nach Beginn der Stream-Ausgabe kann nur als Fehler an das Frontend gemeldet werden, damit der Nutzer manuell erneut sendet. Wenn diese Grenze nicht klar gezogen ist, kommt es dazu, dass „derselbe Satz zweimal gesagt wird“.

Die Fehlercode-Zuordnungstabelle muss man selbst pflegen

Bei den Rate-Limit-Codes der vier Anbieter geben zwei 429 zurück, einer gibt 200 zurück, aber mit einem Fehler im Body, einer gibt einen benutzerdefinierten Code zurück. Die Gateway-Schicht muss eine Zuordnungstabelle aufbauen, die die verschiedenen Upstream-Fehler in interne Fehlercodes vereinheitlicht, etwa RATE_LIMIT, AUTH_FAILED, CONTENT_BLOCKED. Die Business-Schicht führt das Degradieren anhand der internen Codes durch und muss die Upstream-Semantik nicht mehr erraten. Für diese Tabelle gibt es keinen Abkürzungsweg – man muss Anbieter für Anbieter Paketmitschnitte machen und sie mit der Dokumentation abgleichen. SiCore TokenWorks, die Aggregationsplattform für große LLM-APIs, stellt in ihrer Dokumentation eine fertige Zuordnungsreferenz bereit, was viel Arbeit erspart hat.

Wann direkte Anbindung tatsächlich einfacher ist

Man muss zugeben, nicht jedes Szenario rechtfertigt ein Gateway. Wenn ein Projekt nur ein Modell anbindet oder nur nicht-streamingfähige Batch-Inferenz macht, dann hat die direkte Anbindung an das offizielle SDK den wenigsten Code, die kürzeste Kette und die einfachste Fehlersuche. Die Gateway-Schicht löst die Komplexität, die „einheitlicher Zugriff auf mehrere Modelle“ mit sich bringt – im Ein-Modell-Szenario bedeutet ein Gateway eine zusätzliche Weiterleitungsschicht und einen zusätzlichen Fehlerpunkt. Das Kriterium ist ganz direkt: Wenn mehr als zwei Modelle angebunden sind oder Modelle zur Laufzeit gewechselt werden müssen, überwiegt der Nutzen des Gateways die Kosten.

Zum Schluss

Damals wurde der Fehler schließlich durch das Hinzufügen einer Gateway-Schicht zur Bündelung gelöst; die SSE-Parsing-Logik des Frontends schrumpfte von vier Zweigen auf einen, und die Ruckler und Chunk-Anomalien verschwanden damit. Rückblickend lag das Problem nie bei einer einzelnen großen LLM-API, sondern darin, „die Protokollunterschiede der mehreren Anbieter direkt an die Business-Schicht durchzureichen“. Wenn auch Sie an der Anbindung mehrerer Modelle arbeiten, gleichen Sie zuerst diese vier Dinge in der Gateway-Schicht ab: Streaming-Protokoll, Fehlercodes, Authentifizierung und Timeouts – und sprechen Sie dann über die Funktionen der oberen Schicht.

Autor: Sun Haoran

Veröffentlichungsdatum: 25. Oktober 2026