LLM-Guardrails im Streaming: Ein gesendetes Token holt niemand zurück
LLM-Guardrails im Streaming: Ein Token holt niemand zurück. Wie NeMo, Guardrails AI und Bedrock Tempo gegen Sicherheit abwägen und ein Satz-Gate Aussagen prüft.
Ein Token, das ein LLM gestreamt hat, lässt sich nicht zurückholen. Sobald Text beim Leser angekommen ist, kann ein Guardrail, der Einspruch erhebt, nur noch verhindern, was danach kommt. Ein Guardrail auf einem Stream trifft deshalb im Kern eine einzige Entscheidung: vor der Freigabe prüfen und den Leser warten lassen oder zuerst freigeben und hinnehmen, dass eine blockierte Antwort schon gelesen wurde. Praktikabel sind drei Formen: nach der Freigabe prüfen, ein Gate pro festem Chunk oder ein Gate pro ganzem Satz. Der Assistent auf dieser Website, den ich gebaut habe, arbeitet mit einem Gate pro Satz. Der Preis dafür ist ein Satz Wartezeit. Die Grenze: Das Gate prüft, ob eine Aussage belegt ist, nicht, ob sie wahr ist.
Dieser Artikel behandelt die letzte Prüfung, bevor eine quellengestützte Antwort freigegeben wird. Die Schichten davor, von der Ablehnung themenfremder Fragen bis zur Mindestschwelle für den Retrieval-Score, beschreibt Guardrails gegen RAG-Halluzinationen: ein Design in fünf Schichten.
Warum sich ein gestreamtes Token nicht zurücknehmen lässt
Ein nachgelagerter Validator (post-hoc) sieht die vollständige Antwort erst, wenn der Leser sie schon gelesen hat. Erfindet der dritte Satz ein Datum, ändert nachträgliches Löschen die Aufzeichnung, nicht das, was gelesen wurde. Ein aktueller arXiv-Preprint von Christopher M. Frost benennt die Grenze genau: „Eine Kontrolle, die ein verbotenes Muster erst erkennt, nachdem sie den Chunk freigegeben hat, der es vervollständigt, kann es nicht zurückholen.“
Die Anbieter dokumentieren dieselbe Grenze. Der optional zuschaltbare asynchrone Filter von Azure OpenAI gibt Text sofort zurück und garantiert sein Signal für einen Verstoß nur innerhalb von etwa 1.000 Zeichen rund um den richtlinienwidrigen Inhalt. Die Hinweise zur Fehlerbehebung raten, „in Ihrer Anwendung eine Schwärzung von Inhalten zu implementieren, um bereits angezeigte Inhalte zu entfernen“. Der OWASP-Eintrag LLM09:2025 empfiehlt Werkzeuge und Prozesse, „um zentrale Ausgaben automatisch zu validieren“. Offen bleibt, wann die Validierung läuft: vor oder nach der Freigabe.
Drei Wege, einen Stream abzusichern: post-hoc, pro Chunk, pro Satz
Ansatz | Leser sieht den Text | Eine blockierte Antwort | Beispiele |
|---|---|---|---|
Post-hoc (erst freigeben) | Vor seiner Prüfung (NeMo: jeder Chunk, sobald er gepuffert ist; Azure und Bedrock asynchron: schon während der Generierung) | Vor dem Stopp teilweise gelesen | NeMo mit |
Gate pro Chunk | Nachdem jeder Chunk bestanden hat | Blockierter Chunk wird nie angezeigt | Standard bei Azure und Bedrock; NeMo mit |
Gate pro Satz | Nachdem jeder Satz bestanden hat | Blockierter Satz wird nie angezeigt | Der Assistent dieser Website |
Der Zielkonflikt hinter stream_first in NeMo Guardrails
In NeMo Guardrails ist das Streaming der Output Rails standardmäßig ausgeschaltet. Ist es aktiviert, laufen die Output Rails, also die Prüfungen von NeMo auf generiertem Text, standardmäßig auf Chunks von 200 Tokens; jeweils 50 Tokens Kontext werden in den nächsten Chunk mitgenommen. Das Flag stream_first steht standardmäßig auf True: „Der Client erhält jeden Chunk von Tokens, bevor die Output Rails diesen Chunk verarbeiten. Das sorgt für eine kürzere Time to First Token, aber wenn eine Rail den Inhalt blockiert, hat der Nutzer die Tokens bereits erhalten.“ Der Stream endet dann mit einem JSON-Fehler.
Mit False gilt: „Der Nutzer sieht blockierte Inhalte nie, aber die Time to First Token steigt um die Ausführungszeit der Rail pro Chunk.“ Das ist der Zielkonflikt: Time to First Token (TTFT) gegen blockierten Text, der den Leser erreicht. Ist das Streaming der Output Rails eingeschaltet, gibt NeMo standardmäßig zuerst frei; die beiden Cloud-Filter weiter unten prüfen zuerst. Soll eine Rail etwas stoppen, das nie zu sehen sein darf, ändern Sie den Standard.
Der Zielkonflikt bei den satzweisen Validatoren von Guardrails AI
Guardrails AI setzt auf den Satz: „Standardmäßig warten Validatoren, bis sie Inhalt im Umfang eines Satzes vom LLM gesammelt haben, bevor sie die Validierung ausführen.“ Einzelne Sätze statt der ganzen Antwort zu validieren, „hilft, Kosten bei Validatoren zu sparen, die teure Inferenz benötigen“.
Nicht alle on_fail-Typen werden beim Streaming unterstützt, und Korrekturen mehrerer Validatoren warten, „bis alle Validatoren genug Chunks gesammelt haben“, bevor sie zusammengeführt werden. Ob rohe Tokens den Nutzer schon vor der Validierung erreichen, sagt die Dokumentation nicht klar; prüfen Sie das in Ihrer eigenen Integration.
Standardmäßig gepuffert: Azure OpenAI und Amazon Bedrock
Azure OpenAI puffert den Completion-Text standardmäßig, filtert den Puffer und liefert „Content-Chunks“ statt einzelner Tokens. Amazon Bedrock Guardrails scannt Chunks in seinem synchronen Standardmodus, bevor sie gesendet werden; das „verursacht etwas Latenz“, bietet aber „eine bessere Genauigkeit“. Im asynchronen Modus von Bedrock „können Chunks unangemessene Inhalte enthalten, bis der Guardrails-Scan abgeschlossen ist“, und sensible Informationen lassen sich nicht maskieren.
Wie ein Satz-Gate einen Stream validiert
Der Assistent auf dieser Website antwortet nur aus den Inhalten der Website selbst, und eine Frage, die sich nicht auf mich bezieht und von keiner Passage gestützt wird, erreicht das Modell gar nicht erst. Er spricht über mich in der dritten Person. Auf einer Website, die meinen Namen trägt, ist eine erfundene Qualifikation der schlimmste Fehler, und eine Qualifikation ist eine Aussage. Der Satz ist die kleinste Einheit, die eine Aussage trägt; deshalb puffert das Gate bis zu einer Satzgrenze, prüft und gibt erst dann frei. Die Demo RAG vs. ohne im KI-Labor ruft denselben Streaming-Endpunkt auf.
Wo ein Satz endet, während noch Tokens eintreffen
Satzendezeichen:
.!?…, dazu das Fragezeichen؟und der Punkt۔der arabischen Schrift sowie für Chinesisch und Hindi。!?, der Danda।und der doppelte Danda॥. Eine Folge wie?!zählt als eine Grenze. Das Semikolon؛der arabischen Schrift verbindet Satzteile und bleibt deshalb bewusst außen vor.Danach muss Leerraum folgen. Allein diese Regel hält
3.14undexample.comzusammen. Ausgenommen sind Punkt, Ausrufe- und Fragezeichen des Chinesischen, auf die kein Leerzeichen folgt. Innerhalb eines Zitats beenden sie den Satz erst dort, wo das Zitat schließt, und gar nicht, wenn danach ein Komma folgt.Nicht jeder Punkt beendet einen Satz. Folgt ein Kleinbuchstabe, beendet er ihn nicht (
et al. und), und ebenso wenig der Punkt einer Abkürzung aus einer kurzen Liste wiee.g.oderbzw., noch der Punkt nach dem Tag in einem Datum (am 17. September).Das nächste Zeichen entscheidet. Eine Grenze steht erst fest, wenn ein Zeichen eintrifft, das weder ein Leerzeichen noch ein Quellenmarker ist, denn die Marker nach einem Punkt gehören zum Satz davor: alle,
[S1][S2]wie[S1] [S2], nicht nur der erste.Eine Listennummer nach einer Grenze ist kein Satz. Sonst würde ein
1.am Anfang der Antwort oder eines neuen Satzes als Aussage ohne Quellenangabe gewertet und eine belegte Antwort schon beim ersten Aufzählungspunkt stoppen.Eine Obergrenze. Nach tausend Zeichen ohne Grenze wird der Puffer am letzten Leerzeichen geschnitten und trotzdem geprüft.
Eine Leerzeile bleibt erhalten. Folgte ein Satz auf eine Leerzeile, wird er auch nach einer Leerzeile freigegeben, sodass eine Antwort aus zwei Absätzen zwei Absätze behält.
Das Warten auf das nächste Zeichen kostet meist ein Zeichen Verzögerung, etwas mehr, wenn auf den Punkt Marker folgen. Der letzte Satz wird geprüft, wenn der Stream endet. Für Vergleiche werden persische Ziffern und Trennzeichen (۳٫۱۴ entspricht 3.14) sowie arabische Buchstabenformen vereinheitlicht, arabische Kurzvokalzeichen entfernt, und der Zero-Width Non-Joiner zählt als Leerzeichen: dieselben Varianten, mit denen sich die hybride Suche für RAG auf Persisch auf der Retrieval-Seite befasst.
Die vier deterministischen Prüfungen: Quellenangaben, Werte und Perspektive
Gültige Quellennummer. Ein Marker wie
[S7]bei sechs bereitgestellten Quellen verweist auf eine erfundene Quelle.Quellenangabe erforderlich. Ein Satz mit acht oder mehr Wörtern oder mit einer Zahl, URL oder E-Mail-Adresse braucht einen Marker. Kurze Überleitungssätze kommen durch.
Wörtlicher Beleg. Jede Zahl, URL und E-Mail-Adresse muss in einer Quelle vorkommen, die der Satz zitiert. Der Abgleich achtet auf Zifferngrenzen, sodass eine Jahreszahl in einer Quelle keinen Prozentwert belegen kann, der auf dieselben Ziffern endet, und Dezimalzahlen, die sich nur in Nullen am Ende unterscheiden, sind ein Wert:
20.00ist20. Ein Komma, das keine Tausender gruppiert, ist ein Dezimalkomma:2,5ist2.5, nicht25.Perspektive. Ein Wort in der ersten Person stoppt den Stream. Die englische Liste ist kurz und fest: Pronomen der ersten Person und ihre Kurzformen. Die persische Liste ist kuratiert: Pronomen plus einige besonders riskante Verbformen, etwa die persischen Entsprechungen von „ich bin“ und „ich habe“. Das Persische markiert die Person in Verbendungen, die auch gewöhnliche Substantive haben können; eine Regel, die weit genug gefasst wäre, um jede dieser Endungen zu erfassen, würde auch Substantive treffen. Die Listen für die übrigen Sprachen der Website enthalten Personal- und Possessivpronomen, und die Listen für Spanisch, Portugiesisch, Russisch, Arabisch und Hindi zusätzlich einige Verben, weil diese Sprachen „ich“ meist allein mit dem Verb ausdrücken; im Chinesischen wird nach 我 an beliebiger Stelle im Satz gesucht, außer in einem Kompositum mit 自我, „selbst-“, wie 自我修正 („Selbstkorrektur“). Welche Listen gelten, hängt von der Sprache ab, in der die Antwort angefordert wurde. Englisch, Persisch, Arabisch, Russisch, Hindi und Chinesisch gelten für jede Antwort. Englisch ist die Sprache der Anweisungen an das Modell und damit die Sprache, in die eine Antwort am ehesten abgleitet; die anderen fünf stehen in Schriften, in denen kein lateinischer Name und kein Akronym eines ihrer Wörter ergeben kann. Die Ausnahme ist eine arabische Antwort: Sie wird nicht auf das persische „ich“ und „wir“ geprüft, denn dieselben Wörter bedeuten im Arabischen „von“ und „was“. Die deutsche, französische, spanische und portugiesische Liste gelten nur für Antworten, die in der jeweiligen Sprache angefordert wurden: Ihre kurzen Wörter sind auch Namen und gewöhnliche Wörter anderer Sprachen, etwa das „Ma“ in „Ma et al.“. Akronyme in Großbuchstaben, ein Nachname vor „et al.“ und die römische Ziffer in „Anhang I“ werden beiseitegelassen, bevor eine Liste gelesen wird.
Was passiert, wenn ein Satz durchfällt
Jeder Satz, der vor dem Fehlschlag bestanden hat, wird freigegeben, auch frühere Sätze aus demselben Batch.
Das Gate stoppt und bleibt gestoppt: Ein unbelegter Satz entwertet den Rest.
Ein
guard-Event überträgt die Gründe für den Verstoß und einen festen, lokalisierten Text, der besagt, dass der Rest zurückgehalten wurde; das abschließendedone-Event vermerkt den Stopp. Der blockierte Satz wird nie gesendet; der Server protokolliert nur seine Länge.Der Stream des Anbieters wird geschlossen; auf diesem Weg bricht das Design die Upstream-Anfrage ab. Was bis zum Abbruch generiert wurde, steht trotzdem auf der Rechnung und wird deshalb auf die monatliche Kostenobergrenze angerechnet. Beide Anbieterprotokolle melden den Output-Verbrauch in einem abschließenden Event, das ein vorzeitig abgebrochener Stream nie erreicht; der Dienst behält deshalb den empfangenen Text und füllt auf, was der Anbieter nicht gemeldet hat, mit einem Token pro UTF-8-Byte, einer Zählung, die im Zweifel zu hoch ausfällt. Tokens, die ein Reasoning-Modell fürs Nachdenken verbraucht, kommen nie als Text an und fehlen deshalb in dieser Schätzung. Die Obergrenze behandelt der Artikel über die fünf Schichten.
Das Chatfenster kennzeichnet die Antwort als zurückgehalten und behält ihren freigegebenen Text im Gesprächsverlauf, denn dieser Text ist bis zum Stopp belegt.
Abschneiden ist ein anderer Fehler, und eine frühere Version dieses Assistenten hat die beiden verwechselt. Wenn dem Modell der Platz für die Ausgabe ausging, bewertete das Gate das abgeschnittene Fragment als fertigen Satz und hielt es als Aussage ohne Quellenangabe zurück. Der Leser erfuhr so, dass sich eine Antwort, die lediglich unterbrochen worden war, nicht mit den Quellen belegen lasse. Kam gar kein Text zurück, verschwand die Antwort ohne jede Meldung. Heute wird das unfertige Fragment ungeprüft verworfen, und ein Hinweis zeigt an, dass die Antwort abgeschnitten wurde. Ein unterbrochener Satz ist kein unbelegter Satz. Tritt beides ein, hat der Stopp durch das Gate Vorrang.
Quellenmarker bleiben intern
Der Prompt verlangt für jeden Satz, der eine Tatsache nennt, einen [Sn]-Marker, und das Gate setzt die Regel für Quellenangaben bei jedem inhaltlichen Satz durch. Hat ein Satz bestanden, werden seine Marker entfernt und die Abstände korrigiert, auch vor persischen Satzzeichen; Leser sehen also Fließtext, keine Marker. Würde man die Marker stattdessen aus dem Prompt streichen, scheiterte jeder inhaltliche Satz an der Regel für Quellenangaben.
Wenn das Modell nichts zu sagen hat
Decken die Quellen die Frage nicht ab, verlangt der Prompt vom Modell ein festes Token statt eines Satzes. Eine vom Modell formulierte Ablehnung wäre eine Aussage wie jede andere, und zwar eine ohne Quellenangabe: Das Gate würde sie mit einem Hinweis stoppen, der den Quellen die Schuld an einer Frage gibt, die sie nie beantworten sollten.
Das Gate gibt das Token nie frei. Solange der bisherige Text noch zu diesem Token werden kann, hält es ihn zurück; das erste Zeichen, das das Token ausschließt, schickt den Text auf den üblichen Weg, sodass eine gewöhnliche Antwort auf nichts zusätzlich wartet. Beginnt eine Antwort mit dem Token, ersetzt der Dienst sie durch die feste Ablehnung der Website, denselben Text, den jede andere Ablehnung zeigt. Folgt das Token auf bereits freigegebene Sätze, bleiben diese stehen, und die Antwort endet dort.
„Belegt“ prüfen heißt nicht „wahr“ prüfen
In einer von Stanford geleiteten Studie haben Magesh und Kollegen nach eigener Beschreibung die erste präregistrierte Evaluation KI-gestützter Werkzeuge für die juristische Recherche durchgeführt. Bei den KI-Recherchewerkzeugen von LexisNexis und Thomson Reuters lag der Anteil halluzinierter Antworten jeweils zwischen 17 und 33 Prozent. Als halluziniert galt eine Antwort, wenn sie „entweder falsch oder falsch belegt“ war, und falsch belegt (misgrounded) bedeutet, dass „zentrale Tatsachenbehauptungen zwar zitiert werden, aber die Quelle falsch auslegen oder auf eine nicht einschlägige Quelle verweisen“. Falsch belegte Antworten enthalten Quellenangaben. Was das für juristische KI-Produkte bedeutet, behandelt ein eigener Artikel.
Ein Gate, das Zeichenketten prüft, erkennt falsche Belege nicht, und dieses hat bekannte blinde Flecken:
Ein Satz, der zwei Quellen zitiert, kann einen Wert aus jeder der beiden nehmen. Geprüft wird pro Satz, nicht pro Satzteil.
Ein Dezimalpunkt zählt als Grenze.
14.52in einer Quelle kann52%oder14belegen, und ein Tausendertrennzeichen wirkt genauso:1,200belegt200.Wörter werden nicht geprüft. Ein Satz ohne Zahlen, der eine Rolle oder eine Organisation vertauscht, kommt mit jedem Marker im gültigen Bereich durch.
Kurze Sätze ohne Quellenangabe kommen durch. Ein Satz mit weniger als acht Wörtern und ohne Zahl, URL oder Adresse braucht keinen Marker. Das Chinesische setzt keine Leerzeichen zwischen Wörter, deshalb werden dort die Schriftzeichen gezählt, drei auf zwei Wörter: Ein Satz nur aus chinesischen Schriftzeichen braucht unter zwölf keinen Marker.
Feste Wortlisten haben Lücken. Im Englischen kommen
we'dundlet'sdurch, ebenso ein Pronomen ganz in Großbuchstaben wieWEoderUS, das als Akronym gelesen wird. Im Persischen stützt sich die Abdeckung der ersten Person auf den Prompt: Ein Verb in der ersten Person, das nicht auf der kuratierten Liste steht, kommt durch. Auch die Listen der übrigen Sprachen sind kuratiert, und die erste Person auf Deutsch, Französisch, Spanisch oder Portugiesisch in einer Antwort, die in einer anderen Sprache angefordert wurde, bleibt dem Prompt überlassen.Schließende Satzzeichen können Sätze verschmelzen. Nach einem Anführungszeichen, das auf ein Satzendezeichen folgt, wie in
?»oder.", beendet nur ein Großbuchstabe oder ein Zeilenumbruch den Satz, nach einer Klammer wie in.)nur ein Zeilenumbruch oder ein Quellenmarker. Deshalb können zwei Sätze als einer bewertet werden: nach.)vor einem Großbuchstaben, nach؟»im Arabischen, Persischen und Hindi, die keine Großbuchstaben kennen, und nach den deutschen Anführungszeichen„…?“und»…?«, die der Splitter gar nicht als schließend liest. Für die chinesischen Zeichen gilt ihre eigene Regel von oben.
Der Fehler tritt auch in umgekehrter Richtung auf. Eine Abkürzung, die nicht auf der Liste steht, beendet vor einem Großbuchstaben einen „Satz“ zu früh, wie Mr. vor einem Namen, und ebenso eine zitierte Frage, auf die ein Name oder überhaupt ein Substantiv folgt; hat dieses Fragment acht oder mehr Wörter und keinen Marker, wird eine belegte Antwort zurückgehalten. Eine Liste, die von einer Zeile mit Doppelpunkt am Ende eingeleitet wird, hat vor ihrer ersten Listennummer keine Grenze. Der Splitter schneidet direkt nach 1., und dieses Fragment scheitert an der Regel für Quellenangaben. In beiden Fällen versagt das Gate zur sicheren Seite hin (fail closed): Es hält eine belegte Antwort zurück, statt eine ungeprüfte freizugeben.
Auch der Korpus verändert sich. Veröffentlichte Artikel werden für den Assistenten indexiert; ihre Zahlen, auch die 17 und 33 Prozent aus diesem Artikel, werden so zu Werten, die das Gate als belegt durchlässt. Belegt heißt „irgendwo in dem, was der Satz zitiert“, nicht „zu diesem Thema“.
Das Gate ist die letzte Prüfung vor der Freigabe, nicht die einzige. Joren und Kollegen stellten fest, dass größere Modelle bei unzureichendem Kontext „oft falsche Antworten ausgeben, statt sich zu enthalten“, und Google Research hält fest, dass RAG die Gesamtleistung zwar im Allgemeinen verbessert, aber „paradoxerweise die Fähigkeit des Modells verringert, sich einer Antwort zu enthalten, wenn das angebracht wäre“. Deshalb kommen Ablehnung und eine Score-Schwelle zuerst, wie es das Design in fünf Schichten darlegt. Frosts Preprint benennt die Grenze seines eigenen Ergebnisses mit Worten, die auch auf dieses Gate passen: „Richtlinienkonformität für eine deterministische letzte Absicherung, kein Beleg für semantische Sicherheit oder die Vollständigkeit der Richtlinie“.
SSE für LLM-Streaming: fetch, Proxys und Abbruch
Ein Satz-Gate nützt nur, wenn jeder freigegebene Satz den Leser sofort erreicht. Darüber entscheiden vier Details beim Transport.
Warum fetch und ReadableStream statt EventSource
Die WHATWG-Spezifikation gibt dem Konstruktor von EventSource eine URL und eine einzige Option mit, withCredentials: keine Methode und keinen Body. Eine Frage samt Locale und Verlauf ist ein Body; der Endpunkt ist deshalb ein POST, der Server-Sent Events zurückgibt. Der Browser liest ihn mit fetch, einem ReadableStream-Reader und einem AbortController, dessen abort() laut MDN „fetch-Anfragen, das Konsumieren beliebiger Response-Bodys und Streams abbrechen kann“.
Ein selbst geschriebener Parser braucht diese Regeln:
Über Lesevorgänge hinweg puffern. Ein Chunk ist keine Nachricht.
Bei einer Leerzeile das Event auslösen. Bis eine kommt, ist das Event unvollständig.
Kommentarzeilen ignorieren, also Zeilen, die mit
:beginnen.Mehrzeilige Daten zusammenfügen: Alle
data:-Zeilen eines Events werden mit einem Line Feed verbunden.CRLF, LF und CR als Zeilenenden akzeptieren.
Ein Leerzeichen nach dem Doppelpunkt entfernen, und nur eines.
Dekodieren Sie mit TextDecoder und {stream: true}, der Einstellung, die MDN für den Fall nennt, dass man „die Daten in Chunks verarbeitet“. Sonst wird ein persisches Zeichen, das auf zwei Chunks verteilt ist, als Ersatzzeichen dekodiert.
Proxy-Buffering: nginx und Caddy
nginx puffert Antworten, die es als Proxy weiterleitet, standardmäßig, und ein Response-Header X-Accel-Buffering kann das abschalten. Der Stream des Assistenten sendet X-Accel-Buffering: no und Cache-Control: no-cache, no-store, no-transform; no-transform steht dort, weil ein Proxy, der den Body neu komprimiert, Chunks zusammenfassen kann. Caddy reicht text/event-stream-Antworten sofort an den Client durch (Flush), und die Caddy-Konfiguration der Website setzt auf der Chat-Route zusätzlich flush_interval -1. Der Service Worker registriert keine Route für den Stream, denn eine Caching-Strategie würde die Antwort bis zu ihrem Ende festhalten.
Keep-alives und Fehler in einem offenen Stream
Die SSE-Spezifikation schlägt eine Kommentarzeile „etwa alle 15 Sekunden“ vor, als Schutz vor älteren Proxys, die Verbindungen nach einem kurzen Timeout trennen, und der Server sendet in diesem Intervall einen Ping. Wenn das erste Byte den Server verlässt, steht der Status schon auf 200; Fehler werden deshalb als error-Event innerhalb des Streams übertragen.
Ein Abbruch, der auch die Kosten stoppt
Ein Stopp im Browser bricht die fetch-Anfrage ab. Der Server prüft bei jedem eintreffenden Token, ob die Verbindung getrennt wurde. Stellt er eine Trennung fest, verlässt er die Schleife und schließt den Stream des Anbieters, was in diesem Design die Upstream-Anfrage abbricht. Zwei Dinge verdienen einen Test statt einer Annahme. Erstens, ob jeder Proxy und jedes CDN zwischen dem Browser und Ihrer API einen Verbindungsabbruch des Clients weiterreicht. Testen Sie diesen Pfad; verlassen Sie sich nicht auf die Konfiguration. Zweitens, wann Ihr Anbieter den Verbrauch meldet: Kommt der Output-Verbrauch erst mit dem letzten Chunk, erreicht er einen vorzeitig geschlossenen Stream nie, und eine Kostenobergrenze, die gemeldeten Verbrauch zählt, erfasst weniger, als generiert wurde.
So testen Sie einen Streaming-Guardrail
Die Tests decken das Gate auf drei Ebenen ab:
Unit-Tests für das Gate mit festen Quellen. Erfundene Werte, Marker außerhalb des gültigen Bereichs, Aussagen ohne Quellenangabe und Wörter in der ersten Person (in jeder Sprache der Website) stoppen den Stream; belegte Werte und kurze Überleitungssätze kommen durch; frühere gute Sätze überstehen einen späteren schlechten.
Tests für den Splitter. Dezimalzahlen und Domains sind keine Grenzen, nachgestellte Marker bleiben bei ihrem Satz, und
؟beendet einen Satz. Chinesischer Text wird ohne Leerzeichen in Sätze geteilt. Übernehmenswert ist vor allem dieser Test: Text, der in Stücken von drei Zeichen eingespeist wird, muss genauso aufgeteilt werden wie der ganze Text.Stream-Tests mit einem geskripteten Anbieter, der eine Antwort in kleinen Stücken abspielt: Ein erfundener Wert geht nie über die Leitung, ein Stopp durch das Gate oder ein Verbindungsabbruch schließt den Upstream-Stream, und Abschneiden wird als Abschneiden gemeldet.
Eine Skizze der beiden zentralen Tests, eigens für diesen Artikel geschrieben und nicht von der Website übernommen:
# Sketch only: SentenceSplitter and SentenceGate stand for your own classes.
import pytest
ANSWER = "The reading room opens early [S1]. Is the café open? Yes, on weekdays [S2]."
def pieces(text, size):
return [text[i:i + size] for i in range(0, len(text), size)]
def split_streamed(text, size):
splitter = SentenceSplitter()
sentences = [s for piece in pieces(text, size) for s in splitter.feed(piece)]
tail = splitter.flush()
return sentences + ([tail] if tail else [])
@pytest.mark.parametrize("size", [1, 2, 3, 7])
def test_piece_size_does_not_change_sentences(size):
assert split_streamed(ANSWER, size) == split_streamed(ANSWER, len(ANSWER))
def test_unsupported_value_never_reaches_the_reader():
gate = SentenceGate(sources=["The library opens at nine."])
released = ""
for piece in pieces("It opens at nine [S1]. Late fees ended in 2019 [S1]. More", 7):
outcome = gate.feed(piece)
released += outcome.text
if outcome.stopped:
break
assert "2019" not in released and gate.stoppedNichts davon ist Red-Teaming gegen ein laufendes Modell. Gemessen habe ich die Fehler des Gates selbst: in der Produktion aufgezeichnete Antworten, offline erneut durchgespielt, um zu finden, wo es eine belegte Antwort gestoppt hat. Das zählt Fehlstopps für ein Modell und einen Satz von Fragen, nicht, wie oft ein echtes Modell etwas erfindet, deshalb veröffentliche ich es nicht als Rate.
Checkliste: Guardrails für gestreamte Antworten in Produktion bringen
Legen Sie fest, was nie zu sehen sein darf. Davon hängt ab, ob Sie vorher prüfen oder zuerst freigeben, ganz gleich, was Ihr Framework als Standard setzt.
Wählen Sie die Einheit und benennen Sie die Wartezeit: ein fester Chunk oder ein Satz.
Setzen Sie deterministische Prüfungen ein, wo Zeichenketten genügen, und vereinheitlichen Sie vorher Ziffern und Buchstabenformen. Eine modellbasierte Rail addiert ihre Ausführungszeit zu jeder Einheit.
Werten Sie eine Grenze erst als sicher, wenn das nächste Zeichen eintrifft, und begrenzen Sie den Puffer.
Bei einem Verstoß geben Sie frei, was bestanden hat, stoppen, zeigen einen festen Text, geben den blockierten Text nie wieder aus und schließen den Upstream-Stream.
Trennen Sie Abschneiden von Verstößen und zählen Sie, was generiert wurde, nicht, was angezeigt wurde.
Transport: POST mit
fetch, einem Stream-Reader undAbortController; ein Streaming-Decoder; Pings als Kommentarzeilen; Fehler als Events.Proxys:
X-Accel-Buffering: no,no-transform, sofortiges Flushing von Event-Streams, ein getesteter Pfad für Verbindungsabbrüche und kein Service Worker auf dem Stream.Tests: Spielen Sie Antworten in kleinen Stücken ab und stellen Sie per Assertion sicher, dass ein blockierter Wert nie über die Leitung geht.
Wenn Sie einen quellengestützten Assistenten entwerfen und sich eine zweite Meinung zu seinem Freigabepfad wünschen, beginnt dieses Gespräch auf der Seite Projektanalyse.
Häufige Fragen
Lässt sich gestreamter LLM-Output validieren, ohne auf Streaming zu verzichten?
Ja. Puffern Sie bis zu einer prüfbaren Einheit und geben Sie jede Einheit frei, sobald sie bestanden hat. Der Leser sieht den Text dann Satz für Satz oder Chunk für Chunk ankommen statt Token für Token.
Verzögert das Puffern bis zum Satzende die Time to First Token?
Es verzögert die sichtbare Ausgabe um einen Satz: Text erscheint, sobald der erste Satz vollständig und geprüft ist. Prüfungen auf Zeichenketten sind im Vergleich zur Generierung günstig, aber ich habe den Unterschied auf dieser Website nicht gemessen und nenne deshalb keine Zahl.
Sollte man stream_first in NeMo Guardrails abschalten?
Schalten Sie es ab, wenn ein blockierter Chunk nie zu sehen sein darf. NeMo dokumentiert den Preis: Die Time to First Token wächst um die Ausführungszeit der Rail pro Chunk. Behalten Sie den Standard nur, wenn Ihr Produkt verkraften kann, dass ein Leser Tokens sieht, die eine Rail später blockiert.
Warum nicht EventSource für LLM-Streaming verwenden?
EventSource nimmt eine URL und ein Flag für Credentials entgegen und kann daher keinen Request-Body senden. Eine Chat-Anfrage mit Verlauf ist ein POST; verwenden Sie also fetch mit einem Stream-Reader, womit Sie über AbortController zugleich abbrechen können.
Verhindert eine Quellenprüfung Halluzinationen?
Nein. Sie stoppt erfundene Quellennummern, inhaltliche Sätze ohne Quellenangabe und Werte, die in den Quellen fehlen, die ein Satz zitiert. Ob eine zitierte Quelle die Aussage stützt, kann sie nicht erkennen; genau das ist der Fehler „falsch belegt“. Behandeln Sie sie als letzte Absicherung hinter der Ablehnung von Fragen und einer Retrieval-Schwelle.