From 9813caffd24e7982fea0ebbba4bfde9c24778b32 Mon Sep 17 00:00:00 2001 From: Smilewithoutfalling <91455578+Smilewithoutfalling@users.noreply.github.com> Date: Tue, 27 Oct 2026 07:21:39 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20=E9=98=B6=E6=AE=B5=206.5=20=E6=96=B9?= =?UTF-8?q?=E6=A1=88=EF=BC=9A=E5=A4=9A=E6=9C=8D=E5=8A=A1=E5=95=86=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E6=8E=A5=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/DEVLOG.md | 10 ++- docs/roadmap/phase65-models.md | 122 +++++++++++++++++++++++++++++++++ docs/roadmap/plan.md | 1 + 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 docs/roadmap/phase65-models.md diff --git a/docs/DEVLOG.md b/docs/DEVLOG.md index 5d397df..b081579 100644 --- a/docs/DEVLOG.md +++ b/docs/DEVLOG.md @@ -2290,7 +2290,7 @@ 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。 @@ -2298,3 +2298,11 @@ S 10-26 定:只在本机自用,D0 的建议全部采纳;D3~D5 推迟到对 - 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 一行。 diff --git a/docs/roadmap/phase65-models.md b/docs/roadmap/phase65-models.md new file mode 100644 index 0000000..f8314df --- /dev/null +++ b/docs/roadmap/phase65-models.md @@ -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:"`;删除连接时一起删掉。接口永远不返回 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:`。旧条目保留一个版本,下个阶段再删。 +- 迁移后把它设为生效连接。迁移逻辑要有单测:空库、只有 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. 迁移后旧连接的默认名字定为「我的连接」。 diff --git a/docs/roadmap/plan.md b/docs/roadmap/plan.md index d0c2c34..1725604 100644 --- a/docs/roadmap/plan.md +++ b/docs/roadmap/plan.md @@ -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 备案 | 另定 | 多租户 |