Python & AI Tutorials Logo
LangChain & LangGraph

7. Output Strutturato con Pydantic

Nei capitoli precedenti, abbiamo lavorato con gli output degli LLM come stringhe di testo grezze. Questo funziona bene per i chatbot dove gli esseri umani leggono le risposte, ma quando si costruiscono agenti AI dove i programmi devono analizzare e interpretare gli output degli LLM, abbiamo bisogno di dati strutturati e prevedibili. In questo capitolo, imparerai come usare gli schemi Pydantic per far sì che l'LLM restituisca oggetti Python strutturati.

7.1) Perché l'Output Strutturato?

Il Problema con l'Output LLM in Testo Libero

Iniziamo comprendendo perché le risposte in testo grezzo creano problemi nelle applicazioni reali. Considera questo scenario comune:

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
# Chiedi all'LLM informazioni su un prodotto
message = HumanMessage(content="""
Estrai le informazioni sul prodotto da questo testo:
"L'UltraWidget Pro costa $299.99 ed è attualmente disponibile."
""")
 
response = llm.invoke([message])
print(response.content)

Output:

Nome Prodotto: UltraWidget Pro
Prezzo: $299.99
Disponibilità: Disponibile

L'output sembra buono. Ma ora supponi di dover usare questi dati nella tua applicazione Python. Come estrai il prezzo come numero? Come verifichi la disponibilità programmaticamente? Potresti provare un'analisi di stringhe come questa:

python
# Approccio di analisi fragile
text = response.content
price_line = [line for line in text.split('\n') if 'Prezzo:' in line][0]
price_str = price_line.split('$')[1]
price = float(price_str)  # Fragile - cosa succede se il formato cambia?

Questa analisi sembra funzionare. Ma in realtà non funziona. Ecco perché:

Perché Questo Approccio Fallisce:

  1. L'LLM potrebbe formattare la risposta in modo diverso la prossima volta ("Prezzo: 299.99 USD" o "Prezzo al Dettaglio: $299.99")

Ecco esempi di output diversi che possono verificarsi per lo stesso prompt:

# Esempio 1
"Il prodotto è UltraWidget Pro, al prezzo di $299.99, ed è disponibile."
 
# Esempio 2
"Prodotto: UltraWidget Pro
Costo: 299.99 dollari
Stato: Disponibile"
 
# Esempio 3
"UltraWidget Pro - $299.99 (disponibile)"
 
# Esempio 4
"Ho trovato l'UltraWidget Pro. Costa $299.99 ed è attualmente disponibile per l'acquisto."

Quando la risposta dell'LLM cambia, hai bisogno di una logica di analisi completamente diversa. Questo rende difficile costruire applicazioni affidabili.

  1. Le risposte degli LLM sono imprevedibili: Lo stesso prompt può produrre formati diversi ogni volta
  2. L'analisi di stringhe è più difficile di quanto sembri: Devi gestire $, spazi, newline, virgole e altro
  3. Nessuna type safety: Non puoi essere sicuro se price sia un float, una stringa o None
  4. La gestione degli errori è difficile: Se l'LLM dice "Prezzo non disponibile", la tua chiamata float() si blocca
  5. Non manutenibile: Cambia leggermente il prompt e devi riscrivere tutto il codice di analisi

L'Idea Centrale: Python Ha Bisogno di Contratti, Non di Prosa

Pensa a quando comunichi con un server API in Python. Quando chiami una specifica API REST, ti aspetti che restituisca una risposta JSON definita:

python
# Ti aspetti questa struttura
{
    "product_name": "UltraWidget Pro",
    "price": 299.99,
    "in_stock": true
}

Quando costruisci applicazioni AI, hai bisogno dello stesso principio. L'output dell'LLM dovrebbe essere restituito come dati con una struttura definita, non come testo in forma libera ogni volta.

Prosa vs Contratto:

  • Prosa: Testo naturale in forma libera. Buono per gli esseri umani da leggere, ma difficile da elaborare per i programmi.
  • Contratto: Dati con struttura e tipi definiti. Una promessa che "questi campi esisteranno con questi tipi."

Output strutturato significa definire un contratto: "LLM, ho bisogno esattamente di questi campi, con esattamente questi tipi, in esattamente questo formato."

È qui che entra in gioco Pydantic. Pydantic è la libreria di validazione dati più popolare di Python, e LangChain la usa per ricevere gli output degli LLM in forma strutturata.

Il Cambio di Modello Mentale:

  • Prima: "LLM, dimmi qualcosa su questo prodotto" → Analizza testo imprevedibile
  • Dopo: "LLM, rispondi in un formato definito" → Ricevi oggetto Python strutturato

Questo passaggio dalla prosa ai contratti è fondamentale per costruire agenti AI affidabili. Quando un agente deve decidere la sua prossima azione basandosi sulle risposte dell'LLM (ad esempio, acquistare se disponibile, registrarsi per una notifica se non disponibile), deve ricevere risposte in un formato definito.

Analisi Manuale

Output Strutturato

Si Rompe Spesso

Type Safe

Output Testo LLM

Logica Stringhe Fragile

Oggetto Python Tipizzato

Errori Runtime

Codice Affidabile

7.2) Il Tuo Primo Output Strutturato

In 7.1, abbiamo imparato perché gli LLM dovrebbero rispondere con una struttura definita invece di testo in forma libera. Ora vediamo come implementarlo effettivamente.

L'idea chiave: Semplicemente chiedere all'LLM "per favore rispondi in questo formato" non è sufficiente. Devi definire l'esatta struttura dati nel codice Python e far sì che LangChain la passi all'LLM. Questa struttura dati definita è chiamata schema.

Cos'è uno Schema?

Uno schema è un progetto che definisce la struttura dei dati. Specifica:

  • Quali campi devono essere presenti
  • Quale tipo dovrebbe avere ogni campo (stringa, numero, booleano, ecc.)
  • Quali vincoli si applicano (opzionale vs obbligatorio, intervalli validi, ecc.)

In Python, definiamo gli schemi usando la classe BaseModel di Pydantic. Ecco l'esempio più semplice possibile:

python
from pydantic import BaseModel
 
class ProductInfo(BaseModel):
    product_name: str
    price: float
    in_stock: bool

Questo schema dice: "Un oggetto ProductInfo deve avere esattamente tre campi: un product_name (stringa), un price (float) e un in_stock (booleano)."

Il Pattern in Tre Passi: Definire, Collegare, Invocare

Usare l'output strutturato è semplice. Ricorda solo tre passi:

  1. Definire: Crea uno schema con una classe Pydantic
  2. Collegare: Connetti lo schema all'LLM usando .with_structured_output()
  3. Invocare: Chiama .invoke() per ottenere un oggetto tipizzato

Questo è il template standard che userai per la maggior parte delle attività di estrazione strutturata:

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from pydantic import BaseModel, Field
 
# Passo 1: Definire lo schema
class ProductInfo(BaseModel):
    product_name: str = Field(description="Il nome completo del prodotto")
    price: float = Field(description="Prezzo in USD")
    in_stock: bool = Field(description="Se il prodotto è disponibile")
 
# Passo 2: Collegare lo schema all'LLM
llm = ChatOpenAI(model="gpt-4o-mini")
structured_llm = llm.with_structured_output(ProductInfo)
 
# Passo 3: Invocare e ottenere oggetto tipizzato
message = HumanMessage(content="""
Estrai le informazioni sul prodotto da questo testo:
"L'UltraWidget Pro costa $299.99 ed è attualmente disponibile."
""")
 
result = structured_llm.invoke([message])
 
# result è ora un oggetto ProductInfo, non una stringa
print(type(result))  # <class '__main__.ProductInfo'>
print(result.product_name)  # UltraWidget Pro
print(result.price)  # 299.99
print(result.in_stock)  # True

Cosa È Appena Successo?

  1. Definizione Schema: Abbiamo definito i campi e i tipi che vogliamo
  2. Collegamento: .with_structured_output(ProductInfo) configura l'LLM per usare l'output strutturato
  3. Invocazione & Risposta: Quando viene chiamato .invoke(), LangChain passa lo JSON Schema all'LLM, e l'LLM risponde con JSON che corrisponde a quella struttura
  4. Conversione Automatica: LangChain converte il JSON in un oggetto ProductInfo - nessun codice di analisi necessario

Nessuna analisi. Nessuna conversione di tipo. Nessun errore.

Usa questo pattern in 3 passi come tuo template. Seguilo ogni volta che hai bisogno di output strutturato.

Descrizioni dei Campi: La Chiave per Guidare l'LLM

Nell'esempio di definizione dello schema sopra, abbiamo usato Field(description="..."). Questa descrizione non è solo documentazione. Sono istruzioni che l'LLM legge e segue.

Nell'uso tipico di Pydantic, le descrizioni dei Field sono opzionali:

python
# Pydantic normale - la descrizione è documentazione per gli esseri umani
class User(BaseModel):
    name: str = Field(description="Nome dell'utente")  # Funziona bene anche senza

Ma quando si lavora con gli LLM, sono essenziali:

python
# Con gli LLM - la descrizione determina il comportamento dell'LLM
class CustomerFeedback(BaseModel):
    sentiment: str = Field(
        description="Sentimento generale: 'positive', 'negative' o 'neutral'"
    )

L'LLM legge questa descrizione e la usa per decidere come rispondere.

Vediamolo in azione:

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from pydantic import BaseModel, Field
 
class CustomerFeedback(BaseModel):
    sentiment: str = Field(
        description="Sentimento generale: 'positive', 'negative' o 'neutral'"
    )
    main_issue: str = Field(
        description="Il reclamo o la preoccupazione principale, se presente. Usa 'none' se non vengono menzionati problemi."
    )
    urgency: str = Field(
        description="Quanto è urgente il problema: 'low', 'medium' o 'high'"
    )
 
llm = ChatOpenAI(model="gpt-4o-mini")
structured_llm = llm.with_structured_output(CustomerFeedback)
 
message = HumanMessage(content="""
Analizza questo feedback del cliente:
"Il prodotto funziona bene, ma la spedizione ha richiesto 3 settimane. Ho già perso la scadenza del mio progetto. Per favore rispondete immediatamente."
""")
 
result = structured_llm.invoke([message])
print(result.sentiment)    # negative
print(result.main_issue)   # Spedizione lenta
print(result.urgency)      # high

Come le descrizioni modellano le decisioni dell'LLM:

  • Descrizione di sentiment → L'LLM apprende che i valori validi sono 'positive', 'negative', 'neutral' → Il problema di spedizione ha causato la perdita della scadenza, quindi sceglie 'negative'
  • Descrizione di main_issue → L'LLM è istruito a "trovare il reclamo principale" → Identifica "spedizione lenta" come il problema
  • Descrizione di urgency → L'LLM apprende che l'urgenza deve essere 'low', 'medium' o 'high' → Vede "Per favore rispondete immediatamente" e sceglie 'high'

Cosa succede senza descrizioni?

python
sentiment: str  # Nessuna descrizione

L'LLM potrebbe restituire "negative", "bad", "unsatisfied", "2/5", "disappointed" in formati imprevedibili, rendendo difficile per il tuo codice gestire i valori.

Punto chiave: Le descrizioni dei campi sono parte del tuo codice che controlla il comportamento dell'LLM. Scrivile in modo chiaro e specifico.

Campi Categorici: Specificare i Valori Consentiti

Nell'esempio sopra, il campo sentiment può avere solo tre valori: 'positive', 'negative' o 'neutral'. I campi che devono essere uno di un insieme specifico di valori sono chiamati campi categorici.

Per i campi categorici, elenca tutti i valori possibili nella descrizione:

python
sentiment: str = Field(
    description="Sentimento: esattamente 'positive', 'negative' o 'neutral' (minuscolo)"
)

Specificando "esattamente" e "(minuscolo)", enfatizziamo che l'LLM dovrebbe rispondere con precisamente uno di questi tre valori.

Tuttavia, non c'è garanzia che l'LLM risponda sempre con uno dei valori specificati. Ecco perché devi scrivere codice difensivo.

Casi in cui l'LLM restituisce valori inaspettati:

python
result.sentiment = "Positive"    # Maiuscolo
result.sentiment = "NEGATIVE"    # Tutto maiuscolo
result.sentiment = "good"        # Parola completamente diversa

Scrivere codice difensivo:

python
allowed = {"positive", "negative", "neutral"}
 
# Converti in minuscolo e verifica
sentiment = result.sentiment.lower()
 
if sentiment not in allowed:
    sentiment = "neutral"  # Usa il valore predefinito per valori inaspettati
 
# Ora sentiment è garantito essere uno dei valori consentiti

Conclusione chiave:

  1. Specifica i valori consentiti nella descrizione → L'LLM è più probabile che risponda correttamente
  2. Valida nel codice → Gestisci i valori inaspettati in modo sicuro

Nota: Il Capitolo 18 mostra pattern più forti usando gli enum di Python per l'applicazione.

Confronto: Analisi Manuale vs Output Strutturato

Confrontiamo lo stesso compito con e senza output strutturato per vedere la differenza:

Analisi Manuale:

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
message = HumanMessage(content="""
Estrai il nome del prodotto, il prezzo e la disponibilità da:
"L'UltraWidget Pro costa $299.99 ed è attualmente disponibile."
Formato: nome | prezzo | disponibilità
""")
 
response = llm.invoke([message])
text = response.content
 
# Analisi manuale
parts = text.split('|')
product_name = parts[0].strip()
price_str = parts[1].strip().replace('$', '')
price = float(price_str)
availability = parts[2].strip().lower()
in_stock = 'disponibile' in availability or 'available' in availability
 
print(f"Nome: {product_name}")
print(f"Prezzo: ${price}")
print(f"Disponibile: {in_stock}")

Output Strutturato:

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from pydantic import BaseModel, Field
 
class ProductInfo(BaseModel):
    product_name: str = Field(description="Il nome completo del prodotto")
    price: float = Field(description="Prezzo in USD")
    in_stock: bool = Field(description="Se il prodotto è disponibile")
 
llm = ChatOpenAI(model="gpt-4o-mini")
structured_llm = llm.with_structured_output(ProductInfo)
 
message = HumanMessage(content="""
Estrai le informazioni sul prodotto da:
"L'UltraWidget Pro costa $299.99 ed è attualmente disponibile."
""")
 
result = structured_llm.invoke([message])
 
print(f"Nome: {result.product_name}")
print(f"Prezzo: ${result.price}")
print(f"Disponibile: {result.in_stock}")

Differenze Chiave:

  1. Nessuna Logica di Analisi: La versione strutturata ha zero codice di analisi
  2. Type Safety: result.price è garantito essere un float
  3. Codice Più Semplice: Nessun regex, nessuna divisione di stringhe, nessuna conversione manuale di tipo
  4. Validazione: Pydantic assicura che tutti i campi richiesti siano presenti
  5. Manutenibilità: Cambiare lo schema è più facile che aggiornare la logica di analisi

7.3) Considerazioni sulla Progettazione dello Schema

Ora che sai come usare l'output strutturato, impariamo come progettare buoni schemi. Questa sezione copre i principi pratici di progettazione per distinguere i campi obbligatori da quelli opzionali.

Campi Obbligatori

Per impostazione predefinita, tutti i campi in un modello Pydantic sono obbligatori. Questo significa che l'LLM deve estrarre o inferire un valore per ogni campo obbligatorio dal prompt dell'utente e fornirlo nella risposta.

python
from pydantic import BaseModel
 
class ProductInfo(BaseModel):
    product_name: str
    price: float
    in_stock: bool

Quando usi questo schema, l'LLM tenterà di trovare valori per tutti e tre i campi (product_name, price, in_stock) nel testo di input.

Ma cosa succede quando il prompt manca di informazioni per un campo obbligatorio?

Potremmo aspettarci il seguente comportamento:

  1. L'LLM non può trovare le informazioni nel prompt
  2. L'LLM omette quel campo dalla sua risposta
  3. LangChain non può creare un'istanza valida di ProductInfo
  4. Viene sollevato ValidationError

Tuttavia, questo non sempre accade.

Il motivo è che diversi LLM possono gestire le informazioni mancanti in modo diverso.

Alcuni LLM (come i modelli OpenAI) tendono a generare valori anche quando le informazioni richieste non sono presenti nel prompt. In questo caso, ValidationError non si verifica, ma questo può causare problemi più grandi perché la tua applicazione Python potrebbe elaborare informazioni fabbricate come se fossero reali.

Affronteremo come risolvere questo problema nella Sezione 7.4: Quando le Cose Vanno Male.

Per ora, sii solo consapevole che non tutti gli LLM gestiscono le informazioni mancanti allo stesso modo.

Campi Opzionali

Potresti aver bisogno di campi che possono legittimamente essere presenti o assenti, anche in casi normali. Ad esempio, una nota di consegna (delivery_note) può essere fornita o meno dal cliente, anche per un ordine valido.

Quando usare Optional:

  • I dati stessi potrebbero non esistere (ad esempio, quando sono consentite recensioni anonime, le recensioni anonime non hanno nome del recensore)
  • Vuoi che l'LLM indichi esplicitamente le informazioni mancanti piuttosto che fabbricare un valore

Per rendere un campo opzionale, usa il tipo Optional di Python dal modulo typing:

python
from typing import Optional
 
class ProductReview(BaseModel):
    rating: int
    review_text: str
    reviewer_name: Optional[str] = None  # Le recensioni anonime non hanno nome del recensore

Nota: Gli utenti di Python 3.10+ possono usare str | None invece di Optional[str].

Quando un campo è Optional:

  • L'LLM può ometterlo dalla risposta se le informazioni non vengono trovate nel prompt
  • I campi omessi vengono impostati al valore predefinito (None)

Ecco un esempio completo:

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from pydantic import BaseModel, Field
from typing import Optional
 
class ProductInfo(BaseModel):
    product_name: str
    price: float
    in_stock: bool
    discount_percentage: Optional[float] = None
    warranty_years: Optional[int] = None
 
llm = ChatOpenAI(model="gpt-4o-mini")
structured_llm = llm.with_structured_output(ProductInfo)
 
message = HumanMessage(content="""
Estrai le informazioni sul prodotto: "L'UltraWidget Pro costa $299.99 ed è disponibile."
""")
 
result = structured_llm.invoke([message])
print(result.product_name)  # UltraWidget Pro
print(result.price)  # 299.99
print(result.in_stock)  # True
print(result.discount_percentage)  # None (non menzionato)
print(result.warranty_years)  # None (non menzionato)

Checklist per la Progettazione dello Schema

Prima di finalizzare il tuo schema, chiediti:

Selezione dei Campi:

  • I campi obbligatori sono veramente essenziali? (Cosa succede se questo campo manca dal prompt?)
  • I campi opzionali possono legittimamente essere assenti anche in casi normali?

Specifica dei Campi:

  • Ogni campo ha una descrizione chiara?
  • I campi categorici sono esplicitamente vincolati? (ad esempio, "deve essere esattamente 'A', 'B' o 'C'")

7.4) Quando le Cose Vanno Male

Possono verificarsi due problemi quando si usa l'output strutturato:

  1. L'LLM omette i valori dei campi obbligatori → Si verifica ValidationError
  2. L'LLM fabbrica informazioni mancanti → Nessun ValidationError, ma il tuo codice elabora dati errati

Questa sezione copre come gestire ciascuno.

Comprendere gli Errori di Validazione

Quando il prompt dell'utente manca di informazioni per i campi obbligatori definiti nello schema, l'LLM non può fornire valori per quei campi. Il programma Python solleva quindi un ValidationError:

python
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import HumanMessage
from pydantic import BaseModel, ValidationError
 
class ProductInfo(BaseModel):
    product_name: str
    price: float
    in_stock: bool
 
llm = ChatAnthropic(model='claude-sonnet-4-5')
structured_llm = llm.with_structured_output(ProductInfo)
 
# Input mancante di informazioni richieste
message = HumanMessage(content="""
Estrai le informazioni sul prodotto da: "Il widget è fantastico! Altamente raccomandato."
""")
 
try:
    result = structured_llm.invoke([message])
    print(result)
except ValidationError as e:
    print("Si è verificato ValidationError")

Nota: Quando il prompt manca di informazioni per i campi obbligatori, alcuni LLM possono fabbricare valori e fornirli nella risposta. In questo caso, ValidationError non si verificherà, ma sorge un problema più grande. Lo tratteremo nella prossima sezione.

Quando il prompt manca di informazioni per i campi obbligatori e si verifica ValidationError, questo è effettivamente utile per la tua applicazione Python. L'applicazione può rilevare che si è verificato un problema e gestire l'errore in modo controllato. Le strategie di recupero dagli errori sono trattate nel Capitolo 14 (recupero dagli errori a livello di agente) e nel Capitolo 17 (logica di retry con gestione dello stato).

Il Problema Più Grande: L'LLM Fabbrica Informazioni Mancanti

Come abbiamo discusso nella Sezione 7.3, alcuni LLM mostrano un comportamento più pericoloso: fabbricano valori e li forniscono nelle risposte quando le informazioni mancano dal prompt.

Come si verifica il problema:

  1. Il prompt manca di informazioni richieste
  2. L'LLM genera comunque valori dall'aspetto plausibile
  3. ValidationError NON si verifica
  4. La tua applicazione Python elabora dati fabbricati come se fossero reali

Esempio:

python
class ProductInfo(BaseModel):
    product_name: str
    price: float
    in_stock: bool
 
message = HumanMessage(content="""
Estrai le informazioni sul prodotto da: "Il widget è fantastico!"
""")
 
# Quando alcuni LLM fabbricano valori per product_name, price e in_stock
result = structured_llm.invoke([message])
# Nessun errore sollevato!
print(result.product_name)  # "widget" (estratto dal testo)
print(result.price)  # 0.0 (fabbricato!)
print(result.in_stock)  # False (fabbricato!)
 
# Problema: Non puoi dire quali valori sono reali vs fabbricati

Questo è peggio di un ValidationError perché:

  • La tua applicazione Python continua l'esecuzione con dati errati
  • Non sai quali campi sono reali vs fabbricati
  • La logica a valle può prendere decisioni sbagliate basate su dati falsi

Soluzione: Usa Campi Optional con Validazione

La soluzione è definire tutti i campi obbligatori come Optional, quindi usare un validatore per verificare che tutti i campi obbligatori abbiano valori.

python
from typing import Optional
from pydantic import BaseModel, model_validator, ValidationError
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
 
class ProductInfo(BaseModel):
    # Questi sono effettivamente obbligatori, ma dichiarati come Optional
    # La validazione reale avviene nel validatore sotto
    product_name: Optional[str] = None
    price: Optional[float] = None
    in_stock: Optional[bool] = None
    
    @model_validator(mode='after')
    def check_required_fields(self):
        """Valida che tutti i campi essenziali siano presenti"""
        if self.product_name is None or self.price is None or self.in_stock is None:
            raise ValueError("Tutti i campi (product_name, price, in_stock) devono essere forniti")
        return self
 
llm = ChatOpenAI(model="gpt-4o-mini")
structured_llm = llm.with_structured_output(ProductInfo)
 
# Test con dati incompleti
message = HumanMessage(content="""
Estrai le informazioni sul prodotto da: "Il widget è fantastico!"
""")
 
try:
    result = structured_llm.invoke([message])
    # Se arriviamo qui, tutti i campi sono garantiti essere presenti
    print(f"Prodotto: {result.product_name}")
    print(f"Prezzo: ${result.price}")
except ValidationError as e:
    # I campi obbligatori mancano - estrazione fallita
    print(f"Estrazione incompleta: {e}")

Cosa sta succedendo qui:

  • @model_validator è il decoratore di Pydantic che aggiunge logica di validazione personalizzata
  • mode='after' significa che la validazione viene eseguita dopo che tutti i campi sono stati analizzati
  • Se qualsiasi campo è None, solleviamo ValueError per segnalare dati incompleti
  • Pydantic avvolge automaticamente questo ValueError in un ValidationError

Perché funziona:

Quando il prompt manca di informazioni per i campi:

  • L'LLM non fabbrica valori e omette quei campi dalla risposta
  • In questo caso, quei campi diventano None
  • Se quei campi sono effettivamente obbligatori, il validatore Pydantic solleva ValueError
  • Pydantic lo avvolge come ValidationError

In questo modo, la tua applicazione Python riceve un errore esplicito da gestire, piuttosto che dati fabbricati.

Conclusione chiave: Quando il prompt manca di informazioni per i campi obbligatori, ottenere un ValidationError è perfettamente normale e previsto. Il vero pericolo sono i dati fabbricati. Usa campi Optional con validatori per impedire all'LLM di fabbricare informazioni mancanti, rilevando esplicitamente quando i campi obbligatori mancano.


Riepilogo del Capitolo:

In questo capitolo, hai imparato come trasformare l'output degli LLM in oggetti Python affidabili:

  • Perché è importante: L'analisi di testo libero è fragile; l'output basato su schema fornisce type safety
  • Come usarlo: Definisci schemi con BaseModel di Pydantic → Collega con .with_structured_output()
  • Principi di progettazione: Scegli campi obbligatori vs opzionali, guida l'LLM con descrizioni dei campi
  • Gestisci i problemi: ValidationError è normale; il vero pericolo sono i dati fabbricati