Uso avanzado del Chat Model de LangChain
Esta sección presenta dos funciones avanzadas del Chat Model: bind_tools() (vinculación de herramientas) y with_structured_output() (salida estructurada). Son la base de los Agent y de la extracción de datos estructurados.
bind_tools() — permite que el modelo sepa qué herramientas puede usar
Los modelos normales solo pueden generar texto. Pero al llamar abind_tools()después, el modelo puede devolver en su respuestatool_call, que le dice al programa: "Necesito llamar a esta herramienta".
Ejemplo
load_dotenv()
from langchain.chat_models import init_chat_model
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# Describimos la herramienta con un diccionario (formato de function calling de OpenAI)
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Consulta el clima de la ciudad especificada",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "Nombre de la ciudad, como Hangzhou, Beijing"
}
},
"required": ["city"]
}
}
}
]
# bind_tools() vincula la herramienta al modelo
# Ahora el modelo "sabe" que la herramienta get_weather está disponible
model_with_tools = model.bind_tools(tools)
# Hacemos una pregunta que requiere la herramienta
response = model_with_tools.invoke("¿Cómo está el clima en Hangzhou hoy?")
# Comprobamos si el modelo solicita llamar a la herramienta
if response.tool_calls:
print("El modelo solicita llamar a las siguientes herramientas:")
for tc in response.tool_calls:
print(f" Nombre de la herramienta: {tc['name']}")
print(f" Parámetros: {tc['args']}")
print(f" ID de llamada: {tc['id']}")
else:
print(f"Respuesta directa del modelo: {response.content}")
Resultado de la ejecución:
模型请求调用以下工具:
工具名: get_weather
Argumentos: {'city': '杭州'}
ID de llamada: call_abc123def456
Nota: el modelo no ejecuta realmente la función get_weather. bind_tools() solo le dice al modelo "tienes una herramienta disponible"; el modelo devuelve una solicitud de llamada a la herramienta. La ejecución real la realiza el Agent o el código que tú mismo escribas.
Describir herramientas con modelos Pydantic
Para herramientas complejas, usar modelos Pydantic para definir la estructura de parámetros es más claro que escribir diccionarios a mano:
Ejemplo
from langchain.chat_models import init_chat_model
# Usamos Pydantic para definir la estructura de parámetros de la herramienta
class WeatherInput(BaseModel):
"""Consulta el clima de la ciudad especificada"""
city: str = Field(description="Nombre de la ciudad, como Hangzhou, Beijing")
unit: str = Field(
default="celsius",
description="Unidad de temperatura, celsius (grados centígrados) o fahrenheit (grados Fahrenheit)"
)
class CalculatorInput(BaseModel):
"""Realiza cálculos matemáticos"""
expression: str = Field(
description="Expresión matemática a calcular, como '(3 + 5) * 2'"
)
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# Pasamos el modelo Pydantic; LangChain lo convierte automáticamente en una descripción de herramienta
model_with_tools = model.bind_tools([WeatherInput, CalculatorInput])
# Probamos un escenario complejo
response = model_with_tools.invoke(
"¿Cuántos grados hace hoy en Beijing? Por cierto, ayúdame a calcular 123 * 456"
)
print(f"El modelo solicitó {len(response.tool_calls)} llamadas a herramientas:")
for tc in response.tool_calls:
print(f" {tc['name']}({tc['args']})")
Resultado de la ejecución:
模型请求了 2 个工具调用:
get_weather({'city': '北京', 'unit': 'celsius'})
calculate({'expression': '123 * 456'})
Usar Pydantic para definir los parámetros de la herramienta es la práctica recomendada. Proporciona seguridad de tipos, validación automática, y LangChain genera automáticamente la descripción de la herramienta a partir del nombre de la clase y la descripción de Field.
with_structured_output() — permite que el modelo devuelva datos estructurados
with_structured_output()Es una forma más directa que tool_calling. Hace que el modelo devuelva datos según el formato (Schema) que especifiques, en lugar de devolver tool_call.
Ejemplo
from langchain.chat_models import init_chat_model
# Definimos la estructura de salida esperada
class PersonInfo(BaseModel):
"""Información de persona extraída del texto"""
name: str = Field(description="Nombre de la persona")
age: int = Field(description="Edad")
occupation: str = Field(description="Ocupación")
skills: list[str] = Field(description="Lista de habilidades")
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# with_structured_output() hace que el modelo devuelva según el formato PersonInfo
structured_model = model.with_structured_output(PersonInfo)
# Pasamos texto no estructurado y obtenemos datos estructurados
text = "Zhang San tiene 28 años, es ingeniero full stack y domina Python, React y Docker"
result = structured_model.invoke(text)
print(f"Nombre: {result.name}")
print(f"Edad: {result.age}")
print(f"Ocupación: {result.occupation}")
print(f"Habilidades: {', '.join(result.skills)}")
print(f"Tipo: {type(result)}")
Resultado de la ejecución:
姓名: 张三 年龄: 28 职业: 全栈工程师 技能: Python, React, Docker Tipo: <class '__main__.PersonInfo'>
El valor de retorno es directamenteuna instancia del modelo Pydantic, por lo que se puede acceder directamente a atributos como .name, .age, etc.
with_structured_output() vs bind_tools()
Estos dos métodos parecen similares, pero tienen propósitos diferentes:
| Dimensión de comparación | with_structured_output() | bind_tools() |
|---|---|---|
| Propósito | Extraer datos estructurados del texto | Permitir que el modelo conozca la lista de herramientas disponibles |
| Formato de retorno | Devuelve directamente un objeto Pydantic | Devuelve un AIMessage que contiene tool_calls |
| Escenarios de aplicación | Extracción de información, análisis de datos | Llamadas a herramientas del Agent, escenarios que requieren ejecución externa |
| Soporte del modelo | Requiere que el modelo admita salida estructurada nativa | Todos los modelos que admiten function calling |
Salida estructurada anidada
with_structured_output() admite estructuras anidadas complejas:
Ejemplo
from langchain.chat_models import init_chat_model
class Ingredient(BaseModel):
"""Información del ingrediente"""
name: str = Field(description="Nombre del ingrediente")
amount: str = Field(description="Cantidad, como '200g', '2 unidades'")
class CookingStep(BaseModel):
"""Pasos de cocción"""
step_number: int = Field(description="Número de paso")
description: str = Field(description="Descripción del paso")
duration_minutes: int = Field(description="Tiempo requerido para este paso (minutos)")
class Recipe(BaseModel):
"""Receta"""
dish_name: str = Field(description="Nombre del plato")
difficulty: str = Field(description="Dificultad: fácil, media, difícil")
ingredients: list[Ingredient] = Field(description="Lista de ingredientes")
steps: list[CookingStep] = Field(description="Pasos de cocción")
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
structured_model = model.with_structured_output(Recipe)
# Ingresamos una descripción de receta
recipe_text = """
Hoy les enseñaré a preparar un clásico: tomate revuelto con huevo. Este plato es muy sencillo.
Necesitas preparar: 2 tomates, 3 huevos, un poco de cebollín, sal al gusto, un poco de azúcar.
Pasos:
1. Primero corta los tomates en trozos y bate los huevos; tarda unos 5 minutos
2. Calienta el wok con aceite, sofríe los huevos y resérvalos; unos 3 minutos
3. Añade más aceite a la sartén, sofríe los tomates hasta que suelten jugo, añade sal y azúcar; unos 5 minutos
4. Agrega los huevos revueltos, saltea uniformemente, espolvorea cebollín; unos 2 minutos
"""
result = structured_model.invoke(recipe_text)
print(f"Nombre del plato: {result.dish_name}")
print(f"Dificultad: {result.difficulty}")
print(f"Ingredientes ({len(result.ingredients)} tipos):")
for ing in result.ingredients:
print(f" - {ing.name}: {ing.amount}")
print(f"Pasos ({len(result.steps)} pasos):")
for step in result.steps:
print(f" {step.step_number}. {step.description} ({step.duration_minutes} minutos)")
Resultado de la ejecución:
菜名: 番茄炒蛋 Dificultad: 简单 食材 (5 种): - 番茄: 2个 - 鸡蛋: 3个 - 葱花: 少许 - 盐: 适量 - 糖: 少许 步骤 (4 步): 1. 番茄切块,鸡蛋打散 (5分钟) 2. 热锅放油,鸡蛋炒熟盛出 (3分钟) 3. 炒番茄至出汁,加盐和糖 (5分钟) 4. 倒入鸡蛋翻炒均匀,撒上葱花 (2分钟)
Modo JSON Schema
Además de los modelos Pydantic, también se puede pasar directamente un JSON Schema:
Ejemplo
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# Pasamos directamente un JSON Schema
json_schema = {
"title": "SentimentAnalysis",
"description": "Resultado del análisis de sentimiento",
"type": "object",
"properties": {
"sentiment": {
"type": "string",
"enum": ["positive", "negative", "neutral"],
"description": "Tendencia del sentimiento"
},
"confidence": {
"type": "number",
"description": "Confianza, 0~1"
},
"keywords": {
"type": "array",
"items": {"type": "string"},
"description": "Palabras clave de sentimiento"
}
},
"required": ["sentiment", "confidence"]
}
structured_model = model.with_structured_output(json_schema)
result = structured_model.invoke("¡El tutorial Ejemplo es realmente genial, muy recomendado para todos los programadores novatos!")
print(f"Sentimiento: {result['sentiment']}")
print(f"Confianza: {result['confidence']}")
print(f"Palabras clave: {result['keywords']}")
Resultado de la ejecución:
Sentimiento: positive Confianza: 0.95 Palabras clave: ['太棒了', '强烈推荐']
bind_tools y with_structured_output en ConfigurableModel
El modelo configurable también admite estos dos métodos, con un uso completamente idéntico:
Ejemplo
from langchain.chat_models import init_chat_model
# Crear un modelo configurable
configurable_model = init_chat_model(
"deepseek-v4-flash",
configurable_fields=("model", "model_provider"),
temperature=0,
)
# Llamada en cadena: primero vincular las herramientas y luego invocar
configurable_with_tools = configurable_model.bind_tools([...])
# En tiempo de ejecución se pueden usar diferentes modelos para ejecutar
result = configurable_with_tools.invoke(
"Consultar el clima",
config={"configurable": {"model": "claude-sonnet-4-5"}}
)
Otras extensionesAl encadenar bind_tools o with_structured_output en ConfigurableModel, la operación real se ejecuta de forma diferida — solo se vincula realmente cuando el modelo se instancia, por lo que no afecta la capacidad de cambiar dinámicamente de modelo en tiempo de ejecución.