Files
sub2api-cn-relay-manager/docs/PROVIDER_ONBOARDING_PLAYBOOK.md
2026-05-22 07:43:42 +08:00

398 lines
12 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.
# Provider Onboarding Playbook
日期2026-05-21
## 目的
这份文档面向两类场景:
1. 需要把一个新的 OpenAI-compatible 国产模型中转接入到 `sub2api-cn-relay-manager`
2. `sub2api` 宿主版本发生变化后,需要快速确认现有导入链路是否仍然兼容,并稳定完成重新导入与验收
它不是当前 gate 文档,也不替代真实宿主验收步骤文档。
分工如下:
- `docs/SOURCE_OF_TRUTH.md`
- 回答“当前最新真相是什么”
- `docs/PROVIDER_VALIDATION_MATRIX.md`
- 回答“哪些 provider 已有模板、哪些已有官方 key、哪些已经完成 live 验收”
- `docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md`
- 回答“标准真实宿主验收怎么跑”
- `docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md`
- 回答“之前踩过哪些坑”
- 本文
- 回答“以后要新增 provider / 宿主升级后重跑时,最稳妥的工程步骤是什么”
## 适用边界
本文假设:
1. 目标上游是 OpenAI-compatible 接口
2. 目标宿主仍然是 `sub2api`
3. 控制面仍然遵守当前 PRD 首版范围:
- 不修改宿主源码
- 不写宿主数据库
- 通过宿主管理 API 完成 group/channel/account/subscription 等资源编排
如果未来要接入“非 OpenAI-compatible”上游这份文档只能复用框架不能直接照抄字段约定。
## 先记住 8 条不变量
后续所有操作都围绕这 8 条不变量展开。只要其中一条被破坏,导入就容易出现“看起来像通了,实际上没通”的假阳性。
1. provider 的 account 视角模型暴露必须正确
- `credentials.model_mapping` 必须真实落到宿主 account
- `GET /api/v1/admin/accounts/:id/models` 必须返回目标 provider 模型,而不是 GPT 默认集
2. channel 的网关视角模型约束必须完整
- `model_mapping`
- `model_pricing`
- `restrict_models=true`
- `billing_model_source=channel_mapped`
- 这四项缺一不可
3. access ready 不能只看 `/v1/models`
- 当前代码已经把 ready gate 提升到 completion 层
- 必须同时通过 `/v1/models``/v1/chat/completions`
4. `subscription``self_service` 的 probe key 语义不同
- `subscription` 最终 probe key 是宿主 managed key
- `self_service` 最终 probe key 是普通用户 gateway key
5. `self_service` 普通用户 key 的真实认证方式是 `Authorization: Bearer`
- 不是 `x-api-key`
6. account test 不能默认回退到 `gpt-5.4`
- 必须显式传 `provider.SmokeTestModel`
- 否则会把非 GPT provider 错误判成 failed
7. remote/provider 验收要保留 upstream 直探证据
- upstream `/models`
- upstream `/chat/completions`
- `21-summary.json`
8. shared fresh-host 上 `reconcile=drifted` 不能直接覆盖 import/access 成功结论
- 先看 `05-import.json`
- 再看 `07-access-status.json`
- 最后才解释 `09-reconcile.json`
## 一图理解完整链路
把一次稳定 onboarding 看成 5 个阶段:
1. 定义 provider
2. 落 pack
3. 导入宿主
4. 验证 access
5. 固化 artifact 与经验
只有这 5 个阶段都闭环,后续宿主升级或换 key 时才能快速复用。
## 阶段 1定义 provider
### 1.1 先判断这个上游是否值得接
最先回答 5 个问题:
1. `base_url` 是否稳定,是否明确兼容 OpenAI-style `/models``/chat/completions`
2. 是否有至少一个可用于真实 completion 的 key
3. 上游返回的模型名是否稳定,是否可能带 `vendor/model` 前缀
4. 这个 provider 目标走 `self_service``subscription`,还是两者都要支持
5. 是否需要独立的 host 兼容性结论,而不是直接复用别的 provider 结论
如果连第 2 条都不满足,就不要把“导入失败”先归因为控制面。
### 1.2 provider 清单里必须关注的字段
至少明确这些信息:
1. `provider_id`
2. `platform`
3. `default_model`
4. `smoke_test_model`
5. `channel_template.model_mapping`
6. `channel_template.model_pricing`
7. `account_template.credentials.model_mapping`
经验规则:
- `smoke_test_model` 不要留空
- `default_model``smoke_test_model` 可以相同,但不要依赖宿主默认值
- 若 upstream 返回模型名带厂商前缀,要在验收脚本侧考虑归一化,而不是强行修改上游
### 1.3 什么时候要新增模型别名归一化
如果你看到的是这类模型名:
- `deepseek-ai/DeepSeek-V4-Pro`
- `vendor-x/model-y`
而 pack 里写的是:
- `deepseek-v4-pro`
- `model-y`
就不要把 `upstream_models_has_expected_model=false` 直接当失败。先补脚本归一化规则,再看真实 chat 是否通。
## 阶段 2落 pack
### 2.1 最小改动原则
优先只改下面几类文件:
1. `packs/openai-cn-pack/providers/<provider>.json`
2. 必要时更新 `checksums.txt`
3. 若需要测试临时线路,优先复制出临时 pack不要先污染正式 pack
不要为了一个新 provider 先改控制面逻辑。先让 pack 描述能力足够完整,再判断是否真的需要动 Go 代码。
### 2.2 pack 完成后的本地静态检查
至少确认:
1. pack 能被 loader 成功读取
2. provider schema 通过
3. checksum 对齐
4. `smoke_test_model` 非空
如果 pack 里已经缺字段,后面的导入问题会越来越难解释。
## 阶段 3导入宿主
### 3.1 导入前先选 access mode
两条链路语义不同,不要混着看。
`self_service`
1. 目标用户真实存在
2. 普通用户 key 可用
3. key 已绑定目标标准 group
4. 用户有可用余额
`subscription`
1. 目标用户真实存在
2. 普通用户 key 可用
3. 目标 group 是 `subscription` 类型
4. 用户有 active subscription
5. key 已绑定该 subscription group
### 3.2 导入时最容易漏掉的三件事
1. account `credentials.model_mapping`
- 漏掉就会回退 GPT 默认模型集
2. channel `model_pricing`
- 只有 `model_mapping` 不够
3. account test 的 `provider.SmokeTestModel`
- 不显式传递就可能被宿主默认拿 `gpt-5.4` 去测
### 3.3 既有 channel 与新建 channel 的处理方式不同
对于新建 channel
- 创建时就要带全量字段
对于既有 channel
- 不能假设历史字段完整
- 必须走 update 纠偏
如果 live host 上出现“有 `model_mapping` 但没有 `model_pricing`”,先别急着怀疑当前代码。先确认在线 CRM 进程是不是旧版本。
## 阶段 4验证 access
### 4.1 必须分三层落证据
每次 onboarding 至少保留这三层:
1. account 单体视角
- `GET /api/v1/admin/accounts/:id`
- `GET /api/v1/admin/accounts/:id/models`
2. group / 普通用户聚合视角
- `GET /v1/models`
3. completion 视角
- `POST /v1/chat/completions`
任何时候都不要用前一层去替代后一层。
### 4.2 当前 completion-gated 判定标准
对外宣称 ready 之前,至少要满足:
1. `/v1/models` 命中目标 `smoke_test_model`
2. `/v1/chat/completions` 返回成功
否则最多只能说:
- 模型暴露对了
- 还不能说真实调用链路对了
### 4.3 upstream 直探的意义
当 host chat 失败时,必须做 upstream 直探,目的是把问题分成两类:
1. `host_compatibility_gap`
- host `/chat/completions` 失败
- upstream `/chat/completions` 成功
2. `upstream_key_quota_issue`
- upstream 自己就失败
这一步非常关键,因为它决定后续是修控制面、提宿主 issue还是换 key。
## 阶段 5固化 artifact 与经验
### 5.1 哪些 artifact 算“最终有效证据”
只有这类证据值得长期保留:
1. latest-head
2. fresh-host
3. 同时包含 import/access/status/reconcile/rollback 或至少 import + access + completion
4. 已能解释失败分类或通过结论
中间大量半失败、参数错误、旧进程产物,不要全部纳入版本库。
### 5.2 文档必须同步的三处
每次完成一次新的 provider onboarding 或宿主版本重适配,至少同步:
1. `docs/EXECUTION_BOARD.md`
2. `docs/SOURCE_OF_TRUTH.md`
3. 本文或 `docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md`
同步原则:
- 执行板写当前真相
- 真相索引写当前优先证据
- playbook / learnings 写可复用方法和误判点
## 宿主版本变更时的稳定重验流程
如果 `sub2api` 宿主版本升级了,不要立刻重跑全套试错。按下面顺序来。
### 第一步:先验证宿主契约有没有变
重点确认 6 个点:
1. `channel` create/update 的字段契约有没有变
2. `accounts/:id/test` 是否仍接受显式模型
3. `/v1/models` 的普通用户认证语义有没有变
4. `/v1/chat/completions` 的普通用户认证语义有没有变
5. subscription key/group/balance 前置有没有变
6. admin bearer/api-key 认证入口有没有变
如果这一步没确认,后面所有“导入失败”都没有解释力。
### 第二步:只挑一个最稳定的 provider 先复验
推荐顺序:
1. 先挑一条历史上最稳定、completion 已验证通过的 provider
2. 先跑 `subscription`
3. 再跑 `self_service`
原因很简单:
- 这样可以先区分“宿主整体契约变了”还是“某个 provider 自己有问题”
### 第三步:再放大到 provider matrix
当基线 provider 通过后,再去补:
1. 新 provider
2. 较不稳定中转
3. 多 key / replacement account / reconcile / rollback 场景
## 新增 provider 的推荐最短路径
如果目标是“尽快把一个新 provider 接入并完成真实验收”,建议按这个顺序:
1. 准备 provider JSON
2. 明确 `smoke_test_model`
3. 明确 `model_mapping``model_pricing`
4. 用临时 pack 跑 dry-run
5. 跑一轮 fresh-host `subscription`
6. 验证三层证据
7. 跑 upstream `/models``/chat/completions`
8. 若通过,再补 `self_service`
9. 固化 artifact
10. 更新文档板面
不要一开始就:
- 同时改 pack、脚本、Go 逻辑、多个 provider
- 同时在多个宿主环境上跑
- 同时把 self_service 与 subscription 混成一个结论
## 常见失败与最短定位法
### 现象 1`/accounts/:id/models` 正确,但 `/v1/models` 错
优先检查:
1. key/group 绑定
2. subscription 分配
3. 普通用户余额
4. probe key 语义是否用错
### 现象 2`/v1/models` 正确,但 `/chat/completions` 失败
优先检查:
1. host `/chat/completions`
2. upstream `/chat/completions`
3. `21-summary.json`
### 现象 3provider 导入成功,但 account probe 失败
优先检查:
1. `provider.SmokeTestModel` 是否真的传到了 `/accounts/:id/test`
2. SSE `type=error` 是否被正确解析
3. 宿主默认模型是否回退到了 GPT
### 现象 4CRM 显示 `self_service broken`,但用户 key 直打宿主已经通
优先检查:
1. gateway probe 是否错误用了 `x-api-key`
2. 在线 CRM 进程是否已切到最新修复版本
### 现象 5刚升级宿主后最前面的 `create-host/probe-host` 就失败
优先检查:
1. host bearer token 是否过期
2. host admin auth 契约是否变更
3. 不要先把它归因为 provider 本身问题
## 推荐沉淀模板
每新增一个 provider建议至少补这 6 项记录:
1. provider 名称与 base_url
2. `smoke_test_model`
3. `subscription` 是否通过
4. `self_service` 是否通过
5. upstream `/models``/chat/completions` 是否稳定
6. 当前已知限制或宿主兼容性差异
## 最后结论
把新增 provider 做稳定,不在于“多快把 key 导进宿主”,而在于这 4 件事是否同时完成:
1. pack 定义完整
2. access gate 真实闭环
3. upstream 失败能分层归因
4. 经验和证据能复用
如果只完成前两项,系统还只是“能跑一次”。
只有四项都完成,后续宿主升级、换 key、补 provider 时才会稳定且快。