LangChain Chat Model 详解:从常用参数到 bind_tools 与结构化输出
Chat Model 是 LangChain 里一切 Agent 的地基——没有模型,工具、提示词、循环都无从谈起。理解init_chat_model()怎么配置模型行为参数(temperature、max_tokens、timeout 等),以及bind_tools()/with_structured_output()这两个高级能力,是写出可控、可靠、可复用的 Agent 的起点。
本文基于 LangChain 官方文档(Python)与菜鸟教程 LangChain 系列,沿材料分类组件00:Models的路径组织,覆盖常用参数与高级用法两大部分。
一、先厘清:Chat Model 在 LangChain 里的角色
一句话结论:Chat Model 就是"负责说话的模型",它接受消息列表、返回回复;而bind_tools()让它能返回工具调用请求,with_structured_output()让它直接返回结构化数据——这两个能力是 Agent 和结构化提取的基石。
普通模型只能生成纯文本。但模型的能力不止于此:
bind_tools():模型"知道"有哪些工具可用,能在回复中返回tool_call,告诉程序"我需要在此时调用这个工具"。with_structured_output():模型按你指定的 Schema 直接返回结构化数据(Pydantic 对象 / 字典),而不是让你去解析文本。
这两个方法把模型从"文本生成器"升级成"能干活、能交付结构"的智能体组件。
二、常用参数逐个拆解
2.1 temperature——控制创造性与确定性
temperature是最常用的参数,取值范围 0 到 2,控制模型输出的随机程度。
fromlangchain.chat_modelsimportinit_chat_model question="用一句话介绍菜鸟教程 RUNOOB"# temperature=0:输出非常确定,几乎每次结果一样model_low=init_chat_model("deepseek:deepseek-v4-flash",temperature=0)resp1=model_low.invoke(question)resp2=model_low.invoke(question)print(f"两次结果相同:{resp1.content==resp2.content}")# True# temperature=1.5:输出多样化,每次可能不同model_high=init_chat_model("deepseek:deepseek-v4-flash",temperature=1.5)print(f"第1次:{model_high.invoke(question).content}")print(f"第2次:{model_high.invoke(question).content}")| temperature 值 | 效果 | 适用场景 |
|---|---|---|
| 0 ~ 0.3 | 输出稳定、确定,每次结果几乎一致 | 数据提取、分类、代码生成、翻译 |
| 0.5 ~ 0.7 | 适度的创造性,输出自然但不偏离主题 | 日常对话、内容总结 |
| 0.8 ~ 1.2 | 输出多样化,有较多发挥空间 | 创意写作、头脑风暴 |
| 1.3 ~ 2.0 | 输出非常随机,可能出现意外内容 | 探索性生成(不太推荐用于生产) |
temperature=0 不等于"完全相同"。由于模型内部浮点精度差异,极端情况下仍可能有微小差异。如果需要绝对的确定性,有些模型提供了
seed参数。
2.2 max_tokens——控制输出长度与成本
max_tokens限制模型输出的最大 Token 数。一个 Token 大约相当于 0.75 个英文单词或 0.5 个中文字。
fromlangchain.chat_modelsimportinit_chat_model model=init_chat_model("deepseek:deepseek-v4-flash",temperature=0)# max_tokens=30:限制输出在 30 个 Token 以内response_short=model.invoke("详细介绍一下菜鸟教程 RUNOOB 平台",max_tokens=30)print(f"限制 30 tokens ({len(response_short.content)}字符):")print(response_short.content)⚠️ 避坑:max_tokens是对输出长度的硬限制。如果设置过低,模型的回答可能在句中突然截断。一般建议 100-2000;"一句话回答"类场景 30-100 就够,"详细解释"类建议 500-2000。
2.3 timeout 与 max_retries——网络可靠性
在生产环境中,网络请求可能失败。这两个参数控制请求行为:
fromlangchain.chat_modelsimportinit_chat_model# 生产环境推荐配置model=init_chat_model("deepseek:deepseek-v4-flash",timeout=30,# 单次请求最多等待 30 秒max_retries=3,# 失败后最多重试 3 次(总共 4 次请求机会))| 参数 | 说明 | 建议值 |
|---|---|---|
| timeout | 单次请求的最大等待时间(秒)。None 表示不限制 | 30~60(太短易超时,太长体验差) |
| max_retries | 失败后的重试次数。0 表示不重试 | 2~3(足够处理偶发网络问题) |
2.4 base_url——自定义 API 地址
base_url在你需要通过代理、中转服务或私有部署访问模型时非常有用:
fromlangchain.chat_modelsimportinit_chat_model# 场景 1:通过代理访问model=init_chat_model("deepseek:deepseek-v4-flash",base_url="https://your-proxy-domain.com/v1")# 场景 2:使用兼容 OpenAI 接口的第三方服务model=init_chat_model("deepseek:deepseek-v4-flash",base_url="https://api.third-party.com/v1",api_key="your-third-party-key",)# 场景 3:连接本地模型(如 vLLM、Ollama)model=init_chat_model("openai:qwen2.5",base_url="http://localhost:8000/v1",api_key="not-needed",# 本地通常不需要 Key)
base_url改变的是 API 端点地址,但provider参数决定行为模式。比如provider="openai"会用 OpenAI 消息格式,即使 base_url 指向的是别的服务。确保目标服务兼容你指定的 provider 格式。
2.5 其他常用参数
top_p(核采样):另一种控制随机性的方式,模型只从累积概率达到 top_p 的词中采样。一般建议只调temperature或top_p中的一个,不要同时调,否则行为难预测。
model=init_chat_model("deepseek:deepseek-v4-flash",top_p=0.9)# 只考虑累积概率前 90% 的词stop(停止序列):模型遇到这些词会立即停止生成。
response=model.invoke("列出五个编程学习网站,每个一行",stop=["\n"])# 遇到换行就停止seed(可重复性):部分模型支持,相同 seed + 相同输入 = 相同输出。
model=init_chat_model("deepseek:deepseek-v4-flash",seed=42,temperature=0)参数速查表
| 参数 | 类型 | 默认值 | 何时使用 |
|---|---|---|---|
| temperature | float | 因模型而异 | 任务需稳定性时 0~0.3,需创造性时 0.7~1.0 |
| max_tokens | int | 模型上限 | 输出长度需要控制时 |
| timeout | int/float | None | 生产环境建议始终设置 |
| max_retries | int | 因模型而异 | 网络不稳定时建议 2~3 |
| base_url | str | 官方地址 | 使用代理、中转或本地服务时 |
| top_p | float | 1.0 | 需要核采样控制时(替代 temperature) |
| stop | list[str] | 无 | 需要精确控制输出结尾时 |
三、高级用法:bind_tools()——让模型知道有哪些工具
普通模型只能生成文本。调用bind_tools()后,模型能在回复中返回tool_call,告诉程序"我需要调用这个工具".
fromdotenvimportload_dotenv load_dotenv()fromlangchain.chat_modelsimportinit_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() 将工具绑定到模型model_with_tools=model.bind_tools(tools)# 问一个需要工具的问题response=model_with_tools.invoke("杭州今天天气怎么样?")ifresponse.tool_calls:print("模型请求调用以下工具:")fortcinresponse.tool_calls:print(f" 工具名:{tc['name']}")print(f" 参数:{tc['args']}")print(f" 调用ID:{tc['id']}")运行结果:
模型请求调用以下工具: 工具名: get_weather 参数: {'city': '杭州'} 调用ID: call_abc123def456⚠️ 重点:
bind_tools()只是告诉模型"你有一个工具可以用",模型返回的是工具调用的请求。真正的执行由 Agent 或你自己的代码来完成。
3.1 用 Pydantic 模型描述工具
对于复杂工具,用 Pydantic 模型定义参数结构比手写字典更清晰:
frompydanticimportBaseModel,Fieldfromlangchain.chat_modelsimportinit_chat_modelclassWeatherInput(BaseModel):"""查询指定城市的天气情况"""city:str=Field(description="城市名称,如 杭州、北京")unit:str=Field(default="celsius",description="温度单位,celsius(摄氏度)或 fahrenheit(华氏度)")classCalculatorInput(BaseModel):"""执行数学计算"""expression:str=Field(description="要计算的数学表达式,如 '(3 + 5) * 2'")model=init_chat_model("deepseek:deepseek-v4-flash",temperature=0)model_with_tools=model.bind_tools([WeatherInput,CalculatorInput])response=model_with_tools.invoke("北京今天多少度?顺便帮我算一下 123 * 456")print(f"模型请求了{len(response.tool_calls)}个工具调用:")fortcinresponse.tool_calls:print(f"{tc['name']}({tc['args']})")使用 Pydantic 定义工具参数是推荐做法——类型安全、自动校验,LangChain 会自动从类名和 Field 描述生成工具描述。
四、高级用法:with_structured_output()——让模型返回结构化数据
with_structured_output()是比 tool_calling 更直接的方式。它让模型按你指定的 Schema 返回数据,而不是返回 tool_call。
frompydanticimportBaseModel,Fieldfromlangchain.chat_modelsimportinit_chat_modelclassPersonInfo(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)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"类型:{type(result)}")# PersonInfo 实例返回值直接是 Pydantic 模型实例,可用.name、.age等属性访问。
4.1 with_structured_output() vs bind_tools() 对比
这两个方法看起来相似,但用途不同:
| 对比维度 | with_structured_output() | bind_tools() |
|---|---|---|
| 用途 | 从文本中提取结构化数据 | 让模型知道可用的工具列表 |
| 返回格式 | 直接返回 Pydantic 对象 | 返回 AIMessage,其中包含 tool_calls |
| 适用场景 | 信息提取、数据解析 | Agent 工具调用、需要外部执行的场景 |
| 模型支持 | 需模型支持原生 structured output | 所有支持 function calling 的模型 |
4.2 嵌套结构化输出
with_structured_output()支持复杂的嵌套结构:
frompydanticimportBaseModel,Fieldfromlangchain.chat_modelsimportinit_chat_modelclassIngredient(BaseModel):"""食材信息"""name:str=Field(description="食材名称")amount:str=Field(description="用量,如 '200g'、'2个'")classCookingStep(BaseModel):"""烹饪步骤"""step_number:int=Field(description="步骤编号")description:str=Field(description="步骤描述")duration_minutes:int=Field(description="此步骤需要的时间(分钟)")classRecipe(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="""今天来教大家做一道经典的番茄炒蛋......"""result=structured_model.invoke(recipe_text)print(f"菜名:{result.dish_name}")print(f"食材 ({len(result.ingredients)}种):")foringinresult.ingredients:print(f" -{ing.name}:{ing.amount}")print(f"步骤 ({len(result.steps)}步):")forstepinresult.steps:print(f"{step.step_number}.{step.description}({step.duration_minutes}分钟)")4.3 JSON Schema 模式
除了 Pydantic 模型,也可以直接传入 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']}| 置信度:{result['confidence']}| 关键词:{result['keywords']}")JSON Schema 需要包含顶层
title和description键,返回的是字典而非 Pydantic 对象。
五、总结:你真正需要记住的 N 件事
- Chat Model 是 Agent 的地基:model 负责说话,
bind_tools()/with_structured_output()是它的两个高级能力。 - temperature 控制随机度:稳定任务用 0~0.3,创意任务用 0.7~1.2,但别同时调 temperature 和 top_p。
- max_tokens 是硬限制:设置过低会句中截断,按场景设 100-2000。
- 生产环境必设 timeout + max_retries:推荐 30s + 2~3 次重试。
- base_url 改端点、provider 定行为:用代理/兼容接口/本地模型都靠它。
- bind_tools() 只是"告知":模型返回的是工具调用请求,真正执行在 Agent 或你的代码里。
- with_structured_output() 直接给结构:信息提取别绕弯,从文本直接拿 Pydantic 对象。
- 优先用 Pydantic 定义参数:类型安全、自动校验、自动生成描述。
验证清单
- 我为任务选对了 temperature(稳定用低值、创意用高值)
- 我设置了 max_tokens 控制输出长度,且没设过低导致截断
- 生产环境我设了 timeout 和 max_retries
- 用代理/本地模型时我配了 base_url,并确认 provider 兼容
- 需要工具调用时我用 bind_tools(),并清楚真正执行在我这边
- 信息提取我用 with_structured_output(),能拿到 Pydantic 对象或字典
- 复杂结构我用嵌套 Pydantic 模型 / JSON Schema 定义
参考资源
- LangChain 官方文档:Models——https://docs.langchain.com/oss/python/langchain/models
- LangChain Reference:init_chat_model——https://reference.langchain.com/python/langchain/chat_models
- 菜鸟教程 LangChain 系列——https://www.runoob.com/langchain/