1. 本章目标 #
上一章用 create_agent(model="deepseek:...") 直接传了模型字符串。本章把 Chat Model 单独拆开讲清楚:
用统一接口调用聊天模型:初始化、调参、流式、批量,并把它交给 Agent。
学完你应能:
- 用
init_chat_model创建模型(推荐) - 用供应商类(如
ChatDeepSeek)创建模型 - 掌握
invoke/stream/batch - 调
temperature、max_tokens、timeout、max_retries等参数 - 把模型实例传给
create_agent
参考文档:
2. Chat Model 在系统里的位置 #
模型是 Agent 的「大脑」,也可单独使用(翻译、分类、摘要、抽取),不必每次都上 Agent。
单独调用: 用户输入 ──► Chat Model ──► AIMessage
Agent 调用:用户输入 ──► create_agent(内部循环调 Model + Tools)──► 最终回答官方标准接口让你能在供应商之间切换,而业务代码改动很小。除文本生成外,现代聊天模型常见还支持:
| 能力 | 作用 |
|---|---|
| Tool calling | 请求调用外部工具 |
| Structured output | 按 schema 输出 |
| Streaming | 边生成边返回 |
| Multimodality | 图文等多模态 |
3. 两种初始化方式 #
3.1. 方式 A:init_chat_model(推荐) #
统一工厂:用 "provider:model" 字符串即可,切换供应商时改字符串为主。
3.1.1. init_chat_model.py #
# 从 langchain.chat_models 导入统一初始化聊天模型的工厂函数
from langchain.chat_models import init_chat_model
# 从 dotenv 导入用于加载环境变量的函数
from dotenv import load_dotenv
# 加载 .env 中的环境变量;override=True 表示覆盖已存在的同名变量
load_dotenv(override=True)
# 使用「provider:model」字符串创建聊天模型,其余参数以关键字参数传入
model = init_chat_model(
# 指定供应商为 deepseek,模型名为 deepseek-v4-flash
"deepseek:deepseek-v4-flash",
# 设置生成温度;数值越低,输出越稳定、越确定
temperature=0.2,
# 结束 init_chat_model 调用并得到模型实例
)
# 调用模型;传入的字符串会自动当作一条用户消息
result = model.invoke("用一句话介绍 LangChain")
# 打印模型返回的 AIMessage 中的文本内容
print(result.content)3.2. 方式 B:供应商专用类 #
需要细调该厂商特有参数时更直观。DeepSeek 示例:
3.2.1. ChatDeepSeek.py #
# 从 langchain_deepseek 导入 DeepSeek 专用聊天模型类
from langchain_deepseek import ChatDeepSeek
# 从 dotenv 导入用于加载环境变量的函数
from dotenv import load_dotenv
# 加载 .env 中的环境变量;override=True 表示覆盖已存在的同名变量
load_dotenv(override=True)
# 使用供应商专用类创建 DeepSeek 聊天模型实例
model = ChatDeepSeek(
# 指定模型名称为 deepseek-v4-flash
model="deepseek-v4-flash",
# 设置生成温度;数值越低,输出越稳定、越确定
temperature=0.2,
# 设置调用失败时的最大重试次数
max_retries=2,
# 结束 ChatDeepSeek 初始化并得到模型实例
)
# 调用模型;传入的字符串会自动当作一条用户消息
result = model.invoke("用一句话介绍 LangChain")
# 打印模型返回的 AIMessage 中的文本内容
print(result.content)也可用 OpenAI 兼容接口指向 DeepSeek(适合已有 ChatOpenAI 代码迁移):
3.2.2. ChatOpenAI_compatible.py #
# 导入 os 模块,用于读取环境变量
import os
# 从 langchain_openai 导入 OpenAI 兼容的聊天模型类
from langchain_openai import ChatOpenAI
# 从 dotenv 导入用于加载环境变量的函数
from dotenv import load_dotenv
# 加载 .env 中的环境变量;override=True 表示覆盖已存在的同名变量
load_dotenv(override=True)
# 使用 OpenAI 兼容接口创建指向 DeepSeek 的聊天模型实例
model = ChatOpenAI(
# 指定模型名称为 deepseek-v4-flash
model="deepseek-v4-flash",
# 从环境变量读取 DeepSeek API 密钥
api_key=os.getenv("DEEPSEEK_API_KEY"),
# 设置 API 基地址;优先读 OPENAI_API_BASE,否则默认 DeepSeek 官方地址
base_url=os.getenv("OPENAI_API_BASE", "https://api.deepseek.com"),
# 设置生成温度;数值越低,输出越稳定、越确定
temperature=0.2,
# 结束 ChatOpenAI 初始化并得到模型实例
)
# 调用模型并打印返回的 AIMessage 文本内容
print(model.invoke("用一句话介绍 LangChain").content)3.3. 怎么选 #
| 场景 | 建议 |
|---|---|
| 多数业务代码 | init_chat_model("deepseek:...") |
| 深度绑定某一厂商 SDK 能力 | ChatDeepSeek / ChatOpenAI 等 |
| 已有 OpenAI 风格代码,换兼容网关 | ChatOpenAI + base_url |
本文默认:init_chat_model("deepseek:deepseek-v4-flash")。
3.4. 换供应商示意(只改标识与集成包) #
# DeepSeek
model = init_chat_model("deepseek:deepseek-v4-flash")
# OpenAI(需 langchain-openai / langchain[openai])
model = init_chat_model("openai:gpt-4o-mini")
# Anthropic(需 langchain-anthropic)
model = init_chat_model("anthropic:claude-sonnet-4-6")
# Ollama 本地(需 langchain-ollama,且服务已启动)
model = init_chat_model("ollama:llama3.2")完整列表见 Providers。
4. 调用方式:invoke / stream / batch #
4.1. invoke:一次拿完整结果 #
4.1.1. invoke.py #
# 从 langchain.chat_models 导入统一初始化聊天模型的函数
from langchain.chat_models import init_chat_model
# 从 dotenv 导入用于加载 .env 环境变量的函数
from dotenv import load_dotenv
# 加载 .env 文件中的环境变量;override=True 表示覆盖已存在的同名变量
load_dotenv(override=True)
# 初始化 DeepSeek 聊天模型,temperature=0 使输出更确定、更稳定
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# 以单条字符串作为输入调用模型,并打印返回的 AIMessage 文本内容
print(model.invoke("1+1等于几?只答数字。").content)
# 构造多轮对话:用 dict 消息列表(role/content)表示系统提示与用户问题
conversation = [
# 系统消息:设定助手角色与回答风格
{"role": "system", "content": "你是简洁的中文助手,回答尽量短。"},
# 用户消息:提出关于 Chat Model 的问题
{"role": "user", "content": "什么是 Chat Model?"},
# 结束多轮对话消息列表的定义
]
# 将多轮消息列表传入模型并打印返回的 AIMessage 文本内容
print(model.invoke(conversation).content)返回值是 AIMessage(不是裸字符串)。常用字段:
content:文本内容usage_metadata:token 用量(若供应商返回)tool_calls:若绑定了工具且模型决定调用
4.2. stream:边生成边打印 #
4.2.1. stream.py #
# 从 langchain.chat_models 导入统一初始化聊天模型的函数
from langchain.chat_models import init_chat_model
# 从 dotenv 导入用于加载 .env 环境变量的函数
from dotenv import load_dotenv
# 加载 .env 文件中的环境变量;override=True 表示覆盖已存在的同名变量
load_dotenv(override=True)
# 初始化 DeepSeek 聊天模型
model = init_chat_model("deepseek:deepseek-v4-flash")
# 以流式方式调用模型;每个 chunk 是 AIMessageChunk,.text 取增量文本
for chunk in model.stream("用三句话介绍 Agent 是什么"):
# 打印当前增量文本;end="" 不换行,flush=True 立即刷新输出缓冲区
print(chunk.text, end="", flush=True)
# 流式输出结束后打印换行,使终端光标落到下一行
print()需要「流式过程中拼出完整消息」时,可对 chunk 做累加:
4.2.2. stream_accumulate.py #
# 从 langchain.chat_models 导入统一初始化聊天模型的函数
from langchain.chat_models import init_chat_model
# 从 dotenv 导入用于加载 .env 环境变量的函数
from dotenv import load_dotenv
# 加载 .env 文件中的环境变量;override=True 表示覆盖已存在的同名变量
load_dotenv(override=True)
# 初始化 DeepSeek 聊天模型
model = init_chat_model("deepseek:deepseek-v4-flash")
# 用于累加流式 chunk 的变量,初始为 None
full = None
# 以流式方式调用模型,逐块接收 AIMessageChunk
for chunk in model.stream("用一句话解释 temperature 参数"):
# 首个 chunk 直接赋值,后续 chunk 与已有结果相加拼成完整消息
full = chunk if full is None else full + chunk
# 可选:观察「到目前为止」的完整文本
# print(full.text)
# 流式结束后打印累加得到的完整文本
print("最终:", full.text)与 agent.stream 的区别:
| API | 流的是什么 |
|---|---|
model.stream |
模型 token / 文本块 |
agent.stream |
Agent 图节点更新(模型步、工具步等) |
4.3. batch:并行多请求 #
适合一批彼此独立的问题(客户端并行,不是厂商 Batch API)。
4.3.1. batch.py #
# 从 langchain.chat_models 导入初始化聊天模型的函数
from langchain.chat_models import init_chat_model
# 从 dotenv 导入加载环境变量的函数
from dotenv import load_dotenv
# 加载 .env 中的环境变量,并覆盖已存在的同名变量
load_dotenv(override=True)
# 初始化 DeepSeek 聊天模型,温度设为 0 以保证输出更稳定
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# 批量并发调用模型,同时处理多条提示词并收集响应
responses = model.batch(
# 构造包含四季概括任务的提示词列表
[
# 要求用四个字概括春天
"用四个字概括春天",
# 要求用四个字概括夏天
"用四个字概括夏天",
# 要求用四个字概括秋天
"用四个字概括秋天",
# 要求用四个字概括冬天
"用四个字概括冬天",
],
# 设置最大并发数为 4,加快批量请求速度
config={"max_concurrency": 4},
)
# 遍历批量调用返回的全部响应结果
for r in responses:
# 打印当前响应的文本内容
print(r.content)
若希望「谁先完成谁先返回」,可用 batch_as_completed(顺序可能乱,需对照输入索引)。
4.3.2 batch_as_completed.py #
# 从 langchain.chat_models 导入初始化聊天模型的函数
from langchain.chat_models import init_chat_model
# 从 dotenv 导入加载环境变量的函数
from dotenv import load_dotenv
# 加载 .env 中的环境变量,并覆盖已存在的同名变量
load_dotenv(override=True)
# 初始化 DeepSeek 聊天模型,温度设为 0 以保证输出更稳定
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# 批量并发调用模型,同时处理多条提示词并收集响应
responses = model.batch_as_completed(
# 构造包含四季概括任务的提示词列表
[
# 要求用四个字概括春天
"用四个字概括春天",
# 要求用四个字概括夏天
"用四个字概括夏天",
# 要求用四个字概括秋天
"用四个字概括秋天",
# 要求用四个字概括冬天
"用四个字概括冬天",
],
# 设置最大并发数为 4,加快批量请求速度
config={"max_concurrency": 4},
)
# batch_as_completed 按完成顺序产出 (索引, 消息) 元组
for index, r in responses:
# 打印原始输入序号与当前响应文本
print(index, r.content)5. 常用参数调优 #
通过 init_chat_model(..., **kwargs) 传入:
| 参数 | 含义 | 实战建议 |
|---|---|---|
temperature |
随机性;越高越发散 | 抽取 / 工具调用偏 0~0.3;创意写作可更高 |
max_tokens |
最大生成长度 | 控制成本 |
timeout |
等待超时(秒) | 弱网或长生成可加大 |
max_retries |
失败重试次数 | 默认约 6;弱网可 10~15。401/404 等客户端错误不重试 |
5.1. parameters.py #
# 从 langchain.chat_models 导入初始化聊天模型的函数
from langchain.chat_models import init_chat_model
# 从 dotenv 导入加载环境变量的函数
from dotenv import load_dotenv
# 加载 .env 文件中的环境变量,并允许覆盖已存在的同名变量
load_dotenv(override=True)
# 偏确定:适合分类、抽取、要稳的工具决策
# 初始化一个偏确定性的聊天模型实例,赋值给变量 strict
strict = init_chat_model(
# 指定使用的模型名称:DeepSeek V4 Flash
"deepseek:deepseek-v4-flash",
# 将温度设为 0,使输出更稳定、可复现
temperature=0,
# 限制单次回复最多生成 256 个 token
max_tokens=256,
# 设置请求超时时间为 60 秒
timeout=60,
# 请求失败时最多重试 6 次
max_retries=6,
# 结束 init_chat_model 的参数列表
)
# 偏发散:适合头脑风暴
# 初始化一个偏创造性的聊天模型实例,赋值给变量 creative
creative = init_chat_model(
# 指定使用的模型名称:DeepSeek V4 Flash
"deepseek:deepseek-v4-flash",
# 将温度设为 0.9,使输出更发散、更有创意
temperature=0.9,
# 限制单次回复最多生成 256 个 token
max_tokens=256,
# 结束 init_chat_model 的参数列表
)
# 定义提示词:要求模型为咖啡店起名,且只输出店名本身
prompt = "给咖啡店起一个店名,只输出店名本身。"
# 调用确定性模型生成回复,并打印 temperature=0 时的结果内容
print("temperature=0 :", strict.invoke(prompt).content)
# 调用创造性模型生成回复,并打印 temperature=0.9 时的结果内容
print("temperature=0.9:", creative.invoke(prompt).content)
多跑几次你会看到:低 temperature 更稳,高 temperature 更多变。
6. 把模型实例交给 Agent #
create_agent 的 model 既可以是字符串,也可以是已配置好的模型实例。
需要统一 temperature / timeout 时,先 init_chat_model 再传入更清晰。
6.1. agent_with_model.py #
# 从 langchain.agents 导入 create_agent,用于创建智能体
from langchain.agents import create_agent
# 从 langchain.chat_models 导入 init_chat_model,用于初始化聊天模型
from langchain.chat_models import init_chat_model
# 从 dotenv 导入 load_dotenv,用于加载环境变量
from dotenv import load_dotenv
# 加载 .env 中的环境变量;override=True 表示覆盖已存在的同名变量
load_dotenv(override=True)
# 初始化聊天模型实例并赋值给 model
model = init_chat_model(
# 指定提供商与模型名称:deepseek 的 deepseek-v4-flash
"deepseek:deepseek-v4-flash",
# 设置温度为 0,使输出更确定、随机性更低
temperature=0,
# 设置请求超时时间为 60 秒
timeout=60,
)
# 定义获取天气信息的工具函数,参数为城市名,返回字符串
def get_weather(city: str) -> str:
# 函数文档字符串:说明该函数用于获取指定城市的天气信息
"""获取指定城市的天气信息。"""
# 返回模拟的天气结果字符串
return f"{city} 今天晴,气温 25°C。"
# 创建智能体,绑定模型、工具列表与系统提示词
agent = create_agent(
# 传入已初始化的模型实例,而不是模型名称字符串
model=model,
# 将 get_weather 注册为智能体可调用的工具
tools=[get_weather],
# 设置系统提示:需要天气时请调用工具,不要编造
system_prompt="需要天气时请调用工具,不要编造。",
)
# 调用智能体,传入用户关于成都天气的提问消息
result = agent.invoke({"messages": [{"role": "user", "content": "成都天气怎么样?"}]})
# 打印智能体最终回复消息的文本内容
print(result["messages"][-1].content)
7. 能力预览:工具绑定与结构化输出 #
7.1. bind_tools(模型层工具调用) #
不经过完整 Agent 时,也可让模型「提出」要调哪个工具:
7.1.1. bind_tools_preview.py #
# 从 langchain.chat_models 导入初始化聊天模型的函数
from langchain.chat_models import init_chat_model
# 从 langchain.tools 导入 tool 装饰器,用于将函数转换为工具
from langchain.tools import tool
# 从 dotenv 导入加载环境变量的函数
from dotenv import load_dotenv
# 加载 .env 文件中的环境变量,并覆盖已存在的同名变量
load_dotenv(override=True)
# 使用 @tool 装饰器,将下方函数注册为可供模型调用的工具
@tool
# 定义获取天气信息的工具函数,接收城市名称并返回字符串
def get_weather(city: str) -> str:
# 工具的功能说明文档字符串,供模型理解该工具的用途
"""获取指定城市的天气信息。"""
# 返回模拟的天气信息字符串
return f"{city} 今天晴,气温 25°C。"
# 初始化 DeepSeek 聊天模型,温度设为 0 以保证输出更稳定
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# 将天气工具绑定到模型上,使模型能够发起工具调用
model_with_tools = model.bind_tools([get_weather])
# 调用绑定了工具的模型,传入用户关于北京天气的提问
ai = model_with_tools.invoke("北京天气如何?")
# 打印模型返回的文本内容
print("content:", ai.content)
# 打印模型生成的工具调用信息
print("tool_calls:", ai.tool_calls)
注意:bind_tools 只让模型发出调用意图;真正执行工具、把结果塞回对话,通常交给 create_agent(或自己手写循环)。
7.2. with_structured_output(结构化结果) #
需要模型直接返回可校验的结构化对象(而不是自由文本)时,用 with_structured_output:
传入 Pydantic 模型(或 JSON Schema),模型会按字段约束产出结果,适合工单分类、信息抽取、表单填充等场景。拿到的不再是一段话,而是带类型的对象,后续可直接入库或交给业务逻辑。
注意:部分模型在「思考 / thinking」模式下可能不支持强制工具选择;若 with_structured_output 报错,可按下方示例关闭 thinking 后再试。
7.2.1. structured_preview.py #
# 从 typing 模块导入 Literal,用于将字段取值限制为指定字面量
from typing import Literal
# 从 pydantic 导入 BaseModel 与 Field,用于定义带校验的数据模型
from pydantic import BaseModel, Field
# 从 langchain.chat_models 导入 init_chat_model,用于按提供商初始化聊天模型
from langchain.chat_models import init_chat_model
# 从 dotenv 导入 load_dotenv,用于从 .env 文件加载环境变量
from dotenv import load_dotenv
# 加载 .env 环境变量;override=True 表示覆盖进程中已存在的同名变量
load_dotenv(override=True)
# 定义客服工单摘要的 Pydantic 模型类
class Ticket(BaseModel):
# 类文档字符串:说明该类用于表示客服工单摘要
"""客服工单摘要。"""
# 问题类别字段,取值只能是「退款」「物流」「咨询」「其他」之一
category: Literal["退款", "物流", "咨询", "其他"] = Field(description="问题类别")
# 紧急程度字段,取值只能是「低」「中」「高」之一
urgency: Literal["低", "中", "高"] = Field(description="紧急程度")
# 一句话摘要字段,类型为字符串
summary: str = Field(description="一句话摘要")
# deepseek-v4 默认开启 thinking;thinking 下不支持强制 tool_choice,
# 而 with_structured_output 默认走 function_calling,会触发 400
# 调用 init_chat_model 初始化 DeepSeek 聊天模型
model = init_chat_model(
# 指定提供商与模型名称:deepseek 平台的 deepseek-v4-flash
"deepseek:deepseek-v4-flash",
# 将采样温度设为 0,使模型输出更稳定、更确定
temperature=0,
# 通过 extra_body 关闭 thinking,避免结构化输出时触发 400 错误
extra_body={"thinking": {"type": "disabled"}},
# 结束 init_chat_model 调用并完成模型实例创建
)
# 基于 Ticket 模型创建结构化输出提取器,强制模型按该 schema 返回
extractor = model.with_structured_output(Ticket)
# 调用提取器,将用户投诉文本解析为 Ticket 结构化对象
ticket = extractor.invoke("我的快递三天没更新了,很着急。")
# 打印解析得到的工单对象
print(ticket)
8. 查看 token 用量 #
不少供应商会在 AIMessage.usage_metadata 里带回用量,便于估成本。
8.1. token_usage.py #
# 从 langchain.chat_models 导入初始化聊天模型的函数
from langchain.chat_models import init_chat_model
# 从 dotenv 导入加载环境变量的函数
from dotenv import load_dotenv
# 从 rich 导入增强版 print,用于更美观地打印输出
from rich import print
# 加载 .env 中的环境变量,并覆盖进程中已存在的同名变量
load_dotenv(override=True)
# 初始化 DeepSeek 聊天模型(模型标识为 deepseek-v4-flash)
model = init_chat_model("deepseek:deepseek-v4-flash")
# 调用模型,请求用一句话解释什么是 embedding
ai = model.invoke("用一句话解释什么是 embedding")
# 打印模型返回的文本内容
print(ai.content)
# 打印本次调用的 token 用量等元数据
print(ai.usage_metadata)
# 说明 usage_metadata 中常见字段的含义
# 常见字段示意:input_tokens / output_tokens / total_tokens若为 None,可能是该模型/路径未返回用量,或需按供应商文档开启相关选项。
示例输出:
{
'input_tokens': 88,
'output_tokens': 91,
'total_tokens': 179,
'input_token_details': {'cache_read': 0},
'output_token_details': {'reasoning': 60}
}| 字段 | 含义 | 示例值 |
|---|---|---|
input_tokens |
本次请求的输入 token 数(提示词等) | 88 |
output_tokens |
本次响应的输出 token 数(含正文与推理等) | 91 |
total_tokens |
总用量,一般为 input_tokens + output_tokens |
179 |
input_token_details.cache_read |
输入侧从缓存命中读取的 token 数(未命中则为 0) | 0 |
output_token_details.reasoning |
输出侧用于推理 / thinking 的 token 数 | 60 |
说明:*_token_details 是否出现、包含哪些子字段,取决于供应商与模型;例如开启 thinking 时常见 reasoning,启用 prompt cache 时常见 cache_read。
9. 练习 #
- 对比 temperature:同一提示跑
0与0.9各 3 次,记录输出差异。 - 流式拼消息:用
full = full + chunk得到完整AIMessage,再把full作为历史的一部分发起第二轮invoke。 - batch 压测:用
batch一次问 5 个短问题,观察总耗时相对串行invoke的变化。 - 交给 Agent:把带
temperature=0的模型实例传给create_agent,对比字符串写法行为是否一致。
10. 本章小结 #
- Chat Model 可单独用,也可作为 Agent 的大脑。
- 优先用
init_chat_model("provider:model"),切换供应商成本低。 - 三大调用:
invoke(完整)、stream(增量)、batch(并行)。 - 用 temperature / max_tokens / timeout / max_retries 控制稳定性与成本。
bind_tools、with_structured_output是后续章节的入口能力。
下一章:Messages 消息体系——System / Human / AI / Tool,以及多模态内容怎么表示。