一次导致所有运行中 Agent 任务失效的 Prompt 热重载故障
传呼机在晚上 11:47 响了。一名客户在退款对话中进行了十分钟,智能体突然停止调用它在整个会话中一直在推理的 process_refund 工具,幻觉出了一个确认码,并结束了聊天。当我们回溯原因时,事后看来显而易见:一位队友在 11:46 推送了更新后的系统 Prompt。推送很顺利,测试通过了,每个新会话都运行完美。但已经在进行中的几百个会话却出问题了。
我们构建的 Prompt 注册中心支持了 2026 年每家 Prompt 版本控制供应商都作为特性推销的功能:无需重新部署的热重载(hot-reload)。我们将这种能力视为 CDN 缓存刷新——一种在全球范围内立即生效的全量替换。但那天晚上我们学到,它实际上是破坏了契约。每个活跃的会话都是 LLM 与一组指令及工具定义之间正在进行的协商。当注册中心在这些对话进行中更换了 Prompt 时,协商好的一半上下文就过时了。
最终的解决办法是停止将 Prompt 版本视为部署时的全局变量,转而将其视为每个会话的契约。与其说这是一个巨大的架构变更,不如说是一个微小的思维转变。
为什么“热重载”看起来安全,直到它出问题
Prompt 注册中心被吹捧为一种将 Prompt 编辑与应用部署解耦的方法。这个卖点极具吸引力:你的 Prompt 工程师修正了一个措辞问题,点击发布,下一个请求就会采用它。无需 CI 运行,无需容器重启,无需回滚仪式。对于单次补全(one-shot completions)——分类、摘要、单轮问答——这个卖点基本上是成立的。请求从注册中心读取最新的 Prompt,调用模型,然后返回。唯一的风险窗口是在读取和模型响应之间,而这个窗口非常短。
智能体在两个方面打破了这种模型。首先,一个智能体的运行不是单次 LLM 调用;它是一个循环。系统 Prompt 和工具 Schema 在该循环的每一轮都会重新发送给模型,因为这是无状态聊天补全 API 的工作方式。其次,LLM 在会话中的推理是路径依赖的。它已经根据三轮前看到的工具、五轮前系统 Prompt 告诉它的角色以及开头确定的输出格式制定了计划。在循环中途更换其中任何一项,你不是在刷新上下文,而是在对模型进行“煤气灯”操纵(gaslighting)。
注册中心的抽象静默地假设 Prompt 是纯输入。在智能体循环中,Prompt 更接近于 Schema。在数据流写入时更改 Schema,正是分布式数据库花了二十年时间才弄清楚如何安全实现的事情。
