
Pydantic AI: La Guida per la Produzione (Oltre Hello World)
Gli output LLM grezzi rompono le applicazioni. Chiedi JSON, ottieni markdown. Chiedi un numero tra 1 e 10, ottieni "Certo! Ecco un numero: sette." Se hai costruito qualcosa di reale con le API LLM, hai scritto codice di parsing difensivo che ti fa mettere in discussione le tue scelte di carriera. Pydantic AI risolve questo problema -- è il framework per agenti type-safe costruito dallo stesso team dietro Pydantic e FastAPI. Pensalo come "FastAPI per agenti AI": definisci cosa vuoi con i type hint Python, e il framework gestisce validazione, retry e chiamate a strumenti.
Questa guida a Pydantic AI è per gli sviluppatori che hanno già fatto la prima chiamata LLM e vogliono pattern da produzione: output strutturati che non si rompono, dependency injection per agenti testabili, e strumenti reali oltre le API meteo. Alla fine avrai agenti funzionanti con strumenti, DI, streaming e test.
<!-- IMAGE: Architettura agente Pydantic AI -- L'agente riceve il prompt, chiama gli strumenti via RunContext, valida l'output attraverso il modello Pydantic -->Pydantic AI in Sintesi
| Attributo | Dettagli |
|---|---|
| Cos'è | Framework per agenti AI type-safe per Python |
| Costruito da | Team Pydantic (Samuel Colvin et al.) |
| Filosofia | "FastAPI per agenti AI" -- i type hint guidano tutto |
| Licenza | MIT (open-source) |
| Versione Attuale | v1.74.0 (Marzo 2026) |
| Versione Python | 3.9+ |
| Modelli Supportati | OpenAI, Anthropic, Google Gemini, Groq, Mistral, Ollama e altri |
| Funzionalità Chiave | Output strutturati, chiamate a strumenti, dependency injection, streaming, TestModel |
| Stelle GitHub | 16.000+ |
| Pronto per la Produzione | Sì -- v1.0 rilasciata a Settembre 2025 |
| Osservabilità | Integrazione nativa con Logfire (basato su OpenTelemetry) |
| Curva di Apprendimento | Bassa se conosci Pydantic/FastAPI; moderata altrimenti |
Le funzionalità di punta sono gli output strutturati (validati con modelli Pydantic), la dependency injection (come Depends di FastAPI) e TestModel (LLM mock per test senza chiamate API). Se vieni da LangChain e ti chiedi "c'è qualcosa di più pulito?", probabilmente è questa la risposta.
Installazione e Primo Agente
# Installa con supporto OpenAI (sostituisci openai con anthropic, google, ecc.)
pip install "pydantic-ai[openai]"
# Imposta la tua chiave API
export OPENAI_API_KEY="sk-..."Il tuo primo agente in 5 righe:
from pydantic_ai import Agent
agent = Agent("openai:gpt-4o", system_prompt="You are a helpful assistant.")
result = agent.run_sync("What's the capital of France?")
print(result.output) # "Paris"Tutto qui. Agent avvolge il modello, run_sync invia un prompt e restituisce un risultato. result.output è una stringa semplice qui, ma sta per cambiare.
Output Strutturati -- Perché Esiste Pydantic AI
Questa è la funzionalità centrale. Invece di ricevere una stringa dall'LLM e sperare che sia JSON valido, definisci un modello Pydantic e l'agente restituisce un oggetto Python validato.
Prima: Output LLM Grezzo
# Il vecchio modo -- spera nel meglio
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Review the movie Inception. Return JSON with title, rating (1-10), summary."}]
)
# response.choices[0].message.content è una stringa
# Forse è JSON. Forse ha recinzioni di codice markdown. Forse il rating è "eight".
# Sei da solo.Dopo: Strutturato con Pydantic AI
from pydantic import BaseModel
from pydantic_ai import Agent
class MovieReview(BaseModel):
title: str
rating: int # Garantito essere un int, non "eight"
summary: str
recommended: bool
agent = Agent("openai:gpt-4o", result_type=MovieReview)
result = agent.run_sync("Review the movie Inception")
review = result.output # Questa è un'istanza MovieReview, non una stringa
print(f"{review.title}: {review.rating}/10")
print(f"Consigliato: {review.recommended}")
print(review.summary)La differenza è notte e giorno. result.output è un vero oggetto MovieReview. Se l'LLM restituisce rating: "eight" invece di rating: 8, la validazione di Pydantic lo intercetta. Per un'analisi più approfondita di come funziona su diversi provider, vedi la nostra guida sugli output strutturati tra provider LLM.
Cosa Succede Quando la Validazione Fallisce
Ecco la parte che nessun altro tutorial mostra: cosa succede quando l'LLM sbaglia?
from pydantic import BaseModel, Field
from pydantic_ai import Agent
class StrictReview(BaseModel):
title: str
rating: int = Field(ge=1, le=10) # Deve essere 1-10
pros: list[str] = Field(min_length=2) # Almeno 2 pro
agent = Agent("openai:gpt-4o", result_type=StrictReview)
# Se l'LLM restituisce rating=15 o solo 1 pro:
# 1. La validazione Pydantic fallisce
# 2. Il messaggio di errore viene inviato INDIETRO all'LLM
# 3. L'LLM riprova con l'output corretto
# 4. Questo si ripete fino al limite di retry
result = agent.run_sync("Review the movie Inception")Questo ciclo di retry con feedback è la killer feature di Pydantic AI. L'LLM impara dai propri errori di validazione. Non scrivi logica di retry -- il framework la gestisce.
Verdetto: Gli output strutturati sono il singolo motivo migliore per usare Pydantic AI rispetto alle chiamate API grezze. Se stai facendo il parsing del JSON dell'LLM a mano, smettila.
Strumenti e Chiamate a Funzioni
Gli strumenti permettono al tuo agente di chiamare funzioni Python per ottenere dati reali. Invece che l'LLM allucinasse fatti, può interrogare il tuo database, cercare nella tua documentazione o chiamare un'API.
Registrare uno Strumento
from pydantic_ai import Agent
agent = Agent("openai:gpt-4o")
@agent.tool
async def search_docs(query: str) -> str:
"""Search the documentation for relevant articles."""
# La tua logica di ricerca effettiva qui
results = await doc_search_engine.search(query, limit=5)
return "\n".join(r.title + ": " + r.snippet for r in results)Il decoratore @agent.tool registra la funzione. Pydantic AI legge i type hint e il docstring della funzione per dire all'LLM cosa fa lo strumento, quali argomenti accetta e cosa restituisce. Nessuna scrittura manuale di schema -- i tuoi type hint SONO lo schema. Per capire come funziona la chiamata a funzioni LLM sotto il cofano, abbiamo una guida dedicata.
RunContext: Passare Dati agli Strumenti
Qui Pydantic AI si discosta dagli altri framework. RunContext ti permette di passare dati di runtime (connessioni al database, informazioni utente, client API) ai tuoi strumenti senza stato globale.
from dataclasses import dataclass
from pydantic_ai import Agent, RunContext
@dataclass
class SupportDeps:
customer_id: str
db_connection: DatabaseConnection
agent = Agent("openai:gpt-4o", deps_type=SupportDeps)
@agent.tool
async def get_order_history(ctx: RunContext[SupportDeps], limit: int = 5) -> str:
"""Fetch recent orders for the current customer."""
orders = await ctx.deps.db_connection.query(
"SELECT * FROM orders WHERE customer_id = $1 ORDER BY date DESC LIMIT $2",
ctx.deps.customer_id, limit
)
return format_orders(orders)ctx.deps dà allo strumento accesso a tutto ciò che hai passato a runtime. Lo strumento non importa una connessione al database globale -- ne riceve una. Questa è la dependency injection, ed è ciò che rende testabili i tuoi agenti.
Un Esempio di Strumento nel Mondo Reale
@agent.tool
async def run_sql_query(ctx: RunContext[SupportDeps], sql: str) -> str:
"""Run a read-only SQL query against the analytics database.
Only SELECT queries are allowed."""
if not sql.strip().upper().startswith("SELECT"):
return "Error: only SELECT queries are allowed"
results = await ctx.deps.db_connection.fetch(sql)
return json.dumps(results, default=str)Verdetto: Le chiamate a strumenti in Pydantic AI sono più pulite di qualsiasi altro framework grazie ai type hint che fanno il lavoro pesante. Scrivi normali funzioni Python con annotazioni di tipo. Il framework capisce il resto.
Dependency Injection -- La Funzionalità che LangChain Vorrebbe Avere
Se hai usato Depends di FastAPI, già capisci il sistema DI di Pydantic AI. Se non l'hai usato, ecco la versione breve: invece che il tuo agente si allunghi a prendere ciò di cui ha bisogno (connessioni al database globali, client API, config), gli consegni tutto a runtime.
Definire le Dipendenze
from dataclasses import dataclass
from pydantic_ai import Agent
@dataclass
class AppDeps:
db: AsyncDatabasePool
search_client: SearchAPIClient
current_user: User
agent = Agent(
"openai:gpt-4o",
deps_type=AppDeps,
system_prompt="You are a customer support agent."
)Usare le Dipendenze negli Strumenti
@agent.tool
async def lookup_account(ctx: RunContext[AppDeps]) -> str:
"""Look up the current user's account details."""
account = await ctx.deps.db.fetchrow(
"SELECT * FROM accounts WHERE user_id = $1",
ctx.deps.current_user.id
)
return json.dumps(account, default=str)
# Esegui con dipendenze reali
result = await agent.run(
"What's my account status?",
deps=AppDeps(db=real_db, search_client=real_search, current_user=user)
)Perché la DI Rende i Tuoi Agenti Testabili
Questo è il vero guadagno. In LangChain, passeresti il contesto attraverso kwargs della chain o closure -- non c'è un pattern standard. In Pydantic AI, sostituire dipendenze reali con doppie di test è banale:
# Nel tuo file di test
from pydantic_ai import Agent
from your_app import agent, AppDeps
async def test_account_lookup():
mock_deps = AppDeps(
db=MockDatabase({"user_123": {"status": "active", "plan": "pro"}}),
search_client=MockSearch(),
current_user=User(id="user_123")
)
result = await agent.run("What's my account status?", deps=mock_deps)
assert "active" in result.output
assert "pro" in result.outputNessun monkey-patching. Nessun mock di import globali. Passi semplicemente deps diversi.
Verdetto: La dependency injection è il motivo per cui gli sviluppatori Python esperti preferiscono Pydantic AI. È l'influenza di FastAPI che si mostra.
Provider di Modelli -- OpenAI, Anthropic, Gemini, Ollama
Pydantic AI è agnostico rispetto al modello. Cambiare provider è una modifica di una riga:
# OpenAI
agent = Agent("openai:gpt-4o")
# Anthropic
agent = Agent("anthropic:claude-sonnet-4-20250514")
# Google Gemini
agent = Agent("google-gla:gemini-2.0-flash")
# Ollama locale
agent = Agent("ollama:llama3.1")Tutto il resto -- strumenti, output strutturati, DI -- rimane identico. La tua logica di business non cambia quando cambi modello.
| Provider | Modelli | Livello Gratuito | Complessità di Setup |
|---|---|---|---|
| OpenAI | GPT-4o, GPT-4o mini, o1 | Credito $5 (nuovi account) | Bassa -- solo chiave API |
| Anthropic | Claude Sonnet, Haiku, Opus | Nessun livello gratuito | Bassa -- solo chiave API |
| Google Gemini | Gemini 2.0 Flash, Pro | Generoso livello gratuito | Media -- configurazione progetto |
| Groq | Llama, Mixtral | Livello gratuito disponibile | Bassa -- solo chiave API |
| Ollama (locale) | Llama, Mistral, Phi, ecc. | Completamente gratuito | Media -- installa Ollama |
Verdetto: Il design agnostico rispetto al modello significa che non sei mai bloccato a un solo provider. Inizia con OpenAI per comodità, fai benchmark con Anthropic e usa Ollama per lo sviluppo locale.
Risposte in Streaming
Per le UI di chat e le applicazioni in tempo reale, lo streaming è essenziale. Pydantic AI lo supporta mantenendo la type safety:
from pydantic_ai import Agent
from pydantic import BaseModel
class AnalysisResult(BaseModel):
summary: str
sentiment: str
confidence: float
agent = Agent("openai:gpt-4o", result_type=AnalysisResult)
async def stream_analysis(text: str):
async with agent.run_stream(f"Analyze this text: {text}") as stream:
async for partial in stream.stream_structured():
# partial è un AnalysisResult parzialmente validato
print(f"Streaming: {partial}")
# Il risultato finale è completamente validato
result = await stream.get_output()
print(f"Finale: {result.summary} ({result.confidence:.0%} di confidenza)")Funziona perfettamente con StreamingResponse di FastAPI -- stesso ecosistema, stessi pattern. La documentazione degli agenti Pydantic AI copre opzioni di streaming avanzate incluso lo streaming solo-testo con stream_text().
Pydantic AI vs LangGraph vs OpenAI Agents SDK
Sei qui, quindi probabilmente stai chiedendo: "dovrei usare Pydantic AI o LangGraph?" Risposta onesta: risolvono problemi diversi, e potresti usarli entrambi.
Tabella di Confronto Funzionalità
| Funzionalità | Pydantic AI | LangGraph | OpenAI Agents SDK |
|---|---|---|---|
| Type Safety | Completa (modelli Pydantic) | Parziale (TypedDict) | Minima |
| Dependency Injection | Integrata (stile FastAPI) | Nessuna | Nessuna |
| Output Strutturati | Nativo con retry | Via parser di output | Via modalità JSON |
| Chiamate a Strumenti | Decoratore @agent.tool | Decoratore @tool | definizioni di funzione |
| Multi-Agente | Handoff di base | Avanzato (macchine a stati) | Handoff + guardrail |
| Streaming | Streaming tipizzato | Eventi di streaming | Streaming |
| Supporto Modelli | 10+ provider | Principalmente modelli LangChain | Solo OpenAI |
| Test | TestModel integrato | Nessun test integrato | Nessun test integrato |
| Curva di Apprendimento | Bassa (se conosci Pydantic) | Alta (concetti di grafo) | Bassa (API semplice) |
| Dimensione Community | In crescita (16K stelle) | Grande (ecosistema LangChain) | In crescita (supporto OpenAI) |
| Ideale Per | Agenti puliti e testabili | Workflow a stati complessi | Progetti solo OpenAI |
Quando Usare Ciascuno
Scegli Pydantic AI quando vuoi codice agente pulito e type-safe. È ideale per compiti a singolo agente con strumenti (bot di supporto clienti, estrazione dati, agenti di code review) e situazioni dove la testabilità conta. Se il tuo team usa già FastAPI e Pydantic, la curva di apprendimento è quasi piatta.
Scegli LangGraph quando hai bisogno di workflow multi-step complessi con branching condizionale, approvazione human-in-the-loop e gestione avanzata dello stato. LangGraph eccelle nell'orchestrare più step, non nella qualità dell'agente individuale. Per un approfondimento, vedi il nostro confronto completo LangGraph vs CrewAI vs OpenAI Agents SDK.
Scegli OpenAI Agents SDK quando sei al 100% su OpenAI, vuoi il setup più semplice possibile e non hai bisogno di supporto multi-provider o DI.
Il Pattern di Combinazione
Ecco cosa fanno davvero i team esperti: usano Pydantic AI per gli agenti individuali (codice pulito, testabile, output tipizzati) e LangGraph per l'orchestrazione tra agenti (routing, macchine a stati, logica condizionale). Non sono in competizione -- sono livelli complementari.
# Agente Pydantic AI -- pulito, testabile, type-safe
support_agent = Agent("openai:gpt-4o", result_type=SupportResponse, deps_type=SupportDeps)
# Grafo LangGraph -- orchestra quando chiamare quale agente
graph = StateGraph(SupportState)
graph.add_node("classify", classify_intent)
graph.add_node("support", lambda state: support_agent.run_sync(state["query"]))
graph.add_node("escalate", escalate_to_human)Verdetto: Scegli Pydantic AI per codice agente pulito e testabile. Scegli LangGraph per workflow multi-step complessi. Non si escludono a vicenda.
Testare i Tuoi Agenti con TestModel
Questa è la sezione che separa una guida per principianti da una guida per la produzione. Ogni codebase reale ha bisogno di test, e testare gli agenti è notoriamente difficile -- le chiamate LLM sono lente, costose e non deterministiche. Pydantic AI include una soluzione: TestModel.
from pydantic_ai import Agent
from pydantic_ai.models.test import TestModel
from pydantic import BaseModel
class SupportResponse(BaseModel):
answer: str
confidence: float
escalate: bool
agent = Agent("openai:gpt-4o", result_type=SupportResponse)
# Nei test: sostituisci il modello reale con TestModel
def test_support_agent():
with agent.override(model=TestModel()):
result = agent.run_sync("I need help with billing")
# TestModel restituisce dati strutturati validi che corrispondono al tuo result_type
assert isinstance(result.output, SupportResponse)
assert isinstance(result.output.confidence, float)
assert isinstance(result.output.escalate, bool)TestModel genera dati validi che corrispondono al tuo result_type senza fare chiamate API. Zero costo, deterministico, veloce. La documentazione di testing di Pydantic AI copre pattern avanzati come FunctionModel per risposte personalizzate e capture_run_messages per ispezionare le chiamate agli strumenti.
Testare Strumenti e DI Insieme
def test_order_lookup_tool():
# Dipendenze mock
mock_deps = SupportDeps(
customer_id="test-123",
db_connection=MockDB(orders=[{"id": "ord-1", "status": "shipped"}])
)
with agent.override(model=TestModel()):
result = agent.run_sync(
"Where is my order?",
deps=mock_deps
)
assert isinstance(result.output, SupportResponse)Nessuna chiamata API. Nessun test flaky. Nessun costo. Eseguilo in CI/CD insieme al resto della tua suite di test.
Questo è il gap di contenuto #1 sull'intero SERP. Nessun'altra guida a Pydantic AI copre il testing. Se stai costruendo agenti per la produzione, questo è ciò di cui hai bisogno.
Osservabilità -- Integrazione Logfire in 5 Minuti
Gli agenti in produzione hanno bisogno di osservabilità AI. Vuoi vedere ogni chiamata LLM, invocazione di strumento, latenza, conteggio token e costo. Pydantic AI si integra nativamente con Logfire, la piattaforma di osservabilità del team Pydantic (costruita su OpenTelemetry).
import logfire
from pydantic_ai import Agent
logfire.configure() # Usa la variabile d'ambiente LOGFIRE_TOKEN
logfire.instrument_pydantic_ai()
agent = Agent("openai:gpt-4o", result_type=MovieReview)
# Ogni esecuzione è ora tracciata automaticamente
result = agent.run_sync("Review Inception")Tre righe. Ottieni tracce complete che mostrano: prompt inviato, risposta del modello, chiamate agli strumenti (se presenti), passaggi/fallimenti di validazione, retry, latenza e costo stimato. Se Logfire non fa per te, Langfuse è una solida alternativa open-source con supporto per il context engineering per tracciare come evolvono i tuoi prompt.
FAQ
Cos'è Pydantic AI e come si differenzia da LangChain?
Pydantic AI è un framework per agenti type-safe dove i type hint Python guidano validazione, schemi degli strumenti e dependency injection. LangChain è un framework più grande focalizzato sul concatenamento di chiamate LLM. La differenza chiave: Pydantic AI valida gli output a livello di framework e fornisce dependency injection integrata per la testabilità -- LangChain non fa nessuna delle due per default.
Come costruisco un agente AI type-safe con Pydantic AI?
Definisci un Pydantic BaseModel per il tuo output, passalo come result_type ad Agent e chiama run_sync() o run(). L'agente restituisce un'istanza validata del tuo modello, non una stringa grezza. Vedi la sezione Output Strutturati per esempi completi.
Dovrei usare Pydantic AI o LangGraph per agenti in produzione?
Usa Pydantic AI per agenti individuali dove contano type safety, testabilità e codice pulito. Usa LangGraph per orchestrare workflow multi-step complessi con routing condizionale. Molti team usano entrambi -- agenti Pydantic AI dentro uno strato di orchestrazione LangGraph.
Come gestisce Pydantic AI le chiamate a strumenti e la dependency injection?
Decora una funzione con @agent.tool e Pydantic AI legge i suoi type hint per generare lo schema dello strumento. Per la DI, imposta deps_type sull'Agent e accetta RunContext[YourDeps] negli strumenti. Le dipendenze di runtime (connessioni DB, client API) fluiscono senza stato globale.
Come aggiungo lo streaming a un agente Pydantic AI?
Usa agent.run_stream() invece di agent.run(). Restituisce un context manager asincrono che produce risultati parziali via stream_structured() o stream_text(). Il risultato finale è ancora completamente validato rispetto al tuo result_type.
Pydantic AI è pronto per la produzione nel 2026?
Sì. La versione 1.0 è stata rilasciata a Settembre 2025 con un impegno di stabilità dell'API. È supportato dal team Pydantic (la libreria Python per la validazione dei dati più scaricata) e attualmente alla v1.74.0 con aggiornamenti regolari.
Posso usare Pydantic AI con Ollama e modelli locali?
Sì. Usa Agent("ollama:llama3.1") e assicurati che Ollama sia in esecuzione localmente. Installa l'extra del provider ollama: pip install "pydantic-ai[ollama]". Gli output strutturati e gli strumenti funzionano allo stesso modo dei provider cloud.
Come testo gli agenti Pydantic AI?
Usa TestModel -- un modello mock che genera dati strutturati validi corrispondenti al tuo result_type senza chiamate API. Avvolgi il tuo test in agent.override(model=TestModel()) e esegui assertion sull'output. Vedi la sezione Testing per esempi completi di pytest.
Pydantic AI funziona con FastAPI?
Perfettamente. Condividono la stessa filosofia di dependency injection e sono costruiti dallo stesso team. Puoi usare agenti Pydantic AI dentro endpoint FastAPI, condividere tipi di dipendenza tra di loro e fare streaming delle risposte degli agenti attraverso StreamingResponse.
Qual è la differenza tra Pydantic AI e l'OpenAI Agents SDK?
Pydantic AI è agnostico rispetto al modello (funziona con OpenAI, Anthropic, Gemini, Ollama, ecc.), ha dependency injection, TestModel per il testing e validazione Pydantic. L'OpenAI Agents SDK è più semplice ma bloccato ai modelli OpenAI e manca di DI e testing integrato. Scegli Pydantic AI per la flessibilità; scegli OpenAI Agents SDK per il setup più semplice possibile solo OpenAI.
Punti Chiave e Prossimi Passi
| Concetto | Insight Chiave | Prossimo Passo |
|---|---|---|
| Output Strutturati | Il tuo result_type è validato e riprovato automaticamente | Definisci modelli Pydantic per tutti gli output degli agenti |
| Strumenti | I type hint SONO lo schema -- nessuna definizione manuale | Costruisci strumenti con @agent.tool e RunContext |
| Dependency Injection | Passa le deps di runtime esplicitamente per la testabilità | Definisci un dataclass deps_type per ogni agente |
| Testing | TestModel elimina i costi API in CI/CD | Aggiungi agent.override(model=TestModel()) alla tua suite di test |
| Provider di Modelli | Cambio di modello in una riga, nessuna modifica al codice | Inizia con OpenAI, fai benchmark delle alternative dopo |
| Osservabilità | Setup Logfire in 3 righe per tracce complete | Aggiungi logfire.instrument_pydantic_ai() in produzione |
Inizia con un piccolo agente che ha output strutturati. Aggiungi uno strumento. Aggiungi dipendenze. Scrivi un test con TestModel. Questo è il percorso per la produzione -- e ora hai tutto ciò di cui hai bisogno per percorrerlo.
La documentazione ufficiale di Pydantic AI e il repository GitHub sono eccellenti per andare più in profondità. Il framework si muove velocemente, quindi salva tra i preferiti il changelog.