1. 本章目标 #

上一章用 create_agent(model="deepseek:...") 直接传了模型字符串。本章把 Chat Model 单独拆开讲清楚:

用统一接口调用聊天模型:初始化、调参、流式、批量,并把它交给 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(不是裸字符串)。常用字段:

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_agentmodel 既可以是字符串,也可以是已配置好的模型实例。 需要统一 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. 练习 #

  1. 对比 temperature:同一提示跑 00.9 各 3 次,记录输出差异。
  2. 流式拼消息:用 full = full + chunk 得到完整 AIMessage,再把 full 作为历史的一部分发起第二轮 invoke
  3. batch 压测:用 batch 一次问 5 个短问题,观察总耗时相对串行 invoke 的变化。
  4. 交给 Agent:把带 temperature=0 的模型实例传给 create_agent,对比字符串写法行为是否一致。

10. 本章小结 #

  1. Chat Model 可单独用,也可作为 Agent 的大脑。
  2. 优先用 init_chat_model("provider:model"),切换供应商成本低。
  3. 三大调用:invoke(完整)、stream(增量)、batch(并行)。
  4. temperature / max_tokens / timeout / max_retries 控制稳定性与成本。
  5. bind_toolswith_structured_output 是后续章节的入口能力。

下一章:Messages 消息体系——System / Human / AI / Tool,以及多模态内容怎么表示。