你的事故时间线显示“过去 72 小时内没有部署”。你的 Prompt 注册表也印证了这一点:prompt v37 已经冻结三周了。你的评估工具链在周二晚上运行正常。但在周三早上,你其中一个 Agent 的结构化输出失败率翻了三倍,另一个 Agent 的重试预算翻了一倍,而第三个 Agent 开始愉快地忽略一条它已经遵守了一个月的指令。什么都没变。除了确实有东西变了,而且变在组织中两边都没盯着的地方:模型。
Prompt 注册表记录 Prompt 版本。模型网关记录模型版本。但在实践中,几乎没有人追踪这两者的“配对”关系。Prompt v37 并不是一个独立的工件——无论你的工具链是否承认,它都是一份针对特定模型协商后的契约。当平台团队将 claude-sonnet-latest 别名向前推进一个补丁版本时,另一端的契约已经被悄悄修改了。由于部署发生在别人的基础设施上,且名称未变,你的事故时间线依然显示“没有部署”。
没人记录的“配对”关系
Prompt 有版本。模型也有版本。你的注册表存储前者。你的网关解析后者。我观察过的几乎所有系统中,唯独缺少了“联合版本(Joint Version)”——即明确声明“我们针对 2026-04-15 的模型快照测试了 prompt v37,并且该配对在 2026-04-22 发布”。如果没有这个,你就是在运行时组合两个独立版本化的东西,而产出结果的是这种“组合”,而非任何单独一方。
这种情况发生的原因是组织架构层面的,而非技术层面的。Prompt 存在于应用团队的仓库中,由他们的 PR 评审和评估套件把关。模型选择存在于平台团队的网关配置中,由另一套评审流程(通常还有另一套评估套件,如果有的话)把关。每一方都对自己拥有的东西进行版本控制,但没有哪一方拥有这两者的“交叉乘积”。因此,当一方变动时,另一方甚至不知道自己已经被迫发生了变动。
“别名(Alias)”是其中的关键细节。大多数生产系统不会调用像 claude-sonnet-4-6-20250929 这样固定的模型快照。它们调用的是 claude-sonnet-latest,或者是网关侧映射到“本季度选定的任何模型”,又或者是平台团队拥有的环境变量。这些抽象都在履行抽象的职责——隐藏变化的名称——但代价是应用侧对变化变得不可见。在模型变动的那天,Prompt 团队没有任何 Diff 可以查看。
为什么“没有部署”是错误的判断
当平台团队将别名从 4.6 快照升级到 4.7 快照时,应用团队会看到一个更严格遵循指令、更看重最新训练数据、且可能在未明确约束的输出格式上产生细微差异的模型。由于应用团队没有推送代码、没有提升 Prompt 版本、CI 也没有运行,这一切都不会出现在部署日志中。然而,一个外部工件已经被替换进产生行为的“组合”中了。
这并非假设。别名升级引发的事故是 2024 年以来构建的 LLM 系统中最常见的生产故障模式之一。它有一个固定的形态:一个稳定的端点名称解析到了新快照,新快照比旧快照更严格地执行 Prompt 指令,曾经用来掩盖旧模型怪癖的指令现在反而限制了新行为,系统在针对旧特性微调的评估套件上发生了退化。评估工具链没坏,模型从大多数指标来看也更好了,但这个“配对”却是未经测试的。
关于这种失效模式,我见过最清晰的表述是:每一个 Prompt 都是一份与其调试时的模型达成的隐性契约,而“切换模型”就是对该契约的重新谈判,无论是否有人将其记录为契约变更。 将这种变动称为“模型升级”掩盖了它在 Prompt 视角下的本质,即:交易对手变更。
Prompt 并非可移植的资产
人们普遍假设 Prompt 是可移植的——认为只要在供应商 SDK 上加一层薄薄的抽象,就可以像更换数据库驱动程序一样更换模型。这是一个范畴错误。数据库驱动程序隐藏的是传输(Transport)的差异,底层的 Schema 才是契约。模型抽象层隐藏的是传输的差异,而 Prompt 才是契约。仅仅因为传输格式变得可移植,并不代表契约也随之变得可移植。
团队在针对候选替代模型进行并行评估(Side-by-side Eval)时会发现这一点。在现有模型上得分超过 95% 的 Prompt,在原始能力相当的竞争对手模型上可能只得 80 多分。这并非因为新模型更差,而是因为该 Prompt 包含了针对特定对象长达数月的累积微调。新模型在擅长什么、不擅长什么、倾向于过度解释什么、格式化不足什么方面有着不同的分布。Prompt 就像是为一把锁定制的钥匙。另一把锁虽然有着同样的用途和安全等级,但形状却不同。
这还涉及到一个相关问题:Prompt 沉积(Prompt Sediment)。在 Prompt 的生命周期中,你的团队会为了应对特定的失败而不断添加语句——例如在收到用户投诉后加入防御性的“不要包含代码块”,在下游解析器报错后加入“务必以该格式响应”,在发生幻觉事故后加入“不要推测”。这些句子中的每一句都是针对特定模型特定失效模式的修复。新模型可能并没有那个失效模式,于是这句话就成了隐形的累赘,甚至更糟,成了抑制新模型能力的活跃约束。除非你针对当前的生产流量运行“配对”评估,否则这一切都是不可见的。
将 (prompt, model) 元组视为一个单元 实际的解决方法是停止假装这两个版本是相互独立的。通过以下几项改变的组合,可以弥合这一差距。
在调用处同时固定(Pin)双方。 在应用团队可控的边界处解析一次别名(alias),并在记录 Prompt 版本的同一个 artifact 中记录快照 ID(snapshot ID)。当网关滚动(roll)别名时,你的应用程序仍会继续调用其固定的快照,而你将升级固定版本视为一个深思熟虑且可评审的变更。这个固定版本(pin)就是 Prompt 团队了解其背后的基础环境发生变动所需的差异(diff)。
评估元组,而非单一轴。 你的评估套件(eval suite)报告的结果应当以 (prompt_version, model_snapshot) 为索引。针对 (v37, 2026-04-15) 的绿色运行(通过测试)并不能证明 (v37, 2026-05-22) 也是合格的。这听起来显而易见,但几乎从未被强制执行。大多数评估系统要么存储了 Prompt 版本却忘记了是哪个模型产生的得分,要么存储了模型却忘记了当时生效的是哪个 Prompt。这个“笛卡尔积”才是交付的单元,因此这个“笛卡尔积”也应该是被评估和记录的单元。
让别名切换成为应用可见的事件。 当平台团队计划将 claude-sonnet-latest 从一个快照切换到另一个快照时,别名滚动应该向应用团队的评估流水线触发通知,并设定一个期限,在别名正式切换前,要求针对新快照重新运行其套件。这样,模型升级在消费方团队侧就有了部署日志条目,这正是故障响应时寻找上下文的地方。
将 Prompt 沉积(prompt sediment)追踪为模型特定的。 当你因为某个特定模型的错误表现而向 Prompt 中添加防御性指令时,请注明你观察到该错误行为的模型快照。在每次模型升级时,这些注释就成了消融实验(ablation)的候选列表。那些针对旧快照才起作用的指令在被假设对新快照有效之前,应该重新进行测试。携带“死指令”的成本会体现在每一次调用中,直到永远。
在升级过程中加入“这是否仍然有效”的检查。 在你接受模型升级之前,采样你过去一周的生产流量,并通过候选的 (prompt v37, new model snapshot) 组合重新运行。对比输出差异(Diff)。这个差异就是即将交付的内容,也是你的团队在移动固定版本(pin)之前应该评审的产物。
事故时间线会告诉你该监控(instrument)什么 在发生这类事故后,最有价值的问题不是“我们本该做些什么不同的调整?”,而是“什么样的数据能让这件事在时间线上显现出来?”答案几乎总是对“元组”的记录——即事故行中缺失的那一列,它本可以让替换行为变得清晰可辨。一旦元组进入了时间线,其余的都会顺理成章:警报、评估闸门、消融轮换、别名切换评审窗口。
更深层次的转变是将 Prompt 和模型视为一个交付产物的两半。如今,大多数团队交付 Prompt 却是租用模型,而每当房东装修时,租金就会以回归(regressions)的形式支付。只要租赁条款明确且装修有通知期,这种安排是没问题的,甚至是不可避免的。真正受害的是那些从未写下租约、甚至从未意识到自己有租约的团队,他们只有在时间线显示“无部署”但指标却出现异常时才后知后觉。
这项工作并不光鲜。它只是表中的一列、配置中固定的快照、Slack 频道中的一条通知、别名切换前的一次评估重跑。这些都不属于新基础设施。这一切最终都是为了承认:Prompt v37 从来不是独立的——你交付的版本始终是 (v37, some specific model),而这个元组的后半部分一直在你不知情的情况下变动。
会员专享
余下内容仅对会员开放。 会员可读完整内容 —— 每一个公开发布的观点背后,那些框架、决策与推理的全貌。
— 完整文章,包含未公开存档的部分 — 可落地的工作框架,附带权衡与决策依据 — 新文章抢先看,先于公开发布 登录以继续阅读→ 随时取消 · 一次订阅,畅读全部