HoRain云--LangChain Chat Model 高级用法
本节介绍 Chat Model 的两个高级功能bind_tools()绑定工具和 with_structured_output()结构化输出。它们是 Agent 和结构化数据提取的基础。bind_tools()——让模型知道可以使用哪些工具普通模型只能生成文本。但调用 bind_tools() 后模型能在回复中返回 tool_call告诉程序我需要调用这个工具。实例from dotenv import load_dotenvload_dotenv()from langchain.chat_models import init_chat_modelmodel init_chat_model(deepseek:deepseek-v4-flash, temperature0)# 用字典描述工具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})运行结果模型请求调用以下工具 工具名: get_weather 参数: {city: 杭州} 调用ID: call_abc123def456注意模型并没有真正执行 get_weather 函数。bind_tools() 只是告诉模型你有一个工具可以用模型返回的是工具调用的请求。真正的执行由 Agent 或你自己编写的代码来完成。用 Pydantic 模型描述工具对于复杂工具使用 Pydantic 模型定义参数结构比手写字典更清晰实例from pydantic import BaseModel, Fieldfrom langchain.chat_models import init_chat_model# 用 Pydantic 定义工具的参数结构class WeatherInput(BaseModel):查询指定城市的天气情况city: str Field(description城市名称如 杭州、北京)unit: str Field(defaultcelsius,description温度单位celsius摄氏度或 fahrenheit华氏度)class CalculatorInput(BaseModel):执行数学计算expression: str Field(description要计算的数学表达式如 (3 5) * 2)model init_chat_model(deepseek:deepseek-v4-flash, temperature0)# 传入 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]}))运行结果模型请求了 2 个工具调用 get_weather({city: 北京, unit: celsius}) calculate({expression: 123 * 456})使用 Pydantic 定义工具参数是推荐的做法。它提供了类型安全、自动校验而且 LangChain 会自动从类名和 Field 描述生成工具描述。with_structured_output()——让模型返回结构化数据with_structured_output() 是比 tool_calling 更直接的方式。它让模型按你指定的格式Schema返回数据而不是返回 tool_call。实例from pydantic import BaseModel, Fieldfrom 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, temperature0)# with_structured_output() 让模型按照 PersonInfo 格式返回structured_model model.with_structured_output(PersonInfo)# 传入非结构化文本获取结构化数据text 张三今年28岁是一名全栈工程师精通 Python、React 和 Dockerresult 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)})运行结果姓名: 张三 年龄: 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() 支持复杂的嵌套结构实例from pydantic import BaseModel, Fieldfrom langchain.chat_models import init_chat_modelclass 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, temperature0)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}分钟))运行结果菜名: 番茄炒蛋 难度: 简单 食材 (5 种): - 番茄: 2个 - 鸡蛋: 3个 - 葱花: 少许 - 盐: 适量 - 糖: 少许 步骤 (4 步): 1. 番茄切块鸡蛋打散 (5分钟) 2. 热锅放油鸡蛋炒熟盛出 (3分钟) 3. 炒番茄至出汁加盐和糖 (5分钟) 4. 倒入鸡蛋翻炒均匀撒上葱花 (2分钟)JSON Schema 模式除了 Pydantic 模型也可以直接传入 JSON Schema实例from langchain.chat_models import init_chat_modelmodel init_chat_model(deepseek:deepseek-v4-flash, temperature0)# 直接传入 JSON Schemajson_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]})运行结果情感: positive 置信度: 0.95 关键词: [太棒了, 强烈推荐]ConfigurableModel 上的 bind_tools 和 with_structured_output可配置模型也支持这两个方法用法完全相同实例from pydantic import BaseModel, Fieldfrom langchain.chat_models import init_chat_model# 创建可配置模型configurable_model init_chat_model(deepseek-v4-flash,configurable_fields(model, model_provider),temperature0,)# 链式调用先绑定工具再调用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 时实际操作会被延迟执行——直到模型实例化时才真正绑定因此不会影响运行时动态切换模型的功能。