docs(v2): refine batch import architecture

Expand the batch auto-import V2 spec and TDD plan with stability requirements, result state persistence, and result page design. Add a dedicated architecture document for run state, APIs, pages, and UI field layout, and sync the execution board to the new V2 scope.
This commit is contained in:
phamnazage-jpg
2026-05-22 13:18:51 +08:00
parent 9134afed9f
commit afce3da3df
4 changed files with 1474 additions and 168 deletions

View File

@@ -0,0 +1,696 @@
# V2 技术架构 — Batch Auto-Import
日期2026-05-22
状态:设计中
关联文档:
- `docs/2026-05-21-BATCH_AUTO_IMPORT_SPEC.md`
- `docs/2026-05-21-BATCH_AUTO_IMPORT_TDD_PLAN.md`
- `docs/EXECUTION_BOARD.md`
## 1. 文档目标
这份文档回答 4 个问题:
1. V2 在系统里由哪些组件组成
2. 运行状态如何持久化,如何保证导入任务更稳定
3. 结果页到底展示什么,字段如何组织
4. 后续实现时,后端、存储、页面应该如何分工
这份文档不重复 `SPEC` 的产品动机,也不替代 `TDD plan` 的编码顺序。它只负责把 V2 的技术结构和结果页设计讲清楚。
## 2. 设计目标
V2 必须同时满足这 5 个目标:
1. **稳定导入**
- 单条 URL + key 的 probe / provision / confirm / validate 全过程可控
- 批量导入时,单条失败不应让整批状态丢失
2. **状态可恢复**
- 控制面重启后,历史 run 和 item 结果仍可查看
- 对 transient 失败要有明确 retry 轨迹
3. **模型纠错**
- 人工填写的模型名不是最终事实
- 必须基于 key 实探结果自动归一化、匹配、推荐
4. **兼容分流**
- 对国产模型 / 第三方 OpenAI-compatible 上游,必须记录 capability profile
- 后续 routing、确认逻辑、warning 解释都依赖 profile
5. **结果可视**
- 不再只靠 CLI 和日志
- 控制面必须提供 run 列表和 run 详情页
## 3. 非目标
这版架构明确不做:
- 多 key 自动负载均衡
- 高级调度系统
- 实时 WebSocket 推送
- 复杂前端工作台
- 自动价格发现与自动调价
- 宿主数据库直连或宿主 DB 读写
## 4. 逻辑组件
V2 在控制面内部拆成 6 个逻辑层:
```text
operator input
batch import orchestrator
├── probe layer
├── capability profiler
├── provision adapter
├── confirmation engine
├── validation engine
└── run state store
result projection
HTTP API / result pages / CLI output
```
### 4.1 Probe Layer
职责:
- 调用 upstream `/v1/models`
- 探测 `/v1/chat/completions`
- 探测 `/v1/responses`
- 探测 `/v1/messages`
- 抽取原始模型列表
- 计算模型归一化结果
输入:
- `base_url`
- `api_key`
- `requested_models`(可选)
输出:
- `raw_models`
- `normalized_models`
- `resolved_smoke_model`
- `capability_profile`
- `probe_summary`
### 4.2 Provision Adapter
职责:
- 计算 `provider_id`
- 查找或创建目标 channel
- patch `model_mapping`
- patch `model_pricing`
- 创建或更新 account
- 绑定 provider 资源关系
输入:
- `normalized_models`
- `capability_profile`
- `resolved_smoke_model`
输出:
- `channel_id`
- `account_id`
- `provider_id`
- `managed_resources`
### 4.3 Confirmation Engine
职责:
- 处理宿主异步探测窗口
- 处理首次 `403 probe race`
- 处理首次 `503 no available accounts`
- 决定 `confirmation_status`
输出状态:
- `confirmed_active`
- `confirmed_warning`
- `confirmed_broken`
### 4.4 Validation Engine
职责:
- 使用托管 key 对宿主 gateway 真实发起 `/v1/chat/completions`
- 决定最终 `access_status`
- 将 validation 结果写入结果页投影
### 4.5 Run State Store
职责:
- 持久化 run 和 item 的阶段状态
- 持久化 retry、warning、错误阶段
- 为结果页提供可直接读取的数据源
### 4.6 Result Projection
职责:
- 将低层运行状态整理成运营和开发都能直接看的摘要
- 生成 run 列表统计
- 生成 item 详情视图
- 不暴露内部实现噪音
## 5. 运行时状态模型
### 5.1 Run 级状态
`ImportRun.state`
- `running`
- `completed`
- `completed_with_warnings`
- `failed`
- `cancelled`
`ImportRun` 核心字段:
| 字段 | 含义 |
|---|---|
| `run_id` | 一次批量导入任务的主键 |
| `mode` | `strict` / `partial` |
| `access_mode` | `subscription` / `self_service` |
| `total_items` | 总条目数 |
| `completed_items` | 已完成条目数 |
| `active_items` | 最终 active 条目数 |
| `degraded_items` | 最终 degraded 条目数 |
| `broken_items` | 最终 broken 条目数 |
| `state` | run 总状态 |
| `started_at` | 开始时间 |
| `updated_at` | 最近更新时间 |
| `finished_at` | 完成时间 |
### 5.2 Item 级状态
`ImportRunItem.current_stage`
- `probe`
- `provision`
- `confirm`
- `validate`
- `done`
`ImportRunItem.stage_status`
- `discovered`
- `provisioned`
- `confirming`
- `confirmed_active`
- `confirmed_warning`
- `confirmed_broken`
`ImportRunItem` 核心字段:
| 字段 | 含义 |
|---|---|
| `item_id` | 单条导入记录 ID |
| `base_url` | 当前导入目标 URL |
| `provider_id` | 自动生成的 provider 标识 |
| `requested_models` | 人工请求模型名 |
| `raw_models` | upstream 原始模型列表 |
| `normalized_models` | 归一化后模型列表 |
| `resolved_smoke_model` | 最终用于 smoke 的模型 |
| `capability_profile_json` | 能力画像 |
| `channel_id` | 宿主 channel |
| `account_id` | 宿主 account |
| `confirmation_status` | 确认状态 |
| `access_status` | 最终访问状态 |
| `retry_count` | 当前总重试次数 |
| `advisory_messages` | warning / advisory 列表 |
| `last_error_stage` | 最近错误发生阶段 |
| `last_error` | 最近错误文本 |
| `created_at` | 创建时间 |
| `updated_at` | 更新时间 |
## 6. 状态库设计
推荐在控制面 SQLite 中新增两张表:
### 6.1 `import_runs`
```text
id TEXT PRIMARY KEY
mode TEXT NOT NULL
access_mode TEXT NOT NULL
state TEXT NOT NULL
total_items INTEGER NOT NULL
completed_items INTEGER NOT NULL
active_items INTEGER NOT NULL
degraded_items INTEGER NOT NULL
broken_items INTEGER NOT NULL
started_at DATETIME NOT NULL
updated_at DATETIME NOT NULL
finished_at DATETIME NULL
```
索引:
- `idx_import_runs_started_at`
- `idx_import_runs_state`
### 6.2 `import_run_items`
```text
id TEXT PRIMARY KEY
run_id TEXT NOT NULL
base_url TEXT NOT NULL
provider_id TEXT NOT NULL
current_stage TEXT NOT NULL
stage_status TEXT NOT NULL
requested_models_json TEXT NOT NULL
raw_models_json TEXT NOT NULL
normalized_models_json TEXT NOT NULL
resolved_smoke_model TEXT NOT NULL
capability_profile_json TEXT NOT NULL
channel_id INTEGER NULL
account_id INTEGER NULL
confirmation_status TEXT NOT NULL
access_status TEXT NOT NULL
retry_count INTEGER NOT NULL
advisory_messages_json TEXT NOT NULL
last_error_stage TEXT NULL
last_error TEXT NULL
created_at DATETIME NOT NULL
updated_at DATETIME NOT NULL
```
索引:
- `idx_import_run_items_run_id`
- `idx_import_run_items_provider_id`
- `idx_import_run_items_stage_status`
- `idx_import_run_items_access_status`
约束:
- 所有页面和 API 只读这两张控制面表
- 不直接读宿主数据库
- 宿主状态变化要通过控制面自己的确认与验证结果回写
## 7. 稳定性机制
### 7.1 阶段落盘
每个 item 每完成一个阶段都立刻落盘:
- probe 完成后写 `discovered`
- provision 完成后写 `provisioned`
- confirm 开始写 `confirming`
- confirm/validate 结束后写 `confirmed_*`
这样做的价值:
- 进程中断后不丢历史轨迹
- 页面可以显示“卡在哪一阶段”
- 后续如果支持 resume有阶段边界可用
### 7.2 Advisory 与 Blocking 分离
V2 必须显式区分:
- **blocking error**
- 真正阻止继续执行
- **advisory**
- 不阻止完成,但需要展示原因
典型 advisory
- 第三方 upstream 首次 `403 Forbidden` probe race
- 首次 `503 no available accounts` 但重试后恢复
- `/responses` 不支持,但 `/chat/completions` 可用
### 7.3 Retry Policy
不是所有失败都重试。
建议策略:
| 错误类型 | 处理 |
|---|---|
| `401/403 unauthorized` | 不重试,直接失败 |
| 首次 `403 probe race` | advisory等待异步确认再测 |
| `429 rate_limit` | 记录 warning可选限次重试 |
| 首次 `503 no available accounts` | 短时重试 |
| `502/503 upstream unreachable` | 按策略有限重试 |
每次 retry 都必须写入:
- `retry_count`
- `last_error_stage`
- `last_error`
- 最近 retry 时间
### 7.4 Restart Safety
控制面重启后至少保证:
- 历史 run 列表还能查看
- 历史 item 详情还能查看
- 若 run 在中途停止,页面能看到“最后停在什么阶段”
V2 第一阶段可以不做自动 resume但必须让“失败现场可见”。
## 8. 结果页设计
V2 最少提供两个页面:
- `/batch-import/runs`
- `/batch-import/runs/{run_id}`
### 8.1 批次列表页
页面目标:
- 快速看最近有哪些导入批次
- 快速判断哪一批成功、哪一批有 warning、哪一批失败
#### 页面结构
```text
页面标题Batch Import Runs
[筛选区]
- 状态筛选all / running / completed / warning / failed
- access_mode 筛选all / subscription / self_service
- 时间范围
- 关键字搜索run_id / provider_id / base_url
[列表表格]
- Run ID
- Started At
- Finished At
- Mode
- Access Mode
- Total
- Active
- Degraded
- Broken
- State
- Actions
```
#### 字段布局
| 列 | 说明 |
|---|---|
| `Run ID` | 可点击进入详情 |
| `Started At` | 开始时间 |
| `Finished At` | 完成时间,运行中为空 |
| `Mode` | `strict` / `partial` |
| `Access Mode` | `subscription` / `self_service` |
| `Total` | item 总数 |
| `Active` | 绿色数值 |
| `Degraded` | 黄色数值 |
| `Broken` | 红色数值 |
| `State` | run 总状态 badge |
| `Actions` | 查看详情 |
#### 状态表现
- `running`:蓝色 badge
- `completed`:绿色 badge
- `completed_with_warnings`:黄色 badge
- `failed`:红色 badge
- `cancelled`:灰色 badge
### 8.2 批次详情页
页面目标:
- 快速回答“这批次具体哪条 URL 出了什么问题”
- 能看到模型纠错、compatibility、warning 和 retry 轨迹
#### 页面结构
```text
页面标题Batch Import Run Detail
[头部摘要卡]
- Run ID
- State
- Started At / Finished At
- Mode / Access Mode
- Total / Active / Degraded / Broken
[Item 列表表格]
- Item ID
- Base URL
- Provider ID
- Requested Models
- Resolved Smoke Model
- Current Stage
- Confirmation Status
- Access Status
- Retry Count
- Last Error Stage
- Last Error
- Actions
[点击某个 Item 后展开侧栏/详情区]
- URL 与 provider 基础信息
- 模型纠错结果
- capability profile 摘要
- channel/account 资源绑定
- advisory messages
- retry timeline
- 原始错误与最终结论
```
#### Item 表格字段
| 列 | 说明 |
|---|---|
| `Item ID` | 单条记录标识 |
| `Base URL` | 当前 upstream |
| `Provider ID` | 自动生成 provider |
| `Requested Models` | 人工输入 |
| `Resolved Smoke Model` | 最终 smoke 模型 |
| `Current Stage` | `probe/provision/confirm/validate/done` |
| `Confirmation Status` | `confirmed_active/warning/broken` |
| `Access Status` | `active/degraded/broken` |
| `Retry Count` | 已重试次数 |
| `Last Error Stage` | 最近错误阶段 |
| `Last Error` | 最近错误摘要 |
#### Item 详情区字段
1. **基础信息**
- `base_url`
- `provider_id`
- `channel_id`
- `account_id`
2. **模型信息**
- `requested_models`
- `raw_models`
- `normalized_models`
- `resolved_smoke_model`
- `recommended_models`(如果发生纠错)
3. **兼容能力摘要**
- `supports_openai_models`
- `supports_openai_chat_completions`
- `supports_openai_responses`
- `supports_anthropic_messages`
- `model_id_style`
- `known_advisories`
4. **确认与访问结果**
- `confirmation_status`
- `probe_ok`
- `access_status`
- `retry_count`
- `last_error_stage`
- `last_error`
5. **说明区**
- warning 为什么是 warning
- broken 为什么是 broken
- 这个 item 是否建议人工介入
### 8.3 页面交互原则
- 默认先显示摘要,不先展示原始 JSON
- 详情区优先显示“结论”和“原因”
- 原始 capability profile / 模型列表可以折叠查看
- warning 要能解释成一句人能读懂的话
示例:
- `responses_unsupported_but_chat_ok`
- 页面说明:`该上游不支持 /v1/responses系统已自动回退到 /v1/chat/completions`
- `initial_probe_race_expected`
- 页面说明:`账号创建后宿主异步探测尚未稳定,首次 /test 结果已按 advisory 处理`
## 9. API 设计
### 9.1 列表 API
`GET /api/batch-import/runs`
返回结构:
```json
{
"runs": [
{
"run_id": "batch-20260522-001",
"state": "completed_with_warnings",
"mode": "partial",
"access_mode": "subscription",
"total_items": 12,
"active_items": 9,
"degraded_items": 2,
"broken_items": 1,
"started_at": "2026-05-22T10:00:00+08:00",
"finished_at": "2026-05-22T10:02:12+08:00"
}
]
}
```
### 9.2 详情 API
`GET /api/batch-import/runs/{run_id}`
返回:
- run summary
- count summary
- recent warnings
### 9.3 Item 列表 API
`GET /api/batch-import/runs/{run_id}/items`
支持筛选:
- `stage_status`
- `access_status`
- `provider_id`
- `has_warning`
### 9.4 Item 详情 API
`GET /api/batch-import/runs/{run_id}/items/{item_id}`
返回:
- item 全量详情
- capability profile
- advisory
- retry trail
## 10. 后端模块映射
建议模块划分:
### `internal/batch/run_state.go`
职责:
- run / item 状态仓储接口
- run summary 聚合
- 状态投影基础函数
### `internal/batch/status_projection.go`
职责:
- 将底层字段转换成页面友好的摘要
- 统一 warning 文案
- 统一 badge / state 映射
### `internal/app/http_batch_import.go`
职责:
- run 列表 API
- run 详情 API
- item 列表 API
- item 详情 API
### `internal/app/http_batch_runs.go`
职责:
- 渲染最小结果页
- 不关心 probe/provision 细节
- 只依赖 projection 层输出
## 11. 页面草图
### 11.1 列表页草图
```text
+----------------------------------------------------------------------------------+
| Batch Import Runs |
+----------------------------------------------------------------------------------+
| Status: [all v] Access Mode: [all v] Search: [__________________________] |
+----------------------------------------------------------------------------------+
| Run ID | State | Total | Active | Degraded | Broken | Started At |
| batch-20260522-001 | warning | 12 | 9 | 2 | 1 | 10:00:00 |
| batch-20260522-002 | running | 4 | 1 | 0 | 0 | 10:05:13 |
+----------------------------------------------------------------------------------+
```
### 11.2 详情页草图
```text
+----------------------------------------------------------------------------------+
| Batch Import Run: batch-20260522-001 |
+----------------------------------------------------------------------------------+
| State: completed_with_warnings Mode: partial Access: subscription |
| Total: 12 Active: 9 Degraded: 2 Broken: 1 |
+----------------------------------------------------------------------------------+
| Items |
+----------------------------------------------------------------------------------+
| URL | Provider | Stage | Confirm | Access | Retry | ... |
| api.deepseek.com | deepseek | done | warning | active | 1 | ... |
| api.53hk.cn/v1 | minimax | done | active | active | 0 | ... |
+----------------------------------------------------------------------------------+
| Selected Item Detail |
+----------------------------------------------------------------------------------+
| Requested Models: [minimax-m27-highspeed] |
| Resolved Smoke Model: MiniMax-M2.7-highspeed |
| Capability: responses=false, chat=true, messages=false |
| Advisory: 首次 /test 403 已按异步 probe race 处理 |
| Last Error Stage: confirm |
| Last Error: API returned 403: Forbidden |
+----------------------------------------------------------------------------------+
```
## 12. 实施建议
按最小可落地顺序做:
1. 先实现 `run_state` 表与 repo
2. 再让 batch service 在每阶段写入状态
3. 再做 API
4. 最后做最小页面
不要先做页面再补状态库。否则页面会重新依赖日志和运行态对象,后面还得推倒重来。
## 13. 验收标准
这份架构落地后,至少应满足:
1. 导入过程中可以查询到 run 和 item 状态
2. 导入完成后可以通过页面复盘每个 item 的结果
3. 页面可以看出模型纠错是否发生
4. 页面可以看出 capability profile 的关键摘要
5. warning 和 broken 有可读解释
6. 控制面重启后,历史结果仍可查看
7. 页面和 API 只依赖控制面状态库,不依赖宿主数据库