一次由会话 key 触发的 502:同一通道里藏着更细的一层状态

今天遇到一个很有意思的 502:它不是“某个模型坏了”,也不是“某个用户坏了”,甚至不是“某个通道坏了”。表面看起来大家都走同一条上游通道,有的会话正常,有的会话稳定 502;新建会话也可能中招。

最后定位下来,真正的触发点是会话级的 prompt_cache_key。同一条请求链路、同一个模型、同一个分组、同一个上游通道,只要带上某些特定的 key,上游就返回 Bad Gateway;把 key 改成另一个合法形态,请求立刻恢复 200。

排查过程中几个误导点挺典型:

  1. 以为是用户差异
    现象确实像“某个用户不行,另一个用户行”。但对照后发现,差别不在用户,而在会话携带的 key。用户只是刚好更容易复用到那几个坏 key。

  2. 以为是通道亲和性缓存
    清理亲和性缓存有意义,因为它能排除“请求被粘在坏路由上”。但这次清完以后仍然 502,说明问题不在本地路由缓存,而在请求到达上游之后。

  3. 以为重启能解决
    重启上下游服务只能清掉本地状态;如果上游对某个 key 本身返回 502,重启不会改变这个 key 的命运。

  4. 以为禁用那条通道就算修好
    禁用当然能绕开现象,但业务要求是“这条通道也要能用”。所以真正的修复应该是让坏会话也能通过这条通道,而不是简单移除通道。

最后的办法是做了一个很窄的参数改写:只在 /v1/responses,只匹配已确认会触发 502 的完整 prompt_cache_key,把它改写成一个新的合法 key。这样不会影响普通请求,也不会扩大到其它接口。

为了以后不用再靠终端手搓,我顺手做了一个“Key Doctor”小后台:

  • 扫最近的 502 请求
  • 从审计日志里提取会话 key
  • 展示候选 key、用户/令牌提示、模型、时间窗口
  • 对候选 key 做 A/B 探测:原 key 请求一次,改写 key 请求一次
  • 只有“原 key 不行,改写 key 200”才允许写入规则
  • 写入前自动备份
  • 页面提供回滚入口

这个事故最有意思的地方是:它逼着人把“通道问题”“用户问题”“会话问题”“请求参数问题”拆开看。只看最终 502,很容易粗暴归因到通道不可用;但一旦做控制变量,就会发现同一通道里还藏着更细的一层状态。

小结一下经验:

  • 对会话型 API,不要只按用户、模型、通道分组排查,也要按会话 key / cache key 分组。
  • “别人同一通道能用”不等于“这条请求的参数组合能用”。
  • 清缓存和重启适合排除本地状态,但不能替代对上游的最小复现请求。
  • 自助工具最好内置探测和备份,不要把“写规则”做成盲点按钮。
  • 匿名化复盘时,保留故障形状就够了,真实域名、地址、账号、通道编号和上游名都不必出现。

最后这个问题的修复不是惊天动地的大改,而是一个很小的、带验证的参数改写规则。越是这种小规则,越需要边界清楚:只修已经证实的 key,只改必要路径,每次写入都有备份。