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

from dotenv import load_dotenv
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 pydantic import BaseModel, Field
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 pydantic import BaseModel, Field
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ónwith_structured_output()bind_tools()
PropósitoExtraer datos estructurados del textoPermitir que el modelo conozca la lista de herramientas disponibles
Formato de retornoDevuelve directamente un objeto PydanticDevuelve un AIMessage que contiene tool_calls
Escenarios de aplicaciónExtracción de información, análisis de datosLlamadas a herramientas del Agent, escenarios que requieren ejecución externa
Soporte del modeloRequiere que el modelo admita salida estructurada nativaTodos los modelos que admiten function calling

Salida estructurada anidada

with_structured_output() admite estructuras anidadas complejas:

Ejemplo

from pydantic import BaseModel, Field
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

from langchain.chat_models import init_chat_model

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 pydantic import BaseModel, Field
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"}}
)

Al 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.

Otras extensiones