报错提示缺少 API 密钥,通常不是 LangChain 本身出错,而是运行进程读不到对应的环境变量,或者初始化客户端时读取的变量名和实际设置的不一致。先看报错栈定位到具体是哪个集成类(例如 langchain-openai 的 ChatOpenAI、langchain-anthropic 的 ChatAnthropic),再核对它默认读取的环境变量名,比先往代码里加参数更快。
判断顺序建议是:先定位报错来自哪个集成类,再确认当前进程能否读到变量,最后才考虑显式传参兜底。本地脚本、Jupyter、Docker、CI 的变量作用域各不相同,换一个运行入口就可能失效。若报错是 401 或 403,说明密钥已传入但无效,不适用缺密钥的处理路径,应转去检查密钥状态和账号权限。
先确认当前场景和目标
动手改代码前,先确认三件事,否则容易在错误的方向上反复试。第一,报错到底属于哪一类:是「找不到密钥」的初始化异常,还是请求阶段返回的鉴权失败。前者需要补变量,后者需要换密钥或查权限,处理方式完全不同。第二,代码跑在哪个入口:本地终端、IDE 运行配置、Jupyter 内核、Docker 容器、CI 流水线,这几种环境的变量不会自动共享。第三,用的是哪个集成包和哪个类,不同封装默认读取的变量名不一样,需要以安装版本的包内说明或源码为准,不能凭记忆写。
可以先做一个最小判断:在报错的那个运行入口里打印变量是否存在,而不是打印变量值。若变量不可见,问题在加载环节;若变量可见但仍然报错,问题多半在客户端初始化时读的变量名不一致,或者传入的是空字符串。这两种情况后面的处理路径不同。
import os
name = 'OPENAI_API_KEY' # 按实际使用的集成替换
value = os.getenv(name)
print(name, 'exists:', value is not None, 'length:', len(value or ''))
按这个顺序处理
第一步,确认变量在当前进程里真的存在。用上面的片段在原报错的入口跑一遍,不要在另一个终端里验证,Linux 和 macOS 下新开终端、Windows 下新窗口、IDE 里点运行,环境都可能不同。
第二步,检查加载时机。很多人把密钥写进 .env,但 load_dotenv 调用写在了 import 之后,或者在创建客户端之后才执行,这时客户端初始化时读到的还是空值。建议把加载动作放在文件最前面,并且确认工作目录里有这个 .env 文件。
from dotenv import load_dotenv
load_dotenv() # 放在读取密钥的代码之前
import os
from langchain_openai import ChatOpenAI # 以实际安装的集成包为准
llm = ChatOpenAI(model='按账号可用模型替换')
第三步,仍然读不到时,用显式传参做一次定位。显式传参能把「变量没加载」和「变量名不对」区分开:如果显式传参后恢复正常,说明问题在环境变量;如果显式传参仍然报错,说明是密钥本身或参数问题。

llm = ChatOpenAI(
model='按账号可用模型替换',
api_key=os.environ['OPENAI_API_KEY'], # 不同封装的参数名可能是 api_key 或 openai_api_key
)
第四步,按运行入口补齐变量。终端里用 export 设置只对当前会话及子进程有效;Docker 用 -e 或 env_file 传入;CI 用平台的密钥配置项注入;Jupyter 修改后需要重启内核才会重新读取。使用兼容接口或自建网关时,除了密钥,通常还要同时指定 base_url,只配密钥仍然可能连接失败。
改完后怎么验证
验证不要只看有没有异常,要看调用是否真的走通。建议用一次最小调用:传入一句短提示,观察返回对象里是否带有正常内容。返回正常说明变量加载、密钥、模型名三项同时成立;返回鉴权类错误说明变量已生效但密钥无效;仍然提示缺少密钥说明改动没有作用到当前进程。
resp = llm.invoke('回复 OK 即可')
print(type(resp).__name__)
print(getattr(resp, 'content', resp))
还要做一次「换入口复验」:在 IDE、终端、容器里各跑一次同样的最小脚本,确认不是只在某一个入口偶然可用。另外注意日志里不要打印完整密钥,需要排查时只打印长度或首尾少量字符,避免密钥进入日志系统。
容易忽略的边界
变量作用域是最常被忽略的一条。export 只影响当前会话,重启终端就没了;写进 shell 配置文件只对交互式登录生效,CI 和容器不会读取。项目里的 .env 不应该提交到版本库,一旦泄露建议直接轮换密钥,而不是只删文件。
版本差异也需要确认。不同版本的集成包,客户端参数名和默认读取的变量名可能变化,旧写法在新版本里可能静默失效。某些供应商的封装使用自己的一套变量名,或者要求同时提供 base_url 和密钥,照搬 OpenAI 的配法不一定成立,需要结合已安装包的实际行为确认。
如果报错实际是 401、403 或 429,就不适用本文的缺密钥处理路径:401 和 403 通常是密钥无效或权限不足,429 通常是调用频率或额度限制,继续排查环境变量没有意义。回退建议是先用显式传参跑通一次最小调用,确认链路可用后,再把密钥迁回环境变量或密钥管理服务,避免长期把密钥写死在代码里。