← 工程笔记

ENGINEERING NOTE · 2026-10

别让 AI 空口说"改好了":给 Agent 项目装三道闸门

当写代码的是 AI、验收的也是 AI,"完成"这个词就失去了含义。这是一套用真实事故换来的流程。

问题的形状

Agent 写代码有一个人类团队没有的问题:它的汇报和它的改动之间没有因果约束。"已修复,验证通过"这句话,可以是真干了三百步验证,也可以是它根本没打开过那个文件。而会话会结束、上下文会压缩、模型会更换,上一轮立的规矩,下一轮一无所知。

我的解法是把"完成"变成一个有硬定义的东西:

完成 = 改动 + 可复跑的验证证据 + 写进文档的增量学习。三者缺一,不算完成。

支撑这个定义的是三道闸门。每一条背后都有一起事故。

闸门一:改的人不能自验

事故回放:一次材质改动,执行改动的会话自己报告"页面正常、效果达成"。我另开一个独立会话去复核,第一件事就是截图——全黑。改动把渲染管线弄挂了,而"自验"的那份报告里没有一个字提到画面。

为什么会话自己验自己必然失真?因为它知道"应该"看到什么。读一段渲染日志,它会用意图补全证据;而独立验收者没有这个包袱,空输出就是空输出。

规矩:任何视觉/行为类改动,必须派另一个不共享上下文的 reviewer 走一遍取证流程——截图、抓 console、逐条对账。回执里禁止出现"应该/看起来/没问题",只许出现读数和文件路径。

闸门二:量具要先过"阳性对照"

这条是最贵的学费。一个材质普查脚本报告"所有建筑材质健康:都有贴图"。如果信了,后面所有决策都建立在坏数据上。救场的是一个反问:

量具自己证明过它读到了东西吗?

去查原始数据才发现:调色板式贴图的"一格平色摊满一面墙",在"有没有 map"这个判据下就是"健康"。判据本身是坏的。换成连续量(每米贴图像素数)之后,同一批建筑的读数是 0。

从此量具合格线定成六要件,其中最核心的是两条:

  • 阳性对照——拿一个已知真值喂给量具,读数必须对得上。例:地面石砖一格 1.11m × 512 贴图 ⇒ 期望 ≈461 texel/m,量具读出 460±,这把尺子才有资格去量别的东西。
  • 判据写死 + 不可能值即红——面积 8.29 亿平方米、密度 1173 万这类数字必须当场报错,而不是等人肉眼看。后来真有一个 mesh 面积爆表,sanity 闸门直接拦下,避免了一整批基于坏数据的"结论"。

闸门三:回归基线 + 逐字节解释

每个性能/体积指标都有基线值(首屏 gzip、draw call、三角形、目录字节数),push 前必跑闸门脚本,超了就要逐个字节解释。

"逐字节"不是修辞。有一次改动让基线涨了 4.5KB,正确的动作是:这 4.5KB 是新模块的代码,进首屏闭包,写进基线注记。而被明令禁止的动作是:把代码压行、内联、注释删掉,让数字"过闸"。糊弄闸门的瞬间,闸门就永久失效了——下一个真的 4.5KB 异常就没人看得见了。

这条闸门最近一次立功:发现基线自己虚高 1.09MB——之前两次贴图降档的提交改了资源却没回写基线,导致后面所有对比都在错基准上做。修基线的时候顺手把"谁改资源谁同批回写"写成了硬规矩。

三条配套纪律(同样用事故换的)

空输出 = 红,不是"没看到"

一次模块里注释写了 */ 提前闭合块注释,整个模块加载失败,自测输出为空。当时的报告写的是"无异常"。从那以后:探针拿不到数据一律标红,禁止用沉默冒充通过。同类事故还有一次——报错只走 console.warn,而探针不转发 warn,一个真实故障被静默吞了两轮。规矩升级为:新代码的每一条失败通道必须被证明"能到眼前"。

回执不许比数据乐观

报告标题必须与逐条结果一致。用截断的 grep 输出当证据、用"基本正常"概括 17 项检查,都算违规。宁可报"3 项无法判定",不许报"通过"。

声称完成之前,先看远端

最朴素也最常犯:commit 了 ≠ push 了,push 了 ≠ 构建完成生效了。验收最后一步永远是直接拉线上产物比对哈希。有一次本地全绿、报告发完,用户刷新页面还是旧版——差的就是这条。

效果:把"信任"从流程里删掉

三道闸门跑起来之后,我和 Agent 之间的协作发生了一个很实在的变化:我不再需要"相信"任何一份汇报。每份汇报的终点都是一个可以被第三方复跑的读数:截图文件、JSON 输出、基线 diff、线上哈希。汇报骗人没关系,闸门不骗人。

这套东西最初是为"AI 写的代码"设计的,跑了一段时间发现它对人类同样适用。所谓工程纪律,不过是把"我觉得好了"翻译成"你复核一下这个数"。