Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion docs/DEVLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2290,11 +2290,19 @@ S 10-26 定:只在本机自用,D0 的建议全部采纳;D3~D5 推迟到对

**没做:** 安装目录 `%LOCALAPPDATA%\DataChat`(Tauri currentUser 默认的 `$LOCALAPPDATA\${PRODUCTNAME}`)和数据目录 `%LOCALAPPDATA%\datachat` 在 NTFS 上不区分大小写,其实是同一个目录。目前不出错的原因是:卸载器只删它自己装的文件,最后执行的是非递归的 RMDir;勾选「删除应用数据」时整个目录都会被删,而这本来就是卸载。要把两者分开,得改安装目录(需要改 NSIS 模板或 productName),或者改数据目录(会和浏览器模式分家,需要迁移已有数据),两种都超出收尾的范围,所以推迟到 D3 更新之前,记在 phase6-desktop.md 里。desktop.yml 卸载检查 ③ 的 data_kept 结论仍然成立,因为它验证的正是「卸载不递归删除」。

## [71] 2026-10-27 · 阶段 7 方案 · commit <待提交>
## [71] 2026-10-27 · 阶段 7 方案 · commit 906d98f(PR #83)

新文件 docs/roadmap/phase7-accuracy.md(P0 + E0~E4 + A1~A6)。要点:
- 先做评测,再做改进。闸门已经快满分(react 14/15 + 9/10,workflow 多表 10/10),而且已经被逐题分析过,不能再作为目标;M20 的目标改为定在新的留出集 H1 上:先测基线,再由 S 定 X。
- H1 约 40 题(单表 18 / 多表 12 / 画图 5 / 陷阱 5),流程照搬闸门(先写题 → 独立脚本算答案 → 冻结 → 再跑模型)。S 出 ≥ 1/3 的盲题,Hark 只看汇总。
- A4 DuckDB、A5 YAML 语义层是有条件做的(看 H1 的失败分布);A6 反馈沉淀推迟。
- plan.md 阶段 7 的交付物和验收按上面改写。
- 待 S 定:§1 的 5 条(模型 / 网关、每轮预算、出题分工、judge 模型、数据许可)。

## [72] 2026-10-27 · 阶段 6.5 方案:多服务商模型接入 · commit <待提交>

新文件 docs/roadmap/phase65-models.md。起因:S 10-27 要求模型接入要尽量兼容市面上所有接口,参考 LangBot。
- 调研了 LangBot、LobeChat、Cherry Studio、Cline、Pydantic AI、any-llm、Open WebUI、Aider、PandasAI 九个项目。共同的结构都是三层:协议(不超过 5 种)、服务商预设(是数据,不是代码)、模型怪癖和能力。LangBot 的 44 个预设里有 31 个是 OpenAI 兼容接口;LobeChat 的 88 个服务商里有 69 个走兼容工厂。
- 不用 LiteLLM:沙箱实测 litellm 1.106.4 安装后占 152 MB,还会带上 boto3/botocore(28 MB)和 tokenizers(11 MB);sidecar 工件现在是 85.7 MB。
- S 10-27 定:每个服务商各存一个 Key,并且可以建多个用户自己命名的连接(同一个服务商可以有多份);要做「获取模型列表」;真连接只实测 S 那一个连接;迁移后的连接默认名「我的连接」。
- plan.md 在阶段 6 和 7 之间插入 6.5 一行。
122 changes: 122 additions & 0 deletions docs/roadmap/phase65-models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# 阶段 6.5:多服务商模型接入(方案)

> 2026-10-27。起因:S 希望模型接入尽量兼容市面上所有接口(参考 LangBot)。这一阶段排在阶段 7 评测之前;评测时仍然固定一个模型(phase7-accuracy.md §1 第 1 条),本阶段只拿闸门做回归。

## 0. 调研结论(2026-10-27,各仓库 master 浅克隆)

| 项目 | 协议层 | 服务商 | 能力 / 怪癖 |
|---|---|---|---|
| LangBot | LiteLLM(`requesters/litellmchat.py`,1905 行) | 44 个 yaml 预设,其中 31 个 `litellm_provider: openai` | 交给 LiteLLM;自己写了 `_LeasedHTTPHandler`,用来关掉 LiteLLM 隐藏的连接重试 |
| LobeChat | 自写 `openaiCompatibleFactory` + `anthropicCompatibleFactory` | 88 个,其中 69 个走工厂 | model-bank 给每个模型标 `functionCall` |
| Cherry Studio | Vercel AI SDK | 12 种协议类型(openai / openai-response / anthropic / gemini / azure-openai / vertexai / aws-bedrock / ollama …) | `ProviderApiOptions`:isNotSupportArrayContent / StreamOptions / EnableThinking … |
| Cline | Vercel AI SDK | 30 个 vendor | model-capabilities.ts、error-classification.ts |
| Pydantic AI | 自写;models(协议)和 providers(地址 + 鉴权)分开 | DeepSeek / Kimi / 阿里等继承 `_OpenAICompatibleProvider` | profiles 按模型家族区分(deepseek、qwen、zai …) |
| any-llm | 自写 | 约 55 个,其中 22 个继承 `BaseOpenAIProvider` | DeepSeek 单独做了推理内容回放 |
| Open WebUI | 只有 OpenAI 和 Ollama 两个 router,Anthropic 有个特判 | 其余靠用户填兼容地址 | — |
| Aider、PandasAI | LiteLLM(PandasAI 是可选扩展 pandasai-litellm) | — | — |

**共同结构**:协议只有少数几种,服务商是数据,真正费工夫的是怪癖和工具调用能力。

**不用 LiteLLM**:沙箱实测 litellm 1.106.4 安装后 152 MB,另外还带 boto3/botocore 28 MB、tokenizers 11 MB、huggingface_hub 7 MB;sidecar 工件现在是 85.7 MB(DEVLOG [67])。我们自己的重试和错误分类(app/core/llm.py 的 `classify_error`、LLMClient)会和它的隐藏行为冲突,LangBot 就是因此重写了 HTTP handler。LiteLLM Proxy、OneAPI、NewAPI 这类网关,用户可以自己起,再按「自定义(OpenAI 兼容)」接进来。

## 1. 已定(S 10-27)

1. 每个服务商单独存 Key,不共用一个。
2. 引入「连接」:用户可以自己命名,同一个服务商可以建多个连接(比如「DeepSeek 个人」和「DeepSeek 公司」)。同一时间只有一个连接生效。
3. 要做「获取模型列表」。

## 2. 三层结构

### 2a 协议(4 种,都走 LangChain,共用现有的 LLMClient 重试、`classify_error` 和 UsageTracker)
| id | 实现 | 新依赖 |
|---|---|---|
| `openai_chat` | `ChatOpenAI`(现状) | 无 |
| `openai_responses` | `ChatOpenAI(use_responses_api=True)` | 无 |
| `azure` | `AzureChatOpenAI`(langchain-openai 自带) | 无 |
| `anthropic` | `ChatAnthropic` | langchain-anthropic + anthropic(安装后约 28 MB,sidecar 增量实测后写进 DEVLOG) |

Gemini、Ollama、LM Studio、Mistral、Groq 都先走它们的 OpenAI 兼容端点;实测不够用再加原生协议(Gemini 原生约 17 MB)。Bedrock 和 Vertex 的鉴权是云账号体系,推迟。

### 2b 服务商预设:`app/core/providers.json`(只放数据)
每条包括:`id`、显示名、分组(国内 / 国外 / 本地 / 自定义)、`protocol`、`base_url`、是否需要 Key、申请 Key 的链接、`models`(常用模型若干)、`quirks`(见 2c)。

第一批:DeepSeek、阿里云百炼、火山方舟(豆包)、Moonshot(Kimi)、智谱、硅基流动、MiniMax、腾讯混元、OpenAI、Anthropic、Google Gemini、Azure OpenAI、OpenRouter、Groq、Mistral、xAI、Ollama、LM Studio、NewAPI / OneAPI,以及「自定义」(协议可选 OpenAI 兼容 / Anthropic)。

默认 Base URL 和常用模型名,实现时逐家从官方文档核对,并在 PR 里列出出处。

### 2c 怪癖开关(只收实际碰到的;默认全关)
- `no_stream_options`:不发 `stream_options`
- `no_array_content`:content 只发字符串
- `reasoning_replay`:多轮工具调用时把推理内容带回去(DeepSeek 推理模型这一类)
- `thinking_param`:用 `enable_thinking` 或 `thinking` 参数控制思考(Qwen、GLM 这一类)

可以在预设里写,也可以在连接上手动覆盖。每个开关至少配一个单测。

### 2d 能力:靠探测,不维护模型表
「测试连接」分两步:
1. 连接:现在的 `ping`。
2. 工具调用:发一个只有一个工具的请求(`tool_choice` 必须调用),看返回里有没有 `tool_calls`。

结果按「连接 + 模型」存下来。工具调用结果是 ✗ 时,对话页把 ReAct 置灰(悬停说明原因),只能用 workflow;没测过的不拦。

## 3. 数据和接口

- 新表 `connections`,字段:`id`、`name`(唯一,用户命名)、`provider`、`protocol`、`base_url`、`model`、`extra`(JSON,放 Azure 的 deployment / api_version 和 quirks 覆盖)、`probe`(JSON:`{model, connect, tools, checked_at}`)、`created_at`、`updated_at`。
- `settings.active_connection` 记录当前生效的连接。超时仍然是全局设置。
- Key 存在钥匙串里,`SERVICE = "datachat"`、`ACCOUNT = "conn:<id>"`;删除连接时一起删掉。接口永远不返回 Key。
- 新接口(都要 Bearer):
- `GET /providers`:预设列表
- `GET/POST /connections`、`PUT/DELETE /connections/{id}`
- `PUT/DELETE /connections/{id}/api-key`
- `POST /connections/{id}/activate`
- `POST /connections/{id}/test`:连接 + 工具调用两步
- `GET /connections/{id}/models`:读服务商的 `/models`(Anthropic 是 `/v1/models`),超时 10 秒,失败时返回预设里的模型和错误原因
- 旧接口 `/settings`、`/settings/api-key` 保留一个版本,作用到当前生效的连接上,免得前端和后端的版本对不上。
- 每条分析消息的 meta 记下 `connection_name` 和 `model`,历史里能看出当时用的是哪个连接。
- 环境变量 `DEEPSEEK_*`:只在没有任何连接时生效,作为一个只读的虚拟连接「环境变量」,浏览器模式和 CI 都靠它。

## 4. 迁移(第一次启动新版时自动执行,只执行一次)

旧的 `settings`(model / base_url)加上钥匙串里的 `llm_api_key`,生成一个连接:
- `base_url` 是 `api.deepseek.com`(或者为空)→ 服务商 DeepSeek,命名为「DeepSeek」。
- 其他地址 → 服务商「自定义」,命名为「我的连接」(比如 S 的 grgbanking 网关)。
- Key 复制到 `conn:<id>`。旧条目保留一个版本,下个阶段再删。
- 迁移后把它设为生效连接。迁移逻辑要有单测:空库、只有 Key、只有地址、地址和 Key 都有。

## 5. 前端(设置页「模型」一节重做)

- **连接列表**:每张卡片显示名称、服务商图标、模型,以及最近一次测试的 ✓/✗;生效的那张高亮。按钮有「新建连接」、设为生效、编辑、删除(删除要二次确认)。
- **编辑连接**:
- 名称
- 服务商(分组下拉,选好后自动填 Base URL,显示「去申请 Key」的链接)
- 自定义时选协议
- Key(不需要 Key 的服务商隐藏这一项)
- Base URL(可以改,带「恢复默认」)
- 模型(可以输入也可以下拉;下拉里放预设模型,加「获取模型列表」按钮)
- 选 Azure 时显示部署名和 API 版本
- 「高级」里放怪癖开关
- **测试连接**:显示两行结果(连接、工具调用),失败时带上游原文(沿用 T11 user_error,Key 打码)。
- 「当前生效」的来源提示保留,外加连接名。
- 首次启动引导(D2)改成直接新建第一个连接。
- 视觉基线 f4-visual 里的设置页那张要用 CI x64 的 actual 重取。

## 6. 验收

1. pytest:预设 JSON 的 schema 校验(每条字段齐全、协议合法、URL 是 https,本地服务商可以是 http://localhost);4 种协议在 fake 传输下都能构建出模型并调通一次;4 个怪癖开关各有单测;迁移 4 种情况;connections CRUD、Key 不出现在任何响应里;`/models` 成功、失败、超时;探测在 tools ✓/✗ 两种响应下都判对。
2. vitest:连接列表、编辑表单(服务商切换联动、Azure 字段、不需要 Key 的服务商)、测试结果两行、ReAct 置灰。
3. e2e:新建连接 → 测试 → 设为生效 → 分析一题(fake LLM)。
4. 闸门回归:用 S 的连接(迁移后的「我的连接」)跑一轮,要达到闸门门槛(react 单表 14/15、多表 9/10,workflow 多表 10/10,见 phase7-accuracy.md §0);没达到的要逐题解释。
5. 真连接只实测 S 的那个连接(就是第 4 条闸门用的那个)。其余服务商只做单测,用模拟响应;PR 里逐家标明「未实测」,并给出默认 Base URL 和模型名的官方文档出处。(S 10-27 定:不按 S 手上有哪些 Key 来安排实测。)
6. sidecar 体积增量写进 DEVLOG(加了 langchain-anthropic 之后)。

## 7. 不做

- LiteLLM、Bedrock、Vertex、Gemini 原生协议(理由见 §0 和 §2a)。
- 维护模型能力表(改为 §2d 探测)。
- 一个分析里自动切换或回退到其他连接。
- 图片、嵌入、重排模型。

## 8. 已定(S 10-27,第二批)

1. 实测范围:见 §6 第 5 条,不按 S 有哪些 Key 来安排。
2. 迁移后旧连接的默认名字定为「我的连接」。
1 change: 1 addition & 0 deletions docs/roadmap/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
| 4 后端 API | 1–2 周 | 第一步:API Key 进系统钥匙串(keyring);`analyze_stream()` 逐步产出事件,`analyze()` 包一层(M09)。FastAPI:数据集上传(一个数据集可含多个文件,M10)、会话、消息(SSE 流式推送每一步)、设置;SQLite + SQLAlchemy(表结构兼容 PG,预留 user_id);logging + trace id(会话 / 消息 id),runs.jsonl 换成数据库(M08 M14);接口返回 png + 图表数据(M13)。最后:PyInstaller sidecar + 沙箱 worker 在 Actions Windows runner 上的 spike(B35 / M06) | 用 curl 走完"上传 → 提问 → 流式结果 → 历史可查";重启后会话还在;spike 打出的 exe 能在干净 runner 上执行一次沙箱代码 | B18 B35 |
| 5 前端 | 2–3 周 | 第一件事(M13,从阶段 4 顺延,含后端三步):图表库定为 ECharts;定 option 格式与白名单、写 builder `chart(kind, df, x=, y=, series=, title=)`;worker 产出 chart_option(与 result 脱钩,B44);接口加 chart_option;改绘图提示词并重跑闸门。之后:React + Vite + TS:数据集面板、对话流(展示计划、代码、执行步骤)、图表、可展开的代码、👍👎 反馈、Key 设置页,分步与验收见 docs/roadmap/phase5-frontend.md(F0–F5) | 不碰命令行就能完整用一遍 | — |
| 6 桌面打包 | 1–2 周 | Tauri 2 外壳(前提见上:前端只走 HTTP)+ PyInstaller 后端 sidecar;Windows 安装包;自动更新;首次启动引导填 Key;依赖许可证审计;pyarrow 已随 Streamlit 一起去掉(DEVLOG [67]) | 在一台干净的 Windows 上一键安装,可用;对外分发前代码签名,显示已验证的发布者(2024 年起任何证书都不能让 SmartScreen 立刻不拦,见 phase6-desktop.md §6,M19);方案见 docs/roadmap/phase6-desktop.md;对外分发前满足 THREAT_MODEL.md 的 (b) 条件 | — |
| 6.5 模型接入 | 1 周 | 连接(用户命名,可多个)× 服务商预设(约 20 家)× 4 种协议(OpenAI Chat / OpenAI Responses / Azure / Anthropic);每个连接一个 Key(存在系统钥匙串);获取模型列表;测试连接时探测工具调用,不支持就把 ReAct 置灰;旧设置自动迁移。方案见 docs/roadmap/phase65-models.md | 见 phase65-models.md §6 | — |
| 7 准确率 | 2–4 周 | 先做评测:留出集 H1(约 40 题,含陷阱和画图题)、过程验证器、pass^k;再做改进:列画像、结果合理性校验、枚举值召回;DuckDB / YAML 语义层视 H1 失败分布而定;反馈沉淀推迟。方案见 docs/roadmap/phase7-accuracy.md | 在留出集 H1 上:先测基线,再由 S 定目标「比基线高 X 个百分点」(M20);闸门 25 题和自造集只作回归(2026-10-27 改:闸门已接近满分,而且已经被逐题分析过) | B05 B09 |
| 8 Web 上线(后续) | 另定 | PostgreSQL、登录、任务队列、Docker/E2B 沙箱、Langfuse、服务器部署与 ICP 备案 | 另定 | 多租户 |

Expand Down
Loading