摘要:本文记录使用 LangChain 调用阿里云百炼 Qwen 模型的完整过程,包括创建 API Key、在 Windows 中配置环境变量、连接 OpenAI 兼容接口、理解三种消息类型,以及手动维护多轮对话历史。本文使用 Python 3.10.18 和 uv 项目环境。
文章目录
- [一、从"调用模型"开始学习 LangChain](#一、从“调用模型”开始学习 LangChain)
- [二、准备阿里云百炼 API Key](#二、准备阿里云百炼 API Key)
-
- [1、API Key 是什么](#1、API Key 是什么)
- [2、创建自己的 API Key](#2、创建自己的 API Key)
- [三、在 Windows 中安全保存密钥](#三、在 Windows 中安全保存密钥)
- [四、使用 ChatOpenAI 连接 Qwen](#四、使用 ChatOpenAI 连接 Qwen)
-
- [1、为什么调用 Qwen 却使用 ChatOpenAI](#1、为什么调用 Qwen 却使用 ChatOpenAI)
- 2、初始化模型
- [3、Base URL 应该使用哪个](#3、Base URL 应该使用哪个)
- 五、完成第一次模型调用
-
- [1、最简单的 invoke 调用](#1、最简单的 invoke 调用)
- 2、模型什么时候产生费用
- [六、理解 LangChain 的三种消息](#六、理解 LangChain 的三种消息)
-
- [1、SystemMessage 负责设定规则](#1、SystemMessage 负责设定规则)
- [2、HumanMessage 表示用户输入](#2、HumanMessage 表示用户输入)
- [3、AIMessage 表示模型回答](#3、AIMessage 表示模型回答)
- 七、实现真正的多轮对话
-
- 1、模型不会自动记住上一次调用
- 2、手动维护消息列表
- [3、为什么还要保存 AI 的回答](#3、为什么还要保存 AI 的回答)
- 4、用循环实现连续聊天
- 八、用角色指令完成简单翻译
-
- [1、通过 SystemMessage 固定任务](#1、通过 SystemMessage 固定任务)
- 九、常见问题
-
- [1、环境变量明明配置了,Python 却读取不到](#1、环境变量明明配置了,Python 却读取不到)
- [2、出现 401 或 AuthenticationError](#2、出现 401 或 AuthenticationError)
- [3、出现 404 或模型不存在](#3、出现 404 或模型不存在)
- [4、代码运行成功但 PyCharm 仍然标红](#4、代码运行成功但 PyCharm 仍然标红)
- 5、对话越长为什么越慢
- 十、完整示例
- 十一、总结
一、从"调用模型"开始学习 LangChain
1、这一篇要解决什么问题
上一篇已经完成了 uv、Python 3.10.18、项目虚拟环境和 PyCharm 解释器的配置。这一篇正式进入大模型开发,完成下面几件事:
- 获取阿里云百炼 API Key;
- 将密钥安全地保存到 Windows 环境变量;
- 使用
ChatOpenAI连接 Qwen 模型; - 理解系统、用户和 AI 三种消息;
- 实现能够保留上下文的多轮对话;
- 理解模型调用、LangChain 和对话历史之间的关系。
初学时最容易混淆的是:Qwen 是模型,阿里云百炼提供模型服务,LangChain 则负责组织消息、调用模型以及连接后续的提示词、知识库和工具。
它们之间的关系可以简单理解为:
text
Python 程序
↓
LangChain
↓
OpenAI 兼容接口
↓
阿里云百炼 Qwen 模型
↓
模型回答
2、本文运行环境
本文使用:
text
操作系统:Windows 11
Python:3.10.18
环境管理:uv
开发工具:PyCharm + Jupyter Notebook
模型:qwen-plus
环境变量:DASHSCOPE_API_KEY
项目已经存在 pyproject.toml 和 uv.lock,因此先在项目根目录同步环境:
powershell
uv sync --locked
验证核心依赖:
powershell
uv run python -c "import langchain, langchain_openai; print('LangChain 环境正常')"
二、准备阿里云百炼 API Key
1、API Key 是什么
API Key 可以理解为程序访问模型服务时使用的身份凭证。每次调用模型,服务端都会根据它判断:
- 请求属于哪个账号或业务空间;
- 当前账号是否有模型调用权限;
- 调用量和费用应该记录到哪里。
因此,API Key 的作用类似密码,不能直接发布在文章、GitHub 仓库、Notebook 输出或截图中。

2、创建自己的 API Key
进入阿里云百炼控制台,在 API Key 管理页面创建密钥。创建时要确认密钥所属的地域和业务空间,因为后面使用的 API 地址必须与它匹配。


创建成功后,应立即把密钥保存到安全位置。

模型调用流程可以概括为:
text
读取环境变量中的 API Key
↓
向百炼兼容接口发送 messages
↓
百炼验证身份并调用指定模型
↓
返回 AIMessage
↓
读取 response.content

三、在 Windows 中安全保存密钥
1、为什么不能把密钥写进代码
下面这种写法虽然能运行,但不推荐:
python
api_key = "sk-这里是完整密钥"
一旦把文件发送给别人、上传到代码仓库,或者在截图中露出代码,密钥就可能泄露。更安全的方式是把密钥保存在操作系统环境变量中,程序运行时再读取。
本文使用的变量名是:
text
DASHSCOPE_API_KEY
2、通过系统界面配置
按 Win + R 打开运行窗口,输入:
text
sysdm.cpl

进入"高级"→"环境变量",在用户变量区域新建:
text
变量名:DASHSCOPE_API_KEY
变量值:自己的完整 API Key




配置完成后,要关闭已经打开的 PowerShell、PyCharm 和 Jupyter 内核,再重新打开。已经运行的程序不会自动读取新环境变量。
3、安全验证环境变量
不要直接在终端输出完整密钥。可以只检查变量是否存在:
powershell
if ($env:DASHSCOPE_API_KEY) {
Write-Host "DASHSCOPE_API_KEY 已配置"
} else {
Write-Host "DASHSCOPE_API_KEY 未读取到"
}
也可以在 Python 中验证:
python
import os
api_key = os.getenv("DASHSCOPE_API_KEY")
print("API Key 已读取" if api_key else "API Key 未读取")
如果显示"未读取",先重启终端和 IDE,而不是反复创建同名变量。
四、使用 ChatOpenAI 连接 Qwen
1、为什么调用 Qwen 却使用 ChatOpenAI
ChatOpenAI 并不表示只能调用 OpenAI 官方模型。它封装的是 OpenAI 风格的聊天接口。
阿里云百炼提供 OpenAI 兼容接口,所以只要修改三项内容,就可以通过 ChatOpenAI 调用 Qwen:
model:要调用的模型名称;api_key:百炼 API Key;base_url:百炼的 OpenAI 兼容地址。
这就是"兼容接口"的价值:上层代码结构基本不变,只替换模型服务的连接信息。
2、初始化模型
为了兼容课程项目中锁定的 LangChain 版本,本文使用 openai_api_key 和 openai_api_base 参数:
python
import os
from langchain_openai import ChatOpenAI
api_key = os.getenv("DASHSCOPE_API_KEY")
if not api_key:
raise RuntimeError("未读取到 DASHSCOPE_API_KEY,请检查环境变量并重启 IDE")
llm = ChatOpenAI(
model="qwen-plus",
openai_api_key=api_key,
openai_api_base=(
"https://dashscope.aliyuncs.com/"
"compatible-mode/v1"
),
temperature=0.7,
)
新版本 langchain-openai 通常也支持更简洁的参数名:
python
llm = ChatOpenAI(
model="qwen-plus",
api_key=api_key,
base_url=(
"https://dashscope.aliyuncs.com/"
"compatible-mode/v1"
),
)
课程项目有 uv.lock 时,应以当前锁定版本支持的写法为准,不要为了模仿最新示例而随意升级整套依赖。
3、Base URL 应该使用哪个
base_url 是模型服务的访问地址,它不是 API Key,也不能被环境变量中的密钥自动替代。
常见的北京地域兼容地址是:
text
https://dashscope.aliyuncs.com/compatible-mode/v1
百炼目前也为部分地域和业务空间提供专属域名,形式类似:
text
https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
应优先使用自己业务空间页面提供的地址,并确保 API Key、地域和业务空间相匹配。老师示例中的旧地址能够运行,是因为该兼容入口仍然可用,并不代表所有专属模型和业务空间都能混用。
五、完成第一次模型调用
1、最简单的 invoke 调用
模型初始化后,可以直接传入一个字符串:
python
response = llm.invoke("请用一句话介绍你自己")
print(response.content)
这里发生了两件事:
llm.invoke()把输入发送给远程模型;- 模型返回一个
AIMessage对象。
response 不是普通字符串,真正的文本内容保存在:
python
response.content
还可以查看返回对象的类型:
python
print(type(response))
结果通常类似:
text
<class 'langchain_core.messages.ai.AIMessage'>
2、模型什么时候产生费用
创建 ChatOpenAI 对象时只是保存连接配置,不会立即向模型提问。
真正发起网络请求的是:
python
llm.invoke(...)
也就是说,只有运行到调用语句时,程序才会把输入发送到阿里云百炼,并按照实际输入、输出和模型计费规则产生用量。
Notebook 已经显示的旧输出不会重复调用模型;重新执行对应单元格才会再次请求。

六、理解 LangChain 的三种消息
1、SystemMessage 负责设定规则
SystemMessage 用来告诉模型应该扮演什么角色、遵守什么要求:
python
from langchain_core.messages import SystemMessage
system_message = SystemMessage(
content="你是一名耐心的 Python 老师,请用通俗中文回答。"
)
它通常不是用户真正提出的问题,而是整个对话的背景和行为规则。
2、HumanMessage 表示用户输入
HumanMessage 表示用户发送的内容:
python
from langchain_core.messages import HumanMessage
human_message = HumanMessage(
content="什么是 Python 虚拟环境?"
)
3、AIMessage 表示模型回答
调用聊天模型后返回的是 AIMessage:
python
response = llm.invoke([
system_message,
human_message,
])
print(response.content)
三种消息和 OpenAI 兼容接口中的角色一一对应:
| LangChain 消息 | 接口角色 | 含义 |
|---|---|---|
SystemMessage |
system |
系统要求和角色设定 |
HumanMessage |
user |
用户输入 |
AIMessage |
assistant |
模型回答 |
使用消息对象比直接拼接字符串更清晰,也为后面的多轮对话、提示词模板和工具调用打下基础。
七、实现真正的多轮对话
1、模型不会自动记住上一次调用
下面是两个彼此独立的请求:
python
answer1 = llm.invoke("我叫小明。")
answer2 = llm.invoke("我叫什么名字?")
第二次调用时只发送了"我叫什么名字",没有把第一句话一起发送。因此模型大概率不知道答案。
这是大模型 API 与聊天网页最容易让初学者误解的地方:模型接口默认是无状态的。网页看起来有记忆,是因为应用程序在每次请求时重新附带了历史消息。
2、手动维护消息列表
先建立对话历史:
python
from langchain_core.messages import (
SystemMessage,
HumanMessage,
)
messages = [
SystemMessage(
content="你是一名友好的中文助手。"
)
]
第一轮对话:
python
messages.append(
HumanMessage(content="我叫小明,正在学习 LangChain。")
)
response = llm.invoke(messages)
messages.append(response)
print(response.content)
第二轮对话:
python
messages.append(
HumanMessage(content="我叫什么名字?正在学习什么?")
)
response = llm.invoke(messages)
messages.append(response)
print(response.content)
此时第二次调用收到的消息结构是:
text
SystemMessage:你是一名友好的中文助手
HumanMessage:我叫小明,正在学习 LangChain
AIMessage:第一轮回答
HumanMessage:我叫什么名字?正在学习什么?
因为历史消息被完整传入,模型才能结合上下文回答。

3、为什么还要保存 AI 的回答
只保存用户问题是不完整的:
python
messages.append(HumanMessage(content=user_input))
每轮调用后还应该把模型返回的 AIMessage 放入历史:
python
response = llm.invoke(messages)
messages.append(response)
否则下一轮模型只看见用户连续说了什么,却不知道自己之前回答了什么,容易产生上下文断裂。
4、用循环实现连续聊天
把上面的逻辑放进循环,就能得到一个简单的命令行聊天程序:
python
from langchain_core.messages import (
SystemMessage,
HumanMessage,
)
messages = [
SystemMessage(
content="你是一名耐心、简洁的中文学习助手。"
)
]
while True:
user_input = input("我:").strip()
if user_input.lower() in {"exit", "quit", "退出"}:
print("对话结束")
break
messages.append(
HumanMessage(content=user_input)
)
response = llm.invoke(messages)
messages.append(response)
print("AI:", response.content)
这段代码已经具备聊天应用最核心的结构:
text
接收输入
→ 写入历史
→ 调用模型
→ 保存回答
→ 显示结果
八、用角色指令完成简单翻译
1、通过 SystemMessage 固定任务
除了聊天,还可以利用系统消息规定模型的工作:
python
messages = [
SystemMessage(
content="你是一名翻译助手,把用户输入翻译成英文。"
),
HumanMessage(
content="人工智能正在改变我们的学习方式。"
),
]
response = llm.invoke(messages)
print(response.content)

这种写法已经能够完成固定任务,但目标语言被写死在系统消息中。下一篇将使用 ChatPromptTemplate 把目标语言设计成变量,从而用同一份模板完成中文、英文、意大利语和日语等不同任务。

九、常见问题
1、环境变量明明配置了,Python 却读取不到
先检查变量是否存在:
python
import os
print(bool(os.getenv("DASHSCOPE_API_KEY")))
如果结果是 False:
- 关闭并重新打开 PyCharm;
- 重启 Notebook 内核;
- 检查变量名是否准确;
- 确认变量建立在当前用户下;
- 不要在变量值外面额外加引号。
2、出现 401 或 AuthenticationError
这类错误通常与身份验证有关:
- API Key 不完整或复制错误;
- Key 已被删除或禁用;
- Key 与地域、业务空间不匹配;
- 环境变量中保存的仍是旧 Key。
不要通过反复安装 Python 包来解决认证错误,因为依赖安装和服务端身份认证是两件不同的事。
3、出现 404 或模型不存在
重点检查:
base_url是否正确;- 地址末尾是否包含
/compatible-mode/v1; model="qwen-plus"是否拼写正确;- 当前业务空间是否有对应模型的调用权限。
4、代码运行成功但 PyCharm 仍然标红
先确认 Notebook 或脚本使用的是:
text
项目目录\.venv\Scripts\python.exe
再执行:
powershell
uv run python -c "import langchain_openai; print(langchain_openai.__file__)"
如果能够输出 .venv 中的包路径,说明运行环境正常,标红更可能是 IDE 索引尚未刷新。
5、对话越长为什么越慢
手动维护多轮历史时,每一次调用都会重新发送消息列表。历史越长:
- 输入 Token 越多;
- 调用费用可能越高;
- 网络传输和模型处理时间越长;
- 早期信息可能超过模型上下文长度。
实际应用通常会限制历史轮数、压缩旧对话或总结历史,而不是无限追加消息。
十、完整示例
1、可直接运行的多轮对话代码
python
import os
from langchain_openai import ChatOpenAI
from langchain_core.messages import (
SystemMessage,
HumanMessage,
)
api_key = os.getenv("DASHSCOPE_API_KEY")
if not api_key:
raise RuntimeError(
"未读取到 DASHSCOPE_API_KEY,请检查环境变量并重启 IDE"
)
llm = ChatOpenAI(
model="qwen-plus",
openai_api_key=api_key,
openai_api_base=(
"https://dashscope.aliyuncs.com/"
"compatible-mode/v1"
),
temperature=0.7,
)
messages = [
SystemMessage(
content="你是一名耐心、简洁的中文学习助手。"
)
]
while True:
user_input = input("我:").strip()
if user_input.lower() in {"exit", "quit", "退出"}:
print("对话结束")
break
if not user_input:
continue
messages.append(
HumanMessage(content=user_input)
)
response = llm.invoke(messages)
messages.append(response)
print("AI:", response.content)
在项目根目录运行:
powershell
uv run python chat.py
十一、总结
1、本篇掌握的核心知识
通过这次练习,可以得出几个非常重要的结论:
- API Key 是敏感凭证,应保存在环境变量中;
base_url决定请求发送到哪个模型服务;ChatOpenAI可以连接兼容 OpenAI 协议的模型服务;invoke()才是真正发起模型请求的步骤;- 返回结果是
AIMessage,正文通过.content获取; SystemMessage负责规则,HumanMessage负责用户输入;- 模型接口默认没有长期记忆;
- 多轮对话的本质是应用程序重新发送历史消息;
- 用户消息和 AI 回答都应该加入消息列表;
- 历史越长,Token 消耗和调用时间通常越高。
完成这一篇后,我们已经不只是"调用一次模型",而是掌握了聊天应用最基本的数据结构。下一篇将继续学习 ChatPromptTemplate,把写死在代码中的角色、语言和任务变成可复用的提示词模板。