一套代码,调用所有大模型------OpenAI、Claude、DeepSeek、本地模型全搞定
前言:当你开始写第一个模型调用时,会面临什么?
假设你现在要做一个 AI 应用,第一步自然是"让模型能回答问题"。看起来很简单,对吧?直接调 API 就行了。
但当你真正开始做时,问题接踵而来:
-
换个模型(从 GPT-4 换成 Claude),整个调用代码要重写------连返回值的取法都不一样
-
想实现多轮对话,得自己维护消息列表,还得区分系统指令、用户输入、AI 回复
-
想同时问 5 个问题,同步调用要等 10 秒,用户体验稀碎
-
想让用户看到"打字机效果",得自己处理流式输出的解析和拼接
-
想统计这次调用花了多少 token,还得从响应里一层层扒数据
这些杂活跟"业务逻辑"毫无关系,却占了大量开发时间。
Model I/O 就是 LangChain 用来解决这些问题的答案。 它是你与大模型交互的"标准接口"------统一了"传什么""怎么传""怎么调"三个环节,让你把精力放回真正重要的地方。
1. Model I/O 是什么?
Model I/O 回答的是一个最基本的问题:"怎么把问题喂给模型,并拿到有用的结果?"
它由三个环节组成:
| 环节 | 做什么 | 解决什么问题 |
|---|---|---|
| Prompts(提示词模板) | 把用户输入和系统指令格式化成模型能理解的消息 | "怎么组织输入" |
| Models(模型调用) | 调用大模型获取响应 | "怎么传给模型" |
| Output Parsers(输出解析) | 把模型输出解析成需要的格式 | "怎么拿有用结果" |
本文聚焦于 Models 这一层,也就是"怎么调模型"。关于 Prompts 和 Output Parsers,会在后续章节详细介绍。
2. 三类模型,别搞混了
LangChain 中有三类"模型",用途完全不同:
| 类型 | 输入→输出 | 代表类 | 说明 |
|---|---|---|---|
| Chat Models | 消息列表 → AI消息 | ChatOpenAI, ChatAnthropic | 本课程核心。所有现代模型(GPT-4o、Claude、DeepSeek)都是对话模型 |
| LLMs(旧版) | 字符串→字符串 | OpenAI(旧版) | 已基本淘汰。早期文本补全模型(如 GPT-3 text-davinci) |
| Embeddings | 文本→浮点数向量 | OpenAIEmbeddings | 不生成文本,而是将文本转为数字向量,用于语义搜索和 RAG |
本课程全程使用 Chat Models,除非特别说明,"模型调用"都指这一类。
3. 统一接口:LangChain 最核心的价值
不管你用的是 OpenAI、DeepSeek、Claude 还是本地的 Ollama,LangChain 都提供了完全一致的调用方式:
python
# 不同平台,同一套代码
llm = ChatOpenAI(model="gpt-4o-mini") # OpenAI
llm = ChatOpenAI(model="deepseek-chat", ...) # DeepSeek
llm = ChatAnthropic(model="claude-sonnet-4") # Anthropic
llm = ChatOllama(model="qwen2.5:7b") # 本地模型
# 调用方式完全一致
response = llm.invoke("你好") # 同步
response = await llm.ainvoke("你好") # 异步
for chunk in llm.stream("你好"): # 流式
print(chunk.content, end="")
responses = llm.batch(["问题1", "问题2"]) # 批量
这就是 Model I/O 中 Models 层的核心价值:一次学会,到处能用。
4. 为什么不用原生 SDK?
很多同学会问:"OpenAI 的 SDK 已经很好用了,为什么还要学 LangChain?"
原生 SDK 的痛点:切换模型
假设你想用 GPT-4o-mini、DeepSeek、Claude 三个模型跑同一个任务,对比结果:
用原生 SDK:每换一个平台,改一堆代码
python
# OpenAI
client_openai = OpenAI(api_key=..., base_url=...)
resp1 = client_openai.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "解释量子计算"}]
)
print(resp1.choices[0].message.content)
# DeepSeek(接口兼容OpenAI,但要改 client、api_key、base_url、model)
client_deepseek = OpenAI(api_key=..., base_url="https://api.deepseek.com/v1")
resp2 = client_deepseek.chat.completions.create(
model="deepseek-chat",
messages=[{"role": "user", "content": "解释量子计算"}]
)
print(resp2.choices[0].message.content)
# Anthropic Claude(不兼容OpenAI格式,整套代码重写)
client_claude = anthropic.Anthropic(api_key=...)
message = client_claude.messages.create(
model="claude-sonnet-4",
messages=[{"role": "user", "content": "解释量子计算"}]
)
print(message.content[0].text) # 注意:取值方式都不一样!
三个平台,三套 client,三种取值方式。如果还要加流式、重试、对话记忆......代码量会爆炸。
用 LangChain:只改一行初始化
python
# 调用 OpenAI
llm = ChatOpenAI(model="gpt-4o-mini")
print(llm.invoke("解释量子计算").content)
# 换成 DeepSeek?改一行
llm = ChatOpenAI(
model="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com/v1"
)
print(llm.invoke("解释量子计算").content) # 调用代码完全一样
# 换成 Claude?还是改一行
llm = ChatAnthropic(model="claude-sonnet-4")
print(llm.invoke("解释量子计算").content) # 调用代码还是一样
模型只有一两个时感觉差不多,但当你需要对比 3~5 个模型、加上流式输出、再接上 RAG 管道时,原生 SDK 的代码量会呈指数增长,而 LangChain 始终保持简洁。
5. 消息类型:传什么
LangChain 用标准化的消息格式来传递不同角色的内容:
| 消息类型 | 类名 | 用途 | 示例 |
|---|---|---|---|
| 系统消息 | SystemMessage | 设定 AI 的行为、角色和规则 | "你是一个有帮助的助手" |
| 用户消息 | HumanMessage | 用户的输入 | "帮我解释一下量子计算" |
| AI 消息 | AIMessage | AI 的回复,用于对话历史 | "量子计算是..." |
| 工具消息 | ToolMessage | 工具执行返回的结果 | 工具调用的输出 |
记忆口诀:系统定规则,用户提问题,AI 给回复,工具报结果。
实战:多轮对话
python
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini")
# 对话历史——模拟多轮对话
conversation = [
SystemMessage(content="你是一个有帮助的AI助手"),
HumanMessage(content="你好,我叫小明"),
AIMessage(content="你好!小明,有什么我可以帮助你的吗?"),
HumanMessage(content="我叫什么名字?") # 这依赖上一轮的上下文
]
response = llm.invoke(conversation)
print(response.content) # "你叫小明"
6. 传入方式:怎么传
知道"传什么"之后,下一个问题是:"怎么传给模型?"
| 你的需求 | 推荐方式 | 代码示例 |
|---|---|---|
| 简单问答,不需要上下文 | 直接传字符串 | llm.invoke("你好") |
| 需要角色设定或对话历史 | 传消息列表 | llm.invoke([SystemMessage(...), HumanMessage(...)]) |
| 动态构建消息 | 用元组/字典 | llm.invoke([("system", "..."), ("user", "...")]) |
一句话记忆:能用字符串就用字符串(最简单),需要对话/角色时用消息列表(最常见),动态构建时用元组/字典(最灵活)。
7. 调用方式:怎么调
LangChain 提供了四种调用方式,适应不同场景:
7.1 同步调用 invoke() ------ 最常用
适合大多数场景:
python
response = llm.invoke("什么是 LangChain?")
print(response.content)
7.2 异步调用 ainvoke() ------ 高并发
适用于需要同时处理多个请求的场景。
理解异步 :
invoke()就像排队买奶茶------站在柜台前死等,拿到一杯才点下一杯。ainvoke()就像扫码下单------点了 5 杯去旁边坐着,哪杯好了取哪杯,总时间 ≈ 单杯制作时间。
性能对比:5 个请求
| 方式 | 写法 | 总耗时 |
|---|---|---|
| 同步串行 | llm.invoke() × 5 |
~9 秒 |
| 异步+并行 | asyncio.gather(*[llm.ainvoke(p) for p in prompts]) |
~2 秒 |
关键理解 :ainvoke() 解决的是"等待时不阻塞"(让出控制权),asyncio.gather() 解决的是"同时跑多个任务"(利用控制权塞入更多任务),两者缺一不可。
python
import asyncio
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini")
prompts = ["介绍北京", "介绍上海", "介绍广州"]
# 正确写法:ainvoke + gather 同时跑
async def parallel_call():
tasks = [llm.ainvoke(p) for p in prompts]
results = await asyncio.gather(*tasks)
for r in results:
print(r.content[:20])
# Jupyter 中直接 await
await parallel_call()
# 普通 .py 文件中用 asyncio.run()
# asyncio.run(parallel_call())
7.3 流式调用 stream() ------ 打字机效果
提升用户体验:
python
print("AI回答:", end="")
full = None
for chunk in llm.stream("请写一首关于春天的诗"):
full = chunk if full is None else full + chunk
print(chunk.content, end="", flush=True) # flush=True 强制立即显示
7.4 批次调用 batch() ------ 并行处理多个独立请求
python
questions = ["什么是Python?", "什么是JavaScript?", "什么是Go语言?"]
responses = llm.batch(questions)
for q, r in zip(questions, responses):
print(f"Q: {q}\nA: {r.content}\n")
8. 多平台实战
8.1 各平台接入速查表
| 平台 | 用哪个类 | 模型名称示例 | 环境变量 |
|---|---|---|---|
| OpenAI(CloseAI 代理) | ChatOpenAI |
gpt-4o-mini |
OPENAI_API_KEY + OPENAI_BASE_URL |
| DeepSeek | ChatOpenAI |
deepseek-chat |
DEEPSEEK_API_KEY + DEEPSEEK_BASE_URL |
| 硅基流动 | ChatOpenAI |
Qwen/Qwen3-8B |
SILICONFLOW_API_KEY + SILICONFLOW_BASE_URL |
| Anthropic Claude | ChatAnthropic |
claude-sonnet-4 |
ANTHROPIC_API_KEY |
| Google Gemini | init_chat_model |
gemini-2.5-flash |
GEMINI_API_KEY |
规律 :凡是兼容 OpenAI 接口格式的平台(DeepSeek、硅基流动等),都可以直接用 ChatOpenAI,只需改 base_url 和 api_key。
8.2 一键切换多平台:init_chat_model
当你需要在运行时动态切换不同提供商的模型时,init_chat_model 比单独 import 更方便:
python
from langchain.chat_models import init_chat_model
# 一个函数搞定所有提供商
llm_openai = init_chat_model("gpt-4o-mini", model_provider="openai")
llm_claude = init_chat_model("claude-sonnet-4", model_provider="anthropic")
llm_gemini = init_chat_model("gemini-2.5-flash", model_provider="google_genai")
# 调用方式完全一致
for name, llm in [("OpenAI", llm_openai), ("Claude", llm_claude)]:
response = llm.invoke("用一句话介绍你自己")
print(f"{name}: {response.content}")
9. 调用本地模型:Ollama
Ollama 让你在本地运行开源模型,无需联网,也无需 API 费用。
安装与运行
bash
# 安装 Ollama(Linux)
curl -fsSL https://ollama.com/install.sh | sh
# 下载并运行模型
ollama run qwen2.5:7b
# 列出已下载的模型
ollama list
LangChain 调用
python
from langchain_ollama import ChatOllama
llm = ChatOllama(model="qwen2.5:7b", base_url="http://localhost:11434")
response = llm.invoke("你好,介绍一下你自己")
print(response.content)
10. 高级特性
10.1 多模态输入(视觉模型)
支持图像等多模态输入,适用于 GPT-4o 等视觉语言模型(VLM):
python
import base64
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
llm = ChatOpenAI(model="gpt-4o")
# 读取图片并转为 Base64
with open("image.jpg", "rb") as f:
image_data = base64.b64encode(f.read()).decode("utf-8")
# 构造多模态消息
message = HumanMessage(content=[
{"type": "text", "text": "描述这张图片"},
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}}
])
response = llm.invoke([message])
print(response.content)
Base64 的作用:将二进制图片数据编码为可打印文本,以便在 JSON 协议中传输。
10.2 速率限制
避免触发 API 限流:
python
from langchain_core.rate_limiter import InMemoryRateLimiter
from langchain_openai import ChatOpenAI
rate_limiter = InMemoryRateLimiter(
requests_per_second=0.1, # 10 秒只能发 1 个请求
check_every_n_seconds=0.1
)
llm = ChatOpenAI(model="gpt-4o-mini", rate_limiter=rate_limiter)
10.3 Token 使用追踪
统计每次调用的 token 消耗:
python
from langchain_core.callbacks import get_usage_metadata_callback
llm = ChatOpenAI(model="gpt-4o-mini")
with get_usage_metadata_callback() as cb:
llm.invoke("你好")
llm.invoke("再见")
print(cb.usage_metadata)
# {'input_tokens': 总输入token数, 'output_tokens': 总输出token数, 'total_tokens': 总token数}
10.4 提示词缓存(Prompt Caching)
当你反复使用同一段很长的系统提示词时,模型服务商可以在服务端缓存已处理的提示词,后续调用跳过重复计算,大幅省钱。
| 平台 | 缓存方式 | 省钱效果 |
|---|---|---|
| OpenAI | 全自动,零配置 | 缓存命中的 token 费用减半 |
| Anthropic | 需中间件标记 | 缓存命中 token 费用降低 90% |
python
# OpenAI:全自动,什么都不用做
llm = ChatOpenAI(model="gpt-4o-mini")
response = llm.invoke("你好") # 自动缓存
# Anthropic:需要中间件
from langchain_anthropic import ChatAnthropic, AnthropicPromptCachingMiddleware
llm = ChatAnthropic(
model="claude-sonnet-4",
middleware=[AnthropicPromptCachingMiddleware()] # 自动标记长提示词为可缓存
)
注意 :这里的缓存发生在模型服务商的服务器上,不是你本地的磁盘缓存。如果你想要"相同问题直接本地返回、不打 API"的缓存,那是 LangChain 的
LLMCache机制,属于另一个话题。
11. 小结
本文介绍了 Model I/O 中的模型调用部分:
| 主题 | 核心要点 |
|---|---|
| 统一接口 | 一套代码调用所有模型,切换只需改初始化 |
| 消息类型 | SystemMessage, HumanMessage, AIMessage, ToolMessage |
| 传入方式 | 字符串 / 消息列表 / 元组或字典 |
| 调用方式 | invoke, ainvoke, stream, batch |
| 多平台接入 | CloseAI、DeepSeek、硅基流动、Anthropic、Gemini、Ollama |
| 高级特性 | 多模态、速率限制、Token追踪、提示词缓存 |
核心收获
python
# 同样的代码,只需更换初始化方式即可切换模型
llm = init_chat_model("gpt-4o-mini", model_provider="openai")
llm = init_chat_model("claude-sonnet-4", model_provider="anthropic")
llm = init_chat_model("gemini-2.5-flash", model_provider="google_genai")
llm = ChatOllama(model="qwen2.5:7b") # 本地模型
# 调用方式完全一致
response = llm.invoke("你好")
这就是 LangChain Model I/O 的力量:把"怎么调模型"这件事彻底标准化,让你专注于"拿模型做什么"。
下一节预告 :我们将进入 Prompts(提示词模板) 的学习------如何用模板化方式管理提示词,让代码更干净、更可维护。
📌 本文基于 LangChain 官方文档及实战课程整理,所有代码示例均可在 Python 3.12 + uv 环境下运行。如有疑问,欢迎在评论区交流讨论。