with_structured_output
El método with_structured_output
representa la forma más directa y moderna de obtener respuestas estructuradas en JSON desde modelos de lenguaje en LangChain. Este enfoque permite vincular directamente un modelo Pydantic con un modelo de chat, garantizando que las respuestas sigan un esquema predefinido sin necesidad de parseo manual posterior.
Configuración básica
Para utilizar salidas estructuradas, necesitamos definir primero un modelo Pydantic que represente la estructura deseada de nuestra respuesta. Este modelo actúa como un contrato que especifica exactamente qué campos esperamos y sus tipos de datos:
from typing import Optional
from pydantic import BaseModel, Field
from langchain_core.messages import HumanMessage, SystemMessage
from langchain.chat_models import init_chat_model
class ProductReview(BaseModel):
product_name: str = Field(description="Nombre del producto evaluado")
rating: int = Field(description="Puntuación del 1 al 5", ge=1, le=5)
pros: list[str] = Field(description="Aspectos positivos del producto")
cons: list[str] = Field(description="Aspectos negativos del producto")
recommendation: bool = Field(description="Si se recomienda o no el producto")
price_range: Optional[str] = Field(
default=None,
description="Rango de precio estimado"
)
Una vez definido el modelo, podemos crear una instancia estructurada del modelo de chat utilizando el método with_structured_output()
:
# Inicializar el modelo base
model = init_chat_model("gpt-4.1", model_provider="openai")
# Crear la versión estructurada
structured_model = model.with_structured_output(ProductReview)
Invocación y uso práctico
El modelo estructurado se comporta como cualquier otro modelo de LangChain, pero garantiza que la respuesta se ajuste al esquema Pydantic definido:
messages = [
SystemMessage("""
Eres un experto en análisis de productos tecnológicos.
Evalúa productos de forma objetiva considerando características,
rendimiento, precio y experiencia de usuario.
"""),
HumanMessage("Analiza el iPhone 15 Pro Max")
]
# La respuesta será automáticamente una instancia de ProductReview
review = structured_model.invoke(messages)
# Acceso directo a los campos estructurados
print(f"Producto: {review.product_name}")
print(f"Puntuación: {review.rating}/5")
print(f"Recomendado: {'Sí' if review.recommendation else 'No'}")
Ejemplo:
Validación automática de tipos
Una de las ventajas principales de with_structured_output
es la validación automática que proporciona Pydantic. Si el modelo intenta generar datos que no coinciden con el esquema, se producirá un error de validación:
class TaskAssignment(BaseModel):
task_id: str = Field(description="Identificador único de la tarea")
assignee: str = Field(description="Persona asignada a la tarea")
priority: int = Field(description="Prioridad de 1 a 3", ge=1, le=3)
due_date: str = Field(description="Fecha límite en formato YYYY-MM-DD")
estimated_hours: float = Field(description="Horas estimadas para completar")
model = init_chat_model("gpt-4.1", model_provider="openai")
task_model = model.with_structured_output(TaskAssignment)
messages = [
SystemMessage("Eres un gestor de proyectos que asigna tareas al equipo."),
HumanMessage("Crea una tarea para implementar autenticación OAuth")
]
task = task_model.invoke(messages)
# El resultado será una instancia válida de TaskAssignment
Trabajando con estructuras complejas
El método with_structured_output
maneja eficientemente estructuras anidadas y tipos de datos complejos. Podemos definir modelos que contengan listas, objetos anidados y relaciones entre entidades:
class Author(BaseModel):
name: str = Field(description="Nombre completo del autor")
expertise: list[str] = Field(description="Áreas de especialización")
class Article(BaseModel):
title: str = Field(description="Título del artículo")
authors: list[Author] = Field(description="Lista de autores")
abstract: str = Field(description="Resumen del artículo")
keywords: list[str] = Field(description="Palabras clave")
category: str = Field(description="Categoría principal")
estimated_reading_time: int = Field(description="Tiempo de lectura en minutos")
model = init_chat_model("gpt-4.1", model_provider="openai")
article_model = model.with_structured_output(Article)
messages = [
SystemMessage("""
Eres un editor académico que estructura información sobre artículos científicos.
Extrae y organiza la información de manera clara y precisa.
"""),
HumanMessage("Estructura un artículo sobre machine learning en medicina")
]
structured_article = article_model.invoke(messages)
Integración con LCEL
El modelo estructurado se integra perfectamente con LangChain Expression Language (LCEL), permitiendo crear cadenas de procesamiento más complejas:
from langchain_core.prompts import ChatPromptTemplate
class EmailSummary(BaseModel):
sender: str = Field(description="Remitente del email")
subject: str = Field(description="Asunto del email")
key_points: list[str] = Field(description="Puntos clave del contenido")
action_required: bool = Field(description="Si requiere acción del destinatario")
urgency_level: str = Field(description="Nivel de urgencia: bajo, medio, alto")
prompt = ChatPromptTemplate.from_messages([
("system", "Analiza emails y extrae información relevante de forma estructurada."),
("human", "Email: {email_content}")
])
model = init_chat_model("gpt-4.1", model_provider="openai")
structured_model = model.with_structured_output(EmailSummary)
# Crear cadena LCEL
email_analyzer = prompt | structured_model
# Uso de la cadena
result = email_analyzer.invoke({
"email_content": """
De: juan.perez@empresa.com
Asunto: Reunión urgente proyecto Alpha
Necesitamos reunirnos mañana a las 10:00 para revisar el estado del proyecto Alpha.
Hay varios problemas críticos que requieren atención inmediata.
"""
})
Manejo de errores y reintentos
Cuando trabajamos con salidas estructuradas, es importante considerar el manejo de errores de validación. LangChain proporciona mecanismos para gestionar casos donde el modelo no puede generar una respuesta válida:
from langchain_core.runnables import RunnablePassthrough
class DataValidation(BaseModel):
is_valid: bool = Field(description="Si los datos son válidos")
errors: list[str] = Field(description="Lista de errores encontrados")
corrected_data: Optional[dict] = Field(
default=None,
description="Datos corregidos si es posible"
)
model = init_chat_model("gpt-4.1", model_provider="openai")
validator_model = model.with_structured_output(DataValidation)
# Uso con manejo de contexto
def validate_user_data(data: dict) -> DataValidation:
messages = [
SystemMessage("Valida datos de usuario y sugiere correcciones si es necesario."),
HumanMessage(f"Valida estos datos: {data}")
]
return validator_model.invoke(messages)
La flexibilidad de with_structured_output
permite adaptarse a diferentes casos de uso manteniendo la simplicidad en la implementación, mientras que la integración nativa con Pydantic garantiza la consistencia y validación de los datos de salida.
PydanticOutputParser
Guarda tu progreso
Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.
Más de 25.000 desarrolladores ya confían en CertiDevs
El **PydanticOutputParser**
es una herramienta complementaria que facilita la generación de instrucciones de formato para prompts cuando necesitamos obtener salidas estructuradas. A diferencia de with_structured_output
, este parser se centra en proporcionar esquemas de formato que pueden incluirse directamente en los prompts para guiar al modelo en la generación de respuestas JSON válidas.
Generación de instrucciones de formato
La funcionalidad principal del PydanticOutputParser
es crear instrucciones detalladas que describen el formato esperado de la respuesta. Estas instrucciones se generan automáticamente a partir de un modelo Pydantic:
from typing import List
from langchain_core.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
class Employee(BaseModel):
name: str = Field(description="Nombre completo del empleado")
position: str = Field(description="Cargo o posición en la empresa")
department: str = Field(description="Departamento al que pertenece")
years_experience: int = Field(description="Años de experiencia laboral")
class EmployeeList(BaseModel):
employees: List[Employee] = Field(description="Lista de empleados")
total_count: int = Field(description="Número total de empleados")
# Crear el parser
parser = PydanticOutputParser(pydantic_object=EmployeeList)
# Generar instrucciones de formato
format_instructions = parser.get_format_instructions()
print(format_instructions)
Las instrucciones generadas incluyen una descripción detallada del esquema JSON esperado, con ejemplos de estructura y explicaciones de cada campo, lo que ayuda al modelo a comprender exactamente qué formato debe seguir.
Ejemplo:
Integración con prompts
El PydanticOutputParser
se integra especialmente bien con ChatPromptTemplate
mediante el uso de variables parciales. Esto permite incluir las instrucciones de formato directamente en el prompt:
from langchain_core.prompts import ChatPromptTemplate
from langchain.chat_models import init_chat_model
class ProductAnalysis(BaseModel):
product_name: str = Field(description="Nombre del producto analizado")
market_segment: str = Field(description="Segmento de mercado objetivo")
competitors: List[str] = Field(description="Lista de principales competidores")
strengths: List[str] = Field(description="Fortalezas del producto")
weaknesses: List[str] = Field(description="Debilidades identificadas")
market_share_estimate: float = Field(description="Estimación de cuota de mercado en porcentaje")
parser = PydanticOutputParser(pydantic_object=ProductAnalysis)
schema_instructions = parser.get_format_instructions()
prompt = ChatPromptTemplate.from_messages([
("system", """
Eres un analista de mercado especializado en productos tecnológicos.
Analiza productos de forma objetiva y estructurada.
{format_instructions}
"""),
("human", "Analiza el producto: {product_name}")
]).partial(format_instructions=schema_instructions)
model = init_chat_model("gpt-4.1", model_provider="openai")
Parseo de respuestas
Una vez configurado el prompt con las instrucciones de formato, el parser puede convertir la respuesta del modelo directamente en una instancia del modelo Pydantic:
# Crear la cadena completa
analysis_chain = prompt | model | parser
# Invocar la cadena
result = analysis_chain.invoke({"product_name": "Tesla Model S"})
# El resultado es una instancia de ProductAnalysis
print(f"Producto: {result.product_name}")
print(f"Segmento: {result.market_segment}")
print(f"Competidores: {', '.join(result.competitors)}")
Casos de uso específicos
El PydanticOutputParser
resulta especialmente útil cuando necesitamos mayor control sobre el formato del prompt o cuando trabajamos con modelos que requieren instrucciones explícitas de formato:
class ResearchPaper(BaseModel):
title: str = Field(description="Título del artículo de investigación")
authors: List[str] = Field(description="Lista de autores")
abstract: str = Field(description="Resumen del artículo")
methodology: str = Field(description="Metodología utilizada")
key_findings: List[str] = Field(description="Principales hallazgos")
limitations: List[str] = Field(description="Limitaciones del estudio")
future_work: str = Field(description="Trabajo futuro sugerido")
parser = PydanticOutputParser(pydantic_object=ResearchPaper)
prompt = ChatPromptTemplate.from_messages([
("system", """
Eres un investigador académico que estructura información científica.
Extrae y organiza información de artículos de investigación.
IMPORTANTE: Responde únicamente en formato JSON válido.
{format_instructions}
"""),
("human", "Estructura la información de este artículo: {article_text}")
]).partial(format_instructions=parser.get_format_instructions())
model = init_chat_model("gpt-4.1", model_provider="openai")
research_chain = prompt | model | parser
Validación y manejo de errores
El PydanticOutputParser
incluye validación automática durante el proceso de parseo. Si la respuesta del modelo no cumple con el esquema definido, se generará una excepción de validación:
from pydantic import ValidationError
class FinancialReport(BaseModel):
company_name: str = Field(description="Nombre de la empresa")
revenue: float = Field(description="Ingresos en millones de euros")
profit_margin: float = Field(description="Margen de beneficio como decimal", ge=0, le=1)
growth_rate: float = Field(description="Tasa de crecimiento anual")
risk_level: str = Field(description="Nivel de riesgo: bajo, medio, alto")
parser = PydanticOutputParser(pydantic_object=FinancialReport)
try:
# Procesar respuesta del modelo
financial_data = parser.parse(model_response)
print(f"Empresa: {financial_data.company_name}")
print(f"Margen de beneficio: {financial_data.profit_margin:.2%}")
except ValidationError as e:
print(f"Error de validación: {e}")
# Implementar lógica de reintento o manejo de errores
Comparación con with_structured_output
Mientras que with_structured_output
proporciona una integración directa y automática, el PydanticOutputParser
ofrece mayor flexibilidad en el control del prompt y es especialmente útil cuando:
- Necesitas personalizar las instrucciones de formato
- Trabajas con modelos que requieren prompts específicos
- Quieres separar la lógica de parseo de la invocación del modelo
- Necesitas reutilizar las instrucciones de formato en múltiples contextos
# Ejemplo de reutilización de instrucciones
class TaskDescription(BaseModel):
task_name: str = Field(description="Nombre de la tarea")
priority: int = Field(description="Prioridad del 1 al 5", ge=1, le=5)
estimated_duration: str = Field(description="Duración estimada")
dependencies: List[str] = Field(description="Tareas dependientes")
parser = PydanticOutputParser(pydantic_object=TaskDescription)
format_instructions = parser.get_format_instructions()
# Usar las mismas instrucciones en diferentes prompts
prompt_creation = ChatPromptTemplate.from_messages([
("system", "Crea una nueva tarea. {format_instructions}"),
("human", "{task_request}")
]).partial(format_instructions=format_instructions)
prompt_modification = ChatPromptTemplate.from_messages([
("system", "Modifica esta tarea existente. {format_instructions}"),
("human", "Tarea actual: {current_task}\nCambios: {changes}")
]).partial(format_instructions=format_instructions)
El PydanticOutputParser
complementa perfectamente el ecosistema de salidas estructuradas de LangChain, proporcionando una alternativa flexible para casos donde se requiere mayor control sobre el proceso de generación y parseo de respuestas estructuradas.
Aprendizajes de esta lección
- Comprender el uso del método with_structured_output para obtener respuestas JSON estructuradas.
- Aprender a definir modelos Pydantic para validar y estructurar las respuestas del modelo de lenguaje.
- Conocer cómo integrar salidas estructuradas con LangChain Expression Language (LCEL) y manejar estructuras complejas.
- Entender el funcionamiento y aplicación del PydanticOutputParser para generar instrucciones de formato y parsear respuestas.
- Identificar diferencias y casos de uso entre with_structured_output y PydanticOutputParser, incluyendo manejo de errores y validación.
Completa LangChain y certifícate
Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.
Asistente IA
Resuelve dudas al instante
Ejercicios
Practica con proyectos reales
Certificados
Valida tus conocimientos
Más de 25.000 desarrolladores ya se han certificado con CertiDevs