Skip to content

模型抽象与 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]}

llmChatOpenAI 还是 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对应类
openaiChatOpenAIlangchain-openai
anthropicChatAnthropiclangchain-anthropic
tongyiChatTongyilangchain-community
zhipuChatZhipuAIlangchain-community
ollamaChatOllamalangchain-ollama

3.2 配置驱动切换

把模型配置塞进环境变量或 yaml,运行时读取:

yaml
# config.yaml
model:
  name: qwen-plus
  provider: tongyi
  temperature: 0
python
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_outputbind_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.7ChatOllama 默认 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 节点里模型只是组件,可随意替换。
  • 接口一致 ≠ 能力一致,换模型务必测工具调用。

接下来几章分别讲各家模型的集成细节