那个在你待办事项中悄悄更改的弃用日期
弃用通知在某个周二送达,停用日期定在六个月后。你的平台团队将其记录在依赖跟踪器中,贴上 “Q3 切换” 标签,并标记为黄色严重度。它与队列中已有的另外两个迁移任务汇合。三周后,供应商在同一个 URL 下修改了日期,没有 diff,没有收件箱通知,只有一段悄悄更新的文字,将停用日期提前了 60 天,直接挪到了你的代码冻结期中间。
你视为规划文档的生命周期页面,其实一直是一个合同闹钟。唯一改变的是它控制着哪个团队的日历——而拥有这个日历的团队并不是你的。
这种模式在各供应商之间惊人地一致。你最高价值业务负载所依赖的模型出现了一个带有未来日期的弃用横幅。你将其记录在案。你根据其他工作进行优先级排序。你按照原始日期所暗示的严肃程度对待它。然后,日期变了,而且变动后的版本成了唯一存在的版本,因为页面是唯一的事实来源,而页面内容刚刚被就地重写。
延期桶是对供应商记忆的赌博
每个工程组织都有一个名为 “延期”、“Q3” 或 “迁移后” 的优先级分配桶,这在功能上等同于 “我们相信这在处理它之前不会出问题”。这种桶适用于内部工作,因为截止日期由你的团队控制且可以重新协商。但对于外部截止日期,它就失效了,因为唯一的协商渠道是供应商的客户经理,而他们没有任何动力给你宽限。
模型弃用通知属于另一个不同的范畴。它更接近于监管截止日期,而非功能需求。供应商内部已经承诺了停用——路线图上已经写明,支撑你模型的 GPU 算力将被重新分配;后续模型的采用指标取决于削减前代模型的资源;某个财务项目取决于在特定季度关闭旧架构的推理。发布的日期只是供应商内部已经缩小范围的软期限。将其视为规划提示是一种分类错误。
修复方案是将弃用通知通过团队处理严格外部截止日期的任何管道进行分发。如果你的团队有 SOC 2 审计准备的运行手册,那么弃用通知也应该进入同样的运行手册。切换关口应该被命名为弃用公告发布的那一天,而不是你乐观假设它会保持不变的日期的前一周。
页面即记录系统,且支持就地编辑
软件工程师有一种本能的预期,即重要的文档应该是带版本的。但大多数供应商发布的生命周期页面并不符合这种预期。只有一个 URL。该 URL 下的内容是可变的。弃 用列表的修改没有公开的变更日志,没有提交历史。在许多情况下,当停用日期更改时,甚至没有邮件通知——弃用初次发布时的邮件被认为是足够的,而后续修正则是无声的。
将生产决策锚定在这样一个架构细节上是令人惊讶的。依赖日期的团队并不拥有该日期所在的页面。拥有该页面的团队将其视为可以为清晰起见而修改的营销类文档。两个团队对于该字段的稳定程度有着截然不同的先验假设,而这些假设之间的鸿沟正是生产事故产生的根源。
缓解措施是机械化的:定期轮询页面,对比内容,并在弃用表中的任何字段发生变化时发出警报。轮询频率应该是天,而不是周——每天一次是合理的,如果业务负载依赖于一个弃用成本极高的模型,每小时一次也是偏执但合理的。对比目标应该是整个表,而不只是你当前使用的模型行,因为行的出现和消失都很重要。当警报触发时,行动不应该是 “在下一次同步会议上讨论”。行动是打开运行手册,根据新日期重新评估切换关口。
标准集上的评估分数并不代表切换授权
缩短的截止日期迫使切换计划压缩。原计划两周的 A/B 流量测试崩塌为周末的双写和周一早晨的切换。团队需要绿灯,而产生绿灯的产物是评估套件。评估套件返回了一个高于阈值的数字。切换获得授权。
评估套件测量的是固定分布。它是根据编写标签时(几周或几个月前)存在的流量模式和意图组合构建的。生产流 量是一个不断移动的实时分布,而破坏客户信任的部分很少是中位数——而是长尾部分,即那些查询聚集在评估集采样不足的输入空间区域的客户群。
这里的两种失败模式是独立且累积的。评估集是快照,因此它落后于实际分布。评估集也是一种经过策划的表现形式,因此它对标注人员认为乏味、模糊或难以评分的部分代表性不足。一个在评估集上得分接近的模型,可能会在特定客户群上产生三倍的错误率,而这些客户的查询在评估集中采样不足。捕捉到这一信号的不是评估分。信号是按客户群细分的生产错误指标,在切换当天及之后的一周内进行监控。
补救措施是使用来自业务负载服务的每个客户群的分层样本来增强评估集,并向非典型查询的细分群体倾斜。从弃用公告发布之日起,将 1% 的生产流量通过替代模型进行预切换试运行,可以在强制切换前为评估集提供六个月的真实分布反馈。运行这种试运行的团队会在第一周发现长尾回归,而不是在切换后的第六天。
错误率翻了三倍的客户分群
有一种特定的失败模式值得被命名,因为它已经发生过不止一次。切换在周一发布。到周二,核心错误指标看起来正常。到周五,依然看起来正常。到了下周三,某个单一客户的支持工单量超过了阈值,并呼叫了支持值班人员,后者将其升级给工程团队,工程团队发现该客户的特定工作流自切换以来,其故障率一直是之前的三倍。
该客户的流量在总流量中占比极小,以至于其三倍的 错误率并没有波动聚合指标。而聚合指标正是被监控的对象。仪表盘中虽然存在分群细化数据,但并未包含在告警路径中。从切换到触发告警之间的这六天里,该客户的用户一直在经历糟糕的体验,而团队中却无人知晓。
这里的缓解措施是配置切换仪表盘,使其针对每个分群的回归(per-cohort regressions)触发告警,而不仅仅是针对聚合指标。对于代表重要合同价值的分群,阈值应该更加严格;对于那些流失信号比获取成本更难恢复的分群,阈值则应进一步收紧。模型更换后的第一周是这些告警最应该保持敏感的窗口期,因为那是影响最大的回归浮出水面的时候,而且团队的注意力已经处于动员状态。
生命周期页面是供应商的权威系统
从具体事件中抽身出来,你会发现一个令人不安的架构现实。你运行的生产系统依赖于存储在某个权威系统(system of record)中的日期,而这个系统并不归你的团队所有,它归属于一个供应商,其动力是优化他们自己的路线图,且其修改日期的流程并不包括通知你。那些不按计划轮询页面的团队,实际上构建了一个依赖于供应商“记得通知他们”的发布日历。而供应商并不会每次都记得。
这与早期软件时代让团队头疼的每种供应商依赖关系如出一辙——在“小版本升级”中发布破坏性变更的 SaaS API,在未更新允许列表的情况下更改 IP 段的 CDN,以及废弃政策仅在发布说明中寥寥一句话的第三方 SDK。这些领域中成熟的模式同样 适用于此:轮询真相源,在每次轮询时进行比对(diff),对每次变更发出告警,并将告警视为重新评估计划的信号,而不是可以忽略的噪音。
内化了这一点的团队不再将废弃页面视为计划文档,而是将其视为由供应商操作的控制平面。控制平面在供应商选择的时间发送信号。团队的工作是在信号实际发送的时间尺度上接收这些信号,而不是在团队的季度规划周期所偏好的时间尺度上。
在提交废弃通知的当天构建切换闸口
杠杆率最高的一项改变是结构性的。一旦你所依赖的模型收到了废弃通知,当天就会发生三件事:创建一个指明切换闸口(cutover gate)的运维手册(runbook)条目;配置一个回退流量预演,将一小部分生产流量路由到替代模型中;并添加一个轮询任务,监视生命周期页面中该行的任何字段变更。这些工作都不需要等待下个 Sprint。这些工作都不会被分选到延期池中。
运维手册条目是在迁移积压工作重组中幸存下来的产物。它指明了具体的评估分群、具体的仪表盘、具体的切换窗口以及具体的回滚流程。它由特定的人员而非团队负责,因为延期池中往往塞满了团队拥有的、但无人负责的工单。轮询任务能捕捉到悄无声息的修正。预演则积累了标准评估集无法产生的长尾信号。
这一切的成本都很低。而不这样做的代价是,一个错误率翻了三倍的客户分群经历了六天糟糕的体验,而核心指标却纹丝不动。供应商的生命周期页面将 继续就地更新。供应商将继续在不通知的情况下修改日期。团队唯一能控制的变量是:变更是在发生当天就被记录下来,还是在支持工单量迫使你翻开页面的那天。
- https://platform.claude.com/docs/en/about-claude/model-deprecations
- https://www.anthropic.com/research/deprecation-commitments
- https://developers.openai.com/api/docs/deprecations
- https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/model-retirements
- https://openai.com/index/retiring-gpt-4o-and-older-models/
- https://vertesiahq.com/blog/your-model-has-been-retired-now-what
- https://news.ycombinator.com/item?id=37866550
