Cómo construir agentes IA con PydanticAI: del prototipo a producción en Python
Tutorial paso a paso para construir un agente de soporte B2B con PydanticAI: salidas tipadas sin parsing manual de JSON, herramientas con @agent.tool, inyección de dependencias y observabilidad con Pydantic Logfire.
Si buscas construir agentes de inteligencia artificial en Python sin renunciar a la solidez de tipos ni al control de producción, PydanticAI es el framework que lo hace posible. En Cognitiva, agencia chilena de IA, lo evaluamos con equipos B2B que procesan datos de correos, cotizaciones y formularios en flujos automáticos: los resultados confirman que eliminar el parsing manual de JSON reduce errores de integración y acelera el ciclo de entrega. PydanticAI —creado por el equipo detrás de Pydantic— aplica validación automática de esquemas: defines un modelo Pydantic como salida esperada, y el framework construye el JSON Schema, lo envía al LLM como restricción, valida la respuesta recibida y —si hay errores de validación— reintenta antes de propagar el error. El resultado llega como instancia Python tipada, sin conversión adicional de tu parte. Esto, combinado con inyección de dependencias tipada y observabilidad nativa con Pydantic Logfire, cubre los tres escollos habituales de los agentes en producción: salidas inesperadas, dependencias acopladas y falta de trazabilidad. En este tutorial construyes, paso a paso y en aproximadamente 60 minutos, un agente de soporte B2B que extrae datos estructurados de correos, ejecuta herramientas personalizadas y maneja errores sin intervención manual. Compatible con Anthropic Claude y OpenAI.
Prerrequisitos y entorno de trabajo
Para completar este tutorial necesitas Python 3.10 o superior, una cuenta activa en Anthropic o OpenAI con créditos disponibles, y conocimiento básico de Python y Pydantic. No requieres experiencia previa con frameworks de agentes: PydanticAI está diseñado para equipos que ya conocen FastAPI y quieren la misma experiencia de desarrollo en agentes de IA.
- Python 3.10 o superior (pyenv o instalador oficial de python.org)
- API key de Anthropic o de OpenAI con créditos activos
- pip actualizado (pip install --upgrade pip)
- Un editor con soporte Python (VS Code, PyCharm o Zed)
python --version
# Debe mostrar 3.10 o superior
python -m venv .venv
source .venv/bin/activate # macOS y Linux
# .venv\Scripts\activate # Windows
pip install 'pydantic-ai>=2.2.0'
export ANTHROPIC_API_KEY='tu-clave-aqui'Instalación y primer agente con salida tipada
PydanticAI está disponible en PyPI como el paquete pydantic-ai y se instala con un solo comando. El paquete base incluye soporte nativo para Anthropic, OpenAI y Google Gemini sin dependencias adicionales.
pip install 'pydantic-ai>=2.2.0'El concepto central de PydanticAI es el parámetro output_type del Agent. En vez de que el LLM retorne texto libre que debes parsear manualmente, defines un modelo Pydantic con los campos que necesitas. El framework construye el JSON Schema correspondiente, lo envía al LLM como restricción, valida la respuesta recibida y —si hay errores— reintenta antes de propagar el error. El resultado llega como instancia Python tipada, lista para usar directamente en tu lógica de negocio sin ninguna conversión adicional. Esta característica elimina una clase entera de errores de integración que ocurren cuando el LLM no respeta el formato esperado.
from pydantic import BaseModel, Field
from pydantic_ai import Agent
class SolicitudSoporte(BaseModel):
empresa: str = Field(description='Nombre de la empresa remitente')
urgencia: str = Field(description='Nivel de urgencia: baja, media o alta')
categoria: str = Field(description='Categoria del problema tecnico')
resumen: str = Field(description='Resumen del problema en una oracion')
requiere_escalamiento: bool
agente = Agent(
'anthropic:claude-sonnet-4-6',
output_type=SolicitudSoporte,
instructions=(
'Eres un agente de soporte B2B. '
'Extrae los datos estructurados del correo recibido. '
'El campo urgencia debe ser exactamente baja, media o alta.'
)
)Para ejecutar el agente usa run_sync() en código sincrónico o await agent.run() en código asíncrono. El atributo .output del resultado contiene el objeto Pydantic validado, listo para insertar en una base de datos o pasarlo a otro proceso de tu flujo.
correo = (
'Hola, llevamos 3 horas sin acceso al sistema de facturacion. '
'Esto nos impide emitir boletas. Necesitamos solucion urgente.'
)
resultado = agente.run_sync(correo)
solicitud = resultado.output
print(solicitud.urgencia) # 'alta'
print(solicitud.requiere_escalamiento) # True
print(solicitud.categoria) # 'acceso al sistema'Herramientas personalizadas e inyección de dependencias
Las herramientas en PydanticAI son funciones Python decoradas con @agente.tool. El primer parámetro siempre es un RunContext tipado con el tipo de dependencias del agente; los siguientes parámetros son los argumentos que el LLM puede proporcionar. El docstring de la función es la descripción que el LLM usa para decidir cuándo invocar la herramienta —escríbelo con claridad y especificidad para obtener mejores resultados.
Dependencias tipadas con deps_type
Para inyectar dependencias externas —conexiones a bases de datos, clientes HTTP, configuración de entorno— defines una dataclass con el parámetro deps_type del Agent. Las dependencias se pasan en cada invocación y están disponibles en todas las herramientas vía ctx.deps. Este patrón desacopla la lógica del agente de las credenciales, facilita las pruebas unitarias y permite reutilizar el mismo agente con distintas conexiones según el entorno (desarrollo, preproducción, producción).
from dataclasses import dataclass
from pydantic_ai import Agent, RunContext
@dataclass
class DepsAgente:
base_clientes: dict
historial_tickets: list
agente_v2 = Agent(
'anthropic:claude-sonnet-4-6',
output_type=SolicitudSoporte,
deps_type=DepsAgente,
retries=2,
instructions='Eres un agente de soporte B2B. Usa las herramientas disponibles.'
)
@agente_v2.tool
async def buscar_historial(
ctx: RunContext[DepsAgente],
empresa: str
) -> list:
'Busca el historial de tickets de la empresa en los ultimos 30 dias.'
historial = ctx.deps.historial_tickets
return [t for t in historial if t.get('empresa') == empresa]ModelRetry: errores que el LLM puede corregir
ModelRetry es la excepción específica de PydanticAI para errores que el LLM puede resolver por sí solo. Cuando una herramienta lanza ModelRetry, el framework envía el mensaje al LLM para que corrija su llamada y reintenta automáticamente hasta el límite definido en retries. Úsala para validaciones que el LLM puede solucionar con información adicional, como un RUT mal formateado, un nombre de empresa ambiguo o un parámetro faltante que el cliente mencionó en el correo.
from pydantic_ai import ModelRetry
@agente_v2.tool
async def verificar_contrato(
ctx: RunContext[DepsAgente],
rut_empresa: str
) -> dict:
'Verifica si la empresa tiene contrato vigente. Formato esperado del RUT: XX.XXX.XXX-X.'
cliente = ctx.deps.base_clientes.get(rut_empresa)
if cliente is None:
raise ModelRetry(
'Empresa no encontrada con ese RUT. '
'Solicita al remitente que confirme su RUT en formato XX.XXX.XXX-X.'
)
return {
'nombre': cliente['nombre'],
'plan': cliente['plan'],
'tickets_abiertos': cliente.get('tickets_abiertos', 0)
}Agente B2B completo: extractor de correos de soporte
El siguiente agente integra un modelo de salida Pydantic, dependencias tipadas y una herramienta de enriquecimiento en menos de 70 líneas. Procesa correos de soporte B2B, extrae datos estructurados y verifica automáticamente si el cliente tiene contrato vigente antes de registrar el ticket. Es el patrón que Cognitiva aplica en integraciones con sistemas de CRM y soporte de empresas chilenas que manejan volúmenes medios de solicitudes entrantes.
import os
from dataclasses import dataclass
from pydantic import BaseModel, Field
from pydantic_ai import Agent, RunContext, ModelRetry
# Modelo de salida tipado
class TicketSoporte(BaseModel):
empresa: str = Field(description='Nombre de la empresa')
rut: str = Field(default='', description='RUT de la empresa si se menciona')
urgencia: str = Field(description='baja, media o alta')
categoria: str = Field(description='Categoria principal del problema')
resumen: str = Field(description='Descripcion del problema en una oracion')
requiere_escalamiento: bool
# Dependencias tipadas
@dataclass
class DepsSoporte:
base_clientes: dict
umbral_escalamiento: int = 2
# Agente con output_type y deps_type
agente_soporte = Agent(
'anthropic:claude-sonnet-4-6',
output_type=TicketSoporte,
deps_type=DepsSoporte,
retries=2,
instructions=(
'Eres un agente de soporte B2B de primer nivel. '
'Extrae datos estructurados del correo. '
'Verifica siempre el contrato del cliente si mencionan su RUT.'
)
)
@agente_soporte.tool
async def verificar_cliente(
ctx: RunContext[DepsSoporte],
rut: str
) -> dict:
'Verifica el contrato vigente del cliente por RUT (formato XX.XXX.XXX-X).'
cliente = ctx.deps.base_clientes.get(rut)
if not cliente:
raise ModelRetry('RUT no encontrado. Solicita confirmacion del RUT al remitente.')
tickets = cliente.get('tickets_abiertos', 0)
return {
'nombre': cliente['nombre'],
'plan': cliente['plan'],
'escalar': tickets >= ctx.deps.umbral_escalamiento
}Para ejecutar el agente, instancia las dependencias con los datos del entorno y llama a run() con el texto del correo. El objeto de salida estará validado por Pydantic antes de que lo recibas:
import asyncio
clientes = {
'76.123.456-7': {
'nombre': 'Acme SpA',
'plan': 'empresarial',
'tickets_abiertos': 3
}
}
deps = DepsSoporte(base_clientes=clientes, umbral_escalamiento=2)
correo = (
'Hola, soy de Acme SpA, RUT 76.123.456-7. '
'Llevamos 3 horas sin acceso al sistema de facturacion. '
'Necesitamos solucion urgente.'
)
async def main():
resultado = await agente_soporte.run(correo, deps=deps)
ticket = resultado.output
print(ticket.empresa) # 'Acme SpA'
print(ticket.urgencia) # 'alta'
print(ticket.requiere_escalamiento) # True
asyncio.run(main())La diferencia entre un prototipo y un agente de producción no es el modelo: es la validación de datos, la inyección de dependencias y la trazabilidad. PydanticAI resuelve los tres en menos de 70 líneas de código.
Producción: resiliencia y observabilidad con Logfire
Pydantic Logfire es la plataforma de observabilidad nativa del ecosistema PydanticAI, construida sobre OpenTelemetry. Con dos líneas de código instrumentas todos los agentes del proceso: las trazas incluyen latencia por paso, tokens consumidos, validaciones de schema y la cadena de herramientas invocadas. Los datos son compatibles con Datadog, Jaeger y cualquier backend OTLP estándar, por lo que no necesitas cambiar tu infraestructura de observabilidad existente.
import logfire
import os
logfire.configure(token=os.environ['LOGFIRE_TOKEN'])
logfire.instrument_pydantic_ai()
# Todos los agentes del proceso quedan instrumentados automaticamente.
# El panel de Logfire muestra latencia, tokens consumidos y trazas completas
# sin modificar el codigo de los agentes.Para la configuración de producción, define un modelo principal y uno alternativo para degradación controlada. El método agent.override() cambia el modelo sin modificar la lógica del agente, lo que resulta útil para reducir costos en horas de alto volumen o cuando el proveedor principal no está disponible. Para las pruebas automatizadas, TestModel genera respuestas válidas para el output_type sin llamar al LLM ni gastar tokens de API.
from pydantic_ai.models.anthropic import AnthropicModel
from pydantic_ai.models.test import TestModel
modelo_principal = AnthropicModel('claude-sonnet-4-6')
modelo_economico = AnthropicModel('claude-haiku-4-5')
# Agente con configuracion de produccion
agente_prod = Agent(
modelo_principal,
output_type=TicketSoporte,
deps_type=DepsSoporte,
retries=2
)
deps = DepsSoporte(base_clientes={}, umbral_escalamiento=2)
consulta = 'Texto del correo de soporte a procesar...'
# Degradacion controlada: override() es un gestor de contexto temporal,
# NO retorna un agente nuevo. Dentro del bloque with, agente_prod usa el modelo economico.
with agente_prod.override(model=modelo_economico):
resultado = agente_prod.run_sync(consulta, deps=deps)
# Pruebas de CI sin consumo de API: TestModel dentro del mismo gestor de contexto.
with agente_prod.override(model=TestModel()):
resultado_test = agente_prod.run_sync(consulta, deps=deps)| Estrategia | Implementación | Cuándo usarla |
|---|---|---|
| Reintento automático | retries=N en Agent | Validación de schema que falla ocasionalmente |
| Error controlado | raise ModelRetry(...) en @agent.tool | Datos de entrada incompletos o ambiguos |
| Degradación de modelo | with agent.override(model=modelo_economico): | Alto volumen o indisponibilidad del modelo principal |
| Modo prueba | with agent.override(model=TestModel()): | Pruebas unitarias sin consumo de API |
PydanticAI vs LangChain: cuándo elegir cada framework
La pregunta más frecuente al evaluar PydanticAI es cuándo usarlo en vez de LangChain. Son herramientas con fortalezas distintas, no rivales directos. La elección depende del tipo de flujo y del perfil del equipo que lo mantiene.
PydanticAI es la elección natural cuando el agente debe retornar datos estructurados —formularios, tickets, extracciones, clasificaciones—, cuando el equipo ya trabaja con Pydantic en FastAPI, o cuando la validación de schema en producción es un requisito no negociable. LangChain tiene mayor ecosistema de integraciones listas para usar y es más maduro para flujos conversacionales con memoria de largo plazo o cadenas de múltiples modelos en secuencia.
| Característica | PydanticAI | LangChain |
|---|---|---|
| Salidas tipadas | Pydantic nativo, sin parsers adicionales | StructuredOutputParser manual |
| Inyección de dependencias | Nativa con deps_type y RunContext | No nativa; se inyecta manualmente |
| Observabilidad integrada | Pydantic Logfire (OpenTelemetry) | LangSmith (plataforma separada) |
| Compatibilidad con FastAPI | Natural (mismo ecosistema Pydantic) | Mediante adaptadores |
| Validación automática | Reintentos automáticos por schema | Manejo manual del programador |
| Ideal para | Datos estructurados, extracción, B2B | Flujos conversacionales complejos |
La buena noticia es que ambos frameworks pueden coexistir sin conflictos en el mismo proyecto. PydanticAI maneja la capa de extracción y validación estructurada; LangChain puede ocuparse de los flujos conversacionales o las memorias vectoriales donde tiene más recorrido. No es una decisión excluyente.
Construir un agente B2B con PydanticAI en Python
Tutorial paso a paso para crear un agente de soporte que extrae datos tipados de correos, ejecuta herramientas personalizadas y maneja errores de producción con PydanticAI.
Fase 1: Entorno e instalación
Fase 2: Modelo de salida tipado
Fase 3: Herramientas y dependencias
Fase 4: Agente B2B completo
Fase 5: Producción y observabilidad
Preguntas frecuentes
Da el siguiente paso
Hablemos
Cuéntanos sobre tu negocio. Respondemos en 24 horas.
Otros canales
Escríbenos por WhatsApp o agenda una llamada de 30 minutos.