认证问题最容易让人怀疑自己:API Key 写错了吗?provider 名填错了吗?环境变量没生效吗?
NousResearch/hermes-agent issue #15932 讲的是一个更隐蔽的问题:用户把 API key 放进了 ~/.hermes/.env,但 Hermes 的 credential pool 初始化路径只看 os.environ,没有读取 Hermes 自己的 .env 文件。
结果就是:
key 明明在 ~/.hermes/.env 里,Hermes 却报 No API key found。
这类问题对 OpenAI-compatible provider、第三方模型中转站、多 provider 配置都很致命,因为认证链路断了,后面的模型调用根本不会开始。
现象:.env 里有 key,启动时却说没有
复现路径很清楚。
先确保 shell 环境里没有 API key:
unset OPENAI_API_KEY
然后把 key 写进 Hermes 的配置文件:
# ~/.hermes/.env
OPENAI_API_KEY=sk-...
再启动一个需要 API key 的 provider。
预期是 Hermes 能从 ~/.hermes/.env 里读到 key。
实际却是:
No API key found
这说明 Hermes 的某条认证读取路径只查了进程环境变量,没有查 Hermes 自己的 .env 配置源。
根因一:credential_pool._seed_from_env() 只看 os.environ
issue 指出第一个问题在:
credential_pool._seed_from_env()
它只检查:
os.environ
但没有通过 Hermes config 层读取:
hermes_cli.config.get_env_value()
这会造成一个很反直觉的状态:
Hermes 官方/约定位置 ~/.hermes/.env 里有 key
但 credential pool 初始化时看不见
也就是说,key 不是没写,而是认证系统看错了地方。
根因二:_resolve_api_key_provider_secret() 没有回退到 credential pool
issue 还指出第二个问题:
hermes_cli/auth.py:_resolve_api_key_provider_secret()
当环境变量里找不到 key 时,它没有 fallback 到:
credential_pool.load_pool()
这会让认证链路缺少第二道兜底。
一个更健壮的查找顺序应该类似:
显式配置 / provider secret
↓
os.environ
↓
~/.hermes/.env via get_env_value()
↓
credential_pool.load_pool()
而不是只摸 os.environ 这个“口袋”。
如果 key 被用户放在 Hermes 的“保险箱”里,认证系统也应该去那里找。
为什么这个问题会影响 OpenAI-compatible 场景?
很多 provider 都用 API key auth。
例如:
- OpenAI 官方;
- OpenAI-compatible 中转站;
- 自建 NewAPI / OneAPI;
- DeepAI API 中转站;
- 其他兼容 Chat Completions / Responses API 的服务。
这些场景经常会通过 .env 管理:
OPENAI_API_KEY=...
OPENAI_BASE_URL=...
OPENAI_MODEL=...
如果 Hermes 某条认证路径只看 shell env,不看 ~/.hermes/.env,用户就会看到:
我明明填了 key,为什么还说没有?
这类问题很容易被误判成:
- API key 无效;
- provider 不支持;
- base URL 写错;
- 模型名不可用;
- DeepAI 或其他中转站有问题。
但在 #15932 里,真正原因是 Hermes 内部 credential resolution 的读取源不完整。
官方评论:这是 #15914 的重复,修复在 #16101
维护者评论指出:
Likely duplicate of #15914 — same root cause:
_resolve_api_key_provider_secret() only reads os.environ, not credential_pool.
Fix PR at #15920.
后续又确认:
Fixed in #16101 (salvage of #15920)
并明确说两个点都修了:
1. _seed_from_env() 使用 get_env_value(),可以检查 ~/.hermes/.env; 2. _resolve_api_key_provider_secret() 在 env var 为空时 fallback 到 credential_pool.load_pool()。
这说明问题不是用户配置习惯错,而是认证查找链路确实缺了两段。
用户排查时应该怎么确认?
如果你遇到类似问题,可以按下面顺序排查。
1. 确认 shell 环境和 Hermes .env 的区别
先看 shell:
echo "$OPENAI_API_KEY"
再看 Hermes 文件:
grep OPENAI_API_KEY ~/.hermes/.env
如果 shell 为空、.env 有值,而 Hermes 报 no key,就要怀疑读取路径。
2. 确认 provider 是否真的需要这个变量名
不同 provider 可能使用不同 key 名:
OPENAI_API_KEY
ANTHROPIC_API_KEY
GOOGLE_API_KEY
DEEPAI_API_KEY
如果 provider 期望 OPENAI_API_KEY,你却写成别的名字,也会报错。
但 #15932 的重点是:变量名正确时,~/.hermes/.env 仍可能没被读。
3. 检查 Hermes 版本是否包含 #16101
如果你使用旧版本,优先升级到包含 #16101 的版本。
否则临时 workaround 是把 key export 到 shell 环境:
export OPENAI_API_KEY=sk-...
但这只是绕过 .env 读取问题,不是根治。
4. 检查 credential pool 是否被正确加载
如果你使用的是 credential pool,而不是直接 env var,确认认证解析函数是否会 fallback 到:
credential_pool.load_pool()
旧版本可能不会。
这类 bug 的工程教训
认证系统最怕多个“真相来源”:
shell env
~/.hermes/.env
config.yaml
credential pool
provider secret
如果每条代码路径只看其中一部分,用户就会遇到“这里能用,那里不能用”的割裂。
一个成熟的 Agent 框架应该做到:
- 认证查找顺序明确;
- 每个来源的优先级清楚;
- 报错时告诉用户查了哪些地方;
- 不要只说
No API key found,而要说“哪个 provider、哪个变量名、查了哪些来源”。
比如更好的错误信息应该类似:
No API key found for provider=openai.
Checked: os.environ[OPENAI_API_KEY], ~/.hermes/.env, credential_pool.
这比单纯一句 no key 有用得多。
和 DeepAI API 中转站的关系
如果你用 DeepAI API 中转站作为 OpenAI-compatible API 入口,通常会配置:
OPENAI_API_KEY=你的 DeepAI key
OPENAI_BASE_URL=https://api.deepai.wang/v1
或者在工具里填写对应的 base URL、API key 和模型名。
但如果 Hermes 的认证路径没有读取 ~/.hermes/.env,即使 key 是正确的,也可能报:
No API key found
所以这里要区分两类问题:
DeepAI key 无效 / base URL 错误
和:
Hermes 没从正确位置读取 key
#15932 属于后者。
DeepAI 可以提供 OpenAI-compatible 模型入口,但不能替 Hermes 修复 credential pool 的读取逻辑。正确做法是升级 Hermes 或修复认证读取链路。
FAQ
为什么 ~/.hermes/.env 里有 API key,Hermes 还说找不到?
旧版本中 credential_pool._seed_from_env() 只检查 os.environ,没有读取 ~/.hermes/.env。
这是不是 API key 错了?
不一定。先确认 Hermes 是否真的读取到了 key。如果 shell env 为空、.env 有值,而旧 Hermes 只看 shell env,就会误报 no key。
临时解决办法是什么?
把 key export 到 shell 环境,例如:
export OPENAI_API_KEY=sk-...
但更推荐升级到包含 #16101 修复的版本。
修复点是什么?
_seed_from_env() 改为使用 get_env_value() 检查 ~/.hermes/.env;_resolve_api_key_provider_secret() 在 env var 为空时 fallback 到 credential_pool.load_pool()。
DeepAI 能解决这个问题吗?
不能。这是 Hermes 认证读取链路问题。DeepAI 只负责 OpenAI-compatible API 服务入口,不负责 Hermes 如何读取本地 API key。
总结
#15932 的核心教训是:
API key 存在,不代表认证系统看得到。
Hermes 旧版本里,credential pool 的初始化只看 os.environ,而没有读取 ~/.hermes/.env;认证解析函数也没有在 env var 为空时回退到 credential pool。
所以用户看到的是 No API key found,工程上真正缺的是完整的 credential resolution chain。
对 Agent 产品来说,认证不是只要“支持 env var”就够了。只要你提供了 .env、credential pool、config.yaml,多来源查找就必须一致,否则用户迟早会被“明明填了 key 却说没有”折磨。