今天遇到一个很有意思的 502:它不是“某个模型坏了”,也不是“某个用户坏了”,甚至不是“某个通道坏了”。表面看起来大家都走同一条上游通道,有的会话正常,有的会话稳定 502;新建会话也可能中招。
最后定位下来,真正的触发点是会话级的 prompt_cache_key。同一条请求链路、同一个模型、同一个分组、同一个上游通道,只要带上某些特定的 key,上游就返回 Bad Gateway;把 key 改成另一个合法形态,请求立刻恢复 200。
排查过程中几个误导点挺典型:
-
以为是用户差异
现象确实像“某个用户不行,另一个用户行”。但对照后发现,差别不在用户,而在会话携带的 key。用户只是刚好更容易复用到那几个坏 key。 -
以为是通道亲和性缓存
清理亲和性缓存有意义,因为它能排除“请求被粘在坏路由上”。但这次清完以后仍然 502,说明问题不在本地路由缓存,而在请求到达上游之后。 -
以为重启能解决
重启上下游服务只能清掉本地状态;如果上游对某个 key 本身返回 502,重启不会改变这个 key 的命运。 -
以为禁用那条通道就算修好
禁用当然能绕开现象,但业务要求是“这条通道也要能用”。所以真正的修复应该是让坏会话也能通过这条通道,而不是简单移除通道。
最后的办法是做了一个很窄的参数改写:只在 /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,只改必要路径,每次写入都有备份。