Files
sub2api-cn-relay-manager/docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md
2026-05-23 17:34:53 +08:00

38 KiB
Raw Blame History

真实宿主验收经验与已调通细节

日期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
    • 对既有 channelCRM 需要走 UpdateChannel 做纠偏;这一点已在 latest-head fresh rerun 上确认生效
    • 旧现象“MiniMax channel 有 model_mapping 但没有 pricing”已经被 ca1d448 修复并完成 live 验证
  3. subscription 场景的真实 probe key 语义已经确认

    • closure 最终用于宿主 /v1/models 探测的,不是外部传入的原始 access_api_key
    • 真正使用的是 CRM 在宿主侧创建/查找出来的 managed keysk-relay-* 风格)
    • 因此 subscription 验收如果直接拿调用方原始 probe key 去打 /v1/models,出现 403 not assigned to any group 并不代表 CRM 主链路失败,而是 probe key 用错了
    • latest-head 当前实现已把 artifact 语义拆开:
      • requested_probe_api_key 记录调用方传入原始 key
      • effective_probe_key_source=managed_subscription 记录实际 gateway probe 来源
      • effective_probe_key_fingerprint 记录实际 probe key 指纹
      • probe_api_key 只继续保留给 self_service,不再在 subscription closure 里复用
    • 2026-05-23 的干净本地 fresh-host 验收 artifacts/real-host-acceptance/20260523_local_clean_minimax_subscription_probe_semantics 已再次证明这层语义修复生效:
      • closure 里 requested_probe_api_key=sk-raw-probe-20260523b
      • effective_probe_key_source=managed_subscription
      • 不再出现 legacy probe_api_key
      • 同一轮 raw key 直打宿主 /v1/models/v1/chat/completions 仍都是 403 permission_error
    • 这轮 provider 最终仍是 completion_status=429,说明剩余阻断是 MiniMax 官方 upstream rate limit不是 probe key 语义再次混淆
    • 继续在同一 fresh-host 上补的 MiniMax M2.5 缩圈验证,已经把 429 -> 503 的因果链单独坐实:
      • 单独只打一条 MiniMax-M2.5-highspeed 时,真实结果是 upstream 429,见 artifacts/real-host-acceptance/20260523_local_clean_minimax_m25_only_probe
      • 连续第 1 次打 M2.5 时仍是 429
      • 紧接着第 2 次、第 3 次再打同一模型,会变成宿主 503 Service temporarily unavailable
      • 对应宿主日志显示:第一次有 account_id=1upstream_status=429,后两次只剩 account_select_failed error=\"no available accounts\"
      • 因此 M2.5503 不是模型自身固定返回 503,而是唯一账号被前一次 429 打进临时不可调度窗口后的宿主侧结果,见 artifacts/real-host-acceptance/20260523_local_clean_minimax_m25_repeated_probe
  4. self_service 场景的 gateway probe 认证语义已经确认

    • 真实宿主的普通用户 gateway key 访问 /v1/models / /v1/chat/completions 时,使用的是 Authorization: Bearer <gateway-key>
    • 不能把这条普通用户 gateway key 当成宿主管理 API key 再塞进 x-api-key
    • latest-head 最后一个真实阻断就是这里CRM 的 CheckGatewayAccess / CheckGatewayCompletion 之前错误地把 self_service 的普通用户 key 放进了 x-api-key
    • 修复后latest-head self_service 标准 fresh-host 验收 artifacts/real-host-acceptance/20260521_210403 已真实收口到 self_service_ready
  5. 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 是否通过

宿主源码再次确认的设计逻辑

这部分不是基于 artifact 推断,而是直接对照 sub2api-official-fresh 宿主源码确认:

  1. channel admin handler 的真实入参契约

    • backend/internal/handler/admin/channel_handler.go
    • model_mapping 的真实结构是 map[string]map[string]string
    • model_pricing 是独立数组字段,不会从 model_mapping 自动推导
    • billing_model_source 合法值包括 channel_mapped
    • restrict_models 是独立布尔开关
  2. channel pricing platform 为空时,宿主会回退到 anthropic

    • create/update handler 都会在入参 platform 为空时补默认值
    • repository createModelPricingExec 也会把空 platform 写成 anthropic
    • 这意味着 CRM 若不给 OpenAI-compatible provider 显式写 platform宿主会按 anthropic 语义处理,不能接受
    • 因此 CRM 当前策略必须是:
      1. 先用 provider platform
      2. 若调用侧仍为空,再回退 openai
  3. gateway /v1/models 与 completion 共享同一套 API key middleware 前置校验

    • backend/internal/server/middleware/api_key_auth.go
    • 它先校验:
      • key 有效
      • user active
      • IP 限制
      • group / subscription / balance 前置
    • 所以 /v1/models 的 403/429 通常首先反映的是 key/group/subscription/balance 约束,而不等同于 account/channel 落库失败
  4. subscription group 的 key 绑定条件与 standard group 不同

    • backend/internal/service/api_key_service.go
    • standard groupuser.CanBindGroup(...)
    • subscription groupGetActiveByUserIDAndGroupID(...)
    • 也就是说subscription 场景里“group 已存在”或“allowed_groups 已写入”都不够,必须有 active subscription
  5. user 自助创建 key 与 admin 绑定 group 是两步

    • backend/internal/handler/api_key_handler.go + api_key_service.go
    • user 侧 POST /api/v1/api-keys 可创建带 custom_key 的 key
    • CRM managed key 流程里,先以普通用户身份创建 key再用 admin PUT /api/v1/admin/api-keys/:id 绑定 group
    • 这与我们当前 EnsureSubscriptionAccess 的两阶段实现一致

已调通的宿主侧前置动作

self_service

至少满足:

  1. 普通用户已真实创建
  2. 普通用户 key 已生成且可用
  3. 该 key 已绑定目标标准 group
  4. 用户具备可用余额

经验结论:

  • 若只做了 key/group 绑定但没余额,/v1/models 可能从 403 进入 INSUFFICIENT_BALANCE
  • 这不是 CRM 导入逻辑失败,而是宿主运营前置未完成
  • 若普通用户 key 直打宿主 /v1/models / /v1/chat/completions 已经 200,但 CRM 的 self_service closure 仍显示 401/403 broken,优先检查 CRM 的 gateway probe 是否错误地复用了 x-api-key 语义,而不是先怀疑宿主前置

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
    • 如果看到“有 model_mappingmodel_pricing=[]”,不要立刻判定 current-code 仍未执行 UpdateChannel;先核对该 artifact 是否本来就是旧进程产物

MiniMax model_pricing=[] 误判的已确认根因时间线

  1. 旧进程先创建了半成品 channel

    • 证据:artifacts/real-host-acceptance/20260520_222713_crm18100_live_model_mapping_validation/summary.json
    • 其中 MiniMax host_channel.data.id=5
    • model_mapping 已有值,但 model_pricing=[]
    • created_at=updated_at=2026-05-20T20:39:23Z
    • 这说明当时只是旧逻辑创建了 channel没有发生后续 UpdateChannel 纠偏
  2. 新代码已经具备纠偏能力,但必须由新进程实际执行

    • ca1d448 之后,代码路径已改为:
      • 新建 channel 时直接携带完整 model_pricing
      • 命中既有 channel 时执行 UpdateChannel
    • 所以判断“修复是否生效”时,不能只看仓库 HEAD必须看 18100 监听进程的真实启动时间与实际 DB
  3. 当前 18100 新进程已在 live host 上完成纠偏

    • 18100 新进程启动时间:2026-05-21 01:08
    • 当前真实 DB/tmp/sub2api-relay-manager-realhost-18100.db
    • 当前 host admin 直查 GET /api/v1/admin/channels/5 可见:
      • model_pricing 非空
      • model_mapping 仍正确
      • updated_at=2026-05-21T06:45:00Z
    • 这证明新进程已经真正执行过 UpdateChannelMiniMax 既有 channel 已被纠偏
  4. 最终结论

    • “MiniMax channel 有 mapping 但无 pricing”不是 current-code 仍缺失 UpdateChannel
    • 真相是:旧 artifact 反映的是旧 CRM 进程产物;切到新进程并 fresh rerun 后,该问题已被 live 修复
  5. PACK_PATH 使用了 operator 机器的概念路径,而不是 CRM 进程本机可读路径

    • 当 CRM 改在本机运行时,继续传远端 /home/ubuntu/... 会直接触发 stat pack path ... no such file or directory
    • 这个报错属于验收 harness / 环境参数问题,不是 import 业务逻辑问题
  6. remote43/fresh-host 的 Postgres/Redis 容器目标写错

    • 若脚本仍打到旧 relaymgr 宿主,会看到 managed user / key / subscription 状态为空或串台
    • 需要确保脚本明确指向 fresh host 对应的 {postgres,redis} 容器
  7. fresh-host bearer token 过期时,最前面的 host 注册/探测也会伪装成 CRM 侧 502

    • latest-head self_service 收尾时,脚本最前面的 POST /api/hosts 曾直接返回 502
    • 继续往里看upstream detail 才显示 TOKEN_EXPIRED
    • 这类现象不要先误判成 CRM 新代码挂了;应先刷新 fresh-host 管理员 bearer token再继续验收
  8. /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 失败

优先判断:

  • host provider 兼容性
  • 上游 key/quota
  • 不要先回退归因为 CRM 导入失败

现象 D普通用户 key 直打宿主 /v1/models/v1/chat/completions 都是 200但 CRM 的 self_service access/status 仍是 broken

优先判断:

  • CRM 的 gateway probe 是否错误使用了 x-api-key 而不是 Authorization: Bearer
  • 当前 online CRM 进程是否真的已经切到包含该修复的新二进制

2026-05-22 ~ 2026-05-23 多次反复出错后的最终收敛记录

这一节专门记录“不是一次性修掉,而是经过多轮误判、切环境、换宿主版本、补控制面自愈后才真正收口”的问题。后续再遇到相似现象,优先回看这里,不要重复从零推理。

1. Kimi A7M /v1/models 正常但 /v1/chat/completions 长期失败

最终确认这不是单一问题,而是两层问题叠加。

  1. 第一层是宿主把 OpenAI-compatible API key account 默认判成可走 /responses

    • 表象:
      • upstream 直打 /v1/chat/completions = 200
      • 经宿主转发后 host /v1/chat/completions = 502
      • body 常见为 Upstream access forbiddenservice temporarily unavailable
    • 真正根因:
      • 宿主把 openai_responses_supported=true 错写到 account capability
      • managed chat 请求被错误走成 Responses 兼容分支
      • 对 A7M 这类只稳定支持 raw chat-completions 的链路,会直接被上游拒绝
  2. 第二层是宿主升级后 capability 误判会再次出现

    • 即使手工在宿主里把 openai_responses_supported=false 调对,后续异步 probe 或宿主升级仍可能覆写回错误值
    • 所以“只修宿主代码”不够稳,控制面必须有自愈
  3. CRM 侧最终收口策略

    • access closure 首次确认时,如果看到:
      • account probe = API returned 403: Forbidden
      • host completion = 502 upstream_error
      • body 含 service temporarily unavailableno available accounts
    • CRM 会自动把对应 account 的 openai_responses_supported=false 写回宿主,然后立即重试一次 completion
    • 后台 reconcile 也复用同一逻辑,所以宿主升级后再次漂移,下一轮 confirm/reconcile 还能拉回正确状态
  4. 已固化的回归层

    • internal/accesscapability repair 判定与重试
    • internal/provision:首次安装后确认自愈
    • internal/reconcile:宿主升级后的后台持续自愈
    • 因此以后若再看到 “A7M /models 200 但 completion 502”应先确认自愈逻辑是否触发而不是先怀疑 pack 或 subscription 链路

2. host_base_url / stale CRM 进程 / fresh-host 容器串台 导致的假回归

这类问题在这轮里反复出现多次,而且表面上都像“代码修了但线上还是老问题”,实际是环境指向错了。

  1. stale CRM 进程

    • 典型表象:
      • 仓库代码已经包含修复,但 live artifact 仍表现为旧逻辑
      • 最典型的是 “MiniMax channel 有 model_mappingmodel_pricing=[]
    • 真相:
      • 旧 artifact 反映的是旧进程创建的 channel
      • 新代码只有在新进程真的启动并重新跑过 import/update 后,宿主数据才会被纠偏
  2. host_base_url 用成 operator 侧概念地址

    • 典型表象:
      • stat pack path ... no such file or directory
      • host 注册/导入看似失败,但实际上是 CRM 进程所在机器根本读不到该路径或访问不到该宿主地址
    • 真相:
      • host_base_urlPACK_PATH 都必须以 CRM 进程本机视角解释
      • 不能混用 operator 机器、remote43 主机、fresh-host 容器内部这三种地址空间
  3. fresh-host Postgres/Redis 指到了旧容器

    • 典型表象:
      • managed user / subscription / key 状态看起来全部缺失
      • 或者 reconcile / group state 结果和当前验收宿主不一致
    • 真相:
      • harness 查的不是目标 fresh-host 的数据库,而是旧 relaymgr 或别的 fresh-host
  4. 最终经验

    • 在判定“当前代码是否失效”前,必须先确认:
      1. CRM 在线进程启动时间
      2. CRM 实际提交版本
      3. PACK_PATH 是否对 CRM 本机可读
      4. CRM_HOST_BASE 是否真的是 CRM 到宿主的地址
      5. Postgres/Redis 容器是否属于目标 fresh-host

3. models-only ready 假阳性已经关闭,后续不能再按旧经验验收

这条误判在前几轮里也反复出现,必须明确写死。

  1. 旧误判方式

    • 只要宿主 /v1/models 命中 smoke_test_model,就把 access 状态记成 ready
    • 这会把“普通用户 key / group / subscription 前置已完成”与“真实 completion 可用”混为一谈
  2. 真实问题

    • /v1/models = 200 只能证明访问链路和宿主前置成立
    • 不证明上游 completion 一定可用
    • 在 DeepSeek、Kimi、MiniMax 的真实验收里,这一点都出现过
  3. 当前收口后的真相

    • access ready 必须同时满足:
      • /v1/models 命中 smoke_test_model
      • 最小 POST /v1/chat/completions smoke 成功
    • access closure、import runtime artifact、reconcile rerun payload 现在都会持久化 completion 结果
    • 因此后续任何人看到 latest_access_status=ready,都可以默认它已经经过 completion 层验证
  4. 回归建议

    • 若以后再改宿主 gateway 适配或第三方 provider capability不要只验 /v1/models
    • 至少要一起看:
      • host /v1/models
      • host /v1/chat/completions
      • access closure details 里的 completion_* 字段

进一步缩圈DeepSeek chat/completions 当前更像宿主兼容层问题,而不是 key 失效

2026-05-21 新增的直接证据链:

  1. managed key 直打 fresh host 仍稳定失败

    • http://127.0.0.1:18097/v1/models = HTTP 200
    • http://127.0.0.1:18097/v1/chat/completions = HTTP 502
    • 说明普通用户 / subscription / key / group 绑定链路不是这一步的主阻断
  2. 同一台 remote43 主机直打 upstream 反而成功

    • https://aitoken.quanfuli.cn/v1/chat/completions
    • 使用同一 upstream key、同一 deepseek-v4-flash payload
    • 返回 HTTP 200
    • 但响应 Content-Typetext/event-stream
  3. fresh-host app 日志显示 host chat 会在一组重复 DeepSeek accounts 间 failover全部记成 account_upstream_error 500/502

    • 当前 group 5 里有 10 个 active DeepSeek accounts14,15,16,17,19,20,23,25,26,28
    • 它们 credentials.api_key/base_url/model_mapping 相同
    • 请求并不是命中一个固定坏 account而是在重复 account 集合中轮流失败

当前最合理的解释:

  • DeepSeek 这条 completion 阻断已经缩到“宿主 chat 上游兼容/解析层”
  • 不是 CRM 没把模型、channel、subscription、managed key 准备好
  • 重复 account 不是唯一根因,但会把一次失败放大成整组 failover 噪音,增加生产不稳定性

进一步缩圈MiniMax 当前是 quota 阻断,不是 CRM 路由阻断

  1. managed key 视角 /v1/models 已 200
  2. upstream 直探 /chat/completions = 403 insufficient_user_quota
  3. fresh-host group 6 内 6 个 active MiniMax accounts 的 temp_unschedulable_reason 都明确记录了 insufficient_user_quota

因此:

  • MiniMax 当前要解的是“换可用 key / 补额度”
  • 不应继续把它归因为 CRM import/access 逻辑失败
  • 而且要区分两层失败:
    • 第一次 completion 失败是真实 upstream 429 insufficient_user_quota / rate_limit
    • 同一账号冷却窗口内的后续 completion 失败,可能退化成宿主 503 no available accounts
  • 20260523_local_clean_minimax_m25_only_probe20260523_local_clean_minimax_m25_repeated_probe 已证明:429 和后续 503 不是两个独立故障,而是同一条账号冷却链上的前后态

当前建议固化到后续文档/脚本的规则

  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 容器
  5. latest-head self_service 验收通过后,如果 reconcile 仍是 drifted,应优先把它解释为 shared fresh-host 的历史残留资源噪音,而不是主链路未打通;判断时先看 05-import.json / 07-access-status.json 的 ready 结果,再看 09-reconcile.jsonsummary.access_status

凭据与可用性判断矩阵

先记住:本项目里最容易混淆的不是 API 本身,而是“看起来都像 key但其实职责完全不同”的几类凭据。

凭据/身份 属于谁 主要用途 正确验证方式 不能直接证明什么 最常见误判
供应链 key / 上游 key 供应商账号 / 中转账号 写入 host account credentials.api_key,供宿主向上游 provider 发请求 1. account 创建成功 2. POST /api/v1/admin/accounts/:id/test 成功 3. GET /api/v1/admin/accounts/:id/models 返回目标模型 不能直接证明普通用户一定能看到模型,也不能直接证明 /v1/chat/completions 一定可用 把 account /test 成功误说成“普通用户已可用”
普通用户 key 宿主普通用户 走宿主网关访问 /v1/models/v1/chat/completions 1. key/group 绑定正确 2. GET /v1/models 返回目标模型 3. 推荐继续测 /v1/chat/completions 不能直接证明供应链 account 本身健康 把普通用户 403 直接归因为供应链 key 无效
subscription 原始外部 ACCESS_API_KEY 调用方传入的外部 probe key subscription 请求输入,可能只用于触发流程,不一定是最终探测 key 不能单独用它判断最终 gateway closure必须先确认是否被 managed key 覆盖 不能直接代表最终 subscription 场景的普通用户访问结果 拿它直打 /v1/models 收到 403就误判 CRM 主链路失败
managed key (sk-relay-*) CRM 在宿主侧创建/查找的托管普通用户 key subscription 场景最终 gateway probe / managed 普通用户访问 1. managed user / key 已创建 2. group/subscription 已绑定 3. GET /v1/models 返回目标模型 4. 推荐继续测 /v1/chat/completions 不能直接证明上游 provider 一定有 quota 或 host completion 一定兼容 把它和外部原始 ACCESS_API_KEY 混为一谈
host admin token / bearer 宿主管理员 创建 group/channel/plan/account、分配 subscription、读取 admin API 看 admin API 是否能成功执行管理动作 不能直接证明普通用户访问已可用 以为“管理接口全成功 = 普通用户链路也成功”

一眼区分规则

  1. 供应链 key
    • 验证的是“上游供应商 account 是否健康”
    • 不直接验证普通用户访问
  2. 普通用户 key
    • 验证的是“宿主网关路径是否对普通用户开放”
    • 不直接验证上游供应链是否健康
  3. subscription 原始外部 key
    • 只是一种流程输入
    • 不一定等于最终探测 key
  4. managed key
    • 才是 subscription 场景里更接近“最终真实普通用户访问”的 key
  5. admin token
    • 只证明管理面可用
    • 不证明用户面可用

两套最小判断口径

口径 A供应链账号是否成功

看:

  1. account 是否创建成功
  2. /api/v1/admin/accounts/:id/test 是否成功
  3. /api/v1/admin/accounts/:id/models 是否返回目标模型

口径 B普通用户是否真的可用

看:

  1. key/group/subscription/balance 前置是否到位
  2. /v1/models 是否返回目标模型
  3. /v1/chat/completions 是否成功

明确禁止的混用

  • 用 account /test 成功替代普通用户 /v1/models
  • /v1/models 成功替代 /v1/chat/completions
  • 用外部原始 ACCESS_API_KEY 替代 subscription managed key
  • 用 admin API 成功替代普通用户链路成功
  • 看到普通用户 403 就直接判定供应链 key 不可用

FAQ新增模型 / 新增供应链账号 / 普通用户访问

1. “新增模型参数”到底指什么?

这里至少分四层,不能混成一句“模型加上了”:

  1. pack/provider 定义层
    • base_url
    • default_models
    • smoke_test_model
    • channel_template.model_mapping
  2. host 落库层
    • account credentials.model_mapping
    • channel model_mapping
    • channel model_pricing
    • restrict_models
    • billing_model_source
  3. 模型暴露层
    • GET /api/v1/admin/accounts/:id/models
    • GET /v1/models
  4. completion 层
    • POST /v1/chat/completions

经验结论:

  • 前三层正确,不等于第四层一定正确
  • 当前项目最新真相就是:模型暴露层大体已经打通,但 completion 层仍可能受 host 兼容性或上游 quota 影响

2. “新增供应链账号成功”到底以什么为准?

建议区分三档成功标准:

  1. 窄口径成功(只看供应链 account
    • account 创建成功
    • POST /api/v1/admin/accounts/:id/test 成功
    • GET /api/v1/admin/accounts/:id/models 返回目标模型
  2. 完整接入成功(看普通用户是否能看到模型)
    • 上述 account 条件成立
    • 普通用户或 managed key 的 GET /v1/models 返回目标模型
  3. 业务可用成功(看真实调用)
    • 上述条件都成立
    • POST /v1/chat/completions 成功

经验结论:

  • 如果你只说“供应链账号成功”,默认最多只能代表前两档
  • 如果要说“模型完全可用”,必须把 completion smoke 也过掉

3. 新增供应链账号时,系统会不会自动补 group / channel / plan

会,但要分资源类型看:

  1. group
    • 一定会先 ensure
    • 不存在就创建,存在就复用
  2. channel
    • 一定会先 ensure
    • 不存在就创建,存在就 UpdateChannel 纠偏
  3. plan
    • 只在 subscription 模式下需要
    • 不存在就创建,存在就复用
  4. account
    • 最后创建,并绑定到目标 group_ids

经验结论:

  • 新增供应链账号不是“只加 account”
  • 它本质上是“确保资源面完整后再挂 account”

4. 新增模型时,哪些字段必须同时对齐?

至少要对齐:

  • provider base_url
  • default_models
  • smoke_test_model
  • channel_template.model_mapping
  • account credentials.model_mapping
  • channel model_mapping
  • channel model_pricing
  • restrict_models=true
  • billing_model_source=channel_mapped

经验结论:

  • 少了 model_mapping,模型列表可能回退到默认集合
  • 少了 model_pricing/v1/models 可能看起来没问题,但实际聊天流量可能仍失败
  • 只修 account 不修 channel或者只修 channel 不修 account都会留下半通不通的假阳性

5. 如果是“中转 URL / relay URL”而且不在宿主官方已知库里标准会不会不一样

本项目的标准本质不变,但前提是:

  • 这个 provider 必须先在本项目的 pack/provider manifest 中被正确定义

也就是说:

  • “不在宿主官方库里”没关系
  • “没有在本项目 pack 中定义”才不行

只要 manifest 正确提供了:

  • base_url
  • default_models
  • smoke_test_model
  • channel_template.model_mapping

系统就仍然可以自动做:

  • account 创建
  • account /test
  • account /models
  • 普通用户 /v1/models
  • completion smoke如果你把这一步也纳入验收

经验结论:

  • 系统不是“任意 URL 自动猜测器”
  • 系统是“pack/provider 驱动的导入与验证器”

6. 只要 /v1/models 成功,是不是就说明新模型已经完全可用了?

不是。

/v1/models 成功,只能证明:

  • 普通用户或 managed key 访问路径至少已经看到了模型列表

它不能自动证明:

  • provider 上游有 quota
  • host 对该 provider 的 completion 兼容性没问题
  • POST /v1/chat/completions 一定能成功

经验结论:

  • /v1/models = 200 是“模型暴露通过”
  • /v1/chat/completions = 200 才更接近“模型可用通过”

7. 新增供应链账号后,普通用户 key 的 group 信息要不要更新?

通常要,取决于 access mode。

self_service

  • 普通用户 key 必须绑定目标标准 group
  • 如果新模型落在新的 group而 key 没绑定过去,普通用户就看不到或用不到它
  • 若目标 group 是标准计费组,通常还需要余额

subscription

  • 目标 group 必须是 subscription 类型
  • 普通用户必须完成 subscription 分配
  • 普通用户 key 必须绑定该 group
  • 当前 closure 最终优先使用宿主 managed key而不是外部原始 access_api_key

经验结论:

  • “新增了供应链模型”不等于“所有普通用户 key 自动获得访问权”
  • 最终是否能访问,取决于 key/group/subscription 这条链是否同步完成

8. 新增供应链账号后,如果普通用户看不到模型,优先查哪里?

建议按这个顺序查:

  1. account 视角
    • GET /api/v1/admin/accounts/:id
    • GET /api/v1/admin/accounts/:id/models
  2. channel 视角
    • GET /api/v1/admin/channels/:id
    • model_mapping/model_pricing/restrict_models/billing_model_source
  3. 普通用户视角
    • GET /v1/models
  4. completion 视角
    • POST /v1/chat/completions
  5. 环境与运行时
    • CRM 是否是最新提交
    • PACK_PATH 是否正确
    • CRM_HOST_BASE 是否正确

经验结论:

  • 不要一上来就看普通用户 403/502
  • 先查 account 和 channel 落库,更容易快速定位根因

9. 什么时候应该判定是“运营前置没做”,而不是“导入代码失败”?

常见场景:

  1. self_service
    • key 没绑 group
    • 用户没余额
  2. subscription
    • group 不是 subscription 类型
    • user subscription 没写入
    • key 没绑 group
  3. probe key 用错
    • subscription 场景拿外部原始 key 去打 /v1/models
  4. 脚本参数错
    • PACK_PATH
    • 命中错的 Postgres/Redis 容器
    • probe auth 用错

经验结论:

  • 如果 account /models 已对、channel 落库也对,但普通用户流量不对,优先怀疑运营前置或 harness 参数
  • 不要立即重开“导入代码失效”的结论

13. remote43 如果同时有“本地 CRM + 远端宿主 + 远端 DB/Redis”最容易错哪三件事

2026-05-23 这一轮 remote43 kimi-a7m 复验把最容易反复出错的 3 个点彻底暴露出来了。

1) 把 CRM_HOST_BASEREMOTE_HOST_BASE 混成一个地址

  • 本地运行的 CRM 访问宿主时,应该走本地 SSH 隧道,例如 http://127.0.0.1:18089
  • 远端 SSH 内部执行 curldocker exec 时,才应该走远端机器自己能看到的地址,例如 http://127.0.0.1:18097
  • 如果把两者都写成 18097,本地 CRM 会尝试访问自己机器上的 127.0.0.1:18097,结果在 POST /api/hosts 阶段直接掉进 500 internal_error

这类错误的现象通常是:

  • 01a-create-host.json 为空
  • 03-import.body.json 直接是 batch_id=0
  • message 落在 get host versionprobe host capabilities

经验结论:

  • 本地 CRM 到宿主的地址远端 SSH 侧到宿主的地址 必须分开记录
  • 以后若脚本同时涉及 curl CRM APIssh remote curl host API,必须显式区分 CRM_HOST_BASEREMOTE_HOST_BASE

2) 远端 DB/Redis 误指到 relaymgr 数据面

之前 remote43 统一 401 INVALID_API_KEY 的主因不是 provider key 坏,而是:

  • 脚本错误地从 sub2api-relaymgr-pg 里找普通用户 key
  • 但实际宿主是另一套 fresh-host app + postgres + redis

修正后脚本已经改为:

  • 先按目标宿主端口解析远端 app 容器
  • 再自动推导同栈的 postgres/redis

2026-05-23 的 20260523_144937_remote43_kimi-a7m_key_import 已证明这条修正生效:

  • subscription_user_key_prefixmanaged_user_idmanaged_probe_key_prefix 都来自目标 fresh-host 数据面
  • 不再复现统一 401

经验结论:

  • 远端若同时存在 relaymgrfresh-host 两套栈,任何 subscription user / api key / group state / redis invalidation 都必须落到目标宿主自己的数据面
  • 不要再靠固定容器名假设

3) provider status / access status 忘了带 host_id

当本地 CRM 状态库里同一个 provider 已经跑过多个 host 样本时:

  • GET /api/providers/{provider}/status
  • GET /api/providers/{provider}/access/status
  • POST /api/providers/{provider}/access/preview

如果不显式带 host_id,很容易直接返回:

  • provider exists on multiple hosts; host_id is required
  • 外部看起来像验收在最后一步莫名其妙 400

经验结论:

  • 这不是导入失败,也不是宿主坏了
  • 这是 状态查询维度不完整
  • 对带历史样本的 live CRM所有 provider 尾部查询都应该带 host_id

14. 20260523_144937_remote43_kimi-a7m_key_import 到底证明了什么?

这份 artifact 很关键,因为它把“脚本问题”和“宿主问题”拆开了。

它证明了:

  • POST /api/hosts 已成功
  • import 已成功返回 HTTP 200
  • gateway.models=["kimi-k2.6"]
  • has_expected_model=true
  • upstream /models=200
  • upstream /chat/completions=200

同时它也证明:

  • 未改宿主的 host /v1/chat/completions 仍然返回 503 Service temporarily unavailable
  • account probe 仍是 403 Forbidden,但已经只是 advisory / warning不再阻断 import 主链

经验结论:

  • 这份样本可以用来证明:插件脚本的数据面/地址问题已经修掉
  • 它不能用来证明“宿主已经通过”
  • 它应该被归类为:插件侧修复完成,未改宿主 completion 路径仍异常

15. 如果 create-host 阶段突然又回到 500,先查什么?

20260523_145531_remote43_kimi-a7m_key_import 提供了另一类重要样本:

  • 01a-create-host.json 仍成功
  • 03-import.body.json 直接写明:
    • get host version: perform GET /api/v1/admin/system/version request
    • context deadline exceeded

这说明当时不是 provider key 坏,不是脚本回退,而是:

  • 本地 18089 隧道虽然监听着端口
  • 但到远端宿主的链路已经不再返回字节

经验结论:

  • 如果 host tunnel 端口还在监听,但 curl -I --max-time 5 $CRM_HOST_BASE/healthz 无法返回任何 header
  • 那就先把它当成 隧道失活 / 运行时链路问题
  • 不要先把结论写成“导入逻辑回退”或“provider 又坏了”

10. 新增供应链账号或模型后,哪些结果可以算“已确认”,哪些只能算“部分确认”?

可算“已确认”

  • account 已创建
  • account /test 成功
  • account /models 返回目标模型
  • channel 落库包含完整 routing/pricing 字段
  • 普通用户 /v1/models 返回目标模型

只能算“部分确认”

  • 只有 import API 返回成功
  • 只有 batch status 成功
  • 只有 account 创建成功但还没测 /models
  • 只有 /v1/models 成功但没测 /v1/chat/completions

经验结论:

  • “新增模型成功”这句话必须说明你指的是哪一层成功
  • 最容易误导人的说法,就是把“导入成功”直接说成“模型可用成功”

11. 如果新增供应链账号是复用旧 channel而不是新建 channel需要特别注意什么

特别注意两件事:

  1. 旧 channel 不能只复用名字
    • 必须做配置纠偏
    • 至少要补齐 model_mapping + model_pricing + restrict_models + billing_model_source
  2. 不能默认“有 model_mapping 就够了”
    • 这正是之前 MiniMax live 问题踩过的坑

经验结论:

  • 旧 channel 复用比新建更危险
  • 因为它最容易留下“看起来有模型,实际上定价/路由没补齐”的半漂移状态

12. 如果我要把“新增模型/新增供应链账号”做成标准验收 checklist最小应包含哪些项

最小 checklist

  1. provider manifest 已更新
    • base_url
    • default_models
    • smoke_test_model
    • channel_template.model_mapping
  2. import 成功
    • group/channel/(subscription: plan)/accounts 已生成或被正确复用
  3. account 验证成功
    • /test
    • /models
  4. channel 回读成功
    • model_mapping
    • model_pricing
    • restrict_models
    • billing_model_source
  5. 普通用户路径验证成功
    • /v1/models
  6. 业务路径验证成功(推荐)
    • /v1/chat/completions
  7. 若失败,明确归类
    • provider definition drift
    • host compatibility
    • upstream quota/key 问题
    • key/group/subscription/balance 前置问题
    • harness 参数问题

相关证据入口

  • 当前执行真相: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