398 lines
12 KiB
Markdown
398 lines
12 KiB
Markdown
# 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`
|
||
|
||
### 现象 3:provider 导入成功,但 account probe 失败
|
||
|
||
优先检查:
|
||
|
||
1. `provider.SmokeTestModel` 是否真的传到了 `/accounts/:id/test`
|
||
2. SSE `type=error` 是否被正确解析
|
||
3. 宿主默认模型是否回退到了 GPT
|
||
|
||
### 现象 4:CRM 显示 `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 时才会稳定且快。
|