Agent 产线的校验脚本:覆盖范围本身要写进清单

2026-08-25

我自己维护着一条把「一批选题」变成「一批已入库文章」的流水线。每篇文章一个实现者 Agent 加一个独立评审 Agent,人不在中间逐篇看,只在开工前定规格、收工后验收。验收那一步靠一组校验脚本:字数、死链、格式、孤儿页,这些都能机器判。

有一次我新建了一个聚合页,把一批文章挂进去。脚本跑完全绿,我就当它完事了。

绿灯下面藏着的那一处

我这条产线上,新建一个聚合页不是只做页面本身。除了聚合页,还要登记到一份映射表,以及一份侧边栏配置。这三处各管一段连接关系。其中侧边栏那一处管的是最外面那一层:读者从导航里能不能看见这个板块的入口。另外两处具体各自承担什么,跟这篇要讲的事没关系,我不展开。

那一次我漏的就是侧边栏那一处。

漏掉的后果不是报错,是一个更安静的状态:聚合页存在,能打开,文章也都在上面列着。唯独从导航侧边栏进不去。也就是说,只有已经知道这个板块存在的人才找得到它。而校验脚本从头到尾没有任何抱怨——它是绿的,而且是真绿,不是脚本坏了。

我先查的不是那一处漏登记

这一处不是脚本发现的。我没法把功劳记在这道门上,因为它当时确实全绿。

具体是哪一天、在什么场景下撞见的,我没有留下记录。我不打算在这里补一段像样的经过——这条本身就跟这篇要讲的事同构:没有记录就是没有记录,不能用一段完整的叙述把它填上。

能确认的只有一件事:它是在脚本之外被发现的。从这一点往回反推(下面这句是反推,不是当时的记录):这类问题的暴露方式只剩一种,就是有人真的从读者的入口走了一遍,而不是看了一眼结果颜色。脚本永远只会告诉你它检查过的东西怎么样,走一遍才会告诉你缺了什么。这也是为什么我现在不再把「跑一遍校验」当成验收的全部动作。

真正有价值的动作发生在那之后。问题暴露的时候,最自然的反应是马上把漏登记的那一处补上,然后翻篇。我当时没有先做这个,我做的第一件事是去问另一个问题——

这道门为什么没拦住?

于是我去读了那个校验脚本,看它到底在检查什么。答案很快就出来了:它只覆盖了前两处,也就是聚合页本身和那份映射表。侧边栏那一处,它压根就没看。

这个发现比漏登记本身重要得多。漏登记是一次性的,补上就没了;脚本只覆盖前两处,是一个会持续骗我的状态。只要我不去读脚本源码,我就会一直以为「脚本全绿」等于「该登记的都登记了」。而这一次之所以被撞见,纯属运气——如果那个板块的入口没人立刻用到,它可以就这么绿着躺很久。

这也是为什么我建议复盘的时候别急着修问题本身。修问题是几分钟的事,问「这道门为什么没拦住」才是把这次事故换成一条长期收益的地方。

为什么绿灯会被读错

先说明这一节是我事后的反推,不是当时留下的结论。

脚本没写错,它只是没长。一个校验脚本是按写它那天的结构写的,而需要登记的地方会随着结构变化往上加,脚本不会自己跟着加。两者一旦脱节,脱节的那部分就永远处在没人看的状态。

问题出在信息的位置。脚本的覆盖范围,只存在于脚本代码里。除非我打开源码逐行读,否则我看到的只有一个绿色的结果。而绿色这个信号本身不携带边界信息——它说的是「我检查过的这些没问题」,我读到的却是「没问题」。这两句话之间那段差额,就是脚本没覆盖的部分,它恰好是最容易出事的部分,因为没有任何人在看。

这件事在多 Agent 的产线上比在纯人工流程里更危险。人工逐篇过稿的时候,人是带着不完整的检查清单在看的,但人有一种模糊的余光,会顺手注意到清单外的异样。批量化之后这层余光就没了:实现者 Agent 的视野是单篇文章,评审 Agent 的视野也是单篇文章加规格,跨文件的登记关系不在任何一个 Agent 的视野里。写文章的和审文章的都不会注意到导航里少了个入口——那不是它们的活儿。整条线上唯一有可能看见它的,就是脚本这一层。而这一层的边界是隐性的。

我这条线上还有一处同类的情况可以对照:构建的时候用过滤器指定要处理哪个部分,如果过滤写法用错了形式,匹配不到目标时它不会报错,会正常退出并返回成功。屏幕上是一个漂亮的绿色,实际什么都没做。表现形式不一样,机理是一样的——成功状态描述的是「这次执行没有出问题」,不是「你想让它做的事做到了」。

清单里多了一列

改法不复杂,甚至有点土。

第一步是把登记点从「我记得有哪几处」变成一份显式清单,写进新建聚合页的操作说明里,一处一行。

第二步才是关键:清单的每一行后面加一列,标注这一行由脚本覆盖,还是需要人工确认。侧边栏那一行标的是人工确认;哪天这一处真被脚本覆盖了,再把这一行改成脚本覆盖,而不是反过来先假定它被覆盖了。

第三步是顺序约定:以后再多出新的登记点,先改清单,再决定要不要改脚本。清单是权威,脚本是清单的一个子集,而不是反过来把脚本当成事实的全集。

这一列加上去之后,绿灯的含义就变了。以前我看到绿灯的心理活动是「都查过了」;现在是「清单上标了脚本覆盖的那些查过了,剩下标人工的还得我自己看」。同样一个绿色,读出来的信息完全不同,而差别只在于旁边有没有那一列。

这个改动还有个我没预料到的好处:那一列会逼我面对「有些事脚本本来就查不了」。以前这类事项处于一种悬空状态——不在脚本里,也不在任何文档里,只在我脑子里;现在它们至少有个明确的位置,标着「人工」。

脚本查得出什么,查不出什么

顺着这一列往下想,我给自己划了一条更粗的界线。

脚本能查的是客观量:字数够不够、链接死没死、格式对不对、有没有孤儿页、该登记的登记了没有。这些都有确定的判定方法,机器判得比人准,也不会累。

脚本查不出的是事实对不对。一段话里的某个说法是否属实,脚本看不出来。它数得出这篇有多少字,看不出这些字里有一句是错的。

这两类检查不能互相顶替,这是我在这条线上认得比较清楚的一件事。客观体检全绿不代表内容可信,事实核对做完也不代表格式没塌。它们是两道并列的门,不是一道门的两种叫法。我这条线上还有几条同类的口径,都是同一个道理:中日韩字数必须用按 Unicode 区间计数的方式来数,用按字节计的命令行工具会明显虚高,字数门就形同虚设;链接有效性我只认扫描构建产物的那个脚本,Agent 凭阅读判断链接死活,说死的和说活的都会判错。

每一条口径背后,都是某一道门曾经绿着但没起作用。

能带走的那一句

如果这次只能留下一句话,是这一句:

校验脚本的覆盖范围本身要写进清单,否则「脚本通过」会被误当成「全部检查过」。

它可以再拆细一点,方便当场自查:

  • 你的自动检查,边界写在哪里?如果答案是「在代码里」,那它对使用结果的人来说等于不存在。
  • 新增一个需要检查的点时,先改的是清单还是脚本?先改脚本,清单就会永远落后于现实。
  • 一个绿色结果摆在面前,你能不能马上说出它覆盖什么?说不出来,就说明你在拿它当全集用。

放到 Agent 产线上,这条还有一层:当人从流程中间退出去之后,「没人看见」不再是偶发状况,而是默认状态。规格、独立评审、脚本这三层,各自的盲区都要显式写下来,因为不会再有一双余光帮你兜底了。

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

延伸阅读

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