本文介绍了如何使用 LangChain 调用大模型 API,并分析了其接口调用的底层实现原理。
快速上手
大模型 API 获取
截至本笔记写作日期(2025年6月22日),阿里云百炼 还有服务开通送 token 的活动,并且是不同模型的不同版本(少数除外)各送 100w token,足够学习使用了。
在之后的学习过程中,我们默认使用阿里云百炼提供的大模型 DeepSeek-V3。
首先需要一个 API-Key 来访问大模型:
在阿里云百炼的控制台 → API-Key 界面,点击右上角【创建我的 API-KEY】,把 sk 开头的这个字符串记录下来即可。

然后需要知道访问的接口 URL,这个可以在阿里云百炼的 API 参考文档里找到:
https://dashscope.aliyuncs.com/compatible-mode/v1
API Key 和接口 URL 都准备就绪,Token 额度也白嫖好了,接下来就开始调用大模型吧!
另外需要注意查看 DeepSeek 系列模型的 Token 使用情况:DeepSeek - 详情
LangChain 调用
先安装 LangChain 相关的库:
pip install langchain
pip install langchain_openai
通常,我们会把 API Key 和接口 URL 写到环境变量中,然后在 Python 程序里通过 os.getenv 导入对应值。
但是写环境变量还是太麻烦了,而且不同项目可能用到不同的 API Key 和 URL,数量一多就难以管理。
所以方便起见,我们使用库 dotenv 来简化环境变量的管理:pip install dotenv。
它的用法是:
- 在项目目录下新建一个
.env文件,里面写入环境变量,每行一个。例如:
DASHSCOPE_API_KEY=sk-xxxx
BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
- 在项目中调用
load_dotenv(),即可自动将.env文件里的配置写入环境变量,并且只在程序运行时生效,不会干扰到 shell 或其他 Python 程序使用的环境变量。
环境变量准备就绪,接下来就是第一个 LangChain 示例,开始与大模型的首次对话:
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
from langchain.schema import HumanMessage
import os
load_dotenv()
dsv3_chat = ChatOpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url=os.getenv("BASE_URL"),
model="deepseek-v3"
# 通过模型名即可指定对应模型,准确的模型名称需要在阿里云百炼的模型详情里查看
)
msg = HumanMessage(content="你好")
messages = [msg]
dsv3_chat_response = dsv3_chat.invoke(messages)
print(dsv3_chat_response)
当然,考虑到用户体验,我们最好使用流式输出:
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
from langchain.schema import HumanMessage
import os
load_dotenv()
dsv3_chat = ChatOpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url=os.getenv("BASE_URL"),
model="deepseek-v3",
streaming=True # 开启流式输出
)
msg = HumanMessage(
content="你好"
)
messages = [msg]
# 处理响应,逐块打印在控制台
for chunk in dsv3_chat.stream(messages):
print(chunk.content, end="", flush=True)
接口调用原理
上述例子里的请求和响应是经过高度封装的,本质上其实只是请求一个 HTTP URL 、接收它的响应内容并打印。
在一些大模型 API 文档(如 DeepSeek 的官方 API),你会看到这样的调用示例:
curl https://api.deepseek.com/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <DeepSeek API Key>" \
-d '{
"model": "deepseek-chat",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
],
"stream": false
}'
显然,就是一个非常朴素的 HTTP 请求。在 Python 里,也可以用最朴素的 requests 库来请求它,完成大模型调用:
import requests
DeepSeek_API_Key=...
response = requests.post(
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {DeepSeek_API_Key}"
}
url="https://api.deepseek.com/chat/completions",
data={
"model": "deepseek-chat",
"messages": [{
"role": "user",
"content": "你好"
}],
"stream": False
}
)
print(response.text)
显然,为了和大模型说一句“你好”,我们写了一大堆代码。这太丑了!我们当然不想这样。我们可以自己封装样式统一调用接口,但不同的大模型又可能采用不同的请求格式,每碰到一个新的就做兼容,这也很麻烦。
好在 OpenAI 提供了统一的调用接口格式1,各家大模型(例如 DeepSeek 官方 API 在文档里提到的)会自发地适配它:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ.get("OPENAI_API_KEY"),
)
response = client.responses.create(
model="gpt-4o",
instructions="You are a coding assistant that talks like a pirate.",
input="How do I check if a Python object is an instance of a class?",
)
print(response.output_text)
只要我们选择的大模型支持 OpenAI 格式,就可以通过以上方式调用,不用再手写 HTTP 请求了。
但是,LangChain 将模型本身的属性(如 URL 和模型名称)和模型的调用过程做了区分,模型信息写入到 ChatOpenAI 对象中,并且将实际调用过程写在 invoke 方法中。
查看 invoke 的源码,可以追踪到 langchain_core/language_models/chat_models.py 中的方法 _generate_with_cache。它的作用是对单组消息进行生成,并自动处理缓存、速率限制、流式输出、回调、元数据等。
如果是非流式输出,_generate_with_cache会调用 _generate 方法。而 _generate 的具体实现则取决于执行 invoke 的 ChatOpenAI 对象。
为了找到 _generate 具体是如何调用大模型的,我们去看langchain_openai/chat_models/base.py中ChatOpenAI 类的定义。然后发现 ChatOpenAI 并没有实现 _generate 方法,而是它的父类 BaseChatModel 实现了它。
在 BaseChatModel._generate() 中,若非流式输出,则会按属性配置情况进入四个分支之一进行处理。对于上文的简单 Case,我们进入的是最后一个分支:
else:
response = self.client.create(**payload)
熟悉吗?这就是前面我们看到的 OpenAI 的调用代码。
所以,LangChain 的 invoke 方法是基于 OpenAI API 的再次封装,在简单请求大模型的基础上添加了缓存机制、速率限制、元数据补充(如响应信息、ID)和上报调用信息的回调方法等。
HumanMessage 与 AIMessage
除了 ChatOpenAI 和 invoke,示例代码里还有两个新的类:HumanMessage 和 (没有显式出现但是响应的类型是它)AIMessage。
HumanMessage 没什么特别的,只是允许在原始输入的基础上添加其他参数:
def __init__(
self, content: Union[str, list[Union[str, dict]]], **kwargs: Any
) -> None:
"""Pass in content as positional arg.
Args:
content: The string contents of the message.
kwargs: Additional fields to pass to the message.
"""
super().__init__(content=content, **kwargs)
AIMessage 则定义了更多的成员变量,主要是附加了工具信息 tool_calls 和 invalid_tool_calls,即在回答之前,大模型进行了什么工具调用;以及 usage_metadata,附带了 token 用量等统计信息。
关于它们的实际使用,我们会在学习到工具调用时了解。