Skip to content

LangChain Chat Model 高级用法

本节介绍 Chat Model 的两个高级功能:bind_tools()(绑定工具)和 with_structured_output()(结构化输出)。它们是 Agent 和结构化数据提取的基础。


bind_tools()——让模型知道可以使用哪些工具

普通模型只能生成文本。但调用 bind\_tools() 后,模型能在回复中返回 tool\_call,告诉程序"我需要调用这个工具"。

实例

python

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)

# 用字典描述工具(OpenAI function calling 格式)

tools = [

    {

        "type": "function",

        "function": {

            "name": "get_weather",

            "description": "查询指定城市的天气",

            "parameters": {

                "type": "object",

                "properties": {

                    "city": {

                        "type": "string",

                        "description": "城市名称,如 杭州、北京"

                    }

                },

                "required": ["city"]

            }

        }

    }

]

# bind_tools() 将工具绑定到模型

# 模型现在"知道"有 get_weather 这个工具可用

model_with_tools = model.bind_tools(tools)

# 问一个需要工具的问题

response = model_with_tools.invoke("杭州今天天气怎么样?")

# 检查模型是否请求调用工具

if response.tool_calls:

    print("模型请求调用以下工具:")

    for tc in response.tool_calls:

        print(f"  工具名: {tc['name']}")

        print(f"  参数: {tc['args']}")

        print(f"  调用ID: {tc['id']}")

else:

    print(f"模型直接回复: {response.content}")

运行结果:

python
模型请求调用以下工具:
  工具名: get_weather
  参数: {'city': '杭州'}
  调用ID: call_abc123def456

注意模型并没有真正执行 get_weather 函数。bind_tools() 只是告诉模型"你有一个工具可以用",模型返回的是工具调用的请求。真正的执行由 Agent 或你自己编写的代码来完成。


用 Pydantic 模型描述工具

对于复杂工具,使用 Pydantic 模型定义参数结构比手写字典更清晰:

实例

python

from pydantic import BaseModel, Field

from langchain.chat_models import init_chat_model

# 用 Pydantic 定义工具的参数结构

class WeatherInput(BaseModel):

    """查询指定城市的天气情况"""

    city: str = Field(description="城市名称,如 杭州、北京")

    unit: str = Field(

        default="celsius",

        description="温度单位,celsius(摄氏度)或 fahrenheit(华氏度)"

    )

class CalculatorInput(BaseModel):

    """执行数学计算"""

    expression: str = Field(

        description="要计算的数学表达式,如 '(3 + 5) * 2'"

    )

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)

# 传入 Pydantic 模型,LangChain 自动转换为工具描述

model_with_tools = model.bind_tools([WeatherInput, CalculatorInput])

# 测试复杂场景

response = model_with_tools.invoke(

    "北京今天多少度?顺便帮我算一下 123 * 456"

)

print(f"模型请求了 {len(response.tool_calls)} 个工具调用:")

for tc in response.tool_calls:

    print(f"  {tc['name']}({tc['args']})")

运行结果:

python
模型请求了 2 个工具调用:
  get_weather({'city': '北京', 'unit': 'celsius'})
  calculate({'expression': '123 * 456'})

使用 Pydantic 定义工具参数是推荐的做法。它提供了类型安全、自动校验,而且 LangChain 会自动从类名和 Field 描述生成工具描述。


with_structured_output()——让模型返回结构化数据

with\_structured\_output() 是比 tool_calling 更直接的方式。它让模型按你指定的格式(Schema)返回数据,而不是返回 tool_call。

实例

python

from pydantic import BaseModel, Field

from langchain.chat_models import init_chat_model

# 定义期望的输出结构

class PersonInfo(BaseModel):

    """从文本中提取的人物信息"""

    name: str = Field(description="人物姓名")

    age: int = Field(description="年龄")

    occupation: str = Field(description="职业")

    skills: list[str] = Field(description="技能列表")

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)

# with_structured_output() 让模型按照 PersonInfo 格式返回

structured_model = model.with_structured_output(PersonInfo)

# 传入非结构化文本,获取结构化数据

text = "张三今年28岁,是一名全栈工程师,精通 Python、React 和 Docker"

result = structured_model.invoke(text)

print(f"姓名: {result.name}")

print(f"年龄: {result.age}")

print(f"职业: {result.occupation}")

print(f"技能: {', '.join(result.skills)}")

print(f"类型: {type(result)}")

运行结果:

python
姓名: 张三
年龄: 28
职业: 全栈工程师
技能: Python, React, Docker
类型: <class '__main__.PersonInfo'>

返回值直接是 Pydantic 模型实例,可以直接使用 .name、.age 等属性访问。


with_structured_output() vs bind_tools()

这两个方法看起来相似,但用途不同:

对比维度with_structured_output()bind_tools()
用途从文本中提取结构化数据让模型知道可用的工具列表
返回格式直接返回 Pydantic 对象返回 AIMessage,其中包含 tool_calls
适用场景信息提取、数据解析Agent 工具调用、需要外部执行的场景
模型支持需模型支持原生 structured output所有支持 function calling 的模型

嵌套结构化输出

with_structured_output() 支持复杂的嵌套结构:

实例

python

from pydantic import BaseModel, Field

from langchain.chat_models import init_chat_model

class Ingredient(BaseModel):

    """食材信息"""

    name: str = Field(description="食材名称")

    amount: str = Field(description="用量,如 '200g'、'2个'")

class CookingStep(BaseModel):

    """烹饪步骤"""

    step_number: int = Field(description="步骤编号")

    description: str = Field(description="步骤描述")

    duration_minutes: int = Field(description="此步骤需要的时间(分钟)")

class Recipe(BaseModel):

    """菜谱"""

    dish_name: str = Field(description="菜名")

    difficulty: str = Field(description="难度:简单、中等、困难")

    ingredients: list[Ingredient] = Field(description="食材列表")

    steps: list[CookingStep] = Field(description="烹饪步骤")

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)

structured_model = model.with_structured_output(Recipe)

# 输入一个菜谱描述

recipe_text = """

今天来教大家做一道经典的番茄炒蛋,这道菜非常简单。

需要准备:番茄 2 个、鸡蛋 3 个、葱花少许、盐适量、糖少许。

步骤:

1. 先把番茄切块,鸡蛋打散,大概需要 5 分钟

2. 热锅放油,先把鸡蛋炒熟盛出,大概 3 分钟

3. 锅中再放油,炒番茄至出汁,加盐和糖,大概 5 分钟

4. 倒入炒好的鸡蛋,翻炒均匀,撒上葱花,大概 2 分钟

"""

result = structured_model.invoke(recipe_text)

print(f"菜名: {result.dish_name}")

print(f"难度: {result.difficulty}")

print(f"食材 ({len(result.ingredients)} 种):")

for ing in result.ingredients:

    print(f"  - {ing.name}: {ing.amount}")

print(f"步骤 ({len(result.steps)} 步):")

for step in result.steps:

    print(f"  {step.step_number}. {step.description} ({step.duration_minutes}分钟)")

运行结果:

python
菜名: 番茄炒蛋
难度: 简单
食材 (5 种):
  - 番茄: 2个
  - 鸡蛋: 3个
  - 葱花: 少许
  - 盐: 适量
  - 糖: 少许
步骤 (4 步):
  1. 番茄切块,鸡蛋打散 (5分钟)
  2. 热锅放油,鸡蛋炒熟盛出 (3分钟)
  3. 炒番茄至出汁,加盐和糖 (5分钟)
  4. 倒入鸡蛋翻炒均匀,撒上葱花 (2分钟)

JSON Schema 模式

除了 Pydantic 模型,也可以直接传入 JSON Schema:

实例

python

from langchain.chat_models import init_chat_model

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)

# 直接传入 JSON Schema

json_schema = {

    "title": "SentimentAnalysis",

    "description": "情感分析结果",

    "type": "object",

    "properties": {

        "sentiment": {

            "type": "string",

            "enum": ["positive", "negative", "neutral"],

            "description": "情感倾向"

        },

        "confidence": {

            "type": "number",

            "description": "置信度,0~1"

        },

        "keywords": {

            "type": "array",

            "items": {"type": "string"},

            "description": "关键情感词"

        }

    },

    "required": ["sentiment", "confidence"]

}

structured_model = model.with_structured_output(json_schema)

result = structured_model.invoke("菜鸟教程 RUNOOB 真的太棒了,强烈推荐给所有编程新手!")

print(f"情感: {result['sentiment']}")

print(f"置信度: {result['confidence']}")

print(f"关键词: {result['keywords']}")

运行结果:

python
情感: positive
置信度: 0.95
关键词: ['太棒了', '强烈推荐']

ConfigurableModel 上的 bind_tools 和 with_structured_output

可配置模型也支持这两个方法,用法完全相同:

实例

python

from pydantic import BaseModel, Field

from langchain.chat_models import init_chat_model

# 创建可配置模型

configurable_model = init_chat_model(

    "deepseek-v4-flash",

    configurable_fields=("model", "model_provider"),

    temperature=0,

)

# 链式调用:先绑定工具,再调用

configurable_with_tools = configurable_model.bind_tools([...])

# 运行时可以用不同模型来执行

result = configurable_with_tools.invoke(

    "查询天气",

    config={"configurable": {"model": "claude-sonnet-4-5"}}

)

在 ConfigurableModel 上链式调用 bind_tools 或 with_structured_output 时,实际操作会被延迟执行——直到模型实例化时才真正绑定,因此不会影响运行时动态切换模型的功能。

AI 思考中...

LangChain Chat Model 常用参数

LangChain 消息类型

基于 VitePress 构建,部署于 GitHub Pages