Pydantic AI — Guía Completa
Framework de agentes GenAI para Python que lleva la ergonomía de FastAPI al desarrollo con IA generativa.
Tabla de Contenidos
- Fundamentos — Agentes básicos, tipos, validación, Hello World
- Tools y Dependency Injection — @agent.tool, @agent.tool_plain, RunContext
- Patrones de Producción — Testing, streaming, capabilities, override
- 10 Skills de Programación — Type-safe, DI, structured outputs, tools, streaming, model-agnostic
1. Fundamentos
Agentes básicos:
from pydantic_ai import Agent
agent = Agent('openai:gpt-4o', system_prompt='Eres un asistente útil.')
result = agent.run_sync('¿Qué tiempo hace en Madrid?')
print(result.data)
Validación con Pydantic:
from pydantic import BaseModel
class Tiempo(BaseModel):
ciudad: str
temperatura: float
agent = Agent('openai:gpt-4o', output_type=Tiempo)
result = agent.run_sync('¿Qué tiempo hace en Madrid?')
# result.data es una instancia validada de Tiempo
Concepto clave: Agent = LLM + System Prompt + Tools + Output Schema. Todo tipado.
2. Tools y Dependency Injection
Tool básico:
@agent.tool
def sumar(a: int, b: int) -> int:
"""Suma dos números."""
return a + b
Tool con contexto (DI):
from dataclasses import dataclass
from pydantic_ai import Agent, RunContext
@dataclass
class AppDeps:
db: Database
agent = Agent('openai:gpt-4o', deps_type=AppDeps)
@agent.tool
def buscar(query: str, ctx: RunContext[AppDeps]) -> list:
"""Busca en la base de datos."""
return ctx.deps.db.query(query)
Reglas:
@agent.tool→ necesitaRunContext[Deps]como primer parámetro@agent.tool_plain→ sin contextoctx.depspara acceder a dependencias- Preferir
async def(las sync se ejecutan en thread pool) - La docstring es la descripción que ve el LLM
3. Patrones de Producción
Structured Outputs:
class SupportOutput(BaseModel):
advice: str = Field(description='Advice returned to customer')
risk: int = Field(description='Risk level', ge=0, le=10)
agent = Agent('openai:gpt-4o', output_type=SupportOutput)
Testing con TestModel/FunctionModel:
from pydantic_ai import TestModel
with agent.override(model=TestModel()):
result = agent.run_sync("test")
# Para tests custom:
from pydantic_ai import FunctionModel
agent = Agent('openai:gpt-4o', model=FunctionModel(my_model_fn))
Streaming:
async with agent.run_stream('query') as result:
print(result.output) # output parcial en tiempo real
Dynamic Instructions:
@agent.instructions
async def add_context(ctx: RunContext[Deps]) -> str:
return f"Customer: {await ctx.deps.db.name(id=ctx.deps.customer_id)}"
Capabilities:
from pydantic_ai.capabilities import Thinking, WebSearch
agent = Agent('anthropic:claude-sonnet-4-6', capabilities=[Thinking(), WebSearch(...)])
Agent Override (producción):
with agent.override(model='openai:gpt-5.2' if prod else 'ollama:llama3'):
result = await agent.run("query")
4. 10 Skills de Programación
- Type-Safe by Design — Type hints mueven errores de runtime a write-time
- Dependency Injection con Dataclasses —
RunContext[DepsType]como primer parámetro - Structured Outputs con BaseModel — Fuerza al LLM a devolver datos estructurados
- Function Tools —
@agent.tooly@agent.tool_plain - Dynamic Instructions — Inyectar contexto dinámico en runtime
- Composable Capabilities — Bundles reutilizables (Thinking, WebSearch, MCP)
- TestModel/FunctionModel — Tests sin llamadas LLM reales
- Agent Override — Reemplazar modelo/deps en runtime sin modificar el agente
- Streaming con Validación —
run_stream()para outputs estructurados en tiempo real - Model-Agnostic — 20+ proveedores, solo cambias
'provider:model-name'
⚠️ Pitfalls
- No confundir
run_sync(bloqueante) conrun(async) - TestModel no emula native tools — sobrescribe con
Agent.override()en tests - Funciones sync en tools se ejecutan en thread pool → preferir
async def ALLOW_MODEL_REQUESTS=Falsees global → úsalo en tests- Dependencias son dataclasses — no objetos arbitrarios sin tipado
- Pydantic v2 strict validation —
list[float]NO aceptalist[list[float]]