- docs/02 架构设计: 新增 aichat审查/异步审批构想/流式渲染调研/generating状态机/密钥迁移健壮性/工作流脚本执行边界/条件表达式引擎/F-07 trait下沉/Agent架构说明/任务推进构想/功能创意池;更新功能决策记录+归档/对抗论证/文档记录规范/经验记录
- docs/03 模块文档: 新增 AI对话引擎/DAG引擎详解;更新 df-knowledge/df-nodes/df-storage/df-workflow/df-ai
- docs/05 代码审查: 新增 全栈审查/全局review/架构审查/近期改动审查/工作区多角度走查/自研memo流式渲染审查
- docs/09 问题排查: 新增 aichat-apikey-401
- docs/INDEX+README 索引同步;docs/todo 待办看板(2026-06-15 汇总)
- PROGRESS.md Sprint 22-25;URGENT.md 加急清单快照(5 项 P0 已全修)
- scripts/cleanup_orphan_tasks.{py,sh} 孤儿任务清理工具
- .gitignore 补 *.broken.bak + tmp/ 噪音排除
5.5 KiB
5.5 KiB
aichat API Key 鉴权失败排查
2026-06-15 排查 | 看板索引:
docs/todo.md「🔴 aichat API Key 401 排查」区块
现象
- aichat 对话失败,提示「调用失败: API Key 无效或无权限」
- 重新设置 apikey 后仍无效
错误来源
stream_recv.rs:107-113:provider.stream(request)直接返回Err分支- 错误文本:
format!("AI 调用失败: {}", e),e为服务端返回的 401/403 - 即:请求发出去了,被服务端以鉴权/权限为由拒绝。非流式中断、非网络断连。
排查结论:代码链路全对(排除代码 bug)
保存链路(正确)
commands.rs ai_save_provider:329-333:api_key非空 →set_provider_secret写 OS keyring- 写失败会返回「密钥保存到系统钥匙串失败」(用户未报此错 → 写入成功)
:334:DBapi_key列恒空(FR-S1 设计,真实密钥唯一源 = OS keyring)
读取链路(正确)
所有 build_provider 调用点均经 resolve_provider_secret(keyring 优先,fallback DB):
| 调用点 | 行号 | 状态 |
|---|---|---|
| agentic.rs(对话主循环) | :50 | ✓ |
| project.rs(技术栈扫描) | :378 | ✓ |
| knowledge_inject.rs(知识提炼/嵌入) | :33, :323 | ✓ |
| title.rs(标题生成) | :63 | ✓ |
- 例外:
df-nodes/ai_node.rs:118用p.api_key(工作流 AI 节点独立配置,不经 keyring,与本问题无关)
鉴权头(正确)
openai_compat:Authorization: Bearer {api_key}anthropic_compat:x-api-key: {api_key}+anthropic-version
URL 智能拼接(正确,三规则容错)
openai_compat.chat_url(openai_compat.rs:253-262):- 已含
/chat/completions→ 直接用 - 以
/v<数字>结尾(如/v1/v4,GLM 的/api/paas/v4)→ 补/chat/completions - 仅域名(
api.openai.com/api.deepseek.com)→ 补/v1/chat/completions
- 已含
anthropic_compat.messages_url(anthropic_compat.rs:255-264):- 已含
/v1/messages→ 直接用 - 以
/v1结尾 → 补/messages - 否则(
.../api/anthropic、api.anthropic.com)→ 补/v1/messages
- 已含
链路已全部验证无误。401 来自服务端,根因不在 devflow 代码。
根因方向(服务端 401,按概率排序)
1. API Key 本身无效(最常见)
- 过期 / 欠费 / 额度用尽
- 粘贴时带空格、引号、回车或多余字符
- 中转 key 与官方 key 混淆(不同渠道 key 不通用)
2. provider_type 与端点不匹配
openai_compat用Bearer,anthropic_compat用x-api-key,两者鉴权头互斥- 若实际是 OpenAI 兼容端点却选
anthropic_compat(或反之)→ 鉴权头不被识别 → 401 - 典型:
- GLM 官方 chat 端点(
open.bigmodel.cn/api/paas)→openai_compat - GLM 的 Anthropic 兼容端点(
open.bigmodel.cn/api/anthropic)→anthropic_compat - Claude 官方(
api.anthropic.com)→anthropic_compat - 各类中转/聚合站(one-api/new-api)→ 一般
openai_compat
- GLM 官方 chat 端点(
3. base_url 填错
- 多/少
/v1段(智能拼接已容错多数情况,但中转站路径各异仍可能错) - 协议错(
httpvshttps) - 域名拼写错误
4. model 名错/无权限
- 部分 API 网关对无效 model 返回 401 而非 404
- model 名大小写、版本号、前缀错(如
glm-4vsglm-4-flash,claude-3-5-sonnetvsclaude-3-5-sonnet-20241022)
验证步骤(直连测试,区分 key / 配置)
在终端用 curl 直连,绕开 devflow,精准定位是 key 还是配置问题:
A. provider_type = openai_compat(Bearer 鉴权)
curl -s "{base_url}/v1/chat/completions" \
-H "Authorization: Bearer {KEY}" \
-H "Content-Type: application/json" \
-d '{"model":"{MODEL}","messages":[{"role":"user","content":"hi"}],"max_tokens":5}'
B. provider_type = anthropic_compat(x-api-key 鉴权)
curl -s "{base_url}/v1/messages" \
-H "x-api-key: {KEY}" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{"model":"{MODEL}","max_tokens":5,"messages":[{"role":"user","content":"hi"}]}'
{base_url}{KEY}{MODEL}替换为设置页实际填的值(base_url 按拼接规则:不带末尾的/v1/...,但带了也兼容)。
结果判断
| 返回 | 含义 |
|---|---|
| 正常 completion JSON | key+url+type+model 全对 → 问题在 devflow 内部(概率极低,链路已验证;此时查 Rust 日志看实际请求) |
401 / authentication_error |
key 无效,或 provider_type 选错(鉴权头不匹配) |
404 |
base_url 拼接错,端点不存在 |
400 model 相关 |
model 名错/无权限 |
| 连接失败/超时 | base_url 域名或网络问题 |
TODO
- S-260615-01 用户:执行直连测试(A 或 B),确认是 key、provider_type、base_url、model 哪一项的问题
- 用户:核对 provider_type 与实际端点匹配(见「根因方向 2」的典型对照)
- 用户:核对 model 名拼写(对照 API 提供商文档)
- 若直连测试通过、devflow 内仍 401:开 Rust tracing 日志,查实际发出的 url + header 是否与直连一致(排除 body 字段触发的鉴权问题)
- B-260615-01(可选增强)
stream_recv.rs:107Err 分支增加诊断信息:把 provider_type + 实际请求 url + HTTP 状态码记入日志/错误返回,便于 401 快速定位(当前错误只透传服务端文本,看不出打了哪个 url)