From a050042f8e81503553163462eaf284f2ff84b127 Mon Sep 17 00:00:00 2001 From: phamnazage-jpg Date: Thu, 21 May 2026 21:45:56 +0800 Subject: [PATCH] docs: add provider onboarding playbook --- README.md | 1 + docs/PROVIDER_ONBOARDING_PLAYBOOK.md | 395 +++++++++++++++++++++++++ docs/REAL_HOST_ACCEPTANCE_CHECKLIST.md | 1 + docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md | 7 +- docs/SOURCE_OF_TRUTH.md | 19 +- 5 files changed, 417 insertions(+), 6 deletions(-) create mode 100644 docs/PROVIDER_ONBOARDING_PLAYBOOK.md diff --git a/README.md b/README.md index eb8b41d4..aa7916bb 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ sub2api-cn-relay-manager/ - [docs/SOURCE_OF_TRUTH.md](./docs/SOURCE_OF_TRUTH.md) —— 当前文档真相入口;先看这份,避免把历史审查/历史 artifact 误读为当前结论 - [docs/EXECUTION_BOARD.md](./docs/EXECUTION_BOARD.md) —— 当前执行状态、最新 gate、剩余阻断 - [docs/PRODUCTION_CLOSURE_BOARD.md](./docs/PRODUCTION_CLOSURE_BOARD.md) —— 当前是否可按 PRD 首版范围放行 +- [docs/PROVIDER_ONBOARDING_PLAYBOOK.md](./docs/PROVIDER_ONBOARDING_PLAYBOOK.md) —— 新增 provider、宿主版本变更后重验、稳定导入与快速诊断的完整作业手册 - [docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md](./docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md) —— 真实宿主验收标准步骤 - [docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md](./docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md) —— 已调通细节、典型误判点、诊断顺序 diff --git a/docs/PROVIDER_ONBOARDING_PLAYBOOK.md b/docs/PROVIDER_ONBOARDING_PLAYBOOK.md new file mode 100644 index 00000000..3017d6fd --- /dev/null +++ b/docs/PROVIDER_ONBOARDING_PLAYBOOK.md @@ -0,0 +1,395 @@ +# Provider Onboarding Playbook + +日期:2026-05-21 + +## 目的 + +这份文档面向两类场景: + +1. 需要把一个新的 OpenAI-compatible 国产模型中转接入到 `sub2api-cn-relay-manager` +2. `sub2api` 宿主版本发生变化后,需要快速确认现有导入链路是否仍然兼容,并稳定完成重新导入与验收 + +它不是当前 gate 文档,也不替代真实宿主验收步骤文档。 + +分工如下: + +- `docs/SOURCE_OF_TRUTH.md` + - 回答“当前最新真相是什么” +- `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 时才会稳定且快。 diff --git a/docs/REAL_HOST_ACCEPTANCE_CHECKLIST.md b/docs/REAL_HOST_ACCEPTANCE_CHECKLIST.md index 072c6f11..467b82e2 100644 --- a/docs/REAL_HOST_ACCEPTANCE_CHECKLIST.md +++ b/docs/REAL_HOST_ACCEPTANCE_CHECKLIST.md @@ -5,6 +5,7 @@ 用途: - 给后续每次 real-host 验收直接复用 - 只保留最短闭环,不展开历史背景 +- 如果你当前是在“新增 provider”或“宿主版本升级后重新适配”,先看:`PROVIDER_ONBOARDING_PLAYBOOK.md` - 详细解释仍看:`REAL_HOST_ACCEPTANCE_RUNBOOK.md` 与 `REAL_HOST_ACCEPTANCE_LEARNINGS.md` ## 0. 先确认你看的是真相文档 diff --git a/docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md b/docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md index 9413df36..c513bc65 100644 --- a/docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md +++ b/docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md @@ -8,10 +8,13 @@ - 当前 gate、最新阻断、最新 live 真相以它为准。 2. `docs/PRODUCTION_CLOSURE_BOARD.md` - 看是否已经达到可上线口径,以及哪些只是历史 PASS。 -3. `docs/REAL_HOST_ACCEPTANCE_CHECKLIST.md` +3. `docs/PROVIDER_ONBOARDING_PLAYBOOK.md` + - 当你是在“新增 provider”或“宿主升级后重新适配”场景下工作时,先看这份。 + - 它定义稳定的 onboarding / rerun 顺序,而不是只定义一次性的验收动作。 +4. `docs/REAL_HOST_ACCEPTANCE_CHECKLIST.md` - 每次 real-host 验收先走这一页。 - 适合快速确认红线、三层证据和最短诊断顺序。 -4. `docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md` +5. `docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md` - 看已经调通的细节、典型误判点、推荐诊断顺序。 ## 目标 diff --git a/docs/SOURCE_OF_TRUTH.md b/docs/SOURCE_OF_TRUTH.md index d6a09fc8..b73c678b 100644 --- a/docs/SOURCE_OF_TRUTH.md +++ b/docs/SOURCE_OF_TRUTH.md @@ -58,7 +58,17 @@ - 它定义“怎么验收” - 不直接定义当前 gate,当前 gate 仍以上面两份板为准 -### 4. `docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md` +### 4. `docs/PROVIDER_ONBOARDING_PLAYBOOK.md` +用途: +- 新增 provider 的稳定操作顺序 +- 宿主版本变更后的重验路径 +- 如何把一次调通沉淀成可复用的 onboarding 流程 + +解释规则: +- 它定义“后续怎么稳定地继续加 provider / 复验宿主” +- 不直接决定当前 gate,但决定后续变更能否低风险复用 + +### 5. `docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md` 用途: - 已调通的细节 - 高频误判点 @@ -168,9 +178,10 @@ ### 想继续做真实宿主验收 1. `docs/SOURCE_OF_TRUTH.md` -2. `docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md` -3. `docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md` -4. 再看最新 artifact +2. `docs/PROVIDER_ONBOARDING_PLAYBOOK.md` +3. `docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md` +4. `docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md` +5. 再看最新 artifact ### 想回顾为什么会演化成现在这样 1. `docs/SOURCE_OF_TRUTH.md`