那个标题取坏了。
dev.to 上那篇《Three Times My Test Failed and the LLM Was Right》,前几天发的,我读了两遍。案例扎实,复盘也诚实。但我想它会教出一个坏习惯:测试挂了,先信模型。
我的判断是,那三次里只有一次是模型的功劳。另外两次它也对了,但换个办法一样能对,还更便宜、更可靠。三件事混在一起讲,等于把三种运气打包成一种能力。
得拆开。
第一件,GoalDriftDetector。计划是重置密码并发确认邮件,实际动作是 search_kb,测试预期判为漂移。模型说没漂移。理由是重置密码的流程文档就在知识库里,检索是必要前置。这个判断对,对的方式也对——它用上了测试作者没想到的那块领域知识。是模型的功劳,没什么争议。作者对"这个 agent 会怎么干活"的想象,比模型窄。
第二件,成本激增检测器。6 次 a 调 3 次 b,阈值 2.0,作者预期触发,实际没触发。原因:6/9 在 IEEE 754 下是 1.9999999999999998,严格小于 2。检测器没错,是测试假设了精确算术。
这件事里模型干了什么?它算了 6/9。不是判断,是算术。
而 LLM 做算术有多可靠,大家都清楚。这次它对了。你要从这里学出"以后边界值先问模型",迟早有一次它会给你一个圆滑的、听着合理的、错的数字。真正让这个用例露馅的是浮点本身不精确,那行 print 就能看见,用不着一个千亿参数的模型。
第三件,重试风暴检测器。总重试 5 次及以上触发,作者造了 6 次重试、其中 5 次成功的场景,预期不触发。结果触发了。去翻前身项目里的 anomaly-validation-matrix.md——"不区分瞬时错误与系统性错误、不看成功率"是 v0.1.0 就写明的设计限制。
答案在作者自己的仓库里。模型没比作者懂得多,它只是没有作者那份"我以为它该抑制瞬时错误"的错觉。真要说功劳,功劳在那份文档,不在对话。
三件事,三种病:知识缺口、实现细节、文档没读。对策各不相同——补领域知识、在 n+1 处测、写否定用例前先查限制清单。混着叫"LLM 是对的",等于三种病开一副药。
上面把第二件说得有点轻,收紧一点。
不是说模型没用。是有用,但用处不在"当判定者",在"当那个逼你去看 1.9999999999999998 的人"。这两件事差得远。前者你把判断权交出去了,后者你只是被提醒了一下,判还在自己手上。
这个区别在测试上尤其要命。测试的本质,就是把假设写成可被机器检验的形式。假设错了,测试就红。问题在于红了之后有两条路:一条是回头查"我到底假设了什么",另一条是问"这东西到底该不该红"。
第一条慢,但沉淀。第二条快,但每次从零开始。
你可能会反对说,看模型推理又不妨碍自己判,何乐不为。这话对一半。看推理确实不亏,但"看推理"和"把模型当 oracle"是两种姿势——oracle 你只需要接受输出,看推理你得再判一遍。前一种姿势成了习惯,第二次那种浮点情况,你就会跳过那行 print。
我见过太多"AI 说这个断言写错了"就直接改断言的场景。改完测试绿了,但没人知道原来那个断言想守的是什么。
真正值得抄的不是那三次对话,是作者从里面抽出来的两条规则。
数值阈值在 n+1 处测,别在恰好 n 处测。
写否定性用例之前,先翻一遍已知限制清单。
这两条有个共同点:都把隐式的假设变成了显式的清单条目。第一条把"浮点在这附近会抖"写死了,第二条把"这个检测器本来就不管瞬时错误"写死了。写死之后,下一个写测试的人不用再跟模型聊一遍。
所以那篇文章真正的产物不是"LLM 可以当 oracle",是那份清单。
那个附加案例更能说明问题。EmbeddingDriftDetector 拿 agent_name 当键存基线,两个场景都叫 triage,第二个场景就跟第一个的基线比了。修法是改成 triage-ed1 和 triage-ed2。这里模型帮上什么忙了吗?可能有。但真正的修法是把"键要唯一"变成一条规矩。这类坑不会只出现一次。
如果这个判断成立,agent 测试的瓶颈会挪地方。生成用例现在便宜得跟不要钱一样,一个下午能造几百条。但"我们到底假设了什么"这份清单造不快,也没法外包给模型——清单要记的恰恰是模型不知道的事,比如这个团队上个版本刻意没做的东西。
¹ 有个例外得说清楚。领域知识型的失败,就是第一件那种,模型确实能补上人漏掉的部分。这种时候清单帮不上忙,因为你不知道自己不知道。所以说法得收一收:清单能接住的是实现细节和已知限制这两类,知识缺口那类还得靠对话。但这两类恰好最高频。
² 另一个反例:如果被测对象本身就是 LLM 组成的系统,"用 LLM 判 LLM"就很难避免。这时候讨论该不该信没什么意义,该讨论的是怎么让两次判断的错误不相关。这个我还没想清楚。
回到标题。
"三次测试失败,LLM 是对的。"事实没错。但它像一句广告词,把三种不同性质的运气捆成了一种能力的证明。
要我说,更该记住的是作者改的那个习惯:先读推理,再改代码。注意顺序——先读,不是先信。读完改的是代码还是测试,还得自己判。
³ 评论区那几个人在争浮点容差该不该用 epsilon 断言。这个问题比"LLM 当 oracle"实在得多,因为它有唯一正确答案的方向:你得先定"恰好两倍算不算超",定完才知道断言怎么写。这种事模型替不了你,它只能替你算。