Files
sub2api-cn-relay-manager/docs/2026-05-12-sub2api-cn-relay-manager-solution.md
2026-05-12 21:46:19 +08:00

620 lines
15 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.
# sub2api-cn-relay-manager 方案文档
日期2026-05-12
## 1. 背景
宿主 `sub2api` 是一个第三方开源系统,用户较多,迭代很快,且每周都会发布新版本。
当前目标不是修改宿主源码,也不是等待宿主内置原生插件运行时,而是建立一个完全独立交付的外部伴生项目,让任意一台已部署的 `sub2api` 都能通过安装该项目和导入模型包,快速具备国产模型 OpenAI 兼容中转能力。
## 2. 目标
本方案必须满足以下业务目标:
1. 独立交付,不修改宿主 `sub2api` 源码
2. 可跨机器复用,在任意一台兼容版本的 `sub2api` 上重复安装
3. 可一键导入多个国产模型 key
4. 导入后让普通用户继续通过 `sub2api` 标准 API 使用国产模型
5. 尽量热生效,不依赖宿主重编译
6. 能主动发现“内容未真正生效”“生效后漂移”“部分账号失效”
## 3. 核心结论
在“不改宿主代码”的前提下,不能把该能力定义为“宿主原生插件”。
正确做法是:
- 把这套能力定义为一个独立的外部伴生控制面项目
- 把国产模型接入定义为可安装的 `model_pack`
- 由控制面调用宿主已有管理 API 自动创建 `group / channel / account / plan`
- 由控制面维护安装状态、导入批次、探测结果和对账结果
因此,业务上它表现得像“独立安装插件”,但技术上它是“外部伴生安装器 + 模型资源包”。
## 3.1 宿主零改动硬约束
为了确保方案始终满足“绝不改宿主代码”,补充以下硬约束:
1. 不修改宿主源码
2. 不 fork 宿主并运行自定义二进制
3. 不直接写宿主数据库
4. 不向宿主容器或宿主目录写入运行时代码或配置补丁
5. 不依赖宿主未公开的动态加载、初始化钩子或内部运行时对象
6. 只通过宿主现有 HTTP 管理 API 和宿主公开标准 API 工作
这意味着本方案的本质是:
- 技术上:宿主外部自动化编排
- 业务上:像独立插件一样交付和安装
## 3.2 审核后的最终结论
经过对宿主现有能力复核后,可以确认:
- 该方案可以在零宿主代码改动前提下完成国产模型中转能力增加
- 该方案不能伪装成宿主原生插件中心
- 该方案必须把“访问闭环”和“对账闭环”都纳入首版
## 4. 为什么这条路线成立
虽然宿主没有原生插件运行时,但它已有足够的管理 API
- 创建 group`POST /api/v1/admin/groups`
- 创建 account`POST /api/v1/admin/accounts`
- 批量创建 account`POST /api/v1/admin/accounts/batch`
- 测试 account`POST /api/v1/admin/accounts/:id/test`
- 查询 account 模型:`GET /api/v1/admin/accounts/:id/models`
- 创建 channel`POST /api/v1/admin/channels`
- 创建 plan`POST /api/v1/admin/plans`
这意味着控制面完全可以把 `sub2api` 当作一个可编排宿主,而不需要侵入其源码。
但这里有一个经过审核后必须写明的宿主约束:
- 宿主标准 API 网关要求请求最终落到“已分组 API key”或“有效 subscription”
- 仅仅创建 `group / channel / account / plan`,还不足以保证普通用户已经可以调用国产模型
因此,本方案不能只做资源创建,还必须补齐用户访问闭环。
## 5. 总体架构
整体拆成两个发布物:
### 5.1 控制面 / 安装器
项目名:
`sub2api-cn-relay-manager`
职责:
- 连接宿主 `sub2api`
- 安装 `model_pack`
- 预检宿主版本与 API 能力
- 一键导入多个 key
- 通过宿主管理 API 创建和维护资源
- 做 smoke test
- 做持续对账
- 对外展示“可用 / 降级 / 漂移 / 失败”状态
### 5.2 模型包
项目内置样例:
`packs/openai-cn-pack/`
职责:
- 提供 provider 定义
- 提供默认 group / channel / plan / account 模板
- 提供 model mapping 和 smoke test model
- 提供导入策略和校验约束
模型包不携带宿主执行逻辑。
## 6. 运行时组件
控制面内部建议拆成以下模块:
### 6.1 Host Adapter
宿主适配器,只负责把控制面操作翻译成 `sub2api` admin API 调用。
接口建议:
- `GetHostVersion()`
- `ProbeCapabilities()`
- `CreateGroup()`
- `CreateChannel()`
- `CreatePlan()`
- `CreateAccount()`
- `BatchCreateAccounts()`
- `TestAccount()`
- `GetAccountModels()`
- `AssignSubscription()`
- `CheckAccessPath()`
- `DeleteGroup()`
- `DeleteChannel()`
- `DeletePlan()`
- `DeleteAccount()`
- `ListManagedResources()`
### 6.2 Pack Runtime
负责读取和校验 `model_pack`
- `pack.json`
- `providers/*.json`
- `checksums.txt`
### 6.3 Provision Engine
负责把一个 provider + 一批 key 变成宿主真实资源:
- 创建 group
- 创建 channel
- 创建 plan
- 创建 account
- 绑定 group / model mapping / pricing
- 调用测试接口验证账号
### 6.4 Reconciler
负责持续对账,主动发现:
- 资源是否还存在
- key 是否仍可用
- 模型是否仍可列出
- 测试是否通过
- 宿主资源是否被人工改坏
### 6.5 State Store
负责维护控制面自己的状态,因为宿主不知道“插件”概念。
## 7. 模型包结构
建议结构:
```text
openai-cn-pack/
pack.json
providers/
deepseek.json
kimi.json
qwen.json
glm.json
minimax.json
checksums.txt
docs/
```
## 8. 模型包协议
### 8.1 pack.json
```json
{
"pack_id": "openai-cn-pack",
"version": "1.0.0",
"vendor": "YourTeam",
"target_host": "sub2api",
"min_host_version": "0.1.126",
"max_host_version": "0.2.x",
"providers_dir": "providers",
"checksum_file": "checksums.txt"
}
```
### 8.2 provider 定义
每个 provider 文件至少包含:
- `provider_id`
- `display_name`
- `base_url`
- `platform`
- `account_type`
- `default_models`
- `smoke_test_model`
- `group_template`
- `channel_template`
- `plan_template`
- `import`
示例字段:
```json
{
"provider_id": "deepseek",
"display_name": "DeepSeek OpenAI Compatible",
"base_url": "https://api.deepseek.com",
"platform": "openai",
"account_type": "api",
"default_models": ["deepseek-chat", "deepseek-reasoner"],
"smoke_test_model": "deepseek-chat",
"group_template": {
"name": "DeepSeek 默认分组",
"subscription_type": "subscription",
"rate_multiplier": 1.0
},
"channel_template": {
"name": "DeepSeek 默认渠道",
"billing_model_source": "channel_mapped",
"restrict_models": true,
"model_mapping": {
"deepseek-chat": "deepseek-chat",
"deepseek-reasoner": "deepseek-reasoner"
}
},
"plan_template": {
"name": "DeepSeek 默认套餐",
"price": 19.9,
"validity_days": 30,
"for_sale": true
},
"import": {
"supports_multi_key": true,
"supports_strict": true,
"supports_partial": true
}
}
```
## 9. 一键导入流程
### 9.1 安装模型包
1. 上传 `openai-cn-pack.zip`
2. 控制面解压并校验
3. 读取 `pack.json`
4. 校验宿主版本兼容
5. 执行宿主 API 能力探测
6. 注册 provider 定义到控制面状态库
### 9.2 导入多个 key
1. 管理员选择 provider
2. 粘贴多个 key 或上传 `txt / csv`
3. 选择导入模式:
- `strict`
- `partial`
4. 运行预检
5. 创建或复用 group
6. 创建或复用 channel
7. 创建或复用 plan
8. 批量创建 accounts
9. 逐个运行账号测试
10. 汇总结果并写入批次记录
### 9.3 普通用户使用
导入完成后,普通用户继续使用宿主标准 API
- `/v1/chat/completions`
- `/v1/responses`
- 宿主已支持的其他 OpenAI 标准接口
控制面不接管用户流量,只负责把宿主配置成“可中转国产模型”。
### 9.4 用户访问闭环
这是方案审核后新增的强制章节。
宿主网关不是只要存在上游 account 就能调用。
实际还需要满足以下至少一条:
1. 用户持有的 API key 已绑定到目标 group
2. 用户在目标 group 上拥有有效 subscription
否则,普通用户即使拿到宿主标准 API 地址,也不能真正使用国产模型。
因此控制面必须明确支持两种访问模式:
#### 模式 ASubscription 模式
适用场景:
- SaaS 运营
- 后台给用户开通套餐
- 管理员按用户分配访问能力
控制面职责:
- 创建 subscription 类型 group
- 创建默认可售或可分配 plan
- 调用宿主订阅分配接口,把 group 分配给目标用户
#### 模式 BUser Self-Service API Key 模式
适用场景:
- 用户已经登录宿主后台
- 用户自己创建 API key
- 用户自己把 key 绑定到控制面准备好的 group
控制面职责:
- 负责准备好 group / channel / account / plan
- 确保目标 group 对用户可见、可分配、可购买或可授权
#### 首版限制
在零宿主代码改动前提下,控制面不能把“管理员代用户签发最终 API key”作为首版硬依赖。
因此首版应优先保证:
- subscription 模式闭环可用
- user self-service API key 模式可用
## 10. 导入模式
### 10.1 strict
- 任一 key 创建失败或测试失败
- 整个批次回滚
- 适合正式生产导入
### 10.2 partial
- 成功的保留
- 失败的单独记录
- 适合大批量导入
### 10.3 导入完成判定
经过审核,单纯“资源创建成功”不再视为导入完成。
导入完成至少需要满足:
1. 目标 group 存在
2. 目标 channel 存在并已绑定 group
3. 目标 plan 存在,若当前访问模式依赖 plan
4. 至少一个 account 创建成功
5. 至少一个 account smoke test 或模型探测通过
6. 至少一种用户访问模式已经被验证可用
不满足上述条件时,只能标记为:
- `degraded`
- `failed`
## 11. 数据模型
控制面至少需要以下表:
### 11.1 hosts
- 宿主实例信息
- base_url
- 宿主版本
- 最近能力探测结果
### 11.2 packs
- 模型包版本
- checksum
- 安装时间
### 11.3 providers
- provider 元信息
- pack 归属
### 11.4 provider_installs
- 某台宿主安装了哪些 provider
- 当前状态
### 11.5 import_batches
- 一次导入批次
- provider
- 模式
- 请求条数
- 成功条数
- 失败条数
### 11.6 managed_resources
- 宿主侧 group/channel/plan/account 映射
- 宿主资源 ID
- 控制面资源键
### 11.7 reconcile_runs
- 每次对账执行结果
### 11.8 probe_results
- 账号测试与模型探测结果
## 12. 状态机
### 12.1 provider 安装状态
- `discovered`
- `validated`
- `installed`
- `active`
- `degraded`
- `drifted`
- `failed`
- `disabled`
### 12.2 导入批次状态
- `pending`
- `running`
- `succeeded`
- `partially_succeeded`
- `rolled_back`
- `failed`
## 13. 热生效与重启建议
在本方案下,大多数操作应视为热生效:
- 创建 group
- 创建 channel
- 创建 plan
- 创建 account
- 更新 model mapping
- 导入多个 key
因为这些动作都是通过宿主已有 admin API 写入运行时资源。
因此默认策略是:
- 成功写入宿主并通过测试 = 热生效
- 如果出现宿主缓存、运行时异常、版本兼容问题,控制面只给出 `restart_recommended`
- 控制面可选支持宿主重启适配器,但不作为首版必需能力
审核补充:
- `restart_recommended` 只是运维建议,不应成为主成功路径
- 只要核心能力依赖“必须重启宿主后才能导入成功”,就视为不满足首版目标
- 因此首版所有核心链路必须默认按热生效设计
## 14. 主动发现未生效
控制面必须周期性对账,而不是只看“导入成功”。
检查项至少包括:
1. 记录中的 group 是否还存在
2. channel 是否还存在
3. plan 是否还存在
4. account 是否还存在
5. account 测试是否通过
6. account 模型列表是否仍可读取
7. provider 的关键模型是否能通过一次标准测试请求
8. 控制面记录与宿主实际资源是否一致
9. 至少一种用户访问模式仍然成立
对于第 9 条,控制面至少要检查一种访问路径:
- subscription 模式下:目标用户对该 group 是否仍有有效 subscription
- user self-service API key 模式下:是否存在已绑定目标 group 的有效用户 API key 样本
只要任一项不一致,就应标记:
- `degraded`
- `drifted`
- `failed`
## 15. 外部 API 设计
控制面对外至少提供:
### 15.1 宿主管理
- `POST /api/hosts`
- `GET /api/hosts`
- `POST /api/hosts/:id/probe`
### 15.2 模型包管理
- `POST /api/packs/install`
- `GET /api/packs`
- `GET /api/packs/:id/providers`
### 15.3 provider 导入
- `POST /api/providers/:provider_id/preview-import`
- `POST /api/providers/:provider_id/import`
- `GET /api/providers/:provider_id/import-batches`
- `POST /api/import-batches/:id/rollback`
### 15.4 访问闭环管理
- `POST /api/providers/:provider_id/access/preview`
- `POST /api/providers/:provider_id/access/assign-subscriptions`
- `GET /api/providers/:provider_id/access/status`
说明:
- 首版优先支持 subscription 访问闭环
- 不把“管理员代用户创建最终 API key”作为首版必需能力
### 15.5 对账与探测
- `POST /api/providers/:provider_id/reconcile`
- `GET /api/providers/:provider_id/status`
- `GET /api/providers/:provider_id/resources`
## 16. 首版 CLI
建议首版同时提供 CLI降低自动化接入门槛
```bash
cnrelay host add --base-url https://your-sub2api --admin-token xxx
cnrelay pack install ./openai-cn-pack.zip
cnrelay provider import deepseek --keys-file deepseek.txt --mode partial --smoke-test
cnrelay provider status deepseek
cnrelay reconcile run
```
## 17. 与宿主升级的兼容策略
由于宿主 `sub2api` 每周发布新版本,控制面必须内置兼容矩阵:
1. 安装前先探测宿主版本
2. 再跑一组能力探针:
- groups create/list
- accounts create/test/models
- channels create/list
- plans create/list
- subscriptions assign/list
- 至少一种用户访问闭环探测
3. 任何一项不满足,即阻断安装或阻断导入
4. 对宿主版本维护 `supported / warning / unsupported`
这样可以避免宿主快速迭代导致导入流程静默失效。
## 18. MVP 范围
首版只支持:
- 一个宿主适配器:`sub2api`
- 一种包类型:`openai-cn-pack`
- 5 个 provider 模板以内
- 多 key 导入
- strict / partial 两种模式
- account 测试与模型探测
- subscription 访问闭环
- 对账与漂移发现
首版不做:
- 多宿主类型
- 在线插件市场
- 远程代码执行
- 宿主内嵌 UI
- 宿主数据库直写
- 管理员代用户签发最终 API key
## 19. 验收标准
满足以下标准即视为方案落地成功:
1. 任意一台兼容版本的 `sub2api`,只通过控制面和模型包即可接入至少一个国产模型 provider
2. 支持一次导入多个 key并得到逐 key 成功/失败结果
3. 导入成功后,至少一种用户访问模式已经被控制面验证可用
4. 普通用户继续通过 `sub2api` 标准 API 成功调用目标模型
5. 控制面可检测删除、失效、模型缺失、测试失败、访问闭环断裂等漂移
6. 宿主升级后,控制面能在导入前给出兼容性结论,而不是静默失败
## 20. 下一步
建议下一步直接进入实现计划拆解,顺序如下:
1. 定义控制面状态库 schema
2. 实现 `sub2api` 宿主适配器
3. 实现 `model_pack` schema 校验器
4. 实现 provider 导入引擎
5. 实现对账器
6. 再补 CLI 和最小 Web UI