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

15 KiB
Raw Permalink Blame History

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

  • 创建 groupPOST /api/v1/admin/groups
  • 创建 accountPOST /api/v1/admin/accounts
  • 批量创建 accountPOST /api/v1/admin/accounts/batch
  • 测试 accountPOST /api/v1/admin/accounts/:id/test
  • 查询 account 模型:GET /api/v1/admin/accounts/:id/models
  • 创建 channelPOST /api/v1/admin/channels
  • 创建 planPOST /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. 模型包结构

建议结构:

openai-cn-pack/
  pack.json
  providers/
    deepseek.json
    kimi.json
    qwen.json
    glm.json
    minimax.json
  checksums.txt
  docs/

8. 模型包协议

8.1 pack.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

示例字段:

{
  "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降低自动化接入门槛

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