Appearance
模型抽象与 init_chat_model
前面所有示例都用 ChatOpenAI。但 LangGraph 的精髓在于模型可替换——同一套图,换个模型就能跑。这得益于 LangChain 的 ChatModel 抽象。本章讲这个抽象,以及用 init_chat_model 一行切换模型。
一、ChatModel 抽象
LangChain 把所有大模型封装成统一的 BaseChatModel 接口。不管底层是 OpenAI、通义千问还是本地 Ollama,对外都暴露相同的方法:
| 方法 | 作用 |
|---|---|
invoke(messages) | 同步调用,返回 AIMessage |
stream(messages) | 流式调用,逐 token 返回 |
bind_tools(tools) | 绑定工具,返回支持工具调用的模型 |
with_structured_output(schema) | 强制返回结构化输出 |
ainvoke / astream | 异步版本 |
这意味着:你在 LangGraph 节点里写的代码,换模型不用改。
python
# 这段代码对任何 ChatModel 都成立
def agent_node(state):
response = llm.invoke(state["messages"])
return {"messages": [response]}llm 是 ChatOpenAI 还是 ChatTongyi,节点函数根本不关心。
二、为什么需要抽象
设想你写了个智能体,最初用 GPT-4o。后来要部署到内网,必须用本地 Ollama;又要给国内用户换通义千问。如果没抽象,三套代码改三遍;有了抽象,只改一行:
python
# 改这一行即可
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini")
# 换成通义
from langchain_community.chat_models import ChatTongyi
llm = ChatTongyi(model="qwen-plus")
# 换成本地
from langchain_ollama import ChatOllama
llm = ChatOllama(model="qwen2.5:7b")图、节点、工具全不动。这就是抽象的价值。
三、init_chat_model:统一工厂
init_chat_model 是 LangChain 提供的工厂函数,按字符串参数动态创建模型实例。最大好处是配置驱动——模型名从环境变量或配置文件读,代码零修改。
3.1 基本用法
python
from langchain.chat_models import init_chat_model
# 通过 model_provider 指定厂商
llm = init_chat_model(
model="gpt-4o-mini",
model_provider="openai",
temperature=0,
)model_provider 常见值:
| provider | 对应类 | 包 |
|---|---|---|
openai | ChatOpenAI | langchain-openai |
anthropic | ChatAnthropic | langchain-anthropic |
tongyi | ChatTongyi | langchain-community |
zhipu | ChatZhipuAI | langchain-community |
ollama | ChatOllama | langchain-ollama |
3.2 配置驱动切换
把模型配置塞进环境变量或 yaml,运行时读取:
yaml
# config.yaml
model:
name: qwen-plus
provider: tongyi
temperature: 0python
import yaml
from langchain.chat_models import init_chat_model
with open("config.yaml", encoding="utf-8") as f:
cfg = yaml.safe_load(f)["model"]
llm = init_chat_model(
model=cfg["name"],
model_provider=cfg["provider"],
temperature=cfg["temperature"],
)切换模型只改 yaml,不用动代码、不用重新打包。生产环境常用这招做"灰度切换"。
四、BaseChatModel 核心方法详解
4.1 invoke / stream
python
from langchain_core.messages import HumanMessage
# 同步
resp = llm.invoke([HumanMessage(content="你好")])
print(resp.content)
# 流式
for chunk in llm.stream([HumanMessage(content="写一首五言绝句")]):
print(chunk.content, end="", flush=True)4.2 bind_tools
python
from langchain_core.tools import tool
@tool
def add(a: float, b: float) -> float:
"""两数相加。"""
return a + b
llm_with_tools = llm.bind_tools([add])
resp = llm_with_tools.invoke("3+5 等于几?")
# resp.tool_calls 里会有调 add 的请求4.3 with_structured_output
python
from pydantic import BaseModel
class Person(BaseModel):
name: str
age: int
struct_llm = llm.with_structured_output(Person)
p = struct_llm.invoke("张三今年 28 岁")
print(p.name, p.age) # 张三 28注意:with_structured_output 和 bind_tools 不能同时用在一个模型实例上,需要时分别创建。
五、在 LangGraph 中使用
LangGraph 节点里,模型只是一个普通组件。典型用法:
python
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(
model=llm, # init_chat_model 创建的实例
tools=[add],
)或手动节点:
python
from langgraph.graph import StateGraph, START, END, MessagesState
def call_model(state):
return {"messages": [llm.invoke(state["messages"])]}
graph = StateGraph(MessagesState).add_node("model", call_model)
graph = graph.add_edge(START, "model").add_edge("model", END).compile()六、完整示例:三模型切换
下面这个脚本用 init_chat_model 在三种模型间无缝切换,验证抽象的一致性:
python
import os
from langchain.chat_models import init_chat_model
from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
@tool
def add(a: float, b: float) -> float:
"""两数相加。"""
return a + b
def test_model(name, provider, **kwargs):
print(f"\n===== 测试 {name} ({provider}) =====")
try:
llm = init_chat_model(model=name, model_provider=provider,
temperature=0, **kwargs)
# 1. 普通调用
r = llm.invoke([HumanMessage(content="用一句话介绍你自己")])
print("普通调用:", r.content[:80])
# 2. 工具调用
llm_t = llm.bind_tools([add])
r2 = llm_t.invoke("3+5 等于几?")
calls = getattr(r2, "tool_calls", [])
print("工具调用:", [c["name"] for c in calls] if calls else "无")
except Exception as e:
print(f"出错: {e}")
# OpenAI(需 OPENAI_API_KEY)
test_model("gpt-4o-mini", "openai")
# 通义千问(需 DASHSCOPE_API_KEY)
test_model("qwen-plus", "tongyi")
# Ollama(需本地 ollama 服务)
test_model("qwen2.5:7b", "ollama", base_url="http://localhost:11434")跑一遍就能直观看到:不同模型的回答风格不同,但 API 调用方式完全一致。
七、安装依赖
bash
# 核心
pip install langchain langgraph -i https://pypi.tuna.tsinghua.edu.cn/simple
# 按需安装各厂商包
pip install langchain-openai -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install langchain-anthropic -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install langchain-community -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install langchain-ollama -i https://pypi.tuna.tsinghua.edu.cn/simple八、常见踩坑
1. 不同模型工具调用能力差异大
抽象只保证接口一致,不代表能力一致。GPT-4o 工具调用很稳,但小模型/本地模型经常"不调"或"乱调"。生产前要针对目标模型做工具调用测试。
2. temperature 默认值不同
ChatOpenAI 默认 temperature=0.7,ChatOllama 默认 temperature=0.8。换模型时显式指定,避免行为漂移。
3. init_chat_model 找不到 provider
报错 Could not find provider xxx。原因:对应厂商的包没装。比如 tongyi 需要 langchain-community + dashscope。装上即可。
4. base_url / api_base 参数名不一致
不同厂商配置自定义 endpoint 的参数名不一样:OpenAI 用 base_url,旧版有的用 api_base。查对应类的文档。国产模型一般不用改。
5. with_structured_output 兼容性
不是所有模型都支持结构化输出。Ollama 部分模型不支持,会报错。退而求其次用 prompt + JSON 解析。
九、小结
BaseChatModel是统一抽象:invoke/stream/bind_tools/with_structured_output全模型一致。init_chat_model(model, model_provider)是配置驱动的工厂,切换模型只改一行。- LangGraph 节点里模型只是组件,可随意替换。
- 接口一致 ≠ 能力一致,换模型务必测工具调用。
接下来几章分别讲各家模型的集成细节。