2026-05-22 13:18:51 +08:00
|
|
|
|
# V2 技术架构 — Batch Auto-Import
|
|
|
|
|
|
|
|
|
|
|
|
日期:2026-05-22
|
|
|
|
|
|
状态:设计中
|
|
|
|
|
|
关联文档:
|
2026-05-22 13:38:56 +08:00
|
|
|
|
|
2026-05-22 13:18:51 +08:00
|
|
|
|
- `docs/2026-05-21-BATCH_AUTO_IMPORT_SPEC.md`
|
|
|
|
|
|
- `docs/2026-05-21-BATCH_AUTO_IMPORT_TDD_PLAN.md`
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `docs/openapi.yaml`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
|
|
|
|
|
## 1. 文档目标
|
|
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
这份文档只回答 4 个问题:
|
|
|
|
|
|
|
|
|
|
|
|
1. V2 的后端组件如何分层
|
|
|
|
|
|
2. 运行态状态如何持久化并保证稳定推进
|
|
|
|
|
|
3. 结果页到底展示什么、从哪张表读
|
|
|
|
|
|
4. 如何把 run / item / retry / advisory / validation 收口成单一真相
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
## 2. 核心原则
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
V2 必须同时满足:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
1. **可恢复**:控制面重启后 run/item 仍可查看,unfinished item 可继续推进
|
|
|
|
|
|
2. **可观测**:页面和 API 不依赖日志拼接,而依赖 canonical state store
|
|
|
|
|
|
3. **可解释**:warning/broken 必须给出可读原因
|
|
|
|
|
|
4. **可分层**:Probe、Provision、Confirm、Validate 各司其职
|
|
|
|
|
|
5. **可兼容**:针对第三方 OpenAI-compatible 上游,显式记录 transport + model capability
|
2026-05-22 14:15:41 +08:00
|
|
|
|
6. **可复用**:重复导入命中同 URL + 同模型家族时,优先复用已有 provider/account,而不是重复创建
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
## 3. Canonical runtime model
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 3.1 单一真相
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
V2 的单一真相是控制面自己的三类表:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `import_runs`
|
|
|
|
|
|
- `import_run_items`
|
|
|
|
|
|
- `import_run_item_events`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
现有表:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `import_batches`
|
|
|
|
|
|
- `import_batch_items`
|
|
|
|
|
|
- `probe_results`
|
|
|
|
|
|
- `access_closure_records`
|
|
|
|
|
|
- `managed_resources`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
在 V2 中只承担两类职责:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
1. 资源关联和 legacy 追溯
|
|
|
|
|
|
2. 对现有 v1 行为的兼容
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
它们不再是结果页与 V2 API 的主数据源。
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 3.2 状态归属
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `run.state`:只属于 `import_runs`
|
|
|
|
|
|
- `item.current_stage / confirmation_status / access_status`:只属于 `import_run_items`
|
|
|
|
|
|
- `retry trail / advisory / stage transition`:只属于 `import_run_item_events`
|
|
|
|
|
|
|
|
|
|
|
|
## 4. 组件分层
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
operator input
|
|
|
|
|
|
↓
|
2026-05-22 13:38:56 +08:00
|
|
|
|
Batch Import API / CLI
|
|
|
|
|
|
↓
|
|
|
|
|
|
BatchImportService
|
2026-05-22 14:15:41 +08:00
|
|
|
|
├── Reuse Preflight
|
2026-05-22 13:38:56 +08:00
|
|
|
|
├── Probe Layer
|
|
|
|
|
|
├── Capability Profiler
|
|
|
|
|
|
├── Provision Adapter
|
|
|
|
|
|
├── RunStateStore
|
|
|
|
|
|
└── ConfirmationQueue
|
|
|
|
|
|
↓
|
|
|
|
|
|
ConfirmationWorker
|
|
|
|
|
|
↓
|
|
|
|
|
|
ValidationService
|
|
|
|
|
|
↓
|
|
|
|
|
|
ResultProjection
|
|
|
|
|
|
↓
|
|
|
|
|
|
HTTP API / Result Pages / CLI wait output
|
2026-05-22 13:18:51 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 4.1 Batch Import API / CLI
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
|
|
|
|
|
职责:
|
|
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- 接收 `BatchImportRunRequest`
|
|
|
|
|
|
- 校验 `access_mode` 必填输入
|
|
|
|
|
|
- 创建 run 与 item
|
|
|
|
|
|
- 触发 `BatchImportService`
|
|
|
|
|
|
- 可选等待一个短暂窗口,但不负责长期 confirm
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 4.2 BatchImportService
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
|
|
|
|
|
职责:
|
|
|
|
|
|
|
2026-05-22 14:15:41 +08:00
|
|
|
|
- 执行 Reuse Preflight
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- 执行 Stage 1 Probe
|
|
|
|
|
|
- 执行 Stage 2 Provision
|
|
|
|
|
|
- 把 item 推进到 `confirm`
|
|
|
|
|
|
- 将确认任务交给后台 worker
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
不负责:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- 把每个 item 长时间阻塞在请求线程里等待最终稳定
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 4.3 ConfirmationWorker
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
|
|
|
|
|
职责:
|
|
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- 扫描需要确认的 item
|
|
|
|
|
|
- 吸收 probe race / warmup 窗口
|
|
|
|
|
|
- 将 item 从 `pending` 推进到 `confirmed/advisory/failed`
|
|
|
|
|
|
- 推进到 validate 阶段
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 4.4 ValidationService
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
|
|
|
|
|
职责:
|
|
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- 唯一写入 `access_status`
|
|
|
|
|
|
- 使用宿主 gateway 真实 `/v1/chat/completions`
|
|
|
|
|
|
- 产生 `active/degraded/broken`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 4.5 ResultProjection
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
|
|
|
|
|
职责:
|
|
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- 将底层运行态投影为页面/API 视图
|
|
|
|
|
|
- 统一状态 badge / warning 文案 / item 摘要
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
## 5. State machine
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 5.1 Run state
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
`run.state`:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
|
|
|
|
|
- `running`
|
|
|
|
|
|
- `completed`
|
|
|
|
|
|
- `completed_with_warnings`
|
|
|
|
|
|
- `failed`
|
|
|
|
|
|
- `cancelled`
|
|
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
Run 级 projection 规则:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- 只要存在 `broken` item,且未完成恢复,则 run 可能 `failed`
|
|
|
|
|
|
- 无 broken 但存在 advisory/degraded item,则 run 为 `completed_with_warnings`
|
|
|
|
|
|
- 全部 item 为 `confirmed/active`,则 run 为 `completed`
|
|
|
|
|
|
|
|
|
|
|
|
### 5.2 Item lifecycle
|
|
|
|
|
|
|
|
|
|
|
|
`item.current_stage`:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
|
|
|
|
|
- `probe`
|
|
|
|
|
|
- `provision`
|
|
|
|
|
|
- `confirm`
|
|
|
|
|
|
- `validate`
|
|
|
|
|
|
- `done`
|
|
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
`item.confirmation_status`:
|
|
|
|
|
|
|
|
|
|
|
|
- `pending`
|
|
|
|
|
|
- `confirmed`
|
|
|
|
|
|
- `advisory`
|
|
|
|
|
|
- `failed`
|
|
|
|
|
|
|
|
|
|
|
|
`item.access_status`:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `unknown`
|
|
|
|
|
|
- `active`
|
|
|
|
|
|
- `degraded`
|
|
|
|
|
|
- `broken`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 14:15:41 +08:00
|
|
|
|
`item.matched_account_state`:
|
|
|
|
|
|
|
|
|
|
|
|
- `none`
|
|
|
|
|
|
- `active`
|
|
|
|
|
|
- `disabled`
|
|
|
|
|
|
- `deprecated`
|
|
|
|
|
|
- `broken`
|
|
|
|
|
|
|
|
|
|
|
|
`item.account_resolution`:
|
|
|
|
|
|
|
|
|
|
|
|
- `created`
|
|
|
|
|
|
- `reused`
|
|
|
|
|
|
- `reactivated`
|
|
|
|
|
|
- `replaced`
|
|
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 5.3 State ownership
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
| 字段 | 写入者 |
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|---|---|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
| `current_stage` | service / worker / validation |
|
|
|
|
|
|
| `confirmation_status` | confirmation worker |
|
|
|
|
|
|
| `access_status` | validation service |
|
|
|
|
|
|
| `run.state` | result projection / run aggregator |
|
|
|
|
|
|
|
|
|
|
|
|
## 6. Request architecture
|
|
|
|
|
|
|
|
|
|
|
|
### 6.1 Canonical request
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
|
|
|
|
|
```text
|
2026-05-22 13:38:56 +08:00
|
|
|
|
BatchImportRunRequest
|
|
|
|
|
|
- host_id
|
|
|
|
|
|
- mode
|
|
|
|
|
|
- access_mode
|
|
|
|
|
|
- confirm_wait_timeout_sec
|
|
|
|
|
|
- subscription_users
|
|
|
|
|
|
- subscription_days
|
|
|
|
|
|
- probe_api_key
|
|
|
|
|
|
- entries[]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 6.2 Access-mode matrix
|
|
|
|
|
|
|
|
|
|
|
|
| access_mode | 必填字段 | 用途 |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `subscription` | `subscription_users`, `subscription_days` | 订阅绑定和闭环验证 |
|
|
|
|
|
|
| `self_service` | `probe_api_key` | gateway key 验证 |
|
|
|
|
|
|
|
|
|
|
|
|
这一步必须在入口层就校验,不能把不完整请求放进 worker 后再失败。
|
|
|
|
|
|
|
|
|
|
|
|
## 7. Capability architecture
|
|
|
|
|
|
|
|
|
|
|
|
### 7.1 两层 capability
|
|
|
|
|
|
|
|
|
|
|
|
V2 不再把 capability 当成“一个 key 一个总画像”,而是拆成:
|
|
|
|
|
|
|
|
|
|
|
|
1. `transport_profile`
|
|
|
|
|
|
2. `model_profiles[]`
|
|
|
|
|
|
|
|
|
|
|
|
### 7.2 为什么必须按模型维度记录
|
|
|
|
|
|
|
|
|
|
|
|
目标是满足“快速匹配兼容模型”的运营需求。
|
|
|
|
|
|
如果只有 upstream 级总画像,无法表达:
|
|
|
|
|
|
|
|
|
|
|
|
- 同 upstream 下模型 A 可 stream,模型 B 不可
|
|
|
|
|
|
- 同 upstream 下模型 A 支持 reasoning 字段,模型 B 不支持
|
|
|
|
|
|
- 同 upstream 下模型 A smoke 通过,模型 B 失败
|
|
|
|
|
|
|
|
|
|
|
|
### 7.3 Canonical JSON
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"transport_profile": {
|
|
|
|
|
|
"supports_openai_models": true,
|
|
|
|
|
|
"supports_openai_chat_completions": true,
|
|
|
|
|
|
"supports_openai_responses": false,
|
|
|
|
|
|
"supports_anthropic_messages": false,
|
|
|
|
|
|
"auth_style": "bearer",
|
|
|
|
|
|
"model_id_style": "vendor_prefixed",
|
|
|
|
|
|
"known_advisories": [
|
|
|
|
|
|
"responses_unsupported_but_chat_ok",
|
|
|
|
|
|
"initial_probe_race_expected"
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
"model_profiles": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"raw_model_id": "kimi-k2.6",
|
|
|
|
|
|
"normalized_model_id": "kimi-k2.6",
|
2026-05-22 14:15:41 +08:00
|
|
|
|
"canonical_model_family": "kimi-k2.6",
|
2026-05-22 13:38:56 +08:00
|
|
|
|
"supports_stream": true,
|
|
|
|
|
|
"supports_tools": "unknown",
|
|
|
|
|
|
"supports_reasoning_fields": "unknown",
|
|
|
|
|
|
"smoke_chat_ok": true
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-05-22 14:15:41 +08:00
|
|
|
|
### 7.4 Canonical model family 的作用
|
|
|
|
|
|
|
|
|
|
|
|
`normalized_model_id` 只能解决字符串归一化,不能稳定回答跨中转的“是否同一模型家族”。
|
|
|
|
|
|
|
|
|
|
|
|
V2 需要额外记录 `canonical_model_family`,用于识别这类情况:
|
|
|
|
|
|
|
|
|
|
|
|
- `kimi 2.6`
|
|
|
|
|
|
- `kimi-2.6`
|
|
|
|
|
|
- `kimi-k2.6`
|
|
|
|
|
|
|
|
|
|
|
|
它们可能属于同一个模型家族,应支持:
|
|
|
|
|
|
|
|
|
|
|
|
1. 重复导入时快速匹配
|
|
|
|
|
|
2. 已存在 provider 只 patch 别名映射,不重复 provision
|
|
|
|
|
|
3. 结果页解释“为何这次被直接复用”
|
|
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
## 8. State store schema
|
|
|
|
|
|
|
|
|
|
|
|
### 8.1 `import_runs`
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
run_id TEXT PRIMARY KEY
|
2026-05-22 13:18:51 +08:00
|
|
|
|
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
|
2026-05-22 13:38:56 +08:00
|
|
|
|
warning_items INTEGER NOT NULL
|
2026-05-22 13:18:51 +08:00
|
|
|
|
started_at DATETIME NOT NULL
|
|
|
|
|
|
updated_at DATETIME NOT NULL
|
|
|
|
|
|
finished_at DATETIME NULL
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 8.2 `import_run_items`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
|
|
|
|
|
```text
|
2026-05-22 13:38:56 +08:00
|
|
|
|
item_id TEXT PRIMARY KEY
|
2026-05-22 13:18:51 +08:00
|
|
|
|
run_id TEXT NOT NULL
|
|
|
|
|
|
base_url TEXT NOT NULL
|
|
|
|
|
|
provider_id TEXT NOT NULL
|
2026-05-22 14:15:41 +08:00
|
|
|
|
api_key_fingerprint TEXT NOT NULL
|
2026-05-22 13:18:51 +08:00
|
|
|
|
requested_models_json TEXT NOT NULL
|
|
|
|
|
|
raw_models_json TEXT NOT NULL
|
|
|
|
|
|
normalized_models_json TEXT NOT NULL
|
2026-05-22 14:15:41 +08:00
|
|
|
|
canonical_model_families_json TEXT NOT NULL
|
2026-05-22 13:38:56 +08:00
|
|
|
|
resolved_smoke_model TEXT NULL
|
|
|
|
|
|
recommended_models_json TEXT NOT NULL
|
2026-05-22 13:18:51 +08:00
|
|
|
|
capability_profile_json TEXT NOT NULL
|
2026-05-22 13:38:56 +08:00
|
|
|
|
current_stage TEXT NOT NULL
|
2026-05-22 13:18:51 +08:00
|
|
|
|
confirmation_status TEXT NOT NULL
|
|
|
|
|
|
access_status TEXT NOT NULL
|
2026-05-22 14:15:41 +08:00
|
|
|
|
matched_account_state TEXT NOT NULL
|
|
|
|
|
|
account_resolution TEXT NOT NULL
|
|
|
|
|
|
provision_reused INTEGER NOT NULL
|
|
|
|
|
|
reused_from_provider_id TEXT NULL
|
|
|
|
|
|
reused_from_account_id INTEGER NULL
|
2026-05-22 13:38:56 +08:00
|
|
|
|
channel_id INTEGER NULL
|
|
|
|
|
|
account_id INTEGER NULL
|
2026-05-22 13:18:51 +08:00
|
|
|
|
retry_count INTEGER NOT NULL
|
2026-05-22 13:38:56 +08:00
|
|
|
|
confirmation_attempts INTEGER NOT NULL
|
|
|
|
|
|
last_retry_at DATETIME NULL
|
|
|
|
|
|
next_retry_at DATETIME NULL
|
|
|
|
|
|
lease_owner TEXT NULL
|
|
|
|
|
|
lease_until DATETIME NULL
|
2026-05-22 13:18:51 +08:00
|
|
|
|
advisory_messages_json TEXT NOT NULL
|
|
|
|
|
|
last_error_stage TEXT NULL
|
|
|
|
|
|
last_error TEXT NULL
|
2026-05-22 13:38:56 +08:00
|
|
|
|
legacy_batch_id INTEGER NULL
|
|
|
|
|
|
legacy_provider_id TEXT NULL
|
2026-05-22 13:18:51 +08:00
|
|
|
|
created_at DATETIME NOT NULL
|
|
|
|
|
|
updated_at DATETIME NOT NULL
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
关键约束:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `resolved_smoke_model` 可为 `NULL`,因为 Stage 1 可能失败
|
|
|
|
|
|
- `channel_id/account_id` 可为 `NULL`,因为 Stage 2 可能未开始
|
|
|
|
|
|
- `access_status` 初始必须允许 `unknown`
|
2026-05-22 14:15:41 +08:00
|
|
|
|
- `api_key_fingerprint` 只存指纹,不存明文 key
|
|
|
|
|
|
- `canonical_model_families_json` 是 Reuse Preflight 的核心输入,而不是附带展示字段
|
|
|
|
|
|
- `matched_account_state` 用于结果页直接展示“重复已启用 / 已弃用待启用 / 已重新启用”
|
|
|
|
|
|
- `account_resolution` 用于解释这次 item 最终是创建、复用、快速启用还是替换
|
|
|
|
|
|
|
|
|
|
|
|
### 8.3 为什么必须有 key fingerprint
|
|
|
|
|
|
|
|
|
|
|
|
只靠 `provider_id` 不能稳定区分:
|
|
|
|
|
|
|
|
|
|
|
|
- 同一个 URL 下是否是同一把 key
|
|
|
|
|
|
- 是真正的重复导入,还是同 URL 的新账号
|
|
|
|
|
|
|
|
|
|
|
|
因此 V2 必须显式落:
|
|
|
|
|
|
|
|
|
|
|
|
- `api_key_fingerprint`
|
|
|
|
|
|
- `provision_reused`
|
|
|
|
|
|
- `reused_from_provider_id`
|
|
|
|
|
|
- `reused_from_account_id`
|
|
|
|
|
|
|
|
|
|
|
|
这样 Reuse Preflight 才能先按:
|
|
|
|
|
|
|
|
|
|
|
|
1. `host_id + provider_id`
|
|
|
|
|
|
2. `host_id + base_url + api_key_fingerprint`
|
|
|
|
|
|
3. `canonical_model_families`
|
|
|
|
|
|
|
|
|
|
|
|
做稳定判定。
|
|
|
|
|
|
|
|
|
|
|
|
### 8.4 已存在账号状态的标准化
|
|
|
|
|
|
|
|
|
|
|
|
对命中的既有账号,V2 需要统一投影为:
|
|
|
|
|
|
|
|
|
|
|
|
- `active`
|
|
|
|
|
|
- 当前账号已启用,可直接使用
|
|
|
|
|
|
- `disabled`
|
|
|
|
|
|
- 当前账号被停用,但可尝试快速启用
|
|
|
|
|
|
- `deprecated`
|
|
|
|
|
|
- 当前账号不推荐继续调度,但仍可由 operator 重新启用
|
|
|
|
|
|
- `broken`
|
|
|
|
|
|
- 当前账号不可直接复用
|
|
|
|
|
|
|
|
|
|
|
|
对应本次导入的处理结果:
|
|
|
|
|
|
|
|
|
|
|
|
- `created`
|
|
|
|
|
|
- `reused`
|
|
|
|
|
|
- `reactivated`
|
|
|
|
|
|
- `replaced`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 14:15:41 +08:00
|
|
|
|
### 8.5 `import_run_item_events`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
```text
|
|
|
|
|
|
event_id TEXT PRIMARY KEY
|
|
|
|
|
|
run_id TEXT NOT NULL
|
|
|
|
|
|
item_id TEXT NOT NULL
|
|
|
|
|
|
event_type TEXT NOT NULL
|
|
|
|
|
|
stage TEXT NOT NULL
|
|
|
|
|
|
attempt INTEGER NOT NULL
|
|
|
|
|
|
message TEXT NOT NULL
|
|
|
|
|
|
payload_json TEXT NOT NULL
|
|
|
|
|
|
created_at DATETIME NOT NULL
|
|
|
|
|
|
```
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
事件类型示例:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `stage_transition`
|
|
|
|
|
|
- `retry_scheduled`
|
|
|
|
|
|
- `advisory_added`
|
|
|
|
|
|
- `validation_result`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 14:15:41 +08:00
|
|
|
|
### 8.6 为什么必须有 event 表
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
仅靠 `retry_count` 不足以支撑结果页要求。
|
|
|
|
|
|
页面要能展示:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- 第几次重试
|
|
|
|
|
|
- 在哪个阶段重试
|
|
|
|
|
|
- 为什么进入 advisory
|
|
|
|
|
|
- warning/broken 最终解释
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
这必须依赖 event trail。
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
## 9. Confirmation worker design
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 9.1 轮询条件
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
worker 每次 Tick 只捞:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `current_stage='confirm'`
|
|
|
|
|
|
- `confirmation_status='pending'`
|
|
|
|
|
|
- `next_retry_at IS NULL OR next_retry_at <= now`
|
|
|
|
|
|
- `lease_until IS NULL OR lease_until < now`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 9.2 Lease 机制
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
为避免多个 worker 重复确认,同一 item 需要:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `lease_owner`
|
|
|
|
|
|
- `lease_until`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
worker 成功抢到 lease 后才能执行 confirm。
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 9.3 Retry policy
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
建议默认:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- probe race `403`:advisory,不立即失败
|
|
|
|
|
|
- `503 no available accounts`:短暂指数退避,最多 N 次
|
|
|
|
|
|
- definitive `401/403 unauthorized`:立即失败
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
每次 retry 都写 event。
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 9.4 Restart safety
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
V2 要求:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- worker 重启后自动接管过期 lease 的 item
|
|
|
|
|
|
- unfinished item 不需要人工恢复
|
|
|
|
|
|
- CLI 超时退出不影响后台继续推进
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
## 10. Validation architecture
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 10.1 唯一职责
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
ValidationService 只做一件事:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- 对已经完成 confirm 的 item 执行最终 gateway completion 验证
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 10.2 Outcome mapping
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
| confirmation_status | gateway result | access_status |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `confirmed` | `200` | `active` |
|
|
|
|
|
|
| `advisory` | `200` | `active` |
|
|
|
|
|
|
| `confirmed`/`advisory` | transient but exhausted | `degraded` |
|
|
|
|
|
|
| `failed` | any | `broken` |
|
|
|
|
|
|
| any | definitive invalid path | `broken` |
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
## 11. Result projection
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 11.1 Projection fields
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
结果页/API 不再自己拼原始表字段,Projection 层统一输出:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- run summary
|
|
|
|
|
|
- item table row
|
|
|
|
|
|
- item detail
|
|
|
|
|
|
- warning explanation
|
|
|
|
|
|
- badge color mapping
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 11.2 Warning 文案模板
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
建议至少固化:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `responses_unsupported_but_chat_ok`
|
|
|
|
|
|
- `该上游不支持 /v1/responses,系统已自动回退到 /v1/chat/completions`
|
|
|
|
|
|
- `initial_probe_race_expected`
|
|
|
|
|
|
- `账号创建后宿主异步探测尚未稳定,首次 /test 已按 advisory 处理`
|
|
|
|
|
|
- `gateway_warmup_retry_succeeded`
|
|
|
|
|
|
- `初次调度出现 no available accounts,短暂重试后已恢复`
|
2026-05-22 14:15:41 +08:00
|
|
|
|
- `provision_reused`
|
|
|
|
|
|
- `已检测到同 URL + 同模型家族 + 健康账号,系统直接复用已有 provider`
|
|
|
|
|
|
- `patch_only_new_aliases`
|
|
|
|
|
|
- `模型属于已覆盖家族,仅补充别名映射与定价,不重复创建资源`
|
|
|
|
|
|
- `duplicate_active_account`
|
|
|
|
|
|
- `该账号已存在且处于启用状态,本次未重复创建,直接复用`
|
|
|
|
|
|
- `deprecated_account_reactivated`
|
|
|
|
|
|
- `该账号此前处于弃用/停用状态,本次已快速启用并重新确认`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
## 12. Result pages
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 12.1 页面列表
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `/batch-import/runs`
|
|
|
|
|
|
- `/batch-import/runs/{run_id}`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 12.2 列表页字段
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
| 列 | 数据源 |
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|---|---|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
| `Run ID` | `import_runs.run_id` |
|
|
|
|
|
|
| `State` | projection |
|
|
|
|
|
|
| `Mode` | `import_runs.mode` |
|
|
|
|
|
|
| `Access Mode` | `import_runs.access_mode` |
|
|
|
|
|
|
| `Total/Active/Degraded/Broken/Warning` | `import_runs.*` |
|
|
|
|
|
|
| `Started/Finished` | `import_runs.*` |
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
筛选值使用 canonical 枚举:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `running`
|
|
|
|
|
|
- `completed`
|
|
|
|
|
|
- `completed_with_warnings`
|
|
|
|
|
|
- `failed`
|
|
|
|
|
|
- `cancelled`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
页面可把 `completed_with_warnings` 渲染成 badge 文案 `warning`。
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 12.3 详情页字段
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
Item 行至少包含:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `item_id`
|
|
|
|
|
|
- `base_url`
|
|
|
|
|
|
- `provider_id`
|
2026-05-22 14:15:41 +08:00
|
|
|
|
- `api_key_fingerprint`
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `requested_models`
|
2026-05-22 14:15:41 +08:00
|
|
|
|
- `canonical_model_families`
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `resolved_smoke_model`
|
|
|
|
|
|
- `current_stage`
|
|
|
|
|
|
- `confirmation_status`
|
|
|
|
|
|
- `access_status`
|
2026-05-22 14:15:41 +08:00
|
|
|
|
- `matched_account_state`
|
|
|
|
|
|
- `account_resolution`
|
|
|
|
|
|
- `provision_reused`
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `retry_count`
|
|
|
|
|
|
- `last_error_stage`
|
|
|
|
|
|
- `last_error`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
Item 详情至少包含:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `raw_models`
|
|
|
|
|
|
- `normalized_models`
|
2026-05-22 14:15:41 +08:00
|
|
|
|
- `canonical_model_families`
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `recommended_models`
|
|
|
|
|
|
- `transport_profile`
|
|
|
|
|
|
- `model_profiles`
|
2026-05-22 14:15:41 +08:00
|
|
|
|
- `matched_account_state`
|
|
|
|
|
|
- `account_resolution`
|
|
|
|
|
|
- `reused_from_provider_id`
|
|
|
|
|
|
- `reused_from_account_id`
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `channel_id`
|
|
|
|
|
|
- `account_id`
|
|
|
|
|
|
- `advisory_messages`
|
|
|
|
|
|
- `event trail`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
## 13. HTTP API
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 13.1 V2 canonical endpoints
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
```text
|
|
|
|
|
|
POST /api/batch-import/runs
|
|
|
|
|
|
GET /api/batch-import/runs
|
|
|
|
|
|
GET /api/batch-import/runs/{run_id}
|
|
|
|
|
|
GET /api/batch-import/runs/{run_id}/items
|
|
|
|
|
|
GET /api/batch-import/runs/{run_id}/items/{item_id}
|
|
|
|
|
|
```
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### 13.2 Legacy endpoints
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
以下继续保留,但在 OpenAPI 中标为 `deprecated` 或 `legacy`:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- `/api/import-batches/{batchID}`
|
|
|
|
|
|
- `/api/import-batches/{batchID}/rollback`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
## 14. Backend module mapping
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
建议模块边界如下:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
|
|
|
|
|
### `internal/batch/run_state.go`
|
|
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- run/item/event 仓储
|
|
|
|
|
|
- lease 管理
|
|
|
|
|
|
- run 聚合基础能力
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
|
|
|
|
|
### `internal/batch/status_projection.go`
|
|
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- 页面/API 视图 projection
|
|
|
|
|
|
- state/badge/warning 文案映射
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### `internal/batch/service.go`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- run 创建
|
|
|
|
|
|
- Stage 1 + Stage 2
|
|
|
|
|
|
- item 入队 confirm
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### `internal/batch/confirmation.go`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- worker polling
|
|
|
|
|
|
- confirm logic
|
|
|
|
|
|
- retry scheduling
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### `internal/batch/validation.go`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- final gateway validation
|
|
|
|
|
|
- final access_status write
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### `internal/app/http_batch_import.go`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- create/list/detail APIs
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
### `internal/app/http_batch_runs.go`
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- 结果页渲染
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
## 15. Implementation boundary
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
第一阶段实现可以暂不做:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- WebSocket 实时刷新
|
|
|
|
|
|
- 多 worker 分布式协调
|
|
|
|
|
|
- 跨 host 汇总看板
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
但以下不能再降级为“后续再说”:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
- canonical runtime tables
|
|
|
|
|
|
- confirmation worker
|
|
|
|
|
|
- lease + retry scheduling
|
|
|
|
|
|
- event trail
|
|
|
|
|
|
- 结果 API
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
## 16. Architecture acceptance
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
该架构只有满足以下条件,才算真正达到 V2 目标:
|
2026-05-22 13:18:51 +08:00
|
|
|
|
|
2026-05-22 13:38:56 +08:00
|
|
|
|
1. 输入契约、状态枚举、API、页面字段完全一致
|
|
|
|
|
|
2. run/item/event 可持久化并支持重启恢复
|
|
|
|
|
|
3. 结果页只读 canonical state store
|
|
|
|
|
|
4. transport capability 与 model capability 都能表达
|
|
|
|
|
|
5. `confirmation_status` 与 `access_status` 责任边界清楚
|
|
|
|
|
|
6. 第三方兼容 upstream 的异步窗口不会直接把可用链路打成最终失败
|