Zurück zu allen Beiträgen
·5 Min. Lesezeit

LLM-Ausgabe in Code überführen: Schema, Validierung und Reparaturschleife

Sobald Sie ein Sprachmodell an Ihre Anwendung anbinden, stellt sich die erste echte Frage: Wie gelangt die Antwort des Modells in den Code? Freien Text auf dem Bildschirm anzuzeigen ist einfach. Soll die Antwort aber ein Klassifikationsergebnis, ein Bestellentwurf oder ein Befehl für einen anderen Dienst sein, müssen Sie sie auswerten, und genau dort wird es schnell fragil.

In diesem Beitrag beschreibe ich, wie ich bei strukturierter Ausgabe vorgehe: das Schema als Vertrag entwerfen, die Ausgabe auf dem Server immer validieren, bei Fehlern eine begrenzte Zahl von Reparaturen versuchen und, wenn das nicht hilft, auf einen sicheren Pfad ausweichen. Die Beispiele sind in C#, die Idee gilt aber in jeder Sprache.

Modellausgabe ist nicht vertrauenswürdige Eingabe

Die wichtigste gedankliche Umstellung: Das JSON, das ein Modell zurückgibt, ist so wenig vertrauenswürdig wie Formulardaten eines Nutzers. Viele Anbieter unterstützen schemagebundene Ausgabe, was die Fehlerquote spürbar senkt. Sich darauf zu verlassen und die Validierung wegzulassen, wäre trotzdem ein Fehler. Schema-Unterstützung garantiert keine inhaltliche Korrektheit; ein Modell kann gültiges JSON mit einem falschen Wert erzeugen. Auch Verbindungsabbrüche, abgeschnittene Antworten oder ein Anbieterwechsel können das Format zerstören.

Deshalb denke ich in zwei Schichten: Auf Modellseite die Ausgabe zum Schema lenken, auf Anwendungsseite alles so validieren, als käme es von außen.

Das Schema als Vertrag entwerfen

Ein gutes Schema erleichtert dem Modell die Arbeit. Einige praktische Regeln:

  • Halten Sie die Zahl der Felder klein. Jedes zusätzliche Feld ist eine weitere Fehlerquelle.
  • Verwenden Sie geschlossene Mengen (Enums) statt freiem Text, etwa low, medium, high für die Priorität.
  • Wählen Sie aussagekräftige Feldnamen und Beschreibungen; auch die Feldnamen steuern das Modell.
  • Lassen Sie einen expliziten Ausweg für „Ich weiß es nicht": einen leeren Wert oder eine Option wie unknown. Sonst wird das Modell zum Erfinden gedrängt.

Als Beispiel ein Datensatz, der ein Support-Ticket klassifiziert:

public enum Priority { Low, Medium, High }

public sealed record TicketTriage(
    string Category,
    Priority Priority,
    bool NeedsHuman,
    string? Reason);

Das Feld NeedsHuman ist ein kleines, aber wertvolles Sicherheitsventil: Ist das Modell unsicher, kann es das ausdrücklich sagen, und Sie stoppen den automatischen Ablauf.

Validierung auf dem Server

Parsen und Validieren sind getrennte Schritte. Stellen Sie zuerst sicher, dass das JSON wohlgeformt ist, und prüfen Sie dann, ob die Werte Ihre Geschäftsregeln einhalten:

public static bool TryParse(string raw, out TicketTriage? result, out string error)
{
    result = null;
    error = "";

    try
    {
        result = JsonSerializer.Deserialize<TicketTriage>(raw, Options);
    }
    catch (JsonException ex)
    {
        error = $"Ungültiges JSON: {ex.Message}";
        return false;
    }

    if (result is null) { error = "Leere Antwort"; return false; }

    if (!Allowed.Contains(result.Category))
    {
        error = $"Unbekannte Kategorie: {result.Category}";
        return false;
    }

    return true;
}

Zwei Details sind wichtig. Erstens prüfen Sie Felder wie die Kategorie gegen Ihre eigene Liste erlaubter Werte, selbst wenn das Schema das schon vorgibt. Zweitens ist die Fehlermeldung für Menschen lesbar und zugleich für das Modell nützlich: Im nächsten Schritt geben wir sie an das Modell zurück.

Denken Sie daran, JsonStringEnumConverter zu verwenden und bei unbekannten Werten einen Fehler auszulösen; ein stilles Zurückfallen auf einen numerischen Standardwert gehört zu den am schwersten bemerkbaren Fehlern.

Eine begrenzte Reparaturschleife

Schlägt die Validierung fehl, ist der erste Reflex, dieselbe Anfrage zu wiederholen. Ein oder zwei Wiederholungen sind vertretbar, doch den Fehler dem Modell mitzuteilen ist wirksamer als blindes Wiederholen: Fügen Sie die vorherige Antwort und den Validierungsfehler hinzu und bitten Sie um korrigierte Ausgabe.

for (var attempt = 0; attempt < MaxAttempts; attempt++)
{
    var raw = await model.CompleteAsync(messages, ct);

    if (TryParse(raw, out var ok, out var error))
        return ok;

    messages.Add(Assistant(raw));
    messages.Add(User($"Die Ausgabe ist nicht valide: {error}. Gib nur JSON zurück, das dem Schema entspricht."));
}

return null; // weiter zum Fallback-Pfad

Eine Obergrenze ist Pflicht. Eine unbegrenzte Schleife lässt Kosten und Latenz außer Kontrolle geraten. Ich begnüge mich meist mit insgesamt zwei bis drei Versuchen; eine Eingabe, die nach dem dritten Versuch nicht korrekt wird, ist meist mehrdeutig oder liegt außerhalb des Modellbereichs. Protokollieren Sie jeden Versuch samt Grund; welche Felder am häufigsten brechen, ist der beste Hinweis zur Verbesserung des Schemas.

Fallback: Auch ein Scheitern ist ein Ergebnis

Sind auch die Reparaturversuche aufgebraucht, muss vorab entworfen sein, was das System tut. Die Optionen hängen vom Kontext ab:

  • Die Aufgabe an eine Warteschlange für Menschen übergeben.
  • Auf ein einfacheres, regelbasiertes Verfahren zurückfallen.
  • Dem Nutzer klar mitteilen, dass „dies derzeit nicht automatisch verarbeitet werden konnte".

Welche Sie auch wählen, die Regel lautet: Nie unvalidierte Ausgabe weiterlaufen lassen. Ein falscher, aber formal gültiger Befehl, der an einen anderen Dienst geht, ist weit teurer als gar kein Befehl. Setzen Sie außerdem vor jede Aktion mit Nebenwirkungen eine eigene Berechtigungs- und Grenzwertprüfung; dass das Modell „mach es" sagt, heißt nicht, dass es erlaubt ist.

Tests und Überwachung

Testen Sie die Pipeline für strukturierte Ausgabe wie jeden anderen Code. Schreiben Sie gewöhnliche Unit-Tests für Parser und Validator: fehlendes Feld, zusätzliches Feld, falscher Typ, unbekannter Enum-Wert, abgeschnittenes JSON. Diese Tests brauchen keinen echten Modellaufruf, sind schnell und fangen Regressionen ab.

Für das Modellverhalten halten Sie eine kleine Evaluierungsmenge bereit: echte Beispieleingaben und erwartete Ergebnisse. Wenn Sie Prompt oder Modell ändern, führen Sie diese Menge aus und prüfen, dass Validierungserfolgsquote und Genauigkeit nicht gesunken sind. In Produktion messen Sie außerdem den Anteil der Antworten, die beim ersten Versuch bestehen, die Zahl der Reparaturen und die Häufigkeit des Fallbacks. Diese drei Zahlen fassen die Gesundheit der Pipeline zusammen.

Fazit

Strukturierte Ausgabe von einem Modell zu erhalten heißt nicht „JSON anfordern und hoffen", sondern eine kleine Datenverarbeitungspipeline zu bauen. Halten Sie das Schema schlicht und geschlossen, validieren Sie jede Antwort auf dem Server, reparieren Sie begrenzt, indem Sie den Fehler zurückmelden, und weichen Sie bei Misserfolg auf einen sicheren Pfad aus. Diese vier Schritte halten Ihre Anwendung vorhersagbar, egal wie sehr sich das Modell ändert.