Struktureret output med Pydantic
Definér Pydantic-modeller som Deres outputskema, send dem til API'et via den nye funktion til struktureret output, og deserialiser automatisk svar til typede Python-objekter.
Struktureret output med Pydantic er en gratis AI Engineering Academy-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i AI Engineering Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. AI Engineering Academy-kurset indeholder 4 lektioner i alt.
Hvorfor Pydantic til LLM-output?
Pydantic er et Python-bibliotek til datavalidering, der definerer dataskemaer ved hjælp af Pythons typeangivelser. Det er særligt velegnet til validering og deserialisering af data fra eksterne kilder — og LLM-output er en af de mest upålidelige eksterne kilder, du vil møde. Når du kombinerer Pydantic-skemaer med OpenAI's strukturerede outputs, får du typesikre, validerede og automatisk deserialiserede svar fra en AI-model.
I stedet for at skrive data = json.loads(response) efterfulgt af manuel udtrækning af felter og typekonvertering får du et fuldt typet Python-objekt, hvor hvert felt garanteres at have den korrekte type, komplet med autofuldførelse i dit IDE og validering ved kørsel. Sådan håndterer professionelle AI-engineeringteams struktureret udtrækning.
Definition af et grundlæggende Pydantic-skema
En Pydantic-model er en klasse, der nedarver fra BaseModel, med felter defineret ved hjælp af Python-typeannotationer. Felttyper kan være Pythons primitive typer, andre Pydantic-modeller til indlejring eller typer fra modulet typing til lister, valgfrie værdier og unioner.
from pydantic import BaseModel, Field
from typing import Optional, List
from enum import Enum
class Sentiment(str, Enum):
positive = 'positive'
negative = 'negative'
neutral = 'neutral'
class ReviewAnalysis(BaseModel):
sentiment: Sentiment
confidence: float = Field(ge=0.0, le=1.0, description='Confidence score 0-1')
key_themes: List[str] = Field(description='Main topics mentioned in the review')
summary: str = Field(max_length=200, description='One-sentence summary')
product_name: Optional[str] = Field(default=None, description='Product mentioned, if any')
would_recommend: Optional[bool] = None
# Pydantic validates types and constraints at instantiation
example = ReviewAnalysis(
sentiment=Sentiment.positive,
confidence=0.95,
key_themes=['fast delivery', 'good quality'],
summary='Customer loves the product and quick shipping.',
product_name='Wireless Headphones',
would_recommend=True
)
print(example.model_dump_json(indent=2))Pydantic med OpenAI Structured Outputs
Giv din Pydantic-modelklasse direkte til parameteren response_format i client.beta.chat.completions.parse(). SDK'et konverterer automatisk modellen til JSON Schema, sender det til API'et og deserialiserer svaret tilbage til et typet Python-objekt.
import openai
from pydantic import BaseModel
from typing import List, Optional
from enum import Enum
client = openai.OpenAI()
class Sentiment(str, Enum):
positive = 'positive'
negative = 'negative'
neutral = 'neutral'
class ReviewAnalysis(BaseModel):
sentiment: Sentiment
confidence: float
key_themes: List[str]
summary: str
would_recommend: Optional[bool]
review_text = '''
I bought this laptop for my design work and I am blown away. It handles Photoshop
like a dream, the screen colors are beautiful, and it has not slowed down once in
three months. Battery life could be better but overall highly recommend!
'''
result = client.beta.chat.completions.parse(
model='gpt-4o-mini',
messages=[
{'role': 'system', 'content': 'Analyze the customer review and extract structured information.'},
{'role': 'user', 'content': review_text}
],
response_format=ReviewAnalysis
)
analysis = result.choices[0].message.parsed
print(f'Sentiment: {analysis.sentiment.value}')
print(f'Confidence: {analysis.confidence}')
print(f'Themes: {analysis.key_themes}')
print(f'Recommend: {analysis.would_recommend}')Indlejrede Pydantic-modeller
Pydantic-skemaer kan referere til andre Pydantic-modeller, så du kan oprette vilkårligt indlejrede strukturerede outputs. Det er ideelt til at udtrække hierarkiske data fra dokumenter som kontrakter, fakturaer, CV'er og lægejournaler.
from pydantic import BaseModel
from typing import List, Optional
class Address(BaseModel):
street: Optional[str]
city: str
country: str
postal_code: Optional[str]
class ContactInfo(BaseModel):
email: Optional[str]
phone: Optional[str]
address: Optional[Address]
class Person(BaseModel):
full_name: str
age: Optional[int]
job_title: Optional[str]
contact: ContactInfo
skills: List[str]
# When you pass Person to response_format, the API generates:
# {
# "full_name": "...",
# "contact": {
# "email": "...",
# "address": { "city": "...", "country": "..." }
# },
# "skills": ["...", "..."]
# }
print('Nested model defined - pass to response_format for extraction')Feltvalidering med Pydantic-validatorer
Pydantic-validatorer giver dig mulighed for at tilføje tilpasset valideringslogik ud over simpel typekontrol. Du kan validere, at en sikkerhedsscore ligger mellem 0 og 1, at en pris ikke er negativ, eller at en datosstreng har det korrekte format. Når LLM'en returnerer en værdi, der ikke består valideringen, rejser Pydantic en ValidationError, som du kan fange og håndtere.
from pydantic import BaseModel, Field, field_validator
from typing import Optional
import re
class ExtractedContact(BaseModel):
name: str
email: Optional[str] = None
phone: Optional[str] = None
confidence: float = Field(ge=0.0, le=1.0)
@field_validator('email')
@classmethod
def validate_email(cls, v):
if v is not None:
# Basic email format check
if not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', v):
raise ValueError(f'Invalid email format: {v}')
return v
@field_validator('phone')
@classmethod
def normalize_phone(cls, v):
if v is not None:
# Remove non-digit characters for normalization
digits = re.sub(r'[^0-9+]', '', v)
return digits
return v
try:
contact = ExtractedContact(name='Alice', email='not-an-email', confidence=0.9)
except Exception as e:
print(f'Validation error: {e}')Udtrækning af lister med objekter
Et almindeligt mønster er at udtrække flere forekomster af den samme entitet fra et dokument — alle linjeposter fra en faktura, alle handlingspunkter fra et mødereferat eller alle entiteter fra en nyhedsartikel. Omslut din model i en containermodel med et listefelt for at håndtere dette på en enkel måde.
import openai
from pydantic import BaseModel
from typing import List
client = openai.OpenAI()
class ActionItem(BaseModel):
task: str
assignee: str
due_date: str # or use datetime with proper parsing
priority: str # high / medium / low
class MeetingNotes(BaseModel):
meeting_title: str
action_items: List[ActionItem]
key_decisions: List[str]
meeting_transcript = '''
Q3 Planning Meeting - June 2025
Decision: Launch new feature in July.
Decision: Extend free trial to 30 days.
Action: Alice to finalize designs by June 30th - High priority.
Action: Bob to write API docs by July 5th - Medium priority.
Action: Carol to set up staging environment by June 28th - High priority.
'''
result = client.beta.chat.completions.parse(
model='gpt-4o-mini',
messages=[
{'role': 'system', 'content': 'Extract structured data from meeting notes.'},
{'role': 'user', 'content': meeting_transcript}
],
response_format=MeetingNotes
)
notes = result.choices[0].message.parsed
for item in notes.action_items:
print(f'[{item.priority.upper()}] {item.task} -> {item.assignee} by {item.due_date}')Valgfrie felter og standardværdier
Dokumenter fra den virkelige verden er ufuldstændige. Et CV indeholder måske ikke et telefonnummer, en faktura har måske ikke et fakturanummer, og en produktanmeldelse nævner måske ikke produktnavnet. Design dine Pydantic-modeller, så de håndterer manglende data på en robust måde ved hjælp af Optional-felter med passende standardværdier.
Et felt, der er annoteret som Optional[str] = None, fortæller både Pydantic og LLM'en, at feltet kan mangle. Modellen returnerer null i JSON for felter, den ikke kan udtrække, og Pydantic deserialiserer det til Pythons None, så du kan håndtere det korrekt senere uden KeyError-undtagelser.
Konvertering af Pydantic-modeller til JSON Schema
Den Pydantic-model, du definerer, konverteres automatisk til et JSON Schema, når den sendes til API'et. Du kan inspicere dette skema for at forstå præcis, hvad API'et håndhæver. Det er nyttigt, når du skal fejlfinde tilfælde, hvor modellen ikke returnerer den struktur, du forventer.
from pydantic import BaseModel, Field
from typing import List, Optional
import json
class ProductExtraction(BaseModel):
name: str = Field(description='Product name as mentioned in the text')
price_usd: Optional[float] = Field(default=None, description='Price in USD')
features: List[str] = Field(default_factory=list)
in_stock: bool = Field(description='Whether the product is currently available')
# See the JSON Schema that will be sent to the API
schema = ProductExtraction.model_json_schema()
print(json.dumps(schema, indent=2))
# This shows exactly what constraints the API will enforceHåndtering af fejl i udtrækningen
Selv med strukturerede outputs kan udtrækningen mislykkes på to måder: Modellen nægter at svare (returnerer en afvisning), eller dokumentet indeholder reelt ikke de ønskede oplysninger, så modellen returnerer null-værdier for påkrævede felter — hvilket udløser en Pydantic-valideringsfejl, fordi påkrævede felter ikke kan være null.
Den sikreste tilgang er at gøre alle felter Optional med standardværdier, acceptere null-værdier for manglende data og anvende din egen forretningslogikvalidering efter udtrækningen. På den måde adskiller du udtrækningsopgaven (at hente data ud af tekst) fra valideringsopgaven (at kontrollere, at dataene opfylder dine krav).
import openai
from pydantic import BaseModel, ValidationError
from typing import Optional
client = openai.OpenAI()
class ContactExtraction(BaseModel):
name: Optional[str] = None
email: Optional[str] = None
phone: Optional[str] = None
try:
result = client.beta.chat.completions.parse(
model='gpt-4o-mini',
messages=[
{'role': 'system', 'content': 'Extract contact information.'},
{'role': 'user', 'content': 'I would like to discuss partnership opportunities.'}
],
response_format=ContactExtraction
)
msg = result.choices[0].message
if msg.refusal:
print('Refused:', msg.refusal)
else:
contact = msg.parsed
if not any([contact.name, contact.email, contact.phone]):
print('No contact information found in text')
else:
print(contact.model_dump())
except ValidationError as e:
print('Validation failed:', e)Brug af Pydantic med biblioteket instructor
Biblioteket instructor er en populær tredjepartspakke, der tilpasser OpenAI-klienten til at understøtte Pydantic-baseret udtrækning med automatisk nyt forsøg ved valideringsfejl. Hvis modellen returnerer output, der ikke består din Pydantic-validering, prøver instructor automatisk igen med en prompt, der indeholder valideringsfejlen, så modellen får mulighed for at rette sig selv.
Det er især nyttigt i batch-udtrækningspipelines, hvor du ikke manuelt kan gennemgå hvert resultat, og hvor du ønsker, at systemet retter sig selv uden menneskelig indgriben.
# pip install instructor
import instructor
import openai
from pydantic import BaseModel, Field
from typing import Optional
# Patch the OpenAI client with instructor
client = instructor.from_openai(openai.OpenAI())
class ProductInfo(BaseModel):
name: str
price_usd: float = Field(gt=0, description='Price must be positive')
brand: Optional[str] = None
# instructor automatically retries if Pydantic validation fails
product = client.chat.completions.create(
model='gpt-4o-mini',
messages=[
{'role': 'user', 'content': 'The Sony WH-1000XM5 headphones cost $279.99 at Best Buy.'}
],
response_model=ProductInfo, # instructor-specific parameter
max_retries=3
)
print(f'{product.name}: ${product.price_usd} by {product.brand}')Disjunkte unioner og dynamiske skemaer
Pydantic understøtter disjunkte unioner — et skema, hvor strukturen afhænger af værdien i et diskriminatorfelt. Det er nyttigt, når forskellige dokumenttyper deler en fælles grundstruktur, men har forskellige ekstra felter. En udgiftsrapport kan for eksempel indeholde enten en flykvittering (med afgang og ankomst) eller en hotelkvittering (med ind- og udtjekningsdatoer).
Ved at bruge Union-typer med et Literal-diskriminatorfelt kan du definere ét udtrækningsskema, der håndterer flere dokumentvarianter, mens modellen vælger den korrekte undertype ud fra dokumentets indhold. Pydantic validerer automatisk mod den korrekte undertype baseret på diskriminatorværdien.
from pydantic import BaseModel
from typing import Union, Literal, Optional
class FlightExpense(BaseModel):
expense_type: Literal['flight']
airline: str
departure_city: str
arrival_city: str
amount_usd: float
class HotelExpense(BaseModel):
expense_type: Literal['hotel']
hotel_name: str
check_in: str
check_out: str
amount_usd: float
class MealExpense(BaseModel):
expense_type: Literal['meal']
restaurant: Optional[str]
amount_usd: float
class ExpenseReport(BaseModel):
submitter: str
expenses: list[Union[FlightExpense, HotelExpense, MealExpense]]
total_usd: float
print('Discriminated union schema - model selects correct subtype per item')Hurtigt tjek
Test din forståelse af begreberne inden for AI Engineering fra denne lektion.
Opsummering af lektionen
I denne lektion har du lært, at Pydantic BaseModel-underklasser definerer stærkt typede udtrækningsskemaer, som OpenAI's strukturerede outputs håndhæver på API-niveau, at indlejrede modeller, Optional-felter og List-typer håndterer komplekse dokumentstrukturer fra den virkelige verden, og at instructor-biblioteket tilføjer automatisk nyt forsøg ved valideringsfejl for robuste batch-udtrækningspipelines. Som det næste bygger vi en komplet informationsudtrækningspipeline til ustrukturerede tekstkilder.
Lær Python med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 30
- Lektioner
- 120
Ofte stillede spørgsmål
Er lektionen “Struktureret output med Pydantic” gratis?
Ja — hele teksten til “Struktureret output med Pydantic” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af AI Engineering Academy-kurset, skal du opgradere til CoddyKit PRO. AI Engineering Academy-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Struktureret output med Pydantic”?
Definér Pydantic-modeller som Deres outputskema, send dem til API'et via den nye funktion til struktureret output, og deserialiser automatisk svar til typede Python-objekter. Du øver dig i AI Engineering Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på AI Engineering Academy?
Der kræves ingen tidligere erfaring. AI Engineering Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.
Hvor lang tid tager lektionen “Struktureret output med Pydantic”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne AI Engineering Academy-lektion?
Ja. Alle AI Engineering Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- JSON-tilstand og response_format
- Struktureret output med Pydantic
- Udtræk af data fra ustruktureret tekst
- Validering og nye forsøg ved ugyldigt output