Files
sub2api-cn-relay-manager/docs/2026-05-22-BATCH_AUTO_IMPORT_V2_ARCHITECTURE.md
phamnazage-jpg afce3da3df 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.
2026-05-22 13:18:51 +08:00

697 lines
17 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.
# 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 只依赖控制面状态库,不依赖宿主数据库