部分 AI Agent 工具从供应商服务器动态拉取指令块(分钟级刷新),导致相同代码每次运行行为可能不同,调试复现的基础被悄然移除。
可复现性是每次调试循环的底座。一个正在 AI Agent 工具链中蔓延的模式正在抽走这块底座——而且没有告知你。
可复现性是一份让调试成为可能的契约:相同的输入、相同的代码、相同的输出。你复现一个 bug,bisect 它,修复它,然后通过复现其 absence 来确认修复。移除可复现性,整个循环就变成了猜测。一个目前在 AI Agent 工具链中司空见惯的模式恰恰就是这么做的。
现代 AI Agent 工具会发布一个看起来静态的客户端,但并非所有引导模型的指令都驻留在那个客户端里。相当一部分运行上下文是在运行时从供应商的服务器上获取的,合并到模型的行为指令中,并通过一个短周期定时器刷新——大约每分钟一次——从一个供应商控制的特性标志服务。
所以你的 Agent 实际运行的程序是:你的代码,加上你的配置,再加上一个服务器提供的指令块,其内容可以在一次请求和下一次请求之间发生变化,由一个不是你的人决定,出于你不会看到的原因。在一个被充分记录的真实案例中,这个机制随一个版本发布,其 changelog 称其为内部基础设施工作,没有面向用户的变化——同时却向一个有 shell 访问权限的 Agent 注入了行为指令。公开的 bug 讨论串延续了二十多条评论,但没有任何修复。
三个特性,一个可复现性杀手
Remote(远程) — 引导来自供应商一侧,所以你无法像锁定依赖那样锁定它。对于别人控制的服务器,没有 lockfile 可用。
Fast(快速) — 大约 60 秒的刷新周期意味着两次运行相同的提示词,间隔几分钟,就可能基于不同的有效指令执行。可复现性契约被悄然撕毁。
Invisible(不可见) — 被注入的块通常不会写入会话记录中。当你回溯一个录制好的运行时序,那些塑造模型行为的指令在记录中是缺失的。你在用一份你不知道被编辑过的日志进行调试。
把这三个特性放在一起,你就会得到一个无法复现、无法在日志中看到、无法归因的 bug。你的 Agent 在周二表现不同了。你的代码没有变。你的配置没有变。在一个可复现的系统中,"所有输入相同,输出却变了"是一个矛盾,指向你理解中的缺口。在远程注入下,这不过是"又一个周二"——矛盾是真实的,但不是你能解决的。
你没法让别人控制的服务器停下来。但你可以停止对它的盲目依赖,缩小它触及的表面。
捕获组装后的请求并做 diff。 如果你的工具能让你记录线上的完整指令集——不只是你那一部分——每次运行都存储它。当行为发生漂移时,将今天组装的上下文与上周的做 diff。在记录中不可见的块,在线路上是可见的;让变化可见,战斗就赢了一半。
锁定供应商允许你锁定的部分;枚举你无法锁定的部分。 有些引导行为暴露了文档化的开关或环境覆盖;有些则是纯服务器端的。对于你依赖的每一层,写下谁能改变它、你如何发现、以及当它变化时你会怎么做。
将你的保证转移到你自己掌控的地基上。 如果某种行为必须稳定——安全护栏、委托策略、拒绝边界——不要把信任交给一个租来的、远程引导的层。把保证放进你自己的编排逻辑里,放在模型输出周围的你自己的确定性检查中。让模型漂移,不要让保证也跟着漂移。
监控行为,因为你无法监控指令。 唯一可靠的信号表明引导块发生了变化,就是 Agent 的行为发生变化。为你关心的结果埋点——委托率、拒绝率、任务完成形态——并对漂移告警。你会在数字里比在其他任何地方更早看到它。
为什么这不仅仅关乎一个工具
这不是某一个供应商的一次糟糕发布;这是租用他人地基的结构性后果。随着更多的技术栈变成服务器引导和远程可变的,更多东西在构造上就变得不可复现——而可复现性从来都不是可选项。它是根因分析、诚实的事故报告、以及向客户承诺他们付费的东西明天和今天行为一致的必要前提。
我们大多数人会继续租用,因为拥有完整地基代价高昂,而对大多数工作负载来说租用确实是正确的选择。进入时要睁大眼睛:记录你发送了什么,锁定你能锁定的,将你的保证转移到你自己掌控的地基上,注视行为因为你无法注视指令。租来的地基不是稳定的地基,无论今天它有多好——当它移动的那天,你希望从你的监控里听到,而不是从客户那里。
完整带源码引用的版本在配套 gist 中。
如果你在构建必须明天和今天以相同方式行为的 Agent 系统——或者你想看看有意图地拥有整个基础设施栈是什么样的——我在 Pulsed Media 提供支持与基础设施服务。在芬兰我们自己的数据中心、自己的硬件上的 Seedboxes 和存储。开源平台(PMSS,GPL v3),1Gbps 或 10Gbps,欧盟管辖,14 天退款保证。