AI 编程工具出问题时不应批量换血,提出每次调整应是单一变量的有界实验,需有基线、可见成本和明确决策点,否则堆叠变更无法诊断回归。
一次糟糕的运行之后。团队更换了模型、重写了系统提示词、添加了一个 MCP 服务器、升级了测试工具包,还改了上下文规则。重新运行之后通过了。
所有人都松了一口气,但这次运行几乎什么都没教会这个团队。没人知道究竟是哪个改动起了作用,新的配置是否带来了更高的运维成本,或者它能否在另一个任务中存活下来。回滚变得很别扭,因为五个可变部分现在彼此依赖。
AI 编码栈需要变更控制。每一次调整都应该是一个有边界的实验:要有基线、一个命名变量、可见的人和机器成本,以及最终的决策。
升级本质上仍是一种配置实验
模型只是栈的其中一层。Provider 配置、测试工具包版本、系统指令、MCP 工具、仓库记忆、上下文选择、审批规则和沙盒策略都可能改变最终结果。
当多层同时变动时,一次成功的运行无法告诉你哪一层带来了改进。同一堆变更也会让之后的回归更难诊断。你只是用一种不确定的配置换取了另一种。
从一个小型任务集开始,这些任务要从团队实际执行的工作中选取。一个有用的基线任务有已知的起始状态和可观察的结果。这可能意味着一个指定的测试变绿了、某个浏览器流程在特定视口下行为正确,或者一个补丁满足了现有 API 契约且没有触碰无关的包。
忘掉那个通用基准测试。选择一个能暴露这个配置变更所要解决的失败场景的任务。
在切实可行时,一次只改一层。如果一次迁移强制模型、Provider 适配器和提示词格式一起变动,将它们记录为一个捆绑包。这个捆绑包仍然可以评估,但它无法支持关于哪个内部变更值得褒奖的主张。
保留决策,而不是只保留最新配置
大多数配置文件描述的是栈的现状。它们很少说明它为什么存在、它取代了什么,或者上次变更用了什么证据来证明。覆写文件也在覆写团队的运营记忆的一部分。
一个小的、只追加的栈变更记录可以解决这个问题。保留之前的状态,将修正作为后续条目添加,并记录观察是在何时做的、配置是在何时生效的。格式可以保持简单:
hypothesis: 这个变更应该减少哪种具体失败?
baseline_task: 哪个固定任务和起始状态将被比较?
variable: 哪个层或明确命名的捆绑包在变动?
acceptance_checks: 什么样的可观察结果算成功?
outcome: 相对于同样的检查,发生了什么?
machine_cost: 墙钟时间、请求数、计费用量,以及当可用时的缓存行为
human_cost: 引导时间、审查时间,以及重构工作
regressions_and_uncertainty: 什么坏了,什么没有测量,什么削弱了比较?
decision: keep | revert | inconclusive
把这当作一个工作模板。它让下一个决策可解释、可逆;它并不假装是某种行业标准。
记录不应该替团队做决定。证据可以支持一个选择,但不会变成自动执行规则。一个测试结果可能证明某种行为在某种设置下有效;它不能证明这个配置对每个仓库任务都更好。
不要让变更后的栈给自己打分
在运行新配置之前先写好假设和验收检查。否则很容易去检查智能体恰好产生的内容,然后事后发明一个成功标准。
将运行前计划、执行产物、审查和最终解读分开。变更后的栈可以收集命令输出、diff、追踪记录和浏览器观察结果。它不应该把自己自信的总结变成通过条件。
这种分离也让失败变得有用。一次没有通过验收检查的运行,可能仍然会暴露一个损坏的工具边界或上下文问题。保留那个证据,然后让审查者判断它是否能解释这个失败。将执行和判断折叠进一个聊天记录中,会让那个有说服力的最终段落与底层事实竞争。
检查点可以保持小巧。执行之前,记录需要一个假设、一个干净的基线和验收检查。在做出 keep 决策之前,它需要结果补丁或行为、支撑它的证据,以及相对于原始检查所做的审查。缺失证据应该产生一个 inconclusive 结果,而不是一篇解释为什么这次运行可能成功了的故事。
计算仪表盘忽略的成本
一个配置可能生成更多代码,却让工作流变得更糟。
机器成本不只是 token 总量。记录经过时间、请求数、计费用量、重试次数,以及当 Provider 暴露时的缓存行为。某个 Codex-on-Bedrock 配置最近的一个问题报告说,当所需的缓存控制不可用时,缓存写入支出意外地高。那份报告属于一条 Provider 路径,而不是每一种编码智能体设置,但它说明了为什么模型名称和总的 token 计数对配置工作来说太粗粒度了。
人的成本更容易被隐藏。统计花在引导运行、检查其声明、以及重构审查者不再理解的代码上的时间。某位从业者的描述是 AI 辅助输出增加了,但审查变得更加耗费认知、代码理解也变弱了。那是个人经验而非受控的生产力结果,但这个成本类别值得在团队自身的工作中追踪。
审查者的注意力随任务难度和熟悉度而变化,所以虚假的精确度没有帮助。像"审查者不得不追踪三个无关的包来验证这个补丁"这样的简短笔记,可能比一个捏造出来的分数更有用。这个比较只需要足够的细节来表明新设置是否把工作从智能体转移回了人类身上。
用真实的仓库任务运行比较
假设一个团队想要改变其单体仓库的上下文选择策略。假设是:一张生成的依赖图将减少目标包之外的不正确编辑。
团队从一个干净提交中选取一个已知的分页回归。验收检查已经可用:契约测试必须通过、现有的查询数量限制必须保持、补丁不得修改 API 路径之外的包。模型、测试工具包、工具和审批设置保持不变。只有上下文策略在变化。
现在记录中有了一些具体可比较的东西:
两种配置是否满足了同样的契约测试?
是否有任何一次运行编辑了无关的包或需要手动修复?
每次运行用了多少次重试和计费请求?
审查者理解并验证每个补丁花了多长时间?
缺失的遥测或环境差异是否让比较变得薄弱?
这些问题不会崩溃成一个通用分数。它们支撑的是一个本地决策。如果新策略通过了检查且降低了审查重构成本,而没有引入另一项成本,团队就有理由保留它。如果它在基线处理过的某种行为上失败了,就回滚它。如果环境在中途发生了变化或者 Provider 省略了所需的成本数据,将结果标记为 inconclusive,然后仅在预期收益证明时间投入合理时才运行一个更干净的测试。
对更大的迁移使用同样的纪律。命名捆绑包、冻结可以冻结的部分、保留旧配置、收窄主张。一次成功的 Provider 迁移可以证明采用那个捆绑包是合理的,而无需证明其中每个模型、提示词和缓存设置都是最优的。
每次变更都以决策收尾
配置实验容易拖延。一个新的 MCP 工具保持启用状态,因为它可能以后有帮助。一个更长的提示词存活下来,因为一次运行看起来不错。第二套测试工具包被保留着,而团队在争论该用哪一个。每个未解决的实验都在运营环境中增加了又一个分支。
用三种决策之一来关闭记录:
保留这个变更,因为它在声明的结果上击败了基线,且没有不可接受的回归。
回滚它,因为它没有通过检查,或者将太多成本转移到了审查者或基础设施上。
将其标记为 inconclusive,因为比较不够有力,无法支撑任一选择。
不要仅仅因为"感觉更好"就保留一个变更。把那个观察放进笔记,然后设计一个能够暴露疑似改进的任务。如果预期收益太小以至于不值得再测一次,旧的稳定配置默认胜出。
同样要给调优工作设定时间盒。当前的开发者讨论描述了模型、测试工具包、MCP 和提示词的优化成为了一份独立的工作。那是轶事情绪,但失败模式很容易识别:栈消耗了它本应节省的时间。
开篇那个例子中的团队本应该保存其起始配置、命名失败、改变一层,然后用同样的检查和成本比较重新运行。没有那条记录,一次通过的运行只是一个令人愉悦的结果。它不是栈改进了的证据。
如果一个配置无法在真实工作上击败一个稳定基线,就回滚它。否则下一次失败将比上一次更难解释。
AI 没有让我变成一个更差的程序员。它让我变成了一个更差的审查者。
Codex Bedrock 缓存控制问题 #37674
谁在优化 AI 技术上花的时间比使用它还多?