重构: 文档汇总+进度看板+孤儿任务清理脚本+gitignore 噪音排除

- 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/ 噪音排除
This commit is contained in:
2026-06-15 05:14:21 +08:00
parent 4b5f096d1c
commit 04032a2a8d
43 changed files with 5372 additions and 163 deletions

View File

@@ -0,0 +1,125 @@
# 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`DB `api_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`
### 3. base_url 填错
- 多/少 `/v1` 段(智能拼接已容错多数情况,但中转站路径各异仍可能错)
- 协议错(`http` vs `https`
- 域名拼写错误
### 4. model 名错/无权限
- 部分 API 网关对无效 model 返回 401 而非 404
- model 名大小写、版本号、前缀错(如 `glm-4` vs `glm-4-flash``claude-3-5-sonnet` vs `claude-3-5-sonnet-20241022`
## 验证步骤(直连测试,区分 key / 配置)
在终端用 curl 直连,绕开 devflow精准定位是 key 还是配置问题:
### A. provider_type = openai_compatBearer 鉴权)
```bash
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_compatx-api-key 鉴权)
```bash
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:107` Err 分支增加诊断信息:把 provider_type + 实际请求 url + HTTP 状态码记入日志/错误返回,便于 401 快速定位(当前错误只透传服务端文本,看不出打了哪个 url