251 lines
10 KiB
Markdown
251 lines
10 KiB
Markdown
# 模型渠道管理设计文档
|
||
|
||
> **日期**: 2026-04-12
|
||
> **范围**: Neta-monorepo 后端 + 前端
|
||
> **参照**: AI_flow_BackEnd 模型渠道管理实现
|
||
|
||
---
|
||
|
||
## 1. 目标
|
||
|
||
为 Neta-monorepo 新增集中式 LLM 模型渠道管理能力,参照 AI_flow 的三级结构(供应商 → 渠道 → 模型),同时适配 Neta 的 `provider:model` 运行时机制。改造 Agent 和 Skill 的模型配置为渠道级联选择器。
|
||
|
||
**核心价值**:
|
||
- API Key 集中管理,一处修改全局生效
|
||
- 支持同一供应商多套渠道(如测试/生产各一套 Key)
|
||
- 渠道启停、连通性测试等运维能力
|
||
- Agent/Skill 编辑时通过选择器引用,避免手动填凭证
|
||
|
||
---
|
||
|
||
## 2. 数据模型
|
||
|
||
### 2.1 新增表: `netaclaw_model_channel`
|
||
|
||
| 字段 | 类型 | 约束 | 说明 |
|
||
|------|------|------|------|
|
||
| id | int | PK, auto | 主键 |
|
||
| name | varchar(100) | unique | 渠道名称,如"Anthropic-生产" |
|
||
| supplier | varchar(50) | indexed | 供应商类型代码 |
|
||
| baseUrl | varchar(500) | not null | API 基础地址 |
|
||
| apiKey | varchar(500) | nullable | API 密钥 |
|
||
| models | json | not null | 模型列表 `[{name, capability}]` |
|
||
| description | text | nullable | 描述 |
|
||
| status | int | indexed, default 1 | 0=禁用 1=启用 |
|
||
| createTime | datetime | auto | 创建时间 |
|
||
| updateTime | datetime | auto | 更新时间 |
|
||
| deleteTime | datetime | nullable | 软删除时间 |
|
||
|
||
**models JSON 结构**:
|
||
```json
|
||
[
|
||
{ "name": "claude-sonnet-4-20250514", "capability": "text" },
|
||
{ "name": "claude-sonnet-4-20250514", "capability": "multimodal" }
|
||
]
|
||
```
|
||
|
||
### 2.2 系统参数 (`base_sys_param`)
|
||
|
||
新增 2 条参数记录:
|
||
|
||
| keyName | name | dataType | data (JSON) |
|
||
|---------|------|----------|-------------|
|
||
| `model_suppliers` | AI模型供应商 | 0 | `[{"value":"openai","label":"OpenAI","tagType":""},{"value":"anthropic","label":"Anthropic","tagType":"purple"},{"value":"deepseek","label":"DeepSeek","tagType":"success"},{"value":"zhipu","label":"智谱AI","tagType":"success"},{"value":"tongyi","label":"通义千问","tagType":"warning"},{"value":"minimax","label":"MiniMax","tagType":"danger"},{"value":"volcengine","label":"火山引擎","tagType":"warning"},{"value":"ollama","label":"Ollama","tagType":"info"},{"value":"azure","label":"Azure OpenAI","tagType":""}]` |
|
||
| `model_capabilities` | 模型能力类型 | 0 | `[{"value":"text","label":"纯文本"},{"value":"multimodal","label":"多模态"},{"value":"vision","label":"视觉"}]` |
|
||
|
||
供应商和能力类型均通过系统参数表管理,前端不做硬编码。供应商配置包含 `tagType` 字段用于渲染标签颜色,后续可在参数列表页面自由扩展新供应商或新能力类型。
|
||
|
||
### 2.3 Agent `modelConfig` 字段语义扩展
|
||
|
||
**新格式** (选择渠道时):
|
||
```json
|
||
{ "channelId": 5, "modelId": "claude-sonnet-4-20250514", "contextWindow": 200 }
|
||
```
|
||
|
||
**自定义格式** (channelId 为 0 或 null 时,回退手动模式):
|
||
```json
|
||
{ "channelId": 0, "apiUrl": "https://...", "apiKey": "sk-xxx", "modelId": "custom-model", "contextWindow": 200 }
|
||
```
|
||
|
||
**兼容性**: 已有 Agent 的 modelConfig 中无 channelId,视为自定义配置模式,无需数据迁移。
|
||
|
||
---
|
||
|
||
## 3. 后端架构
|
||
|
||
### 3.1 新增文件
|
||
|
||
| 文件 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| Entity | `packages/backend/src/modules/netaclaw/entity/model_channel.ts` | TypeORM 实体 |
|
||
| Service | `packages/backend/src/modules/netaclaw/service/model_channel.ts` | 业务逻辑 |
|
||
| Controller | `packages/backend/src/modules/netaclaw/controller/admin/model_channel.ts` | Admin API |
|
||
|
||
### 3.2 API 端点
|
||
|
||
| 端点 | 方法 | 功能 | 来源 |
|
||
|------|------|------|------|
|
||
| `/admin/netaclaw/model_channel/page` | POST | 分页查询 | @CoolController 自动生成 |
|
||
| `/admin/netaclaw/model_channel/add` | POST | 新增渠道 | @CoolController 自动生成 |
|
||
| `/admin/netaclaw/model_channel/update` | POST | 更新渠道 | @CoolController 自动生成 |
|
||
| `/admin/netaclaw/model_channel/delete` | POST | 删除渠道 | @CoolController 自动生成 |
|
||
| `/admin/netaclaw/model_channel/info` | GET | 渠道详情 | @CoolController 自动生成 |
|
||
| `/admin/netaclaw/model_channel/allModels` | GET | 所有启用渠道的模型列表 | 自定义方法 |
|
||
| `/admin/netaclaw/model_channel/testConnection` | POST | 测试渠道连通性 | 自定义方法 |
|
||
|
||
### 3.3 Service 关键方法
|
||
|
||
**`allModels()`**:
|
||
- 查询所有 `status=1` 的渠道
|
||
- 标准化 models 字段(兼容旧格式字符串数组 → `{name, capability}` 对象)
|
||
- 返回渠道列表及其模型
|
||
|
||
**`testConnection(id: number)`**:
|
||
- 根据 supplier 类型初始化对应 LLM 客户端
|
||
- 发送简单测试请求验证连通性
|
||
- 返回 `{ success, elapsed, message }`
|
||
- 供应商到 Provider 的映射:
|
||
- openai/azure → OpenAI SDK
|
||
- anthropic → Anthropic SDK
|
||
- deepseek → OpenAI SDK (自定义 baseUrl)
|
||
- zhipu/tongyi/minimax/volcengine → OpenAI SDK (自定义 baseUrl)
|
||
- ollama → OpenAI SDK (本地 baseUrl)
|
||
|
||
**`resolveForAgent(channelId: number, modelId: string)`**:
|
||
- 查询渠道获取 `supplier`、`baseUrl`、`apiKey`
|
||
- 将 supplier 映射为 Neta 的 provider 名称
|
||
- 返回 `{ provider, model, apiKey, baseUrl }` 供 Agent 运行时使用
|
||
- supplier → provider 映射规则:
|
||
- `openai` → `openai`
|
||
- `anthropic` → `anthropic`
|
||
- `deepseek` → `deepseek`
|
||
- 其他 (zhipu/tongyi/minimax/volcengine/ollama/azure) → `openai`(均使用 OpenAI 兼容协议)
|
||
|
||
### 3.4 运行时改造
|
||
|
||
**文件**: `packages/backend/src/modules/netaclaw/runtime/agent.ts`
|
||
|
||
在 `runAgent()` 函数中,执行前解析模型配置:
|
||
1. 读取 Agent 的 `modelConfig`
|
||
2. 若 `channelId > 0`,调用 `resolveForAgent(channelId, modelId)` 获取凭证
|
||
3. 若 `channelId` 为 0/null 或无该字段,使用 modelConfig 中的 `apiUrl`/`apiKey`(向后兼容)
|
||
4. 构建 `provider:model` 格式字符串传入运行时
|
||
|
||
### 3.5 Entity 注册
|
||
|
||
在 `packages/backend/src/entities.ts` 中添加新 Entity 的导入。
|
||
|
||
---
|
||
|
||
## 4. 前端架构
|
||
|
||
### 4.1 新增文件
|
||
|
||
| 文件 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| 模型渠道管理页 | `packages/frontend/src/modules/agent/views/model-channel.vue` | 渠道 CRUD 页面 |
|
||
| 渠道选择器组件 | `packages/frontend/src/modules/agent/components/model-channel-selector.vue` | 级联选择器 |
|
||
|
||
### 4.2 模型渠道管理页面 (`model-channel.vue`)
|
||
|
||
**布局**:
|
||
- 顶部工具栏: 新增渠道按钮、供应商筛选下拉、关键词搜索、查询/重置
|
||
- 数据表格: 渠道名称、供应商(Tag)、Base URL、模型汇总(按能力分组计数)、状态(Switch)、操作(编辑/测试/删除)
|
||
- 分页控件
|
||
- 编辑抽屉 (600px 宽度):
|
||
- 渠道名称 (必填)
|
||
- 供应商类型 (下拉,选项从 `model_suppliers` 参数加载,必填)
|
||
- Base URL (必填)
|
||
- API Key (密码输入)
|
||
- 描述
|
||
- 模型列表 (表格式编辑: 模型名 + 能力类型下拉)
|
||
- 添加单行 + 批量导入按钮
|
||
- 批量导入对话框: 默认能力类型 + 文本域(逗号/换行分隔模型名)
|
||
|
||
**供应商 Tag 颜色**: 从 `model_suppliers` 参数的 `tagType` 字段读取,不硬编码。
|
||
|
||
**API 调用**:
|
||
- 参数加载: `GET /admin/base/sys/param/dataByKey?key=model_suppliers`
|
||
- 参数加载: `GET /admin/base/sys/param/dataByKey?key=model_capabilities`
|
||
- 分页查询: `POST /admin/netaclaw/model_channel/page`
|
||
- 新增: `POST /admin/netaclaw/model_channel/add`
|
||
- 更新: `POST /admin/netaclaw/model_channel/update`
|
||
- 删除: `POST /admin/netaclaw/model_channel/delete`
|
||
- 测试: `POST /admin/netaclaw/model_channel/testConnection`
|
||
|
||
### 4.3 渠道选择器组件 (`model-channel-selector.vue`)
|
||
|
||
**Props**:
|
||
- `modelValue?: { channelId?, modelId?, apiUrl?, apiKey?, contextWindow? }` (v-model)
|
||
- `capabilityFilter?: string` (按能力类型过滤可选模型)
|
||
|
||
**Emits**:
|
||
- `update:modelValue`
|
||
- `change`
|
||
|
||
**UI**:
|
||
- 渠道选择下拉 (显示: "渠道名称(N个模型)")
|
||
- 额外选项: "自定义配置(手动输入)" (value=0)
|
||
- 模型选择下拉 (filterable,显示: 模型名 + 能力类型 Tag)
|
||
- 自定义模式时展示: API URL + API Key + Model ID 手动输入框
|
||
|
||
**数据流**:
|
||
1. 初始化时调用 `GET /admin/netaclaw/model_channel/allModels` 获取所有启用渠道
|
||
2. 按 `capabilityFilter` 过滤渠道和模型
|
||
3. 用户选择渠道 → 展示该渠道的模型列表
|
||
4. 用户选择模型 → emit `{ channelId, modelId }`
|
||
5. 若选「自定义配置」→ 展示手动输入表单 → emit `{ channelId: 0, apiUrl, apiKey, modelId }`
|
||
|
||
### 4.4 改造: Agent 编辑页
|
||
|
||
**文件**: `packages/frontend/src/modules/agent/views/agent-edit.vue`
|
||
|
||
**「模型配置」Tab 改造**:
|
||
- 移除原有 apiUrl/apiKey/modelId/contextWindow 4 个输入框
|
||
- 替换为 `<model-channel-selector v-model="form.modelConfig" />`
|
||
- 选择器下方保留 contextWindow 输入框
|
||
- 保留「填入默认」按钮(从 netaclaw 配置读取默认渠道)和「清空」按钮
|
||
|
||
### 4.5 改造: Skill 模型配置
|
||
|
||
**文件**: `packages/frontend/src/modules/agent/components/skill-model.vue`
|
||
|
||
- 将手动输入替换为 `<model-channel-selector :capability-filter="skillType" />`
|
||
- Skill 类型为 `multimodal` 时只展示 multimodal 能力的模型
|
||
- Skill 类型为 `llm` 时展示 text 能力的模型
|
||
|
||
### 4.6 路由配置
|
||
|
||
**文件**: `packages/frontend/src/modules/agent/config.ts`
|
||
|
||
新增路由:
|
||
```typescript
|
||
{ path: '/agent/model-channel', meta: { label: '模型管理' } }
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 文件变更清单
|
||
|
||
| 操作 | 位置 | 文件 |
|
||
|------|------|------|
|
||
| 新增 | backend | `src/modules/netaclaw/entity/model_channel.ts` |
|
||
| 新增 | backend | `src/modules/netaclaw/service/model_channel.ts` |
|
||
| 新增 | backend | `src/modules/netaclaw/controller/admin/model_channel.ts` |
|
||
| 新增 | frontend | `src/modules/agent/views/model-channel.vue` |
|
||
| 新增 | frontend | `src/modules/agent/components/model-channel-selector.vue` |
|
||
| 修改 | frontend | `src/modules/agent/views/agent-edit.vue` |
|
||
| 修改 | frontend | `src/modules/agent/components/skill-model.vue` |
|
||
| 修改 | frontend | `src/modules/agent/config.ts` |
|
||
| 修改 | backend | `src/modules/netaclaw/runtime/agent.ts` |
|
||
| 修改 | backend | `src/entities.ts` |
|
||
| 数据 | database | `base_sys_param` 表新增 2 条参数记录 |
|
||
|
||
---
|
||
|
||
## 6. 兼容性
|
||
|
||
- **无数据迁移**: 已有 Agent 的 modelConfig 中无 channelId,自动视为自定义配置模式
|
||
- **渐进式采用**: 新建/编辑 Agent 时可选择渠道或自定义,不影响已有 Agent 运行
|
||
- **运行时兼容**: channelId 有值走渠道查询,无值走原有 apiUrl/apiKey 逻辑
|