# AgentEvalTool v0.3.0 「拓」开发计划

**开发版本**: v0.3.0-dev
**制定日期**: 2026-07-17
**代码基线**: `9293f9e`
**上一版本**: v0.2.0-dev
**核心目标**: 建立统一模型配置中心，消除工程内分散的大模型连接配置
**状态**: 开发完成，待 t480 发布验证

---

## 一、版本说明

本次新增的是向后兼容的平台能力，按照语义化版本规则从
`0.2.0-dev` 升级为 `0.3.0-dev`。

此前以 v0.3/v0.4 任务名开发的以下能力已经进入当前代码基线，不再列为本版本待办：

- 异步评估规则和统一 LLM 响应解析
- HTTP、tutu-api、OpenClaw 三类评测通道
- semantic similarity、JSON schema、safety 等评估规则
- all、any、weighted 规则组合
- Webhook、Markdown 报告、对比报告
- 场景模板、WebSocket 重连、页面懒加载
- 原始文件管理重构和位置展示
- 194 个后端测试基线

v0.3.0-dev 从提交 `9293f9e` 开始，模型配置中心是本版本唯一的核心功能主线。

---

## 二、现状与问题

当前除 OpenClaw 外，模型连接信息分散在场景和规则参数中：

| 使用位置 | 当前配置 | 问题 |
|---|---|---|
| 动态用例生成 | `Scenario.llm_config` | URL、Key、模型随场景重复保存 |
| LLM 评分 | `llm_score.params` | 凭据嵌在用例 JSON 中 |
| 语义相似度 | `semantic_similarity.params` | embedding 配置无法复用 |
| 安全检测 | `safety.params` | moderation 配置无法复用 |

现有场景 API 会返回完整 `llm_config`，规则参数也可能通过场景和运行日志返回，
因此 API Key 存在明文存储和响应泄露风险。三个模型调用实现还分别直接使用
`httpx`，认证、超时和错误处理没有统一入口。

以下内容不纳入模型配置中心：

- OpenClaw 自身使用的模型，由 OpenClaw 独立管理。
- tutu-api、HTTP、OpenClaw 评测通道配置，它们属于评测对象连接。
- Prompt、temperature、评分阈值、参考答案等业务参数。

---

## 三、目标架构

```text
模型配置页面
    |
    v
ModelConfig API -> ModelConfigService -> model_configs
                                      -> 密钥加密/解密
                                      -> 引用检查
    |
    v
ModelGateway
    |-- chat()      -> 动态用例生成 / LLM 评分
    |-- embed()     -> 语义相似度
    `-- moderate()  -> 安全检测

评测场景只保存 model_config_id 或用途绑定，不保存 URL、Key、模型名称。
```

核心原则：

1. 模型配置只在一个页面维护。
2. 业务模块只引用配置 ID，不接触明文凭据。
3. 所有模型请求通过 `ModelGateway` 发出。
4. 能力类型必须匹配，chat 配置不能用于 embedding 或 moderation。
5. 模型配置变更只影响后续运行，历史运行保留不含密钥的配置快照。

---

## 四、数据设计

### 4.1 model_configs

新增 `model_configs` 表：

| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | UUID | 主键 |
| `name` | string | 唯一配置名称 |
| `provider` | string | V1 为 `openai_compatible` |
| `capability` | string | `chat`、`embedding`、`moderation` |
| `endpoint_url` | string | 完整 API Endpoint |
| `model_name` | string/null | 模型名称，moderation 可为空 |
| `api_key_encrypted` | string/null | 加密后的 API Key |
| `enabled` | bool | 是否允许新任务使用 |
| `is_default` | bool | 当前能力的默认配置 |
| `description` | string | 备注 |
| `created_at` | datetime | 创建时间 |
| `updated_at` | datetime | 更新时间 |

约束：

- `name` 唯一。
- 每种 capability 最多一个默认配置。
- Endpoint 必须是 HTTP 或 HTTPS URL。
- chat/embedding 必须填写 `model_name`。

### 4.2 scenario_model_bindings

新增场景模型用途绑定表：

| 字段 | 说明 |
|---|---|
| `scenario_id` | 场景 ID |
| `purpose` | `generator`、`judge`、`embedding`、`moderation` |
| `model_config_id` | 模型配置 ID |

`(scenario_id, purpose)` 建立唯一约束。删除场景时级联删除绑定；被绑定的模型配置禁止删除。

### 4.3 运行快照

运行开始时保存不含密钥的模型快照：

- 配置 ID和名称
- provider 和 capability
- endpoint URL
- model name
- 配置的 `updated_at`

运行中的调用使用启动时已经解析的不可变运行时配置，避免任务执行期间修改配置导致同一次评测前后不一致。

---

## 五、API 计划

新增 `/api/model-configs` Router：

| 方法 | 路径 | 作用 |
|---|---|---|
| `GET` | `/api/model-configs` | 列表，支持 capability/enabled 筛选 |
| `POST` | `/api/model-configs` | 创建配置 |
| `GET` | `/api/model-configs/{id}` | 查询详情 |
| `PUT` | `/api/model-configs/{id}` | 更新配置 |
| `DELETE` | `/api/model-configs/{id}` | 删除未引用配置 |
| `POST` | `/api/model-configs/{id}/test` | 按能力执行连接测试 |
| `GET` | `/api/model-configs/{id}/references` | 查询引用场景 |

安全约束：

- 任何 GET 响应都不返回 `api_key_encrypted` 或明文 Key。
- 响应只包含 `has_api_key`。
- PUT 未提供 `api_key` 时保留原密钥。
- 只有显式 `clear_api_key=true` 才清除密钥。
- 删除被引用的配置返回 `409 Conflict`。
- 禁用或能力不匹配的配置在启动评测前返回明确错误。

---

## 六、前端计划

### 6.1 模型配置页面

新增侧边栏菜单“模型配置”，路由 `/models`，位于“评测对象”和“评测场景”之间。

页面采用表格加编辑抽屉：

- 表格列：名称、能力、服务商、模型、Endpoint、状态、默认、引用数、测试状态、操作。
- 顶部提供能力筛选、状态筛选、搜索和新增按钮。
- 操作提供测试、编辑、启停、删除。
- API Key 已存在时显示“已配置”，不回填原值。
- 测试连接显示调用耗时和错误摘要，不显示请求凭据。

### 6.2 场景页面

删除现有动态用例区域中的 API URL、API Key 和模型名称输入框，改为“模型引用”：

| 场景能力 | 选择项 | 过滤能力 |
|---|---|---|
| 动态用例 | 问题生成模型 | chat |
| `llm_score` | 评分模型 | chat |
| `semantic_similarity` | 向量模型 | embedding |
| `safety` moderation | 内容审核模型 | moderation |

下拉框只显示已启用且能力匹配的配置。默认模型只用于新建场景时预选，保存后记录明确 ID。

场景模板移除占位 URL 和 API Key，模板应用后要求选择对应能力的模型配置。

---

## 七、后端重构计划

新增模块：

```text
backend/agenteval/
|-- services/model_configs.py
|-- storage/model_config_repository.py
|-- model_gateway.py
`-- web/routers/model_configs.py
```

职责划分：

- Repository 负责模型配置和场景绑定的数据库操作。
- Service 负责校验、默认配置、引用检查、密钥处理和连接测试。
- ModelGateway 负责 chat、embedding、moderation 请求和响应规范化。
- EvalEngine 负责解析场景用途绑定并将 Gateway 注入规则运行上下文。
- LLM 规则只保留 criteria、min_score、reference 等业务参数。

V1 只实现 OpenAI-compatible 协议，但保留 provider 字段，后续可增加独立适配器。

---

## 八、密钥方案

新增依赖 `cryptography`，使用 Fernet 加密 API Key。新增环境变量：

```text
AGENTEVAL_SECRET_KEY=<fernet-key>
```

要求：

- t480 的 `.env` 在执行数据迁移前手动配置密钥。
- 部署脚本 pre-flight 检查远端密钥是否存在，但不打印密钥。
- 密钥不得进入数据库明文字段、API、日志、运行快照或 WebSocket 事件。
- 密钥文件或环境变量需要与数据库一起备份，否则加密配置无法恢复。

本版本不实现在线密钥轮换；密钥轮换另立任务。

---

## 九、旧数据迁移

采用“新增结构、双读兼容、迁移数据、最后清理”的顺序：

1. Alembic 创建新表，不立即删除 `scenarios.llm_config`。
2. 新代码优先读取中心配置，缺少绑定时兼容旧配置。
3. 提供 `scripts/migrate_model_configs.py --dry-run` 输出迁移报告。
4. 按 capability、Endpoint、model 和 API Key 指纹去重创建配置。
5. 将旧动态生成配置迁移为 `generator` 绑定。
6. 将 llm_score、semantic、safety 的内联连接参数迁移为对应用途绑定。
7. 迁移成功后移除用例 JSON 中的 URL、Key 和模型字段。
8. 同一场景同一用途存在多个不同模型时不自动覆盖，保留旧配置并列入冲突报告。
9. 验证 t480 数据后，再在后续版本删除旧 `llm_config` 字段和兼容代码。

迁移脚本必须幂等，可重复执行，不得在部分失败时留下半迁移状态。

---

## 十、开发阶段

| 阶段 | 主要任务 | 交付条件 | 状态 |
|---|---|---|---|
| M0 版本基线 | 升级 `0.3.0-dev`、更新计划 | 版本源和前端版本一致 | 已完成 |
| M1 数据与安全 | Alembic、Repository、Service、Fernet、单测 | CRUD 和密钥测试通过 | 已完成 |
| M2 API 与页面 | REST API、连接测试、模型配置页面 | 可完整管理三类配置 | 已完成 |
| M3 业务接入 | ModelGateway、Engine、四类用途、场景选择器 | 工程无新增内联模型凭据 | 已完成 |
| M4 迁移与发布 | 迁移脚本、兼容测试、文档、t480 完整部署 | 线上数据迁移且健康检查通过 | 代码完成，待发布验证 |

预计总工作量 9.5-13.5 PD。M1 完成后才能并行推进 M2 和部分 M3；M4 必须最后执行。

本次开发以一个完整功能提交交付，发布后通过健康接口记录实际 commit。

---

## 十一、测试计划

### 后端

- ModelConfig Repository CRUD 和唯一约束。
- 默认配置切换和 capability 校验。
- API Key 加密、解密、掩码和清除语义。
- API CRUD、连接测试、引用查询和删除冲突。
- ModelGateway chat/embed/moderate 请求格式和错误处理。
- 动态用例、llm_score、semantic、safety 使用中心配置。
- 禁用配置、缺少配置、错误能力的失败信息。
- 旧配置迁移、重复配置合并、冲突和回滚。
- 场景、运行日志和 WebSocket 响应中不存在明文密钥。

### 前端

- TypeScript 类型检查和生产构建。
- 模型配置列表、筛选、编辑、测试、禁用、删除状态。
- API Key 不回填，留空更新不清除旧值。
- 场景模型选择按 capability 过滤。
- 桌面和 720px 窄屏无重叠或文本溢出。

### 回归

- 当前 194 个测试保持通过。
- tutu-api、HTTP 和 OpenClaw 评测通道不受影响。
- OpenClaw 页面和代理配置不接入模型中心。
- 原始文件、报告和评测执行流程保持可用。

---

## 十二、发布门槛

发布到 t480 前必须满足：

1. `pytest -q` 全部通过。
2. `ruff check backend/` 通过。
3. `npx tsc --noEmit` 通过。
4. `npm run build` 通过。
5. Alembic 在现有数据库副本上升级成功。
6. 迁移脚本 dry-run 无未处理冲突，或冲突已人工确认。
7. API 和日志中检索不到旧明文 API Key。
8. 四类模型用途至少各完成一次模拟集成测试。
9. 代码推送 Gitea 后执行完整 `scripts/deploy-t480.sh`。
10. `/api/health` 返回 `version=0.3.0-dev` 且 commit 与 Gitea 一致。

---

## 十三、完成定义

v0.3.0-dev 功能完成需要同时满足：

- 新增“模型配置”页面，可管理 chat、embedding、moderation 配置。
- 除 OpenClaw 外，工程页面不再编辑模型 URL、API Key 和模型名称。
- 场景和规则只保存模型配置引用及业务参数。
- 所有模型调用统一通过 ModelGateway。
- API Key 加密存储，所有读取接口只返回掩码状态。
- 被引用配置不可删除，禁用配置不可启动新评测。
- 旧配置完成无损迁移，兼容期内仍可回退。
- t480 完整部署后版本、commit、数据库和前端资源全部验证通过。
