Files
Alfred/openspec/changes/add-model-management/design.md
lanyuanxiaoyao 933c2133f0 feat: 新增模型管理功能(供应商 + 模型 CRUD)
- 新增 providers/models 数据库表、迁移和数据访问层
- 新增 15 个后端 API 路由(供应商/模型 CRUD + 连通性测试)
- 新增 AI 服务层(registry.ts: buildProviderRegistry + testProviderConnection)
- 新增前端模型管理页面(Tabs: 供应商/模型,含表格、表单、工具栏)
- 新增前端 hooks(use-providers, use-models)
- 新增共享类型和 MODEL_CAPABILITIES 常量
- 新增 10 个测试文件(66 个测试用例,4 个因 bun test ESM 兼容问题待修复)
- 更新开发文档(architecture, backend, frontend)
- 附带 apply-review 修复:统一错误响应、提取共享常量、清理重复测试

注意:registry.test.ts 中 4 个测试因 bun test 无法解析
createProviderRegistry ESM 导出而失败,详情见 context.md
2026-05-29 12:40:10 +08:00

226 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 背景
Alfred·阿福定位为"基于 AI 的信息综合处理平台"但当前项目v0.1.0)没有任何 AI/LLM 集成。项目已完成基础架构Bun 全栈、SQLite + Drizzle ORM、React 19 + Ant Design 6 + React Router 7、TanStack React Query并拥有管理控制台Dashboard + 项目管理)和工作台控制台。
本次变更在管理控制台中新增"模型管理"功能,让用户能够配置 AI 供应商和模型,为后续所有 AI 功能提供基础设施。这是项目 AI 能力的第一个里程碑。
**当前状态:**
- 后端:`src/server/` 使用 Bun.serve(),路由在 `routes/` 目录按功能拆分DB schema 在 `src/server/db/schema.ts`,数据访问层在 `src/server/db/projects.ts`
- 前端:`src/web/` 使用 console 模式admin 菜单在 `consoles/admin/menu.tsx`,页面在 `pages/` 目录
- 零 AI/LLM 相关代码
## 讨论记录
- 已确认结论:
- 使用 Vercel AI SDK`ai` + `@ai-sdk/openai` + `@ai-sdk/anthropic` + `@ai-sdk/openai-compatible`)作为统一 AI 调用层
- 三种供应商类型:`openai``anthropic``openai-compatible`(覆盖 DeepSeek、Qwen、Ollama 等)
- API Key 明文存储于 SQLite可接受本地/自部署单用户工具)
- AI 注册表不使用缓存层:每次 AI 调用时从 DB 查询供应商+模型 → 构建 `createProviderRegistry` → 调用 AI SDK开销 ~1ms远小于网络 I/O
- 功能调用function calling视为基础能力不列入 capability 标签
- 用户偏好:
- 前端使用 antd `Tabs` 组件在同一页面内展示供应商和模型两个标签页
- 供应商表单中 type 默认值为 `openai-compatible`baseURL 不设默认值
- 连通性测试为可选功能,不阻塞模型创建
- 约束:
- 严格遵循现有代码模式路由拆分、数据访问函数、hooks、页面组件结构
- 不引入新的样式系统
- 被否决方案:
- API Key 加密存储:对本地/自部署单用户场景过度设计
- AI 注册表缓存:增加复杂度但收益极小(~1ms vs 数百毫秒网络 I/O
- LangChain 等重量级框架Vercel AI SDK 更轻量且 API 更统一
## 需求
| 需求 | 验收标准 |
| ---- | -------- |
| 供应商 CRUD | 管理员可新增、查看、编辑、删除供应商名称、类型、baseURL、apiKey |
| 供应商类型支持 | 支持 openai、anthropic、openai-compatible 三种类型,各自对应 AI SDK 不同 provider 工厂 |
| 模型 CRUD | 管理员可新增、查看、编辑、删除模型名称、所属供应商、modelId、能力标签、可选参数 |
| 能力标签多选 | 模型表单中可多选能力标签text / reasoning / image-generation / video-generation / audio-generation / image-recognition / video-recognition / audio-recognition |
| 连通性测试 | 供应商编辑/创建时可测试 API 连通性,返回成功/失败提示 |
| 模型启用/禁用 | 供应商和模型均可启用/禁用,不影响数据 |
| 管理控制台菜单 | Admin 控制台侧栏新增"模型管理"菜单项 |
| AI 注册表服务 | 后端提供 AI 注册表构建服务,按需从 DB 查询构建,供后续 AI 功能调用 |
| 级联约束 | 删除供应商时,若存在关联模型则阻止删除,用户需先处理关联模型 |
## 数据模型规格
### providers 表
| 字段 | 类型 | 约束 | 说明 |
| ---- | ---- | ---- | ---- |
| id | TEXT | PK, UUID | 自动生成 |
| name | TEXT | NOT NULL, UNIQUE | 供应商显示名称 |
| type | TEXT | NOT NULL | 枚举:`openai` \| `anthropic` \| `openai-compatible`,默认 `openai-compatible` |
| baseUrl | TEXT | NOT NULL | API 基础 URL不设默认值由用户填写 |
| apiKey | TEXT | NOT NULL | API 密钥明文存储GET 接口完整返回 |
| enabled | INTEGER | NOT NULL, DEFAULT 1 | 1=启用, 0=禁用 |
| createdAt | TEXT | NOT NULL | ISO 8601 时间戳 |
| updatedAt | TEXT | NOT NULL | ISO 8601 时间戳 |
### models 表
| 字段 | 类型 | 约束 | 说明 |
| ---- | ---- | ---- | ---- |
| id | TEXT | PK, UUID | 自动生成 |
| name | TEXT | NOT NULL | 模型显示名称 |
| providerId | TEXT | NOT NULL, FK → providers.id | 所属供应商 |
| modelId | TEXT | NOT NULL | API 调用用的模型标识(如 `gpt-4o` |
| capabilities | TEXT | NOT NULL | JSON 数组,能力标签(见下方定义) |
| contextLength | INTEGER | 可选 | 上下文窗口长度 |
| maxOutputTokens | INTEGER | 可选 | 最大输出 token 数 |
| enabled | INTEGER | NOT NULL, DEFAULT 1 | 1=启用, 0=禁用 |
| createdAt | TEXT | NOT NULL | ISO 8601 时间戳 |
| updatedAt | TEXT | NOT NULL | ISO 8601 时间戳 |
**唯一约束:** `(providerId, modelId)` 联合唯一——同一供应商下 modelId 不可重复,不同供应商可以有相同 modelId。
**能力标签定义ModelCapability**
```
"text" | "reasoning" | "image-generation" | "video-generation" | "audio-generation" | "image-recognition" | "video-recognition" | "audio-recognition"
```
存储为 JSON 数组字符串,如 `["text","reasoning","image-recognition"]`
## 目标 / 非目标
**目标:**
- 在管理控制台提供完整的供应商和模型管理界面
- 建立后端 AI 服务层(注册表 + 类型定义),为后续 AI 功能提供可复用基础
- 完成 Vercel AI SDK 集成的依赖安装和基础配置
- 新增 DB migration 支持 providers 和 models 表
**非目标:**
- 不实现实际的 AI 调用功能(文本生成、图片生成等)——这是后续变更的内容
- 不实现 API Key 加密存储
- 不实现供应商/模型的导入导出功能
- 不实现多用户权限控制
## 执行约束
- 依赖限制:
- 新增 npm 依赖仅限 `ai``@ai-sdk/openai``@ai-sdk/anthropic``@ai-sdk/openai-compatible`
- 使用 `bun add` 安装,严禁 npm/pnpm
- 优先使用项目已有依赖Drizzle ORM、antd、TanStack React Query 等)
- 约束:
- 后端遵循 Bun 内置 API > es-toolkit > 三方库优先级
- 前端遵循 antd 组件默认能力优先,禁止内联 style、覆盖 antd 内部类名
- Git 提交格式:中文,"类型: 简短描述"
- 质量门禁:
- 新增代码必须编写完善的测试
- 不允许跳过任何测试
- 代码变更需执行文档影响分析
- 相关方:
- 本变更为基础设施层,所有后续 AI 功能变更将依赖本变更的 AI 注册表服务
- 文档 / 沟通:
- 每次代码变更执行文档影响分析:用户可见行为变更 → `docs/user/`,开发流程/架构变更 → `docs/development/`
- 兼容性 / 连续性:
- 无需考虑向前兼容性
## 影响范围
| 范围 | Artifacts / 参考资料 | 预期变更 | 备注 |
| ---- | -------------------- | -------- | ---- |
| DB Schema | `src/server/db/schema.ts` | 新增 providers 和 models 表定义 | 遵循现有 projects 表模式 |
| DB Migration | `drizzle/` | 新增 migration SQL 文件 | 自动生成 |
| 数据访问层 | `src/server/db/projects.ts`(参考模式) | 新增 `providers.ts``models.ts` | CRUD + 启用/禁用 |
| DB 导出 | `src/server/db/index.ts` | 新增 providers 和 models schema 导出 | 遵循现有导出模式 |
| 共享类型 | `src/shared/api.ts` | 新增供应商和模型相关类型定义 | 前后端共用 |
| 后端路由 | `src/server/routes/` | 新增 `providers/``models/` 目录 | CRUD + 连通性测试 |
| 服务器入口 | `src/server/server.ts` | 注册新路由 | 懒加载导入 |
| AI 服务层 | `src/server/ai/`(新建) | 新增 `registry.ts``types.ts` | AI 注册表构建服务 |
| 前端菜单 | `src/web/consoles/admin/menu.tsx` | 新增"模型管理"菜单项 | |
| 前端页面 | `src/web/pages/models/`(新建) | 新增模型管理页面Tabs + 表格 + 表单) | 遵循 projects 页面模式 |
| 前端组件 | `src/web/pages/models/components/`(新建) | ProviderTable、ProviderFormModal、ModelTable、ModelFormModal | |
| 前端路由 | `src/web/routes.tsx` | 新增 `/models` 路由 | |
| 前端 Hooks | `src/web/hooks/` | 新增 `use-providers.ts``use-models.ts` | TanStack React Query |
| 依赖 | `package.json` | 新增 `ai``@ai-sdk/openai``@ai-sdk/anthropic``@ai-sdk/openai-compatible` | |
| 测试 | `tests/` | 新增后端路由、数据访问、前端 hooks 和组件测试 | |
| 文档 | `docs/development/backend.md` | 更新后端架构说明,补充 AI 服务层 | 开发文档 |
| 文档 | `docs/development/frontend.md` | 补充模型管理页面组件说明 | 开发文档 |
## 决策
| 决策 | 理由 | 已否决替代方案 |
| ---- | ---- | ---------------- |
| 使用 Vercel AI SDK | 轻量、统一 API、支持多供应商、TypeScript 优先、与 Bun 兼容 | LangChain过重API 复杂)、自行封装 fetch维护成本高无法统一接口 |
| 三种供应商类型 | 覆盖主流场景OpenAIResponses API + Chat Completions API、Anthropic、OpenAI 兼容协议DeepSeek/Qwen/Ollama 等) | 仅区分"OpenAI 协议"和"其他"(丢失 Anthropic 特有能力) |
| API Key 明文存储 | 本地/自部署单用户场景,加密增加复杂度但安全性提升有限 | 加密存储(过度设计) |
| AI 注册表不缓存 | DB 查询 + 注册表构建开销 ~1ms远小于网络 I/O无缓存则无需处理失效/一致性问题 | 内存缓存(增加复杂度,收益极小) |
| capability 标签用 JSON 数组 | 灵活可扩展SQLite 中 TEXT 字段存储 JSON 数组 | 关联表(过度设计,标签数量有限且固定) |
| 连通性测试不阻塞操作 | 测试是辅助功能,网络波动不应阻止用户保存配置 | 强制测试通过才能保存(用户体验差) |
| 删除供应商时阻止而非级联删除 | 防止误删导致模型数据丢失,用户需先处理关联模型 | CASCADE 删除(数据安全风险) |
| GET 接口完整返回 apiKey | 本地/自部署单用户场景,前端使用 antd Password 组件隐藏显示,无需脱敏 | apiKey 脱敏返回(增加复杂度,编辑时需额外处理) |
| providerId + modelId 联合唯一 | 同一供应商下 modelId 不可重复,不同供应商可有相同 modelId符合 AI SDK 注册表 key 格式 `providerId:modelId` | modelId 全局唯一(限制过严)、无约束(可能导致注册表冲突) |
| 连通性测试使用 generateText 最小请求 | 调用 `generateText({ model, prompt: 'hi' })` 验证连通性和 apiKey 有效性,简单直接 | 仅验证 HTTP 连接(不验证 apiKey、listModels并非所有供应商支持 |
| 启用/禁用使用 enable/disable 双端点 | 语义明确,与 archive/restore 模式一致;供应商/模型的启用禁用是布尔切换,双端点更清晰 | toggle 单端点(语义含糊,前端需知道当前状态才能确定操作) |
| 供应商 type 默认 openai-compatible | 覆盖最广DeepSeek/Qwen/Ollama 等),用户只需填写 baseURL 和 apiKey 即可使用 | 默认 openai限制性强不适合自部署场景 |
| baseURL 不设默认值 | 不同部署环境的 baseURL 差异大,自动填充可能误导用户填错地址 | 设默认值(看似方便,但可能掩盖配置错误) |
## 执行计划
**阶段 1基础层DB + 类型 + 依赖)**
1. 安装 Vercel AI SDK 相关依赖
2.`src/server/db/schema.ts` 新增 providers 和 models 表定义
3. 生成 DB migration
4.`src/shared/api.ts` 新增供应商和模型相关类型定义
5.`src/server/ai/types.ts` 新增 AI 相关类型定义ModelCapability 等)
**阶段 2后端数据访问 + 路由 + AI 服务)**
6. 新增 `src/server/db/providers.ts` 数据访问函数
7. 新增 `src/server/db/models.ts` 数据访问函数
8. 新增 `src/server/routes/providers/` 目录下的 CRUD 路由处理器(含 enable/disable 双端点)
9. 新增 `src/server/routes/models/` 目录下的 CRUD 路由处理器(含 enable/disable 双端点)
10. 新增 `src/server/routes/providers/test.ts` 连通性测试路由
11.`src/server/server.ts` 注册所有新路由
12. 新增 `src/server/ai/registry.ts`,包含 `buildProviderRegistry(db)`(从 DB 查询启用的供应商构建 AI SDK Provider Registry`testProviderConnection(config)`(使用 generateText 测试连通性)
**阶段 3前端页面 + 组件 + Hooks**
13. 新增 `src/web/hooks/use-providers.ts``src/web/hooks/use-models.ts`
14.`src/web/consoles/admin/menu.tsx` 新增"模型管理"菜单项
15.`src/web/routes.tsx` 新增 `/models` 路由
16. 新增 `src/web/pages/models/index.tsx` 页面Tabs 布局)
17. 新增 `src/web/pages/models/components/ProviderTable.tsx`
18. 新增 `src/web/pages/models/components/ProviderFormModal.tsx`
19. 新增 `src/web/pages/models/components/ModelTable.tsx`
20. 新增 `src/web/pages/models/components/ModelFormModal.tsx`
**阶段 4测试**
21. 后端数据访问层测试providers.ts、models.ts
22. 后端路由测试providers/、models/
23. AI 注册表测试
24. 前端 hooks 测试
25. 前端组件测试
**阶段 5文档**
26. 执行文档影响分析,更新 `docs/development/` 相关文档
## 验证计划
| 需求 / 风险 | 验证方式 |
| ----------- | -------- |
| 供应商 CRUD API | 后端路由测试覆盖创建、查询、更新、删除、启用/禁用 |
| 模型 CRUD API | 后端路由测试覆盖创建、查询、更新、删除、启用/禁用 |
| 供应商删除约束 | 测试删除有关联模型的供应商时返回错误 |
| 连通性测试 API | 测试 mock 场景下的成功/失败响应 |
| AI 注册表构建 | 测试从 DB 数据正确构建 AI SDK provider 实例 |
| DB Migration | 测试 migration 正确创建表和索引 |
| 前端供应商管理 | 组件测试覆盖表格渲染、表单提交、启用/禁用操作 |
| 前端模型管理 | 组件测试覆盖表格渲染、表单提交、能力标签多选 |
| 前端菜单和路由 | 测试菜单项显示和路由跳转 |
| 文档完整性 | 检查 docs/development/ 和 docs/user/ 是否已更新 |
## 风险 / 权衡
- [Vercel AI SDK 与 Bun 的兼容性] -> AI SDK 基于 Web 标准 APIBun 对 Web API 支持良好;安装后需验证 import 正常
- [OpenAI-compatible 供应商行为不一致] -> 注册表构建时使用 `createOpenAICompatible` 标准接口;连通性测试帮助用户提前发现问题
- [API Key 明文存储安全风险] -> 文档中说明安全模型(单用户本地部署);未来可按需增加加密
- [migration 与现有数据兼容性] -> 新增表不影响现有 projects 表migration 为增量操作
## 待解决问题
| 状态 | 问题 | 所需决策 |
| ---- | ---- | -------- |
| 无 | 无待解决问题。 | 无需决策 |