# 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/.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 时才会稳定且快。