确认阶段:通过一次,可能只是运气好
这是五阶段系列的第四篇。母篇《从能跑到可交付:一套前端 AI 协作系统的设计笔记 》是总览,上一篇《评测阶段 》讲了评测怎么做:确定性的条目用命令跑,需要判断的条目由评判者看证据打分。这一篇讲确认:评测已经判了通过,为什么还不算完,还要换一个人、把证据全部重做、再判一次。我用的系统是开源的 frontend-harness-cli ,文中规则都能在仓库里找到对应实现。
通过了,又没有完全通过
先看一个具体的场景。
一个功能评测通过了。其中有一条需要判断的条目:页面在数据为空时要显示一个空状态提示。评判者打分的依据是一张截图——截图里空状态好好的,于是打了通过。
问题是,那张截图是上一轮跑的时候截的。后来开发者改过这个组件:空状态的判断条件被挪了位置。改完之后页面在真正没数据时显示的是一个转圈的加载动画,永远转不到空状态。但那张旧截图还躺在证据文件夹里,画面一点没变,评判者对着它打了勾。
这不是谁作弊。没有任何一步是"故意"的:截图是真的,只是过期了;评判者是认真看的,只是它看的证据恰好还成立。这次通过是真实的,也是幸运的——下一次数据、时机、或者评判者读条目的松紧换一个样子,它可能就不过了。而你没办法从"通过"这两个字里分辨:这次到底是真过,还是运气好。
为什么要有第二次评测
评测阶段回答的问题是:“现在这一刻、由这位评判者、看着这批证据,它过不过?”
这个问题有一个隐藏的毛病:需要判断的条目,打分本质上是"读一遍然后判断"。读,就有松紧的波动。同一条"文案是否说清了下一步",评判者心情——准确说是上下文状态——不同,可能一次放过、一次挑出来。再加上证据可能过期、可能恰好对上(测试账号当时正好没数据,空状态"看起来"是对的),一次通过把三件事混在了一起:功能是对的、证据是对的、这次判断是对的。三件里任何一件靠运气成立,总分都是"通过"。
打个比方:学生模拟考考了一次高分。你不能断定他学会了——可能有三道题是猜的。让他换一份卷子、隔一天、由另一个老师批,再考一次还是高分,“学会了"这句话才开始可信。
确认阶段就是第二次考试。它不是把评测重跑一遍——如果只是原样重跑,同样的松紧、同样的旧证据,运气会被原样复制。它和评测有三个机械上的不同,每一个都是冲着一种"运气"去的。
不同一:换一个全新的评判者
确认阶段的评判者,必须是一个全新的上下文,而且不能是产出评测结论的那个上下文。这是硬性要求,不是建议。
为什么?第一个评判者已经"参与"过这个功能了:它看过开发者的修复过程,知道每个条目当初为什么被改,它的打分会被自己的前一次判断锚定——人批第二遍卷子会倾向于同意自己第一遍的批法,模型也一样。更实际的是,它对"作者的意图"是宽容的:它知道这段代码想干什么,就容易替它把含糊的地方脑补圆。
全新的评判者什么都不知道。它只拿到 rubric 和这一轮新生成的证据,像第二个没签过第一份报告的检查员:第一份报告上写了什么,不进入它的判断。它放过的东西,含金量才和第一个人独立。
不同二:证据全部重新生成,旧证据记 0 分
确认阶段开始时,所有需要判断的条目的证据从零重新生成:截图重新截,接口探测重新跑。不许复用评测阶段留下的任何文件。
而且这条不是靠自觉。证据文件带时间戳:任何比上一次评测更旧的证据,自动记 0 分。 不需要谁来指控它过期,系统按时间戳直接判。
为什么要做到这么绝?因为旧证据是最廉价的假通过来源,而且是无声的。证据躺在一个文件夹里,新的一轮如果忘了重新生成,读到的还是上一轮的文件,流程毫无异样地往下走,分数还很好看——开头那个场景就是这么发生的。用时间戳把"旧"变成一个机械事实,等于把这类运气从流程里删掉:证据要么是这一轮的,要么是 0 分,没有中间地带。
不同三:要连续稳定通过,不是一次通过
最后一个不同:功能要达到配置规定的连续稳定通过次数,才算确认完成。中间任何一次不过,计数清零,并且和普通失败一样,退回评测、实现阶段去修。
“连续"两个字是关键。假设一个条目每次判断有不小的概率被宽松放过,那么一次通过说明不了什么;但连续三次、由独立评判者、看着三批各自新生成的证据都通过,“次次都靠运气"的概率就被乘下去了。
连续计数还能抓出另一种东西:抖动。一个条目这次过、下次不过、再下次又过,如果按"取最好成绩"或者"平均分"处理,它会混过去;但计数清零的规则把它当作未完成——这种忽过忽不过本身就是信息:它说明实现或者条目本身有一处不稳定,该修,而不是该祈祷下次过。
至于命令型条目(编译、测试这类退出码说了算的),重跑的结果本来就确定,它们在确认阶段是廉价的复核;上面三条机械差别,火力都集中在需要判断的条目上——波动只存在于那里。
同一个原则,缩小到一个任务
确认阶段的原则缩小到单个任务,就是 verification-before-completion 这份技能管的事:声称"做完了"之前,必须在当前上下文里拿出刚生成的验证证据。
“我改好了"“测试应该能过"“刚才跑过一次没问题”——这些都是声称,不是证据。技能的要求很具体:完成的主张必须附带这一轮真实跑过的命令和它的输出;引用上一轮的运行结果、或者"代码看上去是对的”,都不算数。
这和确认阶段是同一句话的两种尺寸:一个功能不能拿一次通过当证明,一个任务不能拿一句"应该没问题"当回执。“它能跑"是一个主张,主张需要当场出示的收据。
这一阶段管不了什么
把边界说清。
第一,确认提高的是"运气过关"的成本,它不让判断变得绝对可靠。两个相互独立的评判者,终究是同一类模型、读同一份 rubric,完全可能有共同的盲区:比如一个按钮配色丑但每条规则都合规,两个评判者都会放过。这正是美学层至今保留一半人工评审的原因——独立性能消除个人松紧,消除不了集体看不见。
第二,每多确认一轮,都是真实的时间和 token 开销。所以连续通过次数是一个配置项,是拿成本换置信度的权衡,不是越大越好的数字。关键路径上的核心功能可以多设几轮,边角功能一轮就够——把轮数写进配置,而不是写进信仰里。
确认完成之后,功能才有资格进入最后一个阶段:交付——由内核在交付门禁全绿之后置为 done,干活的人依然没有宣布"做完了"的权力。下一篇讲交付阶段。