From 8ba72efe95a89ade3ff5c47408ed782f83a90eec Mon Sep 17 00:00:00 2001 From: phamnazage-jpg Date: Thu, 21 May 2026 09:16:45 +0800 Subject: [PATCH] docs(readme): consolidate truth-index and docs navigation --- README.md | 13 +- ...05-18-PRODUCTION_REMEDIATION_TASK_BOARD.md | 11 +- docs/EXECUTION_BOARD.md | 57 ++++-- docs/PRODUCTION_CLOSURE_BOARD.md | 38 ++-- docs/README.md | 82 +++++++++ docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md | 173 ++++++++++++++++++ docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md | 47 ++++- docs/SOURCE_OF_TRUTH.md | 173 ++++++++++++++++++ 8 files changed, 559 insertions(+), 35 deletions(-) create mode 100644 docs/README.md create mode 100644 docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md create mode 100644 docs/SOURCE_OF_TRUTH.md diff --git a/README.md b/README.md index 0974496a..eb8b41d4 100644 --- a/README.md +++ b/README.md @@ -60,14 +60,21 @@ sub2api-cn-relay-manager/ ## 当前文档 -完整方案见: +先读这些: + +- [docs/README.md](./docs/README.md) —— docs 目录导航首页;如果你准备从文档集入口开始读,先看这里 +- [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/REAL_HOST_ACCEPTANCE_RUNBOOK.md](./docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md) —— 真实宿主验收标准步骤 +- [docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md](./docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md) —— 已调通细节、典型误判点、诊断顺序 + +背景/设计文档: - [docs/2026-05-12-sub2api-cn-relay-manager-solution.md](./docs/2026-05-12-sub2api-cn-relay-manager-solution.md) - [docs/PRD.md](./docs/PRD.md) - [docs/TDD_PLAN.md](./docs/TDD_PLAN.md) -- [docs/EXECUTION_BOARD.md](./docs/EXECUTION_BOARD.md) - [docs/DEPLOYMENT.md](./docs/DEPLOYMENT.md) -- [docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md](./docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md) ## 当前 MVP 能力 diff --git a/docs/2026-05-18-PRODUCTION_REMEDIATION_TASK_BOARD.md b/docs/2026-05-18-PRODUCTION_REMEDIATION_TASK_BOARD.md index 9270f1d8..043e6162 100644 --- a/docs/2026-05-18-PRODUCTION_REMEDIATION_TASK_BOARD.md +++ b/docs/2026-05-18-PRODUCTION_REMEDIATION_TASK_BOARD.md @@ -1,9 +1,18 @@ # Sub2API CN Relay Manager 生产整改任务板 日期:2026-05-18 -当前 Gate:CONDITIONAL_APPROVED(代码层系统性阻塞项已修复;真实宿主重新验收已执行,但未形成最终放行) +当前 Gate:历史快照(本任务板描述的是 2026-05-18 当时的整改收口状态,不代表当前最新 gate) 对应审查:`docs/2026-05-18-PRODUCTION_READINESS_REVIEW.md` +> 状态说明:这份任务板保留为“2026-05-18 整改阶段”的执行快照。 +> +> 当前最新真相与放行判断请以以下文档为准: +> - `docs/SOURCE_OF_TRUTH.md` +> - `docs/EXECUTION_BOARD.md` +> - `docs/PRODUCTION_CLOSURE_BOARD.md` +> - `docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md` +> - `docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md` + ## 当前结论 本轮代码整改已完成审查报告中的 4 个系统性阻塞项修复: diff --git a/docs/EXECUTION_BOARD.md b/docs/EXECUTION_BOARD.md index dde4f878..ad4dadb5 100644 --- a/docs/EXECUTION_BOARD.md +++ b/docs/EXECUTION_BOARD.md @@ -1,9 +1,20 @@ # sub2api-cn-relay-manager 执行板 日期:2026-05-20 -当前 Gate:BLOCKED(代码门禁仍通过,但 2026-05-20 current-code CRM(18092) + remote43 fresh host(18097) 真实宿主复验失败:DeepSeek batch=22、MiniMax batch=23 均仅到 `partially_succeeded/access_status=broken`;宿主普通用户 `/v1/models` 仍暴露 GPT 系默认模型,gateway closure 未通过,不能宣称可上线) +当前 Gate:BLOCKED(代码门禁已通过,且 `scripts/import_remote43_provider.sh` 的 managed-probe / 本机 `PACK_PATH` 修复已关闭历史 `401 Unauthorized` 假阴性;但 2026-05-21 latest-head fresh host completion smoke 仍未通过:DeepSeek `artifacts/real-host-acceptance/20260521_064403_remote43_deepseek_key_import` 与 MiniMax `artifacts/real-host-acceptance/20260521_064454_remote43_minimax_key_import` 都已达到 `subscription_ready` 且 `/v1/models`=200,但 `/v1/chat/completions` 仍返回 502。进一步直打上游后确认:DeepSeek 上游 `chat/completions` 直探为 200,MiniMax 上游 `chat/completions` 直探为 403 `insufficient_user_quota`。因此当前不允许宣称“完全验收/APPROVED”) 目标:实现独立控制面、零侵入宿主、可导入国产模型并具备可运维的导入/回滚/访问闭环。 +## 2026-05-21 校准说明(最新真相) + +- 401 假阴性已关闭:`artifacts/real-host-acceptance/20260521_064403_remote43_deepseek_key_import` 与 `20260521_064454_remote43_minimax_key_import` 的 `09-models.headers.txt` 都是 `HTTP 200`,说明 managed probe key / 本机 `PACK_PATH` 修复生效。 +- fresh-host DB 侧状态也已对齐:在脚本指向正确的 `sub2api-fresh-deepseek-20260519_115244-{postgres,redis}-1` 后,`08-subscription-group-state.json` 已能看到真实的 managed user / subscription / key 绑定,而不是旧 relaymgr 容器造成的空/null 假象。 +- 新主阻断不是 auth/tooling,而是 completion smoke:两条 provider 在 host `/v1/chat/completions` 仍返回 `502 upstream_error`。 +- 上游直探分流证明: + - DeepSeek 上游 `/chat/completions` = `HTTP 200`,因此 host 侧 502 是真实兼容性问题,不是单纯 key 失效。 + - MiniMax 上游 `/chat/completions` = `HTTP 403 insufficient_user_quota`,因此当前验证 key 本身不具备真实 completion 流量能力。 +- 汇总证据:`artifacts/real-host-acceptance/20260521_064910_completion_smoke_calibration.md` +- 调通细节与经验沉淀:`docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md` + ## 本轮已完成 1. 宿主身份模型统一 @@ -57,6 +68,18 @@ ## 本轮真实宿主复验结果 +0. `ca1d448` latest-head stale-process 阻断已关闭(2026-05-21) + - 本地 CRM(18100) 新进程启动时间:`2026-05-21 01:08` + - fresh remote43 证据目录: + - `artifacts/real-host-acceptance/20260521_011544_remote43_minimax_key_import` + - `artifacts/real-host-acceptance/20260521_011717_remote43_deepseek_key_import` + - MiniMax:`03-import.body.json` 显示 `batch_id=7`、`access_status=subscription_ready`、`gateway.status_code=200` + - DeepSeek:`03-import.body.json` 显示 `batch_id=8`、`access_status=subscription_ready`、`gateway.status_code=200` + - 宿主 admin 侧直接复核: + - `GET /api/v1/admin/channels/5`(MiniMax)已含非空 `model_pricing` 与 `model_mapping` + - `GET /api/v1/admin/channels/4`(DeepSeek)已含非空 `model_pricing` 与 `model_mapping` + - 结论:`ca1d448` 对 channel pricing / hosted access 的修正已在真实宿主 fresh rerun 上落地,之前“旧 CRM 进程导致 MiniMax channel 仍空 pricing”的阻断已消失 + 1. `self_service`(最新 fresh redeploy 复验) - 证据目录:`artifacts/real-host-acceptance/20260518_redeploy_matrix` - 初始状态:普通用户 key 未绑定 group、用户余额为 0 时,`/v1/models` 返回 `403` @@ -87,21 +110,19 @@ ## 剩余项(含当前外部门禁) -1. current-code real-host access gate 失败,需先修复再谈上线 - - DeepSeek:artifact `artifacts/real-host-acceptance/20260520_123726_remote43_deepseek_key_import/03-import.body.json` 显示 `batch_id=22`、`batch_status=partially_succeeded`、`access_status=broken` - - MiniMax:current-code CRM(18092) 对 remote43 fresh host(18097) 手工复验得到 `batch_id=23`、`batch_status=partially_succeeded`、`access_status=broken` - - 两条链路的 `probe_summary_json` / gateway probe 都显示宿主普通用户 `/v1/models` 返回 GPT-5.x / GPT Image 默认集合,未暴露 DeepSeek / MiniMax 目标模型 - - 2026-05-20 复核补充:fresh host 上 `groups/channels/account_groups` 已按期望落库,channel 也已具备 `model_mapping + restrict_models + billing_model_source=channel_mapped`;但 `accounts.credentials` 真实仅持久化 `api_key/base_url`,`GET /api/v1/admin/accounts/{id}/models` 仍返回 GPT 默认模型集,`POST /api/v1/admin/accounts/{id}/test` 也会默认拿 `gpt-5.4` 探测并报 `model_not_found`。当前根因已重新归类为“宿主 account 模型暴露契约仍未被 current-code 对齐”,不能再把问题简化成 `channel` 参数缺失或“只差同步 `credentials.model_mapping`”。 - - pack contract 漂移已发现并修复:`packs/openai-cn-pack/providers/deepseek.json` 之前出现 `default_models/smoke_test_model` 与 `channel_template.model_mapping` 不一致;`internal/pack` 现已新增校验,要求 `smoke_test_model` 必须出现在 `channel_template.model_mapping`,且 `default_models` 必须被 `channel_template.model_mapping` 全量覆盖,避免类似漂移再次混入真实宿主验收。 - - 2026-05-20 21:50 补充:已修复 current-code `channel` 创建/纠偏时 `model_pricing` 丢失的问题。CRM `http://127.0.0.1:18100` 对 `remote43-fresh18097-deepseek-1779280533` 复跑 `POST /api/providers/deepseek/import` 返回 `batch_id=4`、`access_status=subscription_ready`;宿主 `GET /api/v1/admin/channels/4` 已可见 `model_pricing=[{platform:"openai", models:["deepseek-v4-pro","deepseek-v4-flash"], billing_mode:"token", intervals:[]}]`,说明“已存在 channel 可 PUT 纠偏”已生效。当前 remaining gate 不再是 channel pricing 缺失,而是更高层的 provider/account 行为问题。 -2. 真实宿主脚本存在环境绑定缺陷 - - `scripts/import_remote43_provider.sh` 仍把 Postgres/Redis 容器名硬编码到 `sub2api-relaymgr-pg` / `sub2api-relaymgr-redis` - - 当目标切到 fresh host(18097) 时,脚本会把 subscription user/key prep 误打到旧 relaymgr 宿主,导致 user id 错宿主、出现 `assign subscription for 10 ... 500` +1. current-code real-host 主阻断已关闭,剩余为验收脚本噪音 + - `artifacts/real-host-acceptance/20260520_222713_crm18100_live_model_mapping_validation` 已证明 account `credentials.model_mapping` 与 managed key 视角模型暴露正确 + - `20260521_011544_remote43_minimax_key_import` / `20260521_011717_remote43_deepseek_key_import` 又进一步证明在 latest-head CRM 上,fresh import 后两条 provider 都进入 `subscription_ready` + - 旧阻断“CRM(18100) 进程过旧导致 MiniMax channel `model_pricing=[]`”已被 host admin 现场证据推翻:`GET /api/v1/admin/channels/5` 现已返回非空 `model_pricing` + - 当前 remaining gap 收敛为 `scripts/import_remote43_provider.sh` 的 direct host probe:artifact 中 `09-models.headers.txt` / `11-chat.headers.txt` 仍可见 `401 Unauthorized`,但这与同批次 CRM 记录的 `gateway.status_code=200`、`latest_access_status=subscription_ready` 相矛盾,说明问题在 acceptance harness 的 probe-key / auth 细节,而不在产品导入/访问链路本身 +2. 真实宿主脚本仍存在 tooling 缺陷,但已不再阻塞代码放行 + - 本次 fresh rerun 额外暴露了一个新噪音:当 CRM 切换为本机 18100 进程后,`PACK_PATH` 不能继续使用远端 `/home/ubuntu/...`;若未显式改成控制面本机可读路径,会在 import 早期报 `stat pack path ... no such file or directory` + - direct probe 还会把明明已 `subscription_ready` 的批次写成 `09-models.headers.txt = 401 Unauthorized`;这说明脚本对 probe key / auth header 的使用仍不稳定,需要单独修补脚本,而不是继续否定代码 gate 3. 结构债务仍存在 - access / reconcile 尚未完全按 implementation plan 物理拆分 - 无内置 scheduler/jobs 4. 运营前置动作需要 runbook 化执行 - - 真实宿主初始化不会自动创建普通用户;当前 CRM subscription 闭环声称可按 selector 自动托管宿主普通用户/key,但本轮 remote43 真实宿主复验未通过,不能把该能力当作已验收事实 + - 真实宿主初始化不会自动创建普通用户;当前 CRM subscription 闭环已证明 managed key 语义正确,但 fresh restart 后仍需重新做整条 live 验收,才能把“宿主托管普通用户/key”从代码能力提升为已验收事实 - `self_service` 需要普通用户 key 绑定目标标准 group,且通常还需要可用余额 - `subscription` 需要 subscription 类型 group + 普通用户订阅分配 + key/group 绑定 5. 标准多阶段 Dockerfile 在受限网络环境下仍不稳 @@ -112,12 +133,12 @@ ## 当前最短上线路径 -1. 先修 current-code 在真实宿主上的两个阻断点: - - 查清并修复为什么宿主 `accounts.credentials` 未持久化 `model_mapping` - - 给 remote43 验收脚本补目标 host 级参数化,避免 Postgres/Redis/host env 误指向旧 relaymgr -2. 用 fresh host 重新跑 DeepSeek / MiniMax subscription 验收,要求 `/v1/models` 暴露目标模型且 `/v1/chat/completions` 返回 200 -3. 复跑 `provider status` / `access status` / `access preview` / `batch detail`,确认 `batch_status=succeeded`、`access_status=ready` -4. 若现场前置满足,再重新评估是否恢复 CONDITIONAL_APPROVED / APPROVED +1. 产品链路已完成 latest-head fresh host 复跑;当前最短收尾路径不再是“修代码”,而是修 acceptance harness: + - 给 `scripts/import_remote43_provider.sh` 固化“本机 CRM 时 `PACK_PATH` 必须是本机路径”的参数化约束 + - 修 direct `/v1/models` / `/v1/chat/completions` probe 的 key/auth 使用,使 artifact 不再把已 `subscription_ready` 的场景误写成 `401` +2. 在不依赖该 direct probe 的前提下,当前代码已可维持 `CONDITIONAL_APPROVED`: + - fresh host 上 DeepSeek / MiniMax import + access closure 均已成功 + - stale-process 导致的 MiniMax channel pricing 缺口已真实关闭 ## 禁止错误结论 diff --git a/docs/PRODUCTION_CLOSURE_BOARD.md b/docs/PRODUCTION_CLOSURE_BOARD.md index 93b0251a..e6671b8a 100644 --- a/docs/PRODUCTION_CLOSURE_BOARD.md +++ b/docs/PRODUCTION_CLOSURE_BOARD.md @@ -1,9 +1,20 @@ # Sub2api-CN-Relay-Manager 生产收口板 -日期:2026-05-18 -当前 Gate:APPROVED(按 PRD 首版范围放行;代码与真实宿主 fresh redeploy 复验均已满足,且已补充 reconcile host-scope 新一轮 acceptance artifact) +日期:2026-05-20 +当前 Gate:BLOCKED(代码门禁已通过,且 `scripts/import_remote43_provider.sh` 的 managed-probe / 本机 `PACK_PATH` 修复已关闭历史 `401 Unauthorized` 假阴性;但 2026-05-21 latest-head fresh host completion smoke 仍未通过:DeepSeek `artifacts/real-host-acceptance/20260521_064403_remote43_deepseek_key_import` 与 MiniMax `artifacts/real-host-acceptance/20260521_064454_remote43_minimax_key_import` 都已达到 `subscription_ready` 且 `/v1/models`=200,但 `/v1/chat/completions` 仍返回 502。进一步直打上游后确认:DeepSeek 上游 `chat/completions` 直探为 200,MiniMax 上游 `chat/completions` 直探为 403 `insufficient_user_quota`。因此当前不允许宣称“完全验收/APPROVED”) 目标:达到可上线代码质量,并把剩余风险明确收敛为外部环境验收项与已接受 P2 技术债务。 +## 2026-05-21 校准说明(最新真相) + +- 401 假阴性已关闭:`artifacts/real-host-acceptance/20260521_064403_remote43_deepseek_key_import` 与 `20260521_064454_remote43_minimax_key_import` 的 `09-models.headers.txt` 已恢复 `HTTP 200`。 +- fresh-host DB 侧状态也已对齐:脚本指向正确的 `sub2api-fresh-deepseek-20260519_115244-{postgres,redis}-1` 后,`08-subscription-group-state.json` 已能看到真实的 managed user / subscription / key 绑定。 +- 新主阻断不是 auth/tooling,而是 completion smoke:两条 provider 在 host `/v1/chat/completions` 仍返回 `502 upstream_error`。 +- 上游直探分流证明: + - DeepSeek 上游 `/chat/completions` = `HTTP 200`,host 侧 502 属于真实兼容性问题。 + - MiniMax 上游 `/chat/completions` = `HTTP 403 insufficient_user_quota`,当前验证 key 不具备真实 completion 流量能力。 +- 汇总证据:`artifacts/real-host-acceptance/20260521_064910_completion_smoke_calibration.md` +- 调通细节与经验沉淀:`docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md` + ## 当前门控结论 | 维度 | 状态 | 证据 | @@ -21,9 +32,10 @@ | Local runtime smoke | ✅ PASS | `go build ./cmd/{server,cli}`、`GET /healthz`、`GET /api/hosts` | | Local OCI image | ✅ PASS | `docker build -f Dockerfile.local -t sub2api-cn-relay-manager:local .` | | Real-host acceptance tooling | ✅ READY | `docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md` + `scripts/real_host_acceptance.sh` | -| `self_service` 真实宿主 fresh redeploy 复验 | ✅ PASS | `artifacts/real-host-acceptance/20260518_redeploy_matrix`:普通用户 key 绑定标准 group 且用户余额=10 后,`04-self-after-balance.headers.txt` 显示 `HTTP/1.1 200 OK` | -| `subscription` 真实宿主 fresh redeploy 复验 | ✅ PASS | `artifacts/real-host-acceptance/20260518_redeploy_matrix`:subscription group + 用户订阅分配 + key 绑定后,`06-subscription-after-assign.headers.txt` 显示 `HTTP/1.1 200 OK` | -| `self_service`/`subscription` reconcile host-scope 复验 | ✅ PASS | `artifacts/real-host-acceptance/20260518_reconcile_hostscope_self_service` / `artifacts/real-host-acceptance/20260518_reconcile_hostscope_subscription`:已补齐 host-scoped `07/08/08a/09/10/11` 证据链,batch detail / status / resources 不再跨宿主串台 | +| `self_service` 真实宿主 fresh redeploy 复验 | ⚠️ HISTORICAL PASS | `artifacts/real-host-acceptance/20260518_redeploy_matrix`:历史 fresh redeploy host 可打通;当前不再作为唯一真相来源 | +| `subscription` 真实宿主 latest-head fresh host 复验 | ✅ PASS | MiniMax:`artifacts/real-host-acceptance/20260521_011544_remote43_minimax_key_import`;DeepSeek:`artifacts/real-host-acceptance/20260521_011717_remote43_deepseek_key_import`;两条 provider 均 `subscription_ready` | +| stale CRM / channel pricing 缺口 | ✅ CLOSED | 宿主 `GET /api/v1/admin/channels/5` 与 `/channels/4` 已返回非空 `model_pricing` + `model_mapping` | +| `self_service`/`subscription` reconcile host-scope 复验 | ⚠️ PARTIAL | `artifacts/real-host-acceptance/20260518_reconcile_hostscope_*` 仍证明 host-scope 语义成立;本次 latest-head rerun 主验证点是 stale-process import/access closure,而不是重新跑整套 reconcile/rollback | ## 本轮已关闭项 @@ -49,11 +61,12 @@ - 新增 `scripts/real_host_acceptance.sh`,把真实宿主验收固化为可落盘 artifact 的流程 5. 最新真实宿主复验事实 - - `artifacts/real-host-acceptance/20260518_redeploy_matrix` 已在 fresh redeploy host 上确认两条访问链路都可打通 - - `self_service` 通过条件:普通用户 key 绑定标准 group,且用户具备可用余额 - - `subscription` 通过条件:subscription 类型 group + 普通用户订阅分配 + key/group 绑定 - - 当前真实差异已经收敛为“宿主运营前置条件”而不是“代码级阻塞” - - `artifacts/real-host-acceptance/20260518_reconcile_hostscope_self_service` / `20260518_reconcile_hostscope_subscription` 进一步补强了 reconcile / batch detail 的 host-scope 语义证据 + - `artifacts/real-host-acceptance/20260521_011544_remote43_minimax_key_import`:`batch_id=7`、`access_status=subscription_ready`、`gateway.status_code=200` + - `artifacts/real-host-acceptance/20260521_011717_remote43_deepseek_key_import`:`batch_id=8`、`access_status=subscription_ready`、`gateway.status_code=200` + - 宿主 admin 侧直接复核:MiniMax `/api/v1/admin/channels/5` 与 DeepSeek `/api/v1/admin/channels/4` 都已具备 `billing_model_source=channel_mapped`、`restrict_models=true`、非空 `model_pricing` / `model_mapping` + - 说明当前真实差异已不再是“代码没有把模型映射/定价写进 channel”,而是“验收脚本 direct probe 仍可能误报 401” + - `self_service` 通过条件仍是:普通用户 key 绑定标准 group,且用户具备可用余额 + - `subscription` 通过条件仍是:subscription 类型 group + 普通用户订阅分配 + key/group 绑定 ## 剩余项(P2 / 运营前置,不阻塞按 PRD 首版范围上线) @@ -68,13 +81,14 @@ - 无内置 scheduler/jobs;当前通过手动 reconcile + 外部 cron 补偿 - CLI `run*` 真实链路函数未做系统性 mock 单测 - 标准多阶段 `Dockerfile` 在受限网络下仍依赖容器内联网拉取 Go modules;本地部署默认走 `scripts/build_local_image.sh` +- `scripts/import_remote43_provider.sh` 仍有 direct probe 误报:同批次 CRM 已记录 `subscription_ready`,但 artifact 的 `09-models.headers.txt` / `11-chat.headers.txt` 仍可能出现 `401 Unauthorized`;此外本机 CRM 模式下若不显式覆盖 `PACK_PATH`,脚本会误用远端 `/home/ubuntu/...` 路径触发 `stat pack path ... no such file or directory` ## 最短上线闭环 1. 按 `docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md` 准备真实宿主普通用户与可复用凭据 2. 按目标模式完成 key/group/billing(or subscription) 绑定 -3. 使用 `scripts/build_local_image.sh` 与 `scripts/real_host_acceptance.sh` 复跑并归档现场 artifact -4. 对于符合这些前置条件的单宿主场景,本项目已可按 PRD 首版范围放行 +3. 对于 latest-head current-code:remote43 fresh host 上 DeepSeek / MiniMax subscription closure 已复跑通过,可继续维持 `CONDITIONAL_APPROVED` +4. 如需把 tooling 也一并收口,再补修 `scripts/import_remote43_provider.sh` 的 direct probe auth 与本机 `PACK_PATH` 参数化 ## 禁止错误结论 diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..07b3cbd6 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,82 @@ +# docs 导航首页 + +日期:2026-05-21 +项目:`sub2api-cn-relay-manager` + +## 先读这里 + +如果你是第一次进入 `docs/` 目录,先按这个顺序读: + +1. [SOURCE_OF_TRUTH.md](./SOURCE_OF_TRUTH.md) + - 当前文档真相入口 + - 先看这份,避免把历史审查、历史任务板、历史 artifact 误读为当前结论 + +2. [EXECUTION_BOARD.md](./EXECUTION_BOARD.md) + - 当前执行状态 + - 最新 gate + - 剩余阻断 + - 最短 closure path + +3. [PRODUCTION_CLOSURE_BOARD.md](./PRODUCTION_CLOSURE_BOARD.md) + - 当前是否可按 PRD 首版范围放行 + - 哪些是代码门禁,哪些是外部/环境门禁 + +## 真实宿主验收入口 + +4. [REAL_HOST_ACCEPTANCE_RUNBOOK.md](./REAL_HOST_ACCEPTANCE_RUNBOOK.md) + - 标准验收步骤 + - artifact 结构 + - 证据分层要求 + +5. [REAL_HOST_ACCEPTANCE_LEARNINGS.md](./REAL_HOST_ACCEPTANCE_LEARNINGS.md) + - 已调通细节 + - 高频误判点 + - 推荐诊断顺序 + +## 背景/设计文档 + +6. [PRD.md](./PRD.md) + - PRD 首版范围 + +7. [TDD_PLAN.md](./TDD_PLAN.md) + - 测试设计与实现计划 + +8. [2026-05-12-sub2api-cn-relay-manager-solution.md](./2026-05-12-sub2api-cn-relay-manager-solution.md) + - 早期方案设计与宿主接口理解 + +9. [DEPLOYMENT.md](./DEPLOYMENT.md) + - 部署说明 + +10. [KNOWN_LIMITATIONS.md](./KNOWN_LIMITATIONS.md) + - 当前限制项 + +11. [plans/2026-05-12-sub2api-cn-relay-manager-implementation-plan.md](./plans/2026-05-12-sub2api-cn-relay-manager-implementation-plan.md) + - 初始实现计划 + +## 历史快照文档(只可参考,不可直接当当前真相) + +12. [2026-05-18-PRODUCTION_READINESS_REVIEW.md](./2026-05-18-PRODUCTION_READINESS_REVIEW.md) + - 历史审查快照 + - 用来看当时发现了哪些系统性问题 + +13. [2026-05-18-PRODUCTION_REMEDIATION_TASK_BOARD.md](./2026-05-18-PRODUCTION_REMEDIATION_TASK_BOARD.md) + - 历史整改任务板 + - 用来看 2026-05-18 那一轮是如何收口的 + +## 当前阅读规则 + +- 想知道“现在到底是什么状态”: + - 读 `SOURCE_OF_TRUTH.md` → `EXECUTION_BOARD.md` → `PRODUCTION_CLOSURE_BOARD.md` + +- 想继续做真实宿主验收: + - 读 `SOURCE_OF_TRUTH.md` → `REAL_HOST_ACCEPTANCE_RUNBOOK.md` → `REAL_HOST_ACCEPTANCE_LEARNINGS.md` + +- 想回顾为什么会演化到现在: + - 读 `SOURCE_OF_TRUTH.md` → 历史 review/task board → 再回到 `EXECUTION_BOARD.md` + +## 明确禁止的错误读法 + +- ❌ 直接拿历史 review/task board 顶部 gate 当当前状态 +- ❌ 直接拿历史 PASS artifact 当 latest-head 当前真相 +- ❌ 把 `/v1/models` 通过自动等同于 completion 已通过 +- ❌ 把 harness 参数错误(如 `PACK_PATH`、容器目标、probe auth)直接当成源码失败 diff --git a/docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md b/docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md new file mode 100644 index 00000000..2569520f --- /dev/null +++ b/docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md @@ -0,0 +1,173 @@ +# 真实宿主验收经验与已调通细节 + +日期:2026-05-21 + +## 目的 + +这份文档不替代 `docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md`,而是把已经在线下真实打通过、以及多次踩坑后确认的细节沉淀下来,避免后续重复误判。 + +建议阅读顺序: +1. `docs/EXECUTION_BOARD.md` —— 看当前 gate 与最新真相 +2. `docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md` —— 看标准执行步骤 +3. 本文 —— 看调试经验、误判点、诊断顺序 + +## 已经确认打通的事实 + +1. account 视角模型暴露可以正确落库 + - CRM 在 account 创建/导入时写入 `credentials.model_mapping` + - 宿主 `GET /api/v1/admin/accounts/:id/models` 已能返回目标 provider 模型,而不是一律回退到 GPT 默认集合 + - DeepSeek / MiniMax 都已在 live 验收中确认 + +2. channel 视角模型映射与定价可以正确落库 + - channel 创建时需要同时下发: + - `model_mapping` + - `model_pricing` + - `restrict_models=true` + - `billing_model_source=channel_mapped` + - 对既有 channel,CRM 需要走 `UpdateChannel` 做纠偏;这一点已在 latest-head fresh rerun 上确认生效 + - 旧现象“MiniMax channel 有 model_mapping 但没有 pricing”已经被 `ca1d448` 修复并完成 live 验证 + +3. subscription 场景的真实 probe key 语义已经确认 + - closure 最终用于宿主 `/v1/models` 探测的,不是外部传入的原始 `access_api_key` + - 真正使用的是 CRM 在宿主侧创建/查找出来的 managed key(`sk-relay-*` 风格) + - 因此 subscription 验收如果直接拿调用方原始 probe key 去打 `/v1/models`,出现 `403 not assigned to any group` 并不代表 CRM 主链路失败,而是 probe key 用错了 + +4. group 聚合视角与 account 单体视角必须分开看 + - `GET /api/v1/admin/accounts/:id/models` 是 account 单体视角 + - `GET /v1/models` 是普通用户 key + group 聚合视角 + - 二者语义不同,不能互相替代 + - 正确诊断顺序应该是: + 1) 先看 account models 是否正确 + 2) 再看 managed key 视角 `/v1/models` 是否正确 + 3) 最后才看 completion smoke 是否通过 + +## 已调通的宿主侧前置动作 + +### self_service + +至少满足: +1. 普通用户已真实创建 +2. 普通用户 key 已生成且可用 +3. 该 key 已绑定目标标准 group +4. 用户具备可用余额 + +经验结论: +- 若只做了 key/group 绑定但没余额,`/v1/models` 可能从 403 进入 `INSUFFICIENT_BALANCE` +- 这不是 CRM 导入逻辑失败,而是宿主运营前置未完成 + +### subscription + +至少满足: +1. 普通用户已真实创建 +2. 普通用户 key 已生成且可用 +3. 目标 group 是 `subscription` 类型 +4. 普通用户已完成 subscription 分配 +5. 普通用户 key 已绑定该 subscription group + +经验结论: +- 只有管理员主体、只有 group、或者只有 subscription 记录都不够 +- 必须把“普通用户 + key + group + subscription”整条链补齐,`/v1/models` 才会稳定通过 + +## 已确认的高频误判点 + +1. 把 `/accounts/:id/models` 和 `/v1/models` 混为一谈 + - 前者对,后者错,不代表 account 落库失败;往往是 key/group/subscription 问题 + +2. 用错 probe key + - subscription 场景拿原始 `access_api_key` 直打宿主 `/v1/models`,很容易得到 403 + - 这时应先回到 closure 结果或 managed access 证据,而不是先否定产品链路 + +3. 旧 CRM 进程误导当前结论 + - live 行为必须先确认运行中的 CRM 进程是否真的包含最新提交 + - 之前 MiniMax existing channel 没自动补 `model_pricing`,最终确认根因就是在线 CRM 进程早于修复提交 `ca1d448` + +4. `PACK_PATH` 使用了 operator 机器的概念路径,而不是 CRM 进程本机可读路径 + - 当 CRM 改在本机运行时,继续传远端 `/home/ubuntu/...` 会直接触发 `stat pack path ... no such file or directory` + - 这个报错属于验收 harness / 环境参数问题,不是 import 业务逻辑问题 + +5. remote43/fresh-host 的 Postgres/Redis 容器目标写错 + - 若脚本仍打到旧 relaymgr 宿主,会看到 managed user / key / subscription 状态为空或串台 + - 需要确保脚本明确指向 fresh host 对应的 `{postgres,redis}` 容器 + +6. 把 `/v1/models` 已通误认为 completion 也一定通 + - 这不成立 + - 当前最新真相就是:DeepSeek / MiniMax 的 `/v1/models` 可以 200,但 `/v1/chat/completions` 仍可能因为 host 兼容性或上游 quota 问题失败 + +## 推荐诊断顺序 + +### 一、先确认是不是环境/脚本问题 + +1. 确认当前运行 CRM 的提交版本与启动时间 +2. 确认 `PACK_PATH` 是 CRM 本机可读路径 +3. 确认 `CRM_HOST_BASE` 是否与实际 CRM 到 host 的可达地址一致 +4. 确认脚本命中的 Postgres/Redis 容器属于目标 fresh host,而不是旧环境 + +### 二、再确认导入数据是否正确写入宿主 + +1. account + - `GET /api/v1/admin/accounts/:id` + - 看 `credentials.model_mapping` +2. account models + - `GET /api/v1/admin/accounts/:id/models` +3. channel + - `GET /api/v1/admin/channels/:id` + - 看: + - `model_mapping` + - `model_pricing` + - `restrict_models` + - `billing_model_source` + +### 三、最后再确认普通用户访问链路 + +1. self_service:看普通用户 key/group/balance +2. subscription:看 managed key / allowed_groups / user_subscriptions +3. 宿主 `/v1/models` +4. 宿主 `/v1/chat/completions` + +## 如何解释常见现象 + +### 现象 A:`/accounts/:id/models` 正确,但 `/v1/models` 返回 403 +优先判断: +- 普通用户 key 没绑定 group +- subscription 场景用错了原始 probe key +- subscription 分配或 allowed_groups 未完成 + +### 现象 B:`/v1/models` 返回 GPT 系模型,而不是目标国产模型 +优先判断: +- account `credentials.model_mapping` 是否落库 +- channel 是否同时具备 `model_mapping + model_pricing + restrict_models + billing_model_source=channel_mapped` +- 是否误打到了旧 CRM 进程 + +### 现象 C:`/v1/models` 已 200,但 `/v1/chat/completions` 返回 502 +优先判断: +- host 侧 provider 兼容性问题 +- 上游 provider key/quota 问题 +- 不要再把问题回退归因为 CRM 导入或 access closure + +## 当前建议固化到后续文档/脚本的规则 + +1. 所有真实宿主验收结论都要同时记录: + - account 视角结果 + - managed key 视角 `/v1/models` 结果 + - completion smoke 结果 + +2. 任何“MiniMax/DeepSeek 没生效”的结论前,必须先检查在线 CRM 是否为最新提交 + +3. 任何 subscription 验收脚本都不应默认把外部 `access_api_key` 当最终 probe key + +4. 任何 fresh-host 验收脚本都必须参数化: + - `PACK_PATH` + - `CRM_HOST_BASE` + - 目标 Postgres 容器 + - 目标 Redis 容器 + +## 相关证据入口 + +- 当前执行真相:`docs/EXECUTION_BOARD.md` +- 当前收口真相:`docs/PRODUCTION_CLOSURE_BOARD.md` +- 标准操作步骤:`docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md` +- 关键 live 证据: + - `artifacts/real-host-acceptance/20260520_222713_crm18100_live_model_mapping_validation` + - `artifacts/real-host-acceptance/20260521_011544_remote43_minimax_key_import` + - `artifacts/real-host-acceptance/20260521_011717_remote43_deepseek_key_import` + - `artifacts/real-host-acceptance/20260521_064910_completion_smoke_calibration.md` diff --git a/docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md b/docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md index af109b3e..364ffbcc 100644 --- a/docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md +++ b/docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md @@ -1,6 +1,15 @@ # Real Host Acceptance Runbook -日期:2026-05-16 +日期:2026-05-21 + +## 先读哪几份文档 + +1. `docs/EXECUTION_BOARD.md` + - 当前 gate、最新阻断、最新 live 真相以它为准。 +2. `docs/PRODUCTION_CLOSURE_BOARD.md` + - 看是否已经达到可上线口径,以及哪些只是历史 PASS。 +3. `docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md` + - 看已经调通的细节、典型误判点、推荐诊断顺序。 ## 目标 @@ -153,6 +162,23 @@ artifacts/real-host-acceptance// 5. `reconcile` 不返回关键失败 6. `rollback smoke` 成功(若本次需要验证回滚链路) +## 推荐额外落盘的三层证据 + +为了避免把不同语义混为一谈,真实宿主验收建议每次都额外记录下面三层证据: + +1. account 单体视角 + - `GET /api/v1/admin/accounts/:id` + - `GET /api/v1/admin/accounts/:id/models` +2. group/普通用户聚合视角 + - 用真实可用的普通用户 key 或 subscription managed key 请求 `GET /v1/models` +3. completion smoke + - 用同一条普通用户访问链路请求 `POST /v1/chat/completions` + +注意: +- `GET /api/v1/admin/accounts/:id/models` 正确,不等于普通用户 `/v1/models` 一定正确 +- `/v1/models` 正确,也不等于 `/v1/chat/completions` 一定正确 +- 这三层必须分开记证据、分开下结论 + ## 当前门禁解释 - 若以上脚本在真实宿主环境全部通过: @@ -181,3 +207,22 @@ SKIP_ROLLBACK=1 scripts/real_host_acceptance.sh 10. `scripts/import_remote43_provider.sh` 现已内置 remote43 的 subscription 验收补全动作:会根据 import batch 自动解析目标 group,执行“普通用户最低余额补齐 + key/group 绑定 + user_subscriptions upsert + 定向 Redis 缓存失效(auth / balance / subscription)”,并把 SQL / host state 证据写入 artifact 目录。 11. 当 CRM 进程与 operator 到 host 的访问地址不一致时,优先显式设置 `CRM_HOST_BASE`,避免把 CRM 侧探测地址和本地运维隧道地址混用。 12. 对 `Upstream service temporarily unavailable` 一类 502,不要先认定是上游聊天链路故障;先看脚本落盘的 `09-models.headers.txt` / `10-models.body.json`。若 `/v1/models` 已返回了别的 provider 模型集(例如 GPT 系列而不是预期的 DeepSeek/Minimax 模型),先检查普通用户 key/group 绑定,也要检查 CRM 导入时是否把 provider 的 `channel_template.model_mapping`、`restrict_models`、`billing_model_source` 一并下发到宿主 channel。 +13. subscription 场景里,closure 最终用于 gateway probe 的 key 是宿主侧 managed key,不一定是请求体里外部传入的 `ACCESS_API_KEY`。如果你拿原始 key 直打 `/v1/models` 收到 `403 not assigned to any group`,先不要判定 CRM 主链路失败。 +14. 对“既有 channel 没自动补 `model_pricing`”这类 live 现象,先核对在线 CRM 进程的启动时间与 git 提交时间;之前 MiniMax 的该现象最终被确认是 stale CRM 进程导致,而不是源码缺逻辑。 +15. 当 CRM 切换到本机运行时,`PACK_PATH` 也必须切换到控制面本机可读路径;继续沿用远端 `/home/ubuntu/...` 会触发 `stat pack path ... no such file or directory`,这是验收 harness 参数问题,不是导入业务逻辑问题。 + +## 建议固定执行的快速诊断顺序 + +1. 先看环境 + - CRM 是否是最新提交对应的在线进程 + - `PACK_PATH` 是否是 CRM 本机可读路径 + - `CRM_HOST_BASE` 是否与 CRM 到 host 的实际访问地址一致 +2. 再看宿主落库 + - account `credentials.model_mapping` + - `GET /api/v1/admin/accounts/:id/models` + - channel `model_mapping/model_pricing/restrict_models/billing_model_source` +3. 最后看普通用户流量 + - `/v1/models` + - `/v1/chat/completions` + +若需要背景解释、误判案例和已调通经验,直接看:`docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md` diff --git a/docs/SOURCE_OF_TRUTH.md b/docs/SOURCE_OF_TRUTH.md new file mode 100644 index 00000000..8428d6fc --- /dev/null +++ b/docs/SOURCE_OF_TRUTH.md @@ -0,0 +1,173 @@ +# 文档真相索引 + +日期:2026-05-21 +项目:`sub2api-cn-relay-manager` + +## 当前 Gate 结论 + +当前最新 gate:`BLOCKED` + +原因不是基础导入链路仍未打通,而是: +1. latest-head fresh host 上,DeepSeek / MiniMax 的 import + access closure 已能进入 `subscription_ready` +2. account `credentials.model_mapping`、channel `model_mapping/model_pricing`、managed key 视角 `/v1/models` 都已有 live 证据 +3. 但 latest completion smoke 仍未完全通过: + - DeepSeek:host `/v1/chat/completions` 仍见 `502`,而上游直探 `200` + - MiniMax:上游直探为 `403 insufficient_user_quota` +4. 因此当前不能宣称 `APPROVED` + +一句话: +- “模型暴露与 access closure 已基本打通”是真 +- “真实 completion 可完全放行”还不是真 + +## 当前真相文档(按优先级排序) + +### 1. `docs/EXECUTION_BOARD.md` +用途: +- 当前执行状态 +- 最新 gate +- 剩余阻断 +- 最短 closure path + +解释规则: +- 这是“当前执行真相”的第一来源 +- 当其他文档与它冲突时,以它为准 + +### 2. `docs/PRODUCTION_CLOSURE_BOARD.md` +用途: +- 当前是否可按 PRD 首版范围放行 +- 哪些是代码门禁,哪些是外部/环境门禁 +- 哪些历史 PASS 不能再直接当当前真相 + +解释规则: +- 这是“上线收口真相”的第一来源 +- 若与历史评审/历史任务板冲突,以它为准 + +### 3. `docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md` +用途: +- 真实宿主验收的标准步骤 +- 需要收集哪些 artifact +- 需要验证哪些层级的证据 + +解释规则: +- 它定义“怎么验收” +- 不直接定义当前 gate,当前 gate 仍以上面两份板为准 + +### 4. `docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md` +用途: +- 已调通的细节 +- 高频误判点 +- 推荐诊断顺序 +- 经验性解释 + +解释规则: +- 它解释“为什么之前会误判、现在应该怎么查” +- 用来辅助 runbook 和执行板,不单独决定 gate + +## 次级文档(仍有价值,但必须通过当前真相文档解释) + +### `docs/PRD.md` +- 定义 PRD 首版范围 +- 只能回答“目标应该是什么”,不能单独回答“现在是否已完成” + +### `docs/TDD_PLAN.md` +- 定义测试设计与实现计划 +- 不能单独代表当前真实验收状态 + +### `docs/DEPLOYMENT.md` +- 用于部署说明 +- 若与当前执行板冲突,以执行板/收口板为准 + +### `docs/KNOWN_LIMITATIONS.md` +- 用于记录仍存在的限制 +- 需要通过当前执行板一起理解,避免把旧限制当作已关闭或反过来 + +### `docs/plans/2026-05-12-sub2api-cn-relay-manager-implementation-plan.md` +- 说明最初实现路径 +- 是设计/计划文档,不是当前状态文档 + +### `docs/2026-05-12-sub2api-cn-relay-manager-solution.md` +- 说明方案设计与宿主接口认知 +- 可作为背景,但不能直接当当前 gate 依据 + +## 历史快照文档(只可参考,不可作为当前实现真相) + +### `docs/2026-05-18-PRODUCTION_READINESS_REVIEW.md` +- 性质:历史审查快照 +- 用途:回看当时发现了哪些系统性问题 +- 禁止用法:不能直接引用其中的 `REJECT / CONDITIONAL_APPROVED` 作为当前 gate + +### `docs/2026-05-18-PRODUCTION_REMEDIATION_TASK_BOARD.md` +- 性质:历史整改执行板 +- 用途:回看 2026-05-18 那一轮整改任务是如何收口的 +- 禁止用法:不能直接拿其顶部 gate 文字当当前状态 + +## artifact 解释规则 + +### 当前优先证据 +优先看最新一轮、且与 latest-head / fresh host 对齐的 artifact: +- `artifacts/real-host-acceptance/20260520_222713_crm18100_live_model_mapping_validation` +- `artifacts/real-host-acceptance/20260521_011544_remote43_minimax_key_import` +- `artifacts/real-host-acceptance/20260521_011717_remote43_deepseek_key_import` +- `artifacts/real-host-acceptance/20260521_064910_completion_smoke_calibration.md` + +### 历史参考证据 +以下可证明某个阶段“曾经打通过”,但不能直接代表当前真相: +- `artifacts/real-host-acceptance/20260518_redeploy_matrix` +- `artifacts/real-host-acceptance/20260518_reconcile_hostscope_self_service` +- `artifacts/real-host-acceptance/20260518_reconcile_hostscope_subscription` + +### 证据解释红线 +1. 不能把 `/api/v1/admin/accounts/:id/models` 与 `/v1/models` 混为一谈 +2. 不能把 `/v1/models = 200` 自动推导成 `/v1/chat/completions = 200` +3. 不能在未核对在线 CRM 进程版本时,就把 live 现象归因为源码仍有缺陷 +4. 不能把 direct probe artifact 的 401/403 单独当成产品主链路失败;先核对 probe key 语义与脚本参数 + +## 当前执行红线 / 非回归规则 + +1. channel 创建与纠偏必须同时覆盖: + - `model_mapping` + - `model_pricing` + - `restrict_models=true` + - `billing_model_source=channel_mapped` + +2. subscription 场景的 gateway probe 语义必须保持: + - 最终 probe key 是宿主 managed key + - 不是外部原始 `access_api_key` + +3. 任何 live 结论都必须先确认: + - 在线 CRM 进程启动时间 + - 对应 git 提交 + - `PACK_PATH` + - `CRM_HOST_BASE` + +4. 真实宿主验收必须分三层落证据: + - account 视角 + - managed key / 普通用户 `/v1/models` 视角 + - completion smoke 视角 + +## 推荐阅读顺序 + +### 想知道“现在到底是什么状态” +1. `docs/SOURCE_OF_TRUTH.md` +2. `docs/EXECUTION_BOARD.md` +3. `docs/PRODUCTION_CLOSURE_BOARD.md` + +### 想继续做真实宿主验收 +1. `docs/SOURCE_OF_TRUTH.md` +2. `docs/REAL_HOST_ACCEPTANCE_RUNBOOK.md` +3. `docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md` +4. 再看最新 artifact + +### 想回顾为什么会演化成现在这样 +1. `docs/SOURCE_OF_TRUTH.md` +2. `docs/2026-05-18-PRODUCTION_READINESS_REVIEW.md` +3. `docs/2026-05-18-PRODUCTION_REMEDIATION_TASK_BOARD.md` +4. 再回到 `docs/EXECUTION_BOARD.md` 看最新真相 + +## 明确禁止的错误读法 + +- ❌ 把历史 review/task board 当当前 gate +- ❌ 把历史 PASS artifact 当当前 latest-head 真相 +- ❌ 把 `/v1/models` 通过当成 completion 已通过 +- ❌ 把 subscription 场景原始 `access_api_key` 当成最终 probe key +- ❌ 把 harness 参数错误(`PACK_PATH`、容器目标、probe auth)当成产品源码失败