Blog

Durchsetzung und Validierung der LLM-Ausgabe mit Pydantic

Timo Uelen

Aktualisiert Oktober 15, 2025
5 Minuten

Einführung

Große Sprachmodelle (Large Language Models, LLMs) sind hervorragend in der Generierung von Text, haben aber oft Schwierigkeiten, strukturierte Ausgaben zu erzeugen. Durch den Einsatz der Typvalidierung und des Prompt-Engineering von Pydantic können wir die von LLMs erzeugte Ausgabe erzwingen und validieren.

Alle Codebeispiele in diesem Blogbeitrag sind in Python geschrieben. Der verwendete LLM ist der gpt-3.5-Turbo von OpenAI.

Abfrage des LLM

Um den LLM abzufragen, verwenden wir die folgende Funktion:

import openai

def query(prompt: str) -> str:
    """Query the LLM with the given prompt."""
    completion = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=[
            {
                "role": "user",
                "content": prompt,
            }
        ],
        temperature=0.0,
    )
    return completion.choices[0].message.content

Wir rufen die Funktion dann mit einer einfachen Frage auf:

response = query("What is the largest planet in our solar system?")
print(response)
'The largest planet in our solar system is Jupiter.'

JSON-Ausgabe mit einer Eingabeaufforderung erzwingen

In unserer Eingabeaufforderung können wir den LLM auffordern, in einem bestimmten Format zu antworten:

prompt = """
I will ask you questions and you will respond. Your response should be in the following format:
```json
{
    "thought": "How you think about the question",
    "answer": "The answer to the question"
}
```
"""

Dann fragen wir das Modell ab:

question = "What is the largest planet in our solar system?"
response = query(prompt + question)
print(response)
'{
    "thought": "This is a factual question that can be answered with scientific knowledge.",
    "answer": "The largest planet in our solar system is Jupiter."
}'

Das ist großartig, denn so können wir die strukturierte Ausgabe leicht parsen:

import json

parsed_response = json.loads(response)
print(parsed_response["answer"])
'The largest planet in our solar system is Jupiter.'

Validierung der Ausgabe

from pydantic import BaseModel


class ThoughtAnswerResponse(BaseModel):
    thought: str
    answer: str


raw_response = query(prompt)

# Note: When you are using pydantic<2.0, use parse_raw instead of model_validate_json
validated_response = ThoughtAnswerResponse.model_validate_json(raw_response)

print(validated_response)
thought='This is a factual question that can be answered with scientific knowledge.' answer='The largest planet in our solar system is Jupiter.'

print(type(validated_response))
<class 'ThoughtAnswerResponse'>

Verwendung des pydantischen Modells in der Eingabeaufforderung

Im Moment beschreiben wir unser Antwortformat an zwei Stellen:

  • eine JSON-Beschreibung in unserer Eingabeaufforderung
  • ein entsprechendes pydantisches Modell

Wenn wir das Antwortformat aktualisieren möchten, müssen wir sowohl die Eingabeaufforderung als auch das Pydantic-Modell ändern. Dies kann zu Inkonsistenzen führen.

Wir können dieses Problem lösen, indem wir das Pydantic-Modell in ein JSON-Schema exportieren und das Schema zur Eingabeaufforderung hinzufügen. Dadurch werden die Antwort und das Pydantic-Modell konsistent.

response_schema_dict = ThoughtAnswerResponse.model_json_schema()
response_schema_json = json.dumps(response_schema_dict, indent=2)

prompt = f"""
I will ask you questions, and you will respond.
Your response should be in the following format:
```json
{response_schema_json}
```
"""

Die Eingabeaufforderung sieht nun wie folgt aus:

I will ask you questions, and you will respond. Your response should be in the following format:
```json
{
    "properties": {
        "thought": { "title": "Thought", "type": "string" },
        "answer": { "title": "Answer", "type": "string" }
    },
    "required": ["thought", "answer"],
    "title": "ThoughtAnswerResponse",
    "type": "object"
}

Die Antwort sieht dann so aus:

{
  "thought": "The largest planet in our solar system is Jupiter.",
  "answer": "Jupiter"
}

Wenn Sie nun das Pydantic-Modell ändern, wird das entsprechende Schema in der Eingabeaufforderung angezeigt. Beachten Sie, dass das Schema komplexer geworden ist als zuvor. Ein Vorteil ist, dass wir nun genauer festlegen können, welche Antworten wir benötigen.

Fehlerbehandlung

Der LLM kann immer noch Ergebnisse liefern, die nicht mit unserem Modell übereinstimmen. Wir können etwas Code hinzufügen, um dies abzufangen:

from pydantic import ValidationError

try:
    validated_response = ThoughtAnswerResponse.model_validate_json(raw_response)
except ValidationError as e:
    print("Unable to validate LLM response.")
    # Add your own error handling here
    raise e

Bestimmte Werte mit Hilfe eines Literal erzwingen

Manchmal möchten Sie die Verwendung von bestimmten Werten für ein bestimmtes Feld erzwingen. Wir fügen das Feld "difficulty" zu unserem Antwortobjekt hinzu. Der LLM sollte es verwenden, um Informationen über den Schwierigkeitsgrad der Frage bereitzustellen. Bei einer normalen Eingabeaufforderung würden wir wie folgt vorgehen:

prompt = """Your response should be in the following format:
```json
{
  "thought": "How you think about the question",
  "answer": "The answer to the question",
  "difficulty": "How difficult the question was. One of easy, medium or hard"
}
```
"""

Natürlich könnte das Modell auch andere Werte verwenden. Um sie zu validieren, müssten wir eigenen Code schreiben.

Mit Pydantic ist das sehr viel einfacher. Wir erstellen einen neuen Typ namens Difficulty unter Verwendung eines Literal. Mit einem Literal können wir die Verwendung einer Auswahlliste von Werten festlegen. Wir fügen dem Feld difficulty in unserem Pydantic-Modell einen Hinweis auf den Typ Difficulty hinzu:

from typing import Literal

from pydantic import BaseModel


# We create a new type
Difficulty = Literal["easy", "medium", "hard"]


class ThoughtAnswerResponse(BaseModel):
    thought: str
    answer: str
    difficulty: Difficulty

Der LLM antwortet möglicherweise mit einem Wert, den wir nicht zulassen:

{
  "thought": "The largest planet in our solar system is Jupiter.",
  "answer": "Jupiter",
  "difficulty": "Unknown"
}

Wenn wir dieses Ergebnis parsen, überprüft Pydantic die Werte für das Feld difficulty. Unknown stimmt nicht mit einem der Werte überein, die in dem von uns definierten Literal-Typ angegeben sind. Daher erhalten wir die folgende Fehlermeldung:

validated_response = ThoughtAnswerResponse.model_validate_json(response)

ValidationError: 1 validation error for ThoughtAnswerResponse
difficulty
    Input should be 'easy', 'medium' or 'hard' [type=literal_error, input_value='Unknown', input_type=str]

Fazit

Durch den Einsatz von Pydantic und Prompt Engineering können Sie die Ausgabe von LLMs erzwingen und validieren. Dadurch erhalten Sie eine bessere Kontrolle über die LLM-Ausgabe und können robustere KI-Systeme entwickeln.


Foto von Andrew Ridley auf Unsplash

Verfasst von

Timo Uelen

Timo Uelen is a Machine Learning Engineer at Xebia.

Contact

Let’s discuss how we can support your journey.