验证 Agent 产物的手段自己失效了怎么办

2026-08-25

我自己那条产线上,代码这一侧的活是交给 Agent 写的:子代理只写文件,构建和提交我自己串行做。这套分工跑起来之后,我一度觉得代码比内容好管——内容的好坏没有客观真值,代码有:构建过不过、测试红不红,机器说了算。

后来撞了几次才明白,「有真值」恰恰是危险的来源。因为有真值,我和 Agent 都会在看到「通过」的那一刻停止怀疑。而我遇到的那几次事故,问题都不在被检查的代码里,在检查本身里。

四件事,检查都说通过

按时间先后我已经记不清了,但类型很清楚,是四类:

第一类,渲染层绕过了全局处理。 我的内容仓库里有一层全局的链接降级处理,用来把指向「还没写出来的目标」的引用降级掉,免得变成死链。有几条渲染路径没有走这一层,绕过去了。结果是那些前向引用直接变成了死链,而构建照样成功,自动检查什么都没报——因为在构建的视角里,它确实成功了。

第二类,打包后路径基准变了。 有一处写法是依赖模块自身的路径去推导资源目录的。开发的时候完全正常,服务端打包之后这个基准变了,推导出来的目录指向了错误的位置。开发环境看不出任何异常。

第三类,模板注释被计入了正文长度。 长度质量门数的是渲染前的文本,模板自带的注释也被算进去了。这道门于是长期给出一个偏高的数,长期给出的是假结果——它一直在「通过」,只是这个通过不代表任何事。

第四类,验证时看到的是旧代码。 残留的开发服务进程没被终止,我对着它验证改动,看到的是改之前的表现。改对了会以为没生效,改错了会以为没问题,两个方向都会错。

这四件事表面上八竿子打不着:一个是渲染管线,一个是打包,一个是字数统计,一个是进程残留。但它们是同一件事——不是代码错了没被查出来,是查的方法本身已经失效了,却依然返回「通过」

它们是怎么冒出来的

这一段我想写清楚,因为发现方式比问题本身更值钱。

先说一个否定的事实:没有任何一类是被当时正在跑的那道检查报出来的。这几乎是定义性的——如果那道检查能报出来,它就不叫失效了。所以指望「加一道检查去查检查」,在同一个口径下是绕不出来的。

真正让它们冒头的,是口径被换过一次。

字数那件事,是我改用 Python 按 Unicode 区间重新数了一遍才对上号的。原先用的是按字节计的命令行文本工具,中日韩字符按字节算会虚高好几倍,两个口径一对,差得离谱,才回头去看这道门到底在数什么,然后才看到注释也被算进去了。两个独立口径给出的答案对不上,是唯一能从外部戳破一道门的信号,单跑一个口径永远只会得到自洽的结果。

链接那件事同理。我试过让评审 Agent 顺手判断链接死活,它两个方向都会误判——存在的说成死的,死的说成好的。所以现在我这条线上,链接有效性只认扫描构建产物的那个脚本,Agent 的判断一律不作数。这不是说 Agent 判断力不行,是说一道验收口径容不下「两个方向都可能偏」,宁可换成一个不做判断、只做扫描的东西。

至于残留进程和打包路径,暴露的时机都是「产物和过程说的不一样」:直接去看构建出来的东西,和开发时看到的对不上。所以我现在的习惯是,凡是要下结论的验证,要么先把残留进程杀掉,要么干脆绕开开发服务,直接验证构建产物。

同一个规律我在别处也撞到过,形态完全一样:构建过滤器如果用包名式写法,匹配不到目标时会正常退出、退出码是 0,看起来是「构建通过」,实际上什么都没构建。那一次我改成了路径式写法。这一条和上面四类是一码事——退出码 0 不等于事情办成,验证环节自己空转了一圈,还给了你一张通过的条子。

为什么会这样

我的解释是:验证手段自己也是代码,也有前提,而这些前提没有人验证。

一道字数门的前提是「我数的这段文本就是最终正文」。一道死链检查的前提是「我扫的这份产物就是上线的那份」。一次本地验证的前提是「我看到的进程跑的就是我刚改的代码」。这些前提平时都成立,所以从来没人写进任何清单里;一旦某次不成立,门不会报错,它会照常返回一个通过——因为在它自己的世界里,它确实完成了它的工作

这跟被检查对象是不是 Agent 写的没关系,但 Agent 参与之后,后果会被放大。人一次改三五个文件,出了偏差自己看得见;交给 Agent 批量做,一道失效的门会连续放行一整批,等到有人从外面用另一个口径看一眼,问题已经铺开了。

我改成了什么

不是加检查,是给检查定死口径,外加一道人工的抽查。

具体到我这条线上:中日韩字数只认 Python 按 Unicode 区间数出来的值,任何按字节计的工具一律不作数;链接有效性只认扫描构建产物的脚本,Agent 说的不算;构建过滤器一律用路径式写法,不用包名式;要下结论的验证,先终止残留的开发进程,或者直接对着构建产物验。

这些都是很小的规定,但每一条背后都对应一次实际翻的车,所以我把它们回写进了规格里。规格是被当代码维护的,从零想不全,但踩过的必须回写,否则同一个坑会踩第二遍。

比这几条更要紧的是那道人工抽查。我没有取消它,也没有打算取消。抽查的目标不是替机器再查一遍代码——那件事机器做得比我好——而是每隔一段时间,用一个和现有检查完全不同的角度,去看一眼最终产物:随手点开一个页面看链接是不是通的,随手挑一篇用另一种方式数一下长度。它不需要覆盖率,它只需要偶尔独立。

顺带说一句我踩过的相邻的坑:这类检查刚上线的头几轮,工作量几乎全在压误报。误报的代价不是多花时间,是执行者会开始整体忽略这道门,此时门等于不存在。所以我的判据宁可写窄一点、漏一些,也不敢写宽。

能带走的那句话

自动化能覆盖「代码是否正确」,覆盖不了「验证是否有效」。

所以我让 Agent 参与工程的时候,会专门为「验证手段本身失效」这件事留一道人工抽查。这道抽查在系统运转正常的时候看起来完全多余——它几乎每次都告诉你没事,性价比低得可怜。但它是整条链路上唯一不共享那些隐含前提的一环,也是唯一能发现「门坏了」的一环。

判断一道门有没有可能已经坏了,我用的问法只有一个:这道门上一次报出问题是什么时候? 如果它已经很久只输出通过,那它是真的没问题,还是它其实已经不在数了——这两件事从输出上看一模一样。

这篇写的是我自己那套流程里的一次实际情况,不是通行做法。你的场景、工具和团队规模不同,结论未必适用,判断方式可能比结论更值得拿走。

延伸阅读

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。