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:
from typing import Optional
from pydantic import BaseModel, Field
from langchain.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:
model = init_chat_model("gpt-5")
# 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(content="""
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(content="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'}")
Validación automática de tipos
Una de las ventajas principales de with_structured_output es la validación automática que proporciona Pydantic:
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-5")
task_model = model.with_structured_output(TaskAssignment)
messages = [
SystemMessage(content="Eres un gestor de proyectos que asigna tareas al equipo."),
HumanMessage(content="Crea una tarea para implementar autenticación OAuth")
]
task = task_model.invoke(messages)
# El resultado será una instancia válida de TaskAssignment
Estructuras anidadas
El método with_structured_output maneja eficientemente estructuras anidadas y tipos de datos complejos:
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")
model = init_chat_model("gpt-5")
article_model = model.with_structured_output(Article)
messages = [
SystemMessage(content="Eres un editor académico que estructura artículos científicos."),
HumanMessage(content="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):
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-5")
structured_model = model.with_structured_output(EmailSummary)
# Crear cadena LCEL
email_analyzer = prompt | structured_model
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.
Hay varios problemas críticos que requieren atención inmediata.
"""
})
PydanticOutputParser
El **PydanticOutputParser** es una herramienta complementaria que facilita la generación de instrucciones de formato para prompts. A diferencia de with_structured_output, este parser se centra en proporcionar esquemas de formato que pueden incluirse directamente en los prompts.
Generación de instrucciones de formato
La funcionalidad principal del PydanticOutputParser es crear instrucciones detalladas que describen el formato esperado:
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")
parser = PydanticOutputParser(pydantic_object=EmployeeList)
# Generar instrucciones de formato
format_instructions = parser.get_format_instructions()
print(format_instructions)
Integración con prompts
El PydanticOutputParser se integra bien con ChatPromptTemplate mediante variables parciales:
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")
parser = PydanticOutputParser(pydantic_object=ProductAnalysis)
prompt = ChatPromptTemplate.from_messages([
("system", """
Eres un analista de mercado especializado en productos tecnológicos.
{format_instructions}
"""),
("human", "Analiza el producto: {product_name}")
]).partial(format_instructions=parser.get_format_instructions())
model = init_chat_model("gpt-5")
# Crear la cadena completa
analysis_chain = prompt | model | parser
result = analysis_chain.invoke({"product_name": "Tesla Model S"})
print(f"Producto: {result.product_name}")
print(f"Competidores: {', '.join(result.competitors)}")
Comparación: with_structured_output vs PydanticOutputParser
Mientras que with_structured_output proporciona una integración directa y automática, el PydanticOutputParser ofrece mayor flexibilidad en el control del prompt:
-
with_structured_output: Recomendado cuando el modelo soporta llamadas a funciones nativas (OpenAI, Anthropic). Más fiable y eficiente.
-
PydanticOutputParser: Útil cuando necesitas personalizar las instrucciones de formato o trabajas con modelos que no soportan salidas estructuradas nativas.
# Reutilización de instrucciones en diferentes prompts
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")
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)
La recomendación general es utilizar with_structured_output como primera opción por su simplicidad y fiabilidad, reservando PydanticOutputParser para casos donde se requiera mayor control sobre el proceso de generación.
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en LangChain
Documentación oficial de LangChain
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, LangChain es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de LangChain
Explora más contenido relacionado con LangChain y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Usar with_structured_output para vincular modelos Pydantic con modelos de chat, crear esquemas estructurados con validación automática, trabajar con estructuras anidadas, integrar salidas estructuradas con LCEL, y usar PydanticOutputParser para mayor control sobre el formato.
Cursos que incluyen esta lección
Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje