
Pydantic AI: La Guía de Producción (Más Allá del Hello World)
Las salidas brutas de los LLM rompen las aplicaciones. Pides JSON y obtienes Markdown. Pides un número entre 1 y 10 y obtienes "¡Claro! Aquí tienes un número: siete." Si has construido algo real con APIs de LLM, has escrito código de análisis defensivo que te hace cuestionar tus elecciones de carrera. Pydantic AI soluciona esto -- es el framework de agentes con seguridad de tipos construido por el mismo equipo detrás de Pydantic y FastAPI. Piénsalo como "FastAPI para agentes IA": defines lo que quieres con anotaciones de tipo Python, y el framework gestiona la validación, los reintentos y las llamadas a herramientas.
Esta guía de Pydantic AI es para desarrolladores que ya han hecho su primera llamada a un LLM y quieren patrones de producción: salidas estructuradas que no se rompan, inyección de dependencias para agentes testables, y herramientas del mundo real más allá de las APIs del tiempo. Al final, tendrás agentes funcionales con herramientas, DI, streaming y pruebas.
<!-- IMAGE: Arquitectura de agente Pydantic AI -- El agente recibe un prompt, llama a herramientas via RunContext, valida la salida a través del modelo Pydantic -->Pydantic AI de un Vistazo
| Atributo | Detalles |
|---|---|
| Qué es | Framework de agentes IA con seguridad de tipos para Python |
| Construido por | El equipo de Pydantic (Samuel Colvin et al.) |
| Filosofía | "FastAPI para agentes IA" -- las anotaciones de tipo lo impulsan todo |
| Licencia | MIT (código abierto) |
| Versión actual | v1.74.0 (marzo 2026) |
| Versión Python | 3.9+ |
| Modelos soportados | OpenAI, Anthropic, Google Gemini, Groq, Mistral, Ollama y más |
| Características clave | Salidas estructuradas, llamadas a herramientas, inyección de dependencias, streaming, TestModel |
| Estrellas GitHub | 16.000+ |
| Listo para producción | Sí -- v1.0 lanzada en septiembre de 2025 |
| Observabilidad | Integración nativa con Logfire (basado en OpenTelemetry) |
| Curva de aprendizaje | Baja si conoces Pydantic/FastAPI; moderada de otro modo |
Las características destacadas son las salidas estructuradas (validadas con modelos Pydantic), la inyección de dependencias (como Depends de FastAPI) y TestModel (LLM simulado para pruebas sin llamadas a la API). Si vienes de LangChain y te preguntas "¿hay algo más limpio?", probablemente sea esto.
Instalación y Primer Agente
# Instalar con soporte para OpenAI (reemplaza openai por anthropic, google, etc.)
pip install "pydantic-ai[openai]"
# Establece tu clave API
export OPENAI_API_KEY="sk-..."Tu primer agente en 5 líneas:
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"Eso es todo. Agent envuelve el modelo, run_sync envía un prompt y devuelve un resultado. El result.output es una simple cadena aquí, pero eso está a punto de cambiar.
Salidas Estructuradas -- Por Qué Existe Pydantic AI
Esta es la característica central. En lugar de obtener una cadena del LLM y esperar que sea JSON válido, defines un modelo Pydantic y el agente devuelve un objeto Python validado.
Antes: Salida Bruta del LLM
# La forma antigua -- esperar lo mejor
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 es una cadena
# Tal vez es JSON. Tal vez tiene comillas de bloque Markdown. Tal vez rating es "eight".
# Estás solo.Después: Estructurado con Pydantic AI
from pydantic import BaseModel
from pydantic_ai import Agent
class MovieReview(BaseModel):
title: str
rating: int # Garantizado que sea un int, no "eight"
summary: str
recommended: bool
agent = Agent("openai:gpt-4o", result_type=MovieReview)
result = agent.run_sync("Review the movie Inception")
review = result.output # Esto es una instancia MovieReview, no una cadena
print(f"{review.title}: {review.rating}/10")
print(f"Recomendado: {review.recommended}")
print(review.summary)La diferencia es como el día y la noche. result.output es un objeto MovieReview real. Si el LLM devuelve rating: "eight" en lugar de rating: 8, la validación de Pydantic lo detecta. Para una mirada más profunda a cómo funciona esto con diferentes proveedores, consulta nuestra guía sobre salidas estructuradas con proveedores LLM.
Qué Pasa Cuando Falla la Validación
Aquí está la parte que ningún otro tutorial muestra: ¿qué sucede cuando el LLM comete un error?
from pydantic import BaseModel, Field
from pydantic_ai import Agent
class StrictReview(BaseModel):
title: str
rating: int = Field(ge=1, le=10) # Debe ser 1-10
pros: list[str] = Field(min_length=2) # Al menos 2 ventajas
agent = Agent("openai:gpt-4o", result_type=StrictReview)
# Si el LLM devuelve rating=15 o solo 1 ventaja:
# 1. La validación de Pydantic falla
# 2. El mensaje de error se envía DE VUELTA al LLM
# 3. El LLM lo intenta de nuevo con la salida corregida
# 4. Esto se repite hasta el límite de reintentos
result = agent.run_sync("Review the movie Inception")Este bucle de reintento con retroalimentación es la característica asesina de Pydantic AI. El LLM aprende de sus propios errores de validación. No escribes lógica de reintentos -- el framework lo maneja.
Veredicto: Las salidas estructuradas son la mejor razón única para usar Pydantic AI en lugar de llamadas directas a la API. Si estás analizando JSON de LLM manualmente, detente.
Herramientas y Llamadas a Funciones
Las herramientas permiten a tu agente llamar a funciones Python para obtener datos reales. En lugar de que el LLM alucine hechos, puede consultar tu base de datos, buscar en tus documentos o llamar a una API.
Registrar una Herramienta
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."""
# Tu lógica de búsqueda real aquí
results = await doc_search_engine.search(query, limit=5)
return "\n".join(r.title + ": " + r.snippet for r in results)El decorador @agent.tool registra la función. Pydantic AI lee las anotaciones de tipo y el docstring de la función para decirle al LLM qué hace la herramienta, qué argumentos acepta y qué devuelve. Sin escritura manual de esquemas -- tus anotaciones de tipo SON el esquema. Para contexto sobre cómo funcionan las llamadas a funciones de LLM bajo el capó, tenemos una guía dedicada.
RunContext: Pasar Datos a las Herramientas
Aquí es donde Pydantic AI diverge de otros frameworks. RunContext te permite pasar datos de tiempo de ejecución (conexiones de base de datos, información del usuario, clientes API) a tus herramientas sin estado global.
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)El ctx.deps da a la herramienta acceso a lo que pasaste en tiempo de ejecución. La herramienta no importa una conexión de base de datos global -- recibe una. Esto es inyección de dependencias, y es lo que hace que tus agentes sean testables.
Un Ejemplo de Herramienta del Mundo Real
@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)Veredicto: Las llamadas a herramientas en Pydantic AI son más limpias que en cualquier otro framework gracias a las anotaciones de tipo que hacen el trabajo pesado. Escribes funciones Python normales con anotaciones de tipo. El framework resuelve el resto.
Inyección de Dependencias -- La Característica que LangChain Quisiera Tener
Si has usado Depends de FastAPI, ya entiendes el sistema DI de Pydantic AI. Si no, aquí está la versión corta: en lugar de que tu agente salga a buscar lo que necesita (conexiones de base de datos globales, clientes API, configuración), le das todo en tiempo de ejecución.
Definir Dependencias
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."
)Usar Dependencias en Herramientas
@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)
# Ejecutar con dependencias reales
result = await agent.run(
"What's my account status?",
deps=AppDeps(db=real_db, search_client=real_search, current_user=user)
)Por Qué DI Hace Tus Agentes Testables
Esta es la recompensa real. En LangChain, pasarías el contexto a través de kwargs de cadena o closures -- no hay un patrón estándar. En Pydantic AI, intercambiar dependencias reales por dobles de prueba es trivial:
# En tu archivo de prueba
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.outputSin monkey-patching. Sin mockear imports globales. Simplemente pasas deps diferentes.
Veredicto: La inyección de dependencias es por qué los desarrolladores Python con experiencia prefieren Pydantic AI. Es la influencia de FastAPI mostrándose.
Proveedores de Modelos -- OpenAI, Anthropic, Gemini, Ollama
Pydantic AI es agnóstico al modelo. Cambiar de proveedor es un cambio de una línea:
# 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 local
agent = Agent("ollama:llama3.1")Todo lo demás -- herramientas, salidas estructuradas, DI -- permanece idéntico. Tu lógica de negocio no cambia cuando cambias de modelo.
| Proveedor | Modelos | Nivel gratuito | Complejidad de configuración |
|---|---|---|---|
| OpenAI | GPT-4o, GPT-4o mini, o1 | Crédito de $5 (cuentas nuevas) | Baja -- solo clave API |
| Anthropic | Claude Sonnet, Haiku, Opus | Sin nivel gratuito | Baja -- solo clave API |
| Google Gemini | Gemini 2.0 Flash, Pro | Nivel gratuito generoso | Media -- configuración de proyecto |
| Groq | Llama, Mixtral | Nivel gratuito disponible | Baja -- solo clave API |
| Ollama (local) | Llama, Mistral, Phi, etc. | Completamente gratis | Media -- instalar Ollama |
Veredicto: El diseño agnóstico al modelo significa que nunca estás atado a un proveedor. Empieza con OpenAI por conveniencia, benchmarkea con Anthropic y usa Ollama para el desarrollo local.
Respuestas en Streaming
Para interfaces de chat y aplicaciones en tiempo real, el streaming es esencial. Pydantic AI lo soporta manteniendo la seguridad de tipos:
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 es un AnalysisResult parcialmente validado
print(f"Streaming: {partial}")
# El resultado final está completamente validado
result = await stream.get_output()
print(f"Final: {result.summary} ({result.confidence:.0%} de confianza)")Esto funciona perfectamente con StreamingResponse de FastAPI -- mismo ecosistema, mismos patrones. La documentación de agentes Pydantic AI cubre opciones de streaming avanzadas incluyendo streaming solo de texto con stream_text().
Pydantic AI vs LangGraph vs OpenAI Agents SDK
Estás aquí, así que probablemente te preguntas: "¿debería usar Pydantic AI o LangGraph?" Respuesta honesta: resuelven problemas diferentes, y podrías usar ambos.
Tabla de Comparación de Características
| Característica | Pydantic AI | LangGraph | OpenAI Agents SDK |
|---|---|---|---|
| Seguridad de tipos | Completa (modelos Pydantic) | Parcial (TypedDict) | Mínima |
| Inyección de dependencias | Integrada (estilo FastAPI) | Ninguna | Ninguna |
| Salidas estructuradas | Nativa con reintento | Via analizadores de salida | Via modo JSON |
| Llamadas a herramientas | Decorador @agent.tool | Decorador @tool | Definiciones de funciones |
| Multi-agente | Traspasos básicos | Avanzado (máquinas de estado) | Traspasos + guardrails |
| Streaming | Streaming tipado | Eventos de streaming | Streaming |
| Soporte de modelos | 10+ proveedores | Principalmente modelos LangChain | Solo OpenAI |
| Pruebas | TestModel integrado | Sin pruebas integradas | Sin pruebas integradas |
| Curva de aprendizaje | Baja (si conoces Pydantic) | Alta (conceptos de grafo) | Baja (API simple) |
| Tamaño de comunidad | Creciendo (16K estrellas) | Grande (ecosistema LangChain) | Creciendo (respaldo de OpenAI) |
| Mejor para | Agentes limpios y testables | Flujos de trabajo de estado complejos | Proyectos solo con OpenAI |
Cuándo Usar Cada Uno
Elige Pydantic AI cuando quieras código de agente limpio y con seguridad de tipos. Es ideal para tareas de agente único con herramientas (bots de soporte al cliente, extracción de datos, agentes de revisión de código) y situaciones donde la testabilidad importa. Si tu equipo ya usa FastAPI y Pydantic, la curva de aprendizaje es casi plana.
Elige LangGraph cuando necesites flujos de trabajo de múltiples pasos complejos con ramificación condicional, aprobación humana en el bucle y gestión de estado sofisticada. LangGraph sobresale en la orquestación de múltiples pasos, no en la calidad de los agentes individuales. Para un análisis profundo, consulta nuestra comparación completa de LangGraph vs CrewAI vs OpenAI Agents SDK.
Elige OpenAI Agents SDK cuando estés al 100% con OpenAI, quieras la configuración más simple posible y no necesites soporte multi-proveedor ni DI.
El Patrón de Combinación
Lo que los equipos con experiencia realmente hacen: usar Pydantic AI para agentes individuales (código limpio, testable, salidas tipadas) y LangGraph para la orquestación entre agentes (enrutamiento, máquinas de estado, lógica condicional). No compiten -- son capas complementarias.
# Agente Pydantic AI -- limpio, testable, con seguridad de tipos
support_agent = Agent("openai:gpt-4o", result_type=SupportResponse, deps_type=SupportDeps)
# Grafo LangGraph -- orquesta cuándo llamar a qué 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)Veredicto: Elige Pydantic AI para código de agente limpio y testable. Elige LangGraph para flujos de trabajo complejos de múltiples pasos. No son mutuamente excluyentes.
Probando Tus Agentes con TestModel
Esta es la sección que separa una guía para principiantes de una guía de producción. Todo codebase real necesita pruebas, y probar agentes es notoriamente difícil -- las llamadas a LLM son lentas, caras y no deterministas. Pydantic AI incluye una solución: 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)
# En pruebas: reemplazar el modelo real con TestModel
def test_support_agent():
with agent.override(model=TestModel()):
result = agent.run_sync("I need help with billing")
# TestModel devuelve datos estructurados válidos que coinciden con tu result_type
assert isinstance(result.output, SupportResponse)
assert isinstance(result.output.confidence, float)
assert isinstance(result.output.escalate, bool)TestModel genera datos válidos que coinciden con tu result_type sin hacer llamadas a la API. Cero costo, determinista, rápido. La documentación de pruebas de Pydantic AI cubre patrones avanzados como FunctionModel para respuestas personalizadas y capture_run_messages para inspeccionar las llamadas a herramientas.
Probando Herramientas y DI Juntos
def test_order_lookup_tool():
# Dependencias simuladas
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)Sin llamadas a la API. Sin pruebas inestables. Sin costo. Ejecuta esto en CI/CD junto al resto de tu suite de pruebas.
Esta es la brecha de contenido n.° 1 en todo el SERP. Ninguna otra guía de Pydantic AI cubre las pruebas. Si estás construyendo agentes para producción, esto es lo que necesitas.
Observabilidad -- Integración con Logfire en 5 Minutos
Los agentes en producción necesitan observabilidad IA. Quieres ver cada llamada a LLM, invocación de herramienta, latencia, conteo de tokens y costo. Pydantic AI se integra de forma nativa con Logfire, la plataforma de observabilidad del equipo de Pydantic (construida sobre OpenTelemetry).
import logfire
from pydantic_ai import Agent
logfire.configure() # Usa la variable de entorno LOGFIRE_TOKEN
logfire.instrument_pydantic_ai()
agent = Agent("openai:gpt-4o", result_type=MovieReview)
# Cada ejecución ahora se traza automáticamente
result = agent.run_sync("Review Inception")Tres líneas. Obtienes trazas completas que muestran: prompt enviado, respuesta del modelo, llamadas a herramientas (si las hay), pases/fallos de validación, reintentos, latencia y costo estimado. Si Logfire no es lo tuyo, Langfuse es una sólida alternativa de código abierto con soporte de ingeniería de contexto para rastrear cómo evolucionan tus prompts.
FAQ
¿Qué es Pydantic AI y en qué se diferencia de LangChain?
Pydantic AI es un framework de agentes con seguridad de tipos donde las anotaciones de tipo Python impulsan la validación, los esquemas de herramientas y la inyección de dependencias. LangChain es un framework más grande enfocado en encadenar llamadas a LLM. La diferencia clave: Pydantic AI valida las salidas a nivel de framework y proporciona inyección de dependencias integrada para la testabilidad -- LangChain no hace ninguna de las dos cosas por defecto.
¿Cómo construyo un agente IA con seguridad de tipos con Pydantic AI?
Define un BaseModel de Pydantic para tu salida, pásalo como result_type a Agent y llama a run_sync() o run(). El agente devuelve una instancia validada de tu modelo, no una cadena bruta. Consulta la sección de Salidas Estructuradas para ejemplos completos.
¿Debería usar Pydantic AI o LangGraph para agentes en producción?
Usa Pydantic AI para agentes individuales donde la seguridad de tipos, la testabilidad y el código limpio importan. Usa LangGraph para orquestar flujos de trabajo de múltiples pasos complejos con enrutamiento condicional. Muchos equipos usan ambos -- agentes Pydantic AI dentro de una capa de orquestación LangGraph.
¿Cómo gestiona Pydantic AI las llamadas a herramientas y la inyección de dependencias?
Decora una función con @agent.tool y Pydantic AI lee sus anotaciones de tipo para generar el esquema de la herramienta. Para DI, establece deps_type en el Agent y acepta RunContext[TusDeps] en las herramientas. Las dependencias en tiempo de ejecución (conexiones DB, clientes API) fluyen sin estado global.
¿Cómo añado streaming a un agente Pydantic AI?
Usa agent.run_stream() en lugar de agent.run(). Devuelve un gestor de contexto asíncrono que produce resultados parciales via stream_structured() o stream_text(). El resultado final sigue estando completamente validado contra tu result_type.
¿Está Pydantic AI listo para producción en 2026?
Sí. La versión 1.0 se lanzó en septiembre de 2025 con un compromiso de estabilidad de API. Está respaldado por el equipo de Pydantic (la biblioteca Python más descargada para la validación de datos) y actualmente está en v1.74.0 con actualizaciones regulares.
¿Puedo usar Pydantic AI con Ollama y modelos locales?
Sí. Usa Agent("ollama:llama3.1") y asegúrate de que Ollama esté ejecutándose localmente. Instala el extra del proveedor ollama: pip install "pydantic-ai[ollama]". Las salidas estructuradas y las herramientas funcionan igual que con los proveedores en la nube.
¿Cómo pruebo los agentes Pydantic AI?
Usa TestModel -- un modelo simulado que genera datos estructurados válidos que coinciden con tu result_type sin llamadas a la API. Envuelve tu prueba con agent.override(model=TestModel()) y ejecuta assertions sobre la salida. Consulta la sección de Pruebas para ejemplos completos de pytest.
¿Funciona Pydantic AI con FastAPI?
Perfectamente. Comparten la misma filosofía de inyección de dependencias y están construidos por el mismo equipo. Puedes usar agentes Pydantic AI dentro de endpoints FastAPI, compartir tipos de dependencias entre ellos y hacer streaming de respuestas de agentes a través de StreamingResponse.
¿Cuál es la diferencia entre Pydantic AI y el OpenAI Agents SDK?
Pydantic AI es agnóstico al modelo (funciona con OpenAI, Anthropic, Gemini, Ollama, etc.), tiene inyección de dependencias, TestModel para pruebas y validación de Pydantic. El OpenAI Agents SDK es más simple pero está limitado a modelos OpenAI y carece de DI y pruebas integradas. Elige Pydantic AI para flexibilidad; elige OpenAI Agents SDK para la configuración más simple posible solo con OpenAI.
Conclusiones Principales y Próximos Pasos
| Concepto | Idea Clave | Próximo Paso |
|---|---|---|
| Salidas Estructuradas | Tu result_type se valida y reintenta automáticamente | Definir modelos Pydantic para todas las salidas de agentes |
| Herramientas | Las anotaciones de tipo SON el esquema -- sin definiciones manuales | Construir herramientas con @agent.tool y RunContext |
| Inyección de Dependencias | Pasar deps de tiempo de ejecución explícitamente para la testabilidad | Definir una dataclass deps_type para cada agente |
| Pruebas | TestModel elimina los costos de API en CI/CD | Añadir agent.override(model=TestModel()) a tu suite de pruebas |
| Proveedores de Modelos | Cambio de modelo en una línea, sin cambios de código | Empieza con OpenAI, benchmarkea alternativas más tarde |
| Observabilidad | Configuración de Logfire en 3 líneas para trazas completas | Añadir logfire.instrument_pydantic_ai() a producción |
Empieza con un agente pequeño que tenga salidas estructuradas. Añade una herramienta. Añade dependencias. Escribe una prueba con TestModel. Ese es el camino de producción -- y ahora tienes todo lo que necesitas para recorrerlo.
La documentación oficial de Pydantic AI y el repositorio GitHub son excelentes para profundizar más. El framework se mueve rápido, así que marca el changelog como favorito.