12 KiB
SPEC: Batch Auto-Import by URL + Key (v2)
日期:2026-05-21
技术架构:docs/2026-05-22-BATCH_AUTO_IMPORT_V2_ARCHITECTURE.md
1. Objective
V2 的目标不是“又一条导入命令”,而是把这件事做成稳定、可恢复、可追踪的控制面能力:
- 上游发现:基于
(base_url, api_key)自动发现模型,而不是默认信任人工输入 - 模型纠错:自动归一化、别名匹配、推荐正确模型名
- 兼容画像:记录每个上游和每个模型的兼容能力,避免重复踩坑
- 宿主演化:自动创建/更新 channel、account、provider binding
- 异步确认:吸收宿主异步 probe、首次
403/503的预热窗口 - 闭环验证:以宿主网关真实
/v1/chat/completions结果作为最终可用性判断 - 结果可视:提供 run 列表、run 详情、item 详情,而不是只靠日志和 artifact
2. Scope
V2 在现有 v1 pack-based 路径旁边新增一条URL + key auto-import 路径。
In Scope
- 批量输入
(base_url, api_key, requested_models?) - 兼容
subscription和self_service - 运行态状态持久化
- 后台异步确认与有限重试
- 最小结果 API 与结果页
- 模型纠错与 capability profile 持久化
Out of Scope
- 多 key 自动负载均衡
- 宿主数据库直连
- 自动价格发现/自动调价
- 实时 WebSocket 推送
- 复杂工作台前端
3. Canonical Contract
V2 从这里开始只认一套 canonical contract,后续所有文档、API、页面、状态库都必须遵循这套命名。
3.1 ID 规则
run_id:一次批量导入任务 ID,字符串item_id:run 内单条导入记录 ID,字符串provider_id:{normalized_host}-{url_hash_last8}
provider_id 生成规则:
- 取完整
base_url规范化后参与计算,不能只取 host normalized_host用于可读性url_hash_last8用于区分同 host 不同 path
示例:
https://api.deepseek.com/v1→api-deepseek-9f31c2abhttps://api.deepseek.com/proxy/v1→api-deepseek-4a2d88f1
3.2 Run 级状态
run.state 固定为:
runningcompletedcompleted_with_warningsfailedcancelled
说明:
completed_with_warnings是 run 级总状态- 页面可以显示成黄色 badge
warning - 但 API/状态库里一律写全量枚举值
completed_with_warnings
3.3 Item 级状态
item.current_stage 固定为:
probeprovisionconfirmvalidatedone
item.confirmation_status 固定为:
pendingconfirmedadvisoryfailed
item.access_status 固定为:
unknownactivedegradedbroken
约束:
confirmation_status只描述“宿主异步窗口是否已确认稳定”access_status只描述“最终网关真实可用性”Validation Engine是access_status的唯一写入方
3.4 Legacy 兼容规则
V1 的 import_batches / import_batch_items / managed_resources 继续保留,但在 V2 中:
- 仅作为 legacy execution evidence 或资源关联来源
- 不再作为结果页主数据源
- V2 结果页/API 只读
import_runs/import_run_items/import_run_item_events
4. Request / Result Contract
4.1 Batch Import Request
BatchImportRunRequest
- host_id: string
- mode: "strict" | "partial"
- access_mode: "subscription" | "self_service"
- confirm_wait_timeout_sec: int # CLI/HTTP 可选等待时间
- entries: []BatchImportEntry
- subscription_users: []string # access_mode=subscription 必填
- subscription_days: int # access_mode=subscription 必填
- probe_api_key: string # access_mode=self_service 必填
BatchImportEntry
- base_url: string
- api_key: string
- requested_models: []string # 可选,仅作为提示
4.2 Access Mode 必填规则
subscription:
- 必填:
subscription_users - 必填:
subscription_days - 不接受只写
access_mode=subscription但不带订阅目标
self_service:
- 必填:
probe_api_key probe_api_key用于最终 gateway access validation
4.3 Batch Import Result
BatchImportRunResult
- run_id: string
- state: string
- total_items: int
- active_items: int
- degraded_items: int
- broken_items: int
- warning_items: int
- result_page: string
4.4 Item Projection
BatchImportRunItemView
- item_id: string
- base_url: string
- provider_id: string
- requested_models: []string
- raw_models: []string
- normalized_models: []string
- resolved_smoke_model: string | null
- recommended_models: []string
- current_stage: string
- confirmation_status: string
- access_status: string
- retry_count: int
- last_retry_at: string | null
- advisory_messages: []string
- last_error_stage: string | null
- last_error: string | null
- channel_id: int64 | null
- account_id: int64 | null
- capability_profile: object
5. Core Pipeline
5.1 Five-stage pipeline
Stage 0: Run Setup
create import_run + import_run_items
persist operator input
Stage 1: Probe
/v1/models
capability probe
completion smoke
normalize aliases
Stage 2: Provision
find/create channel
patch model_mapping + model_pricing + restrict_models + billing_model_source
create/update account
persist managed resource link
Stage 3: Confirm
background confirmer absorbs async probe race / warmup window
writes confirmation_status
Stage 4: Validate
host gateway real /v1/chat/completions
writes final access_status
Stage 5: Project
update run summary
serve result API / pages
5.2 Ownership boundaries
Probe Layer负责发现和分类,不决定最终access_statusProvision Adapter负责创建/更新宿主资源Confirmation Engine负责把瞬时403/503吸收到pending/advisory/failedValidation Engine负责最终access_statusResult Projection负责把状态库转换成页面/API 视图
6. Capability Profile
6.1 为什么要分两层
真实场景里兼容能力不是“一个 key 一个总画像”就能表达清楚的。必须拆成:
- transport profile:这个 upstream 支不支持
/models、/chat/completions、/responses、/messages - model profiles:这个 upstream 下的具体模型,在 stream/tools/reasoning 字段上是否可用
6.2 Canonical schema
{
"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": "deepseek-ai/DeepSeek-V4-Pro",
"normalized_model_id": "deepseek-v4-pro",
"supports_stream": true,
"supports_tools": "unknown",
"supports_reasoning_fields": "unknown",
"smoke_chat_ok": true
}
]
}
6.3 用途
- 决定是否跳过
/responses - 决定是否直接走 raw
/chat/completions - 决定 warning 文案
- 决定推荐 smoke model
- 决定后续快速匹配“哪个模型在哪种兼容层下靠谱”
7. Channel / Account Evolution Contract
V2 不再使用“薄 patch 接口”表达 channel 更新。宿主 patch 必须以完整 contract 表达:
ChannelPatchContract
- model_mapping: map[string]string
- model_pricing: map[string]PriceSpec
- restrict_models: true
- billing_model_source: "channel_mapped"
约束:
model_mapping同时记录 raw → canonicalmodel_pricing默认可填零值,但字段必须完整存在- patch 不得破坏旧模型
PatchChannel(addModels []string)这类接口不再作为 V2 canonical contract
8. Async Confirmation Mechanism
8.1 为什么 V2 必须有后台 confirmer
V2 的稳定性目标不能建立在“请求线程里顺序 sleep + retry”。必须有独立后台机制推进:
confirmingitem- 因 probe race 暂时 advisory 的 item
- 因
503 no available accounts等待预热的 item
8.2 Canonical executor
V2 必须实现 ConfirmationWorker:
ConfirmationWorker
- poll import_run_items where current_stage='confirm'
- condition: next_retry_at <= now
- acquire lease
- run confirm logic
- update item state
- release lease
8.3 必需字段
import_run_items 至少要有:
confirmation_attemptsretry_countlast_retry_atnext_retry_atlease_ownerlease_until
8.4 Restart safety
V2 第一版即要求:
- 进程重启后 unfinished confirm item 会被 worker 重新拾取
- 页面能看到 item 停在哪个阶段
- CLI
--confirm-wait-timeout只是“等待窗口”,不是确认机制本身
9. Single Source of Truth
9.1 Canonical runtime tables
V2 运行态只认三类表:
import_runsimport_run_itemsimport_run_item_events
9.2 Legacy linkage
若某个 V2 item 调用了现有 v1 provision 流程,可在 item 上保留:
legacy_batch_idlegacy_provider_id
但这些字段仅作为追溯链接,不能替代 V2 状态源。
9.3 Result page data source
结果页/API 只读 V2 canonical tables,不直接拼接:
import_batchesprobe_resultsaccess_closure_records- 宿主数据库
10. Result API and Pages
10.1 API
V2 标准 API:
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}
Legacy API /api/import-batches/* 保留,但标为 v1/legacy。
10.2 Pages
/batch-import/runs
/batch-import/runs/{run_id}
结果页必须能直接回答:
- 哪条 URL 导入成功
- 哪条卡在
probe/provision/confirm/validate - 哪条发生模型纠错
- 哪条是 advisory 而不是 broken
- 重试过几次
- 当前 warning 的原因是什么
11. CLI Contract
go run ./cmd/cli batch-import \
--host-id "<host_id>" \
--entry "https://example.com/v1,sk-xxx" \
--batch-file "./keys.csv" \
--mode "strict|partial" \
--access-mode "subscription|self_service" \
--subscription-users "u1,u2" \
--subscription-days 30 \
--probe-api-key "<user_gateway_key>" \
--confirm-wait-timeout 15s
CLI 输出必须至少包含:
run_idresult_page- 每个 entry 的
resolved_smoke_model - capability 摘要
confirmation_statusaccess_status- 推荐模型名(若发生纠错)
12. Error Policy
Blocking
401/403 unauthorized且证据表明 key 无效/v1/models完全不可用且无替代路径- provision 明确失败
Advisory
- 第三方 upstream
/responses=403但/chat/completions=200 - 首次
/accounts/:id/test=403,但 probe race 已被识别 - 首次
/v1/chat/completions=503 no available accounts,且重试后恢复 429 rate_limit
Access status ownership
confirmation_status=advisory不自动等于access_status=degraded- 只有 Validation Engine 可以把 item 标成
active/degraded/broken
13. Success Criteria
access_mode输入契约完整,subscription/self_service都可单独落地- run / item 状态、重试、warning、错误阶段能持久化并在重启后恢复可见
- 结果页和 API 只读 V2 canonical tables
- 模型纠错结果、capability profile、推荐模型名可追溯
- 第三方兼容 upstream 的
/responses误判和宿主异步窗口不会把可用链路直接打成最终失败 - 页面可以清楚地区分
confirmed/advisory/failed与active/degraded/broken - OpenAPI、SPEC、TDD、Architecture 对同一字段和同一状态枚举保持一致
14. Non-goals for first implementation
- 多 key 自动调度
- 实时推送
- 自动定价策略
- 自动负载均衡
15. Final decisions
provider_id采用normalized_host + url_hash_last8requested_models仅作提示,不作为事实源Validation Engine是access_status唯一写入方- V2 runtime canonical tables 为
import_runs/import_run_items/import_run_item_events ConfirmationWorker是 V2 必备组件,不是可选增强