7. Oktober 2026
Drei Ausgänge statt zwei
Ein LLM Klassifikator wird oft zu simpel modelliert: Entweder liefert er ein Urteil, oder der Aufruf ist fehlgeschlagen. Für Inhalts-Prüfung und Moderation reicht dieses Modell nicht.
Ich trenne drei Ergebnisse:
- Urteil: Das Modell hat eine verwertbare Klassifikation geliefert.
- Technischer Fehler: Timeout, Netzwerkproblem, ungültige Anfrage oder eine tatsächlich nicht auswertbare Antwort.
- Inhaltliche Verweigerung: Das Modell antwortet absichtlich nicht auf die angeforderte Prüfung.
Eine LLM Verweigerung erkennen zu können, ist keine kosmetische Verbesserung der Fehlerbehandlung. Es entscheidet darüber, ob eine Pipeline das Ergebnis wiederholt, verwirft, als unbekannt markiert oder an einen definierten Folgeprozess gibt.
Wer eine Verweigerung als technischen Fehler behandelt, erzeugt leicht falsche Folgeverarbeitung: etwa einen unnötigen Retry, einen Parserfehler oder ein vermeintlich negatives Moderationsurteil. Keine dieser Reaktionen beschreibt, was tatsächlich passiert ist.
Vier Formen über einen Router
Über einen Router wie OpenRouter kann dieselbe fachliche Situation unterschiedlich bei der Anwendung ankommen. Entscheidend ist daher nicht allein der HTTP-Status.
-
HTTP 400 oder 403 mit generischer Meldung
Die sichtbare Fehlermeldung kann generisch sein. Die eigentliche Verweigerung steckt dabei als JSON-String in Metadaten. Wer nur die oberste Fehlermeldung protokolliert oder auswertet, verliert das relevante Signal.
-
HTTP 200 mit
finish_reasonstopund leerem InhaltTransport und Request gelten hier als erfolgreich. Trotzdem liegt kein Urteil vor. Ein Schema-Parser sieht lediglich leeren Content und meldet womöglich einen Formatfehler. Fachlich kann der leere Inhalt aber eine Verweigerung ausdrücken.
-
HTTP 200 mit
finish_reasonerrorAuch ein erfolgreicher HTTP-Status garantiert keine verwertbare Modellantwort. Für die Auswertung von OpenRouter
finish_reasonmuss daher klar sein: HTTP-Erfolg und fachlicher Erfolg sind zwei verschiedene Ebenen. -
HTTP 400 mit anbieterspezifischem Marker
Manche Antworten enthalten einen Marker, der die Verweigerung direkt kennzeichnet. Dieser Marker gehört in die Erkennungslogik, nicht in einen pauschalen Block für technische Fehler.
Die vier Varianten sind unterschiedlich verpackt, aber sie können dieselbe Konsequenz haben: Der LLM Klassifikator hat kein Urteil geliefert, weil eine inhaltliche Verweigerung vorliegt.
Der Erkenner gehört vor den Schema-Parser
Der häufigste Architekturfehler ist die Reihenfolge: Erst wird die Antwort gegen ein erwartetes Schema geparst, danach wird versucht, den Fehler zu interpretieren. Damit ist wertvolle Information oft schon reduziert worden.
Mein Rat: Ein Verweigerungs-Erkenner muss vor dem Schema-Parser liegen. Er erhält die vollständige Rohantwort und klassifiziert zuerst den Transport- und Anbieterzustand. Nur Antworten, die weder technischer Fehler noch LLM Refusal sind, gehen in die fachliche Schema-Prüfung.
Ein vereinfachtes Muster kann so aussehen:
type Outcome = "verdict" | "technical_error" | "refusal";
function classifyResponse(response: RawResponse): Outcome {
if (hasProviderRefusalMarker(response)) {
return "refusal";
}
if (hasRefusalInMetadataJson(response)) {
return "refusal";
}
if (response.httpStatus === 200 && response.finishReason === "stop" && response.content === "") {
return "refusal";
}
if (response.httpStatus === 200 && response.finishReason === "error") {
return "refusal";
}
if (isTransportFailure(response)) {
return "technical_error";
}
return "verdict";
}Das Beispiel ist absichtlich schematisch. Welche Metadaten und Marker geprüft werden müssen, hängt von der tatsächlich empfangenen Router-Antwort ab. Wichtig ist die Reihenfolge, nicht ein einzelnes Feld.
Rohantworten durchreichen
Damit die Erkennung funktioniert, muss jede Kapselungsschicht die Rohinformation erhalten. Ein HTTP-Client darf sie nicht auf einen allgemeinen Fehlertext verkürzen. Ein Router-Adapter darf Metadaten nicht verwerfen. Und ein Parser darf nicht die einzige Instanz sein, die eine Antwort sieht.
Praktisch heißt das: Status, Header soweit verfügbar, Response-Body, Metadaten, finish_reason und Content sollten bis zur Entscheidungsstelle erhalten bleiben. Erst dort wird aus der Transportantwort ein fachlicher Zustand.
Auch das Hosting ist Teil des Verhaltens
Ob ein Modell verweigert, kann vom Host abhängen. Dasselbe Modell verweigerte bei einem Anbieter und antwortete bei einem anderen vollständig. Für die Architektur folgt daraus: Die Modellkennung allein beschreibt das Laufzeitverhalten nicht ausreichend. Router, Host und Antwortformat gehören zur Beobachtung und zur Auswertung dazu.
Eine robuste Pipeline protokolliert deshalb die gewählte Route zusammen mit dem normalisierten Ergebnis. Nicht um Verweigerungen zu übergehen, sondern um ihre Ursache und ihre Verteilung korrekt einzuordnen.
Wie unterscheidet eure Pipeline heute einen Timeout von einer Verweigerung?