12k
All articles

JSON Schema für strukturierte LLM-Ausgaben

JSON Schema Structured Outputs erklärt: wie OpenAI, Gemini und Claude gültige Schemata erzwingen, plus Setup-Tipps und Fallstricke.

OpenReplay Team
OpenReplay Team
JSON Schema für strukturierte LLM-Ausgaben

Strukturierte Ausgaben (Structured Outputs) beschränken die Antwort eines LLM auf ein von dir bereitgestelltes JSON Schema. So gibt das Modell maschinenlesbares JSON zurück, das deinen Feldern, Typen und Enums entspricht – statt Freitext, den du anschließend mühsam von Hand parsen musst.

Wer schon einmal ein LLM-Feature ausgeliefert hat, kennt die Alternative: eine Regex, um verirrte Markdown-Fences zu entfernen, ein try/catch um JSON.parse und eine Retry-Schleife, die häufiger anspringt als gewünscht. Das funktioniert – bis es an einem Morgen stillschweigend nicht mehr funktioniert.

Der Anbieter erzwingt das Schema während der Generierung, wodurch aus „hoffen wir, dass das Modell gültiges JSON liefert“ ein verbindlicher Vertrag wird. Das funktioniert heute bei OpenAI, Google Gemini und Anthropic Claude. Alle drei akzeptieren JSON Schema, sodass dasselbe Schema portabel ist und du lediglich die Request-Verdrahtung anpasst, nicht den Vertrag. Dieser Artikel behandelt, was strukturierte Ausgaben sind, wie JSON Schema sie steuert, wie die Erzwingung unter der Haube funktioniert, wie du sie pro Anbieter einrichtest und welche Fehlermodi Absicherung verdienen.

Die wichtigsten Erkenntnisse

  • JSON-Modus garantiert lediglich syntaktisch gültiges JSON; strikte strukturierte Ausgaben garantieren JSON, das deinem konkreten Schema entspricht: der Unterschied zwischen „es parst“ und „es enthält die Felder, die du brauchst“.
  • Die Erzwingung erfolgt über Constrained Decoding: Bei jedem Token darf das Modell nur Fortsetzungen erzeugen, die die Ausgabe gegenüber deinem Schema gültig halten. Konformität wird also während der Generierung erzwungen und nicht im Nachhinein geprüft.
  • OpenAI, Gemini und Claude sprechen alle JSON Schema, sodass ein Schema anbieterübergreifend portierbar ist; Pydantic und Zod kompilieren zu JSON Schema – das ist der Workflow, den die meisten Teams tatsächlich nutzen.
  • Selbst bei aktiviertem Strict-Modus gibt eine Verweigerung (Refusal) oder eine durch Längenbegrenzung abgeschnittene Antwort einen Erfolgsstatus zurück, ist aber kein schemakonformes JSON. Validiere das geparste Objekt daher, bevor du ihm vertraust.
  • Jeder Anbieter unterstützt nur eine Teilmenge von JSON Schema, sodass Keywords wie minimum, pattern oder tiefe Rekursion verworfen oder abgelehnt werden können. Prüfe dies anhand der Dokumentation zur unterstützten Teilmenge des jeweiligen Anbieters.

Von Freitext zu schema-erzwungenem JSON

Ohne Vorgaben erzeugen LLMs Freitext, der Parser zum Scheitern bringt: Sie fügen Prosa um das JSON herum ein, lassen Anführungszeichen weg oder erfinden Felder. Strukturierte Ausgaben lösen das, indem sie die Antwort auf ein JSON Schema beschränken, sodass die Ausgabe maschinenlesbar und zuverlässig parsebar ist.

Das ist das Upgrade gegenüber dem älteren JSON-Modus. OpenAI führte den JSON-Modus 2023 ein, um gültiges JSON zu erzwingen – allerdings garantiert er nur, dass die Ausgabe parsebar ist, nicht dass sie einem von dir definierten Schema folgt. Strikte strukturierte Ausgaben schließen diese Lücke, indem sie das Schema selbst erzwingen. In OpenAIs eigener Evaluierung zur Einhaltung komplexer Schemata erreichte das Modell gpt-4o-2024-08-06 mit Structured Outputs 100 %, verglichen mit unter 40 % beim älteren gpt-4-0613. Das ist ein modellspezifischer Benchmark und keine universelle Garantie, illustriert aber den Wandel von „parst meistens“ zu „entspricht dem Schema“.

Wie passt JSON Schema hier hinein?

Ein JSON Schema ist ein deklarativer Vertrag für deine Daten: Es deklariert Typen, Pflichtfelder, Enums und Wertebeschränkungen. Du übergibst es mit dem Request, und der Anbieter beschränkt die Generierung entsprechend. Ein kompaktes Schema für eine Kontaktextraktion sieht so aus:

{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "email": { "type": "string" },
    "plan_interest": {
      "type": "string",
      "enum": ["starter", "pro", "enterprise"]
    }
  },
  "required": ["name", "email", "plan_interest"],
  "additionalProperties": false
}

In der Produktion schreiben nur wenige Leute solche Schemata von Hand. Der übliche Workflow besteht darin, die Struktur in Pydantic (Python) oder Zod (TypeScript) zu definieren und das SDK das JSON Schema erzeugen zu lassen. Die SDKs von OpenAI unterstützen dies direkt: Übergib ihnen ein Pydantic- oder Zod-Objekt, und sie generieren das passende JSON Schema, wandeln die Antwort zurück in dein typisiertes Objekt und legen Verweigerungen offen. Gemini hat denselben Komfort nachgezogen und die JSON-Schema-Unterstützung auf jedes aktiv unterstützte Gemini-Modell ausgeweitet, sodass Pydantic- und Zod-Schemata ohne Konvertierungsschritt funktionieren.

Wenn du das Schema oder den anbieterspezifischen Wrapper nicht von Hand zusammensetzen willst, erledigt der JSON Schema Builder von OpenReplay beides direkt im Browser. Du fügst Felder mit Typen, Beschreibungen, Enums und Verschachtelung hinzu oder fügst ein Beispiel-JSON ein, um einen Ausgangspunkt abzuleiten, und kopierst das Ergebnis dann aus dem Export-Panel, das JSON Schema, OpenAI response_format, OpenAI Function Tool, Anthropic, Gemini, Zod und Pydantic anbietet. Nichts, was du eingibst, verlässt die Seite.

Wie funktioniert die Schema-Erzwingung?

Strukturierte Ausgaben funktionieren, weil der Anbieter das Decoding beschränkt: Bei jedem Generierungsschritt darf das Modell nur Tokens erzeugen, die die Ausgabe gegenüber deinem Schema gültig halten. Das Schema wird in eine Grammatik (oder einen endlichen Automaten) kompiliert, und Tokens, die es verletzen würden, werden vor dem Sampling maskiert. Anthropics eigene Dokumentation beschreibt denselben Mechanismus: Dein Schema wird in eine Grammatik kompiliert, und Constrained Sampling hält die Generierung innerhalb dieser Grammatik – das Modell hat also keine Möglichkeit, ein Token auszugeben, das das Schema brechen würde.

Dasselbe Prinzip lässt sich über JSON hinaus verallgemeinern. Für lokale und selbst gehostete Modelle nutzt llama.cpp GBNF-Grammatikdateien, und Outlines wendet Regex- und grammatikbasierte Beschränkungen an. Beide erzwingen beliebige Formate (SQL, eigene DSLs oder JSON) nach demselben Token-Masking-Prinzip.

Strukturierte Ausgaben bei den verschiedenen Anbietern

Alle drei großen Anbieter sprechen JSON Schema, sodass sich das Muster übertragen lässt. Unterschiede gibt es bei der Request-Verdrahtung und den Fehlersignalen.

AnbieterWo das Schema hingehörtSchema-DialektSignal für Verweigerung / Unvollständigkeit
OpenAIresponse_format (Chat Completions) oder text.format (Responses API), mit strict: trueJSON-Schema-TeilmengeFeld refusal; finish_reason: "length"
GeminiresponseFormat.text (mimeType + schema) in generationConfigJSON-Schema-Teilmenge (inkl. anyOf, $ref)abgeschnittener Candidate; Ablehnung zu komplexer Schemata
Claudeoutput_config.format oder strict: true im input_schema eines ToolsJSON-Schema-Teilmengestop_reason: "refusal" / "max_tokens"

OpenAI. Setze strict: true und übergib das Schema. OpenAIs Leitfaden empfiehlt, neue Projekte mit den aktuellen Modellen zu starten, und weist darauf hin, dass die Responses API den Parameter verschoben hat: Verwende response_format: { type: "json_schema", strict: true } bei Chat Completions oder text: { format: { type: "json_schema", strict: true } } bei der Responses API.

Gemini. Übergib das Schema über generationConfig:

response = client.models.generate_content(
    model="gemini-3.5-flash",
    contents="Extract the contact from this email...",
    config={
        "response_format": {
            "text": {
                "mime_type": "application/json",
                "schema": person_schema,
            }
        },
    },
)

Claude. Anthropic liefert nativ strukturierte Ausgaben – kein Workaround über Tool Calls mehr. Es gibt zwei komplementäre Features: JSON-Ausgaben über output_config.format für den Response Body und strikte Tool-Nutzung über strict: true für Tool-Inputs, unabhängig voneinander oder gemeinsam nutzbar. Strikte Tool-Nutzung garantiert, dass die Argumente eines Aufrufs zu seinem input_schema passen, weil dieses Schema in eine Grammatik kompiliert wird, die das Sampling beschränkt – dieselbe Technikfamilie, die OpenAI und Gemini verwenden. Beachte, dass sich die API-Oberfläche mit dem GA-Release geändert hat: Der Parameter output_format ist zu output_config.format gewandert, und Beta-Header sind nicht mehr erforderlich.

Fallstricke und Best Practices

Der Strict-Modus ist keine Garantie für parsebare Ausgaben, und die Schema-Unterstützung ist nicht universell. Sichere dich gegen Folgendes ab.

Halte Schemata flach. Tief verschachtelte oder rekursive Strukturen sind die häufigste Ursache für „schema too complex“-Fehler und verschlechterte Reasoning-Qualität. Claude macht das direkt sichtbar und gibt einen 400 zurück, wenn die kompilierte Grammatik zu groß wird; die Gemini-Dokumentation warnt, dass sehr große oder tief verschachtelte Schemata abgelehnt werden können. Lange Property-Namen, große Arrays, Enums mit vielen Werten und Objekte voller optionaler Properties erhöhen alle die Kosten. Zerlege große Extraktionen in kleinere, flachere Schemata.

Behandle Verweigerungen und Truncation. Selbst bei aktiviertem Strict-Modus gibt eine Verweigerung oder eine durch Längenbegrenzung abgeschnittene Antwort einen Erfolgsstatus zurück, ist aber kein schemakonformes JSON. OpenAI hat dafür ein eigenes Signal eingeführt: ein refusal-Feld in der Antwort zeigt dir, dass das Modell abgelehnt hat, anstatt etwas zurückzugeben, das deinem Schema entspricht. Es gibt kein universelles Output-Token-Limit. Die Obergrenzen variieren je Modell, und jede Antwort, die die Grenze mitten im Objekt erreicht, ergibt ungültiges JSON. Dimensioniere max_tokens also für den Worst Case und prüfe die dokumentierte Obergrenze deines Modells.

Prüfe die unterstützte Teilmenge. Jeder Anbieter unterstützt im Strict-Modus nur einen Teil von JSON Schema. OpenAIs Leitfaden sagt ausdrücklich, dass ein guter Teil der Spezifikation abgedeckt ist, einige Teile aber ausgelassen werden – entweder aus Performance-Gründen oder aus technischen Gründen. Keywords wie minimum, pattern oder Default-Werte können verworfen oder abgelehnt werden. Prüfe daher die Dokumentation zur unterstützten Teilmenge des Anbieters, statt von der vollständigen Spezifikation auszugehen.

Validiere trotzdem. Da Verweigerungen und Truncation Antworten mit Erfolgsstatus, aber ungültigem JSON erzeugen, solltest du das Objekt vor der Weiterverarbeitung gegen dein Schema parsen und validieren – auch mit strict: true.

Erst denken, dann ausgeben. Das Beschränken der Ausgabe kann bei manchen Aufgaben die Reasoning-Qualität verringern. Ein Praxisleitfaden zu Claudes strukturierten Ausgaben behandelt dies als echten Tradeoff gegenüber Extended Thinking: Wenn eine Aufgabe mehr vom Reasoning des Modells profitiert als von garantierter Schema-Konformität, lass das Denken unbeschränkt. Ein praktikabler Mittelweg ist, das Modell in einer Thinking-Phase frei denken zu lassen und nur das finale JSON zu beschränken.

Strukturierte Ausgaben machen aus LLM-Antworten etwas, das du wie eine typisierte API behandeln kannst, und das Muster lässt sich sauber zwischen OpenAI, Gemini und Claude übertragen, weil alle JSON Schema akzeptieren. Beginne damit, deine Struktur in Pydantic oder Zod zu definieren, aktiviere den Strict-Modus bei deinem Anbieter, halte das Schema flach und umgib das Parsing mit einer Validierung, die Verweigerungen und Truncation abdeckt – dann verdrahte dasselbe Schema mit dem Anbieter, gegen den du deployest.

FAQs

Was ist der Unterschied zwischen JSON-Modus und strikten strukturierten Ausgaben?

Der JSON-Modus garantiert nur, dass das Modell syntaktisch gültiges JSON zurückgibt, das fehlerfrei parst; er garantiert aber nicht, dass die Ausgabe einem bestimmten Schema entspricht. Strikte strukturierte Ausgaben erzwingen dein konkretes JSON Schema während der Generierung, sodass das zurückgegebene Objekt die von dir definierten Felder, Typen und Enums enthält. Der Unterschied lautet: 'es parst' versus 'es enthält die Felder, die du brauchst'. OpenAI führte den JSON-Modus 2023 ein und ergänzte später schema-erzwungene strukturierte Ausgaben, um diese Lücke zu schließen.

Garantiert der aktivierte Strict-Modus, dass ich immer gültiges, parsebares JSON erhalte?

Nein. Der Strict-Modus beschränkt die Token-Generierung im normalen Completion-Verlauf auf dein Schema, aber eine sicherheitsbedingte Verweigerung oder eine durch Längenbegrenzung abgeschnittene Antwort gibt weiterhin einen Erfolgsstatus zurück und erzeugt dabei eine Ausgabe, die kein schemakonformes JSON ist. OpenAI stellt ein eigenes refusal-Feld sowie einen finish_reason von length bereit; Claude signalisiert dies über einen stop_reason von refusal oder max_tokens. Da diese Antworten mit 200 zurückkommen und abgerechnet werden, solltest du das Objekt gegen dein Schema parsen und validieren, bevor du ihm vertraust.

Kann ich dasselbe JSON Schema für OpenAI, Gemini und Claude wiederverwenden?

Weitgehend ja. OpenAI, Google Gemini und Anthropic Claude akzeptieren alle JSON Schema, sodass dasselbe Schema portabel ist und du nur die Request-Verdrahtung anpasst, nicht den Vertrag. Unterschiedlich ist, wo das Schema hingehört: OpenAI verwendet response_format oder text.format mit strict true, Gemini verschachtelt das Schema unter responseFormat.text in generationConfig, und Claude nutzt output_config.format oder strikte Tool-Nutzung. Jeder Anbieter unterstützt nur eine Teilmenge von JSON Schema, prüfe also nicht unterstützte Keywords anhand der Dokumentation zur unterstützten Teilmenge des jeweiligen Anbieters, bevor du von vollständiger Portabilität ausgehst.

Warum wird mein Schema als zu komplex abgelehnt, und wie behebe ich das?

Komplexitätsgrenzen entstehen durch tief verschachtelte oder rekursive Strukturen, lange Property-Namen, große Array-Limits, Enums mit vielen Werten oder Objekte mit vielen optionalen Properties. Claude gibt einen 400 zurück, wenn die kompilierte Grammatik zu groß wird, und Gemini kann sehr große oder tief verschachtelte Schemata ablehnen. Die Lösung besteht darin, Schemata flach zu halten und große Extraktionen in kleinere, flachere Schemata zu zerlegen. Tief verschachtelte Strukturen sind außerdem eine häufige Ursache für verschlechterte Reasoning-Qualität, sodass ein flacheres Design sowohl die Akzeptanz als auch die Ausgabequalität verbessert.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.