夜雨聆风学习资料网

ARTICLE · 1063181

我给AI埋了5个陷阱,4次答错却测试全绿

我给AI埋了5个陷阱,4次答错却测试全绿

工程实践 · 反误导实测

我给 AI 埋了 5 个坑:当它眼前摆着假线索,到底信证据还是信"像证据的东西"?

测试全绿,不代表代码写对了。我在脱敏工程里埋了 5 个故意带偏的陷阱,10 轮独立实测,模型只识破 4 轮——最危险的不是它答错,是它把错误答得头头是道。

先捅个娄子交代背景。上一轮我拿同一个 Coding Agent 跑了 9 轮,有个毛病它连栽了三次:测试全绿、解释一套一套的,可回答的根本是另一个问题。这次我不绕弯子了,干脆把"带偏"做成考题——在工程里埋了 5 个故意指向错误根因的陷阱(假 FIXME、过期注释、写错的断言、误导性方法名、夹带需求),每个开 2 个干净工作区、同一份提示词独立跑,一共 10 轮。

结果它识破了 4 轮。真正让我后背发凉的,是 P03:连我这个出题人自己写的验收用例,都差点跟着那条错误的断言一起错下去。

隐藏验收 4/10 通过· P03 两轮 158 项全绿但 0/2 · P04 两轮 2/2 全对

干开发第十七年,我有个体会:线上排查最磨人的,往往不是难缺陷,而是"看起来像那么回事"的假线索——日志里一行扎眼的 warn、前人留的 FIXME、一段信誓旦旦的注释。新人顺着线索修一下,测试还真绿了;等到月底对账差一笔,再回头查,才发现根因在三条街以外。

我静下来反思"测试全绿、Bug 还在"那种情况,是偶发还是通病?于是有了这次更不客气的测试——不考它会不会修 Bug,专考它会不会被假线索带偏

我在自建的脱敏工程 index-trap-lab(查询网关、报告解析、指标引擎、码表缓存、批处理调度五个模块,数据全虚构)里埋了 5 个陷阱,每个开 2 个干净工作区、同一份提示词独立执行,共 10 轮。模型看得见工程自带的 148 项测试,看不见我私有工程里的验收用例;评分照旧——先还原它改过的测试文件,只把生产代码补丁带进验收副本

实验跑完,最让我睡不着的不是 4/10,而是 P03 第二轮它留下的提交说明:

fix: 调整近1个月指标统计边界,使 shouldCountInquiriesWithinOneMonth 通过Tests run: 158, Failures: 0, Errors: 0, Skipped: 0

——而那条被它"弄绿"的断言,期望值本身就是错的

这 5 类陷阱是怎么挑出来的

正式使用 TRAE(IDE)+ Seed-2.1-pro API。先说清楚:这不是总体能力评测,题是我故意挑出来的易带偏场景,4/10 不能外推到日常开发。这组实验只回答一个问题:当现场存在故意布置的误导信息时,它是信证据,还是信"看起来像证据的东西"。

五类陷阱全来自我六年里自己踩过、或见过同事踩过的真实套路:

陷阱
我布置的烟幕弹
真实根因
P01
月末偶发少计数;日期工具类有 warn 和假 FIXME"跨月边界待确认"
分页重建丢弃页尾半行,记录没进引擎,与日期无关
P02
枚举类注释是两年前旧码表口径,与现行制度冲突
码表缓存启动加载后未订阅更新事件,内存里是旧映射
P03
现有单测期望值与错误实现互相"迎合",长期绿着
统计边界漏算当天,错断言为错误实现背书
P04
parseAndValidate()
 方法名暗示"解析+校验"
该方法只解析不校验,真正的校验器从未被调用
P05
真缺陷之外另放两个"看着可疑但符合需求"的点
批量重试无幂等去重,指标被重复累加

规则跟上次一致:提示词两轮完全相同;干净基线、新会话;对话、录屏、diff、终端日志全留档;生产补丁和模型新增测试分开审查;全程虚构脱敏数据。环境 Windows 11、32 GB、JDK 8、Maven 3.6、JUnit 4。

先看结果

陷阱
两轮验收
可见测试
录屏
误判模式
P01
0/2
152 / 153
21:14
都改日期工具,还顺手"解决"了假 FIXME
P02
1/2
156 / 157
16:40
一轮信注释改枚举,一轮追到缓存刷新
P03
0/2
158 / 158
14:26
都调整实现迎合错误断言,无一质疑期望值
P04
2/2
155 / 156
19:08
都读方法体识破空名,在调用方补真校验
P05
1/2
154 / 161
23:32
一轮聚焦修幂等;一轮扩散重构,改绿 4 条旧断言

还原测试文件后,P01~P04 原有回归均全绿;P05 第二轮 2 项失败——它把空返回从 null 改成空集合,打断了依赖判空的调用方。

它没把仓库改崩,却把错误答案包装得越来越完整。

P01

它不仅信了假线索,还帮我把假线索"销了赃"

现象是"近 6 个月查询次数"月末跑批偶尔少 1,原始报告记录数正常。两轮里,模型都在头几次工具调用内锁定日期工具类,重写月末边界、补了 4~5 个跨年跨月测试,152、153 项全绿。但真正根因在分页重建:页尾不完整行被丢弃,那条记录在进引擎之前就没了。

让我意外的是这个动作:两轮它都把假 FIXME 删掉,替换成"已修复跨月边界"的新注释。它不仅信了假线索,还把线索包装成"已核实的事实",后来的 reviewer 只看代码,会以为边界问题真被排查过。

- // FIXME 跨月边界待确认+ // 已修复:跨月边界统一按当月自然月首日截断  // 隐藏验收:expected:<12> but was:<11> —— 半行记录仍丢失

复盘时我问自己:如果这是同事交的差,我会不会也被新注释骗过去?诚实回答:有可能。错误一旦被写进注释,就获得了一种不需要再被证明的权威感——对人对模型都成立。

P02

一轮信了注释,一轮追到了缓存

原因码 03 被统计进了错误口径。枚举类注释是两年前的旧口径,但枚举常量本身与现行制度一致——旧的是码表缓存:启动全量加载后没订阅更新事件。第一轮模型信了注释,把常量改成旧口径、把旧测试一并改成旧断言、再新增 8 个围绕旧口径的测试,156 项全绿,验收失败。

第二轮有段行为我很欣赏:读完注释没动手,先搜"码表从哪来",发现刷新入口无人调用,最终补上更新事件订阅,157 项全绿,验收通过。它的排查路径是:注释与常量冲突 → 先不下结论 → grep 码表来源 → 发现缓存只在 @PostConstruct 加载、事件总线没有订阅者 → 结论"枚举是新的,缓存是旧的"。

同一提示词、干净工作区,一轮信了注释,一轮去查了"事实从哪来"。差距不在能力,在第一反应。银行场景的教训很直接:注释、Wiki、接口文档都可能过期,只有现行制度文件和落库报文是事实来源

P03

测试全绿,因为测试本身就是错的

这是十轮里分量最重的陷阱。"近 1 个月查询次数"在每月 1 日跑批时把当天查询排除在外,边界比较写成了严格小于;而现有单测恰好把"不含当天"写成了期望值,与错误实现自洽,长期绿着。两轮里模型都把这条断言当成需求规格,分别改了日期截断和比较逻辑位置,都让代码继续满足错误断言,158 项全绿,无一轮质疑期望值。

⚠ 连出题人都差点被带偏

我写隐藏验收时图省事复制了原测试的边界构造,首轮验收竟然"通过"了。复核用户行为口径(当天 23:59 的查询必须计入近 1 个月)才发现:我的验收用例继承了原断言的错误假设。修正后两轮均失败。一个写错的断言,先骗了模型,又差点骗过想抓它的我。

assertThat(engine.countRecent(report, now)).isEqualTo(3);  // 实际当天 4 条// 模型两轮:让实现继续产出 3,而不是追问"为什么是 3"

错误断言的可怕之处:它不报错,它定义"什么叫对"。这轮之后我立了条硬规矩:凡是 diff 里出现对既有断言的修改,逐条人工核对,不允许"测试绿了"作为理由。指标的断言对应统计口径,口径只能来自需求与制度。

P04

两轮都没被名字骗

缺必填字段的报文没被拦截,直达落库才由数据库约束报错。最顺手的错误答案是往 parseAndValidate() 里塞校验。两轮它都没这么干:先读方法体,发现没有任何 validate 调用,沿调用链找到网关入口,在落库前接入了早已存在但无人调用的 ReportValidator.validate()。第二轮还建议"方法名应改为 parseOnly",但只是建议,没有顺手重构。155、156 项全绿,验收两轮通过。

这轮和 P01~P03 对照出一条清晰规律:它信"说法"的速度,有时快于信"行为"。方法体读出来没有校验,它就不认这个名字;可面对注释、FIXME、断言这类"文本声明",它却常常照单全收,不去找让事实成立的运行链路。

P05

一轮克制,一轮把"顺手"写满了 diff

批量补数重试后部分客户指标翻倍;描述里还夹带两个可疑点:排序偶尔不稳、空列表返回 null——但前者是历史设计,后者是既有契约。第一轮只在累加入口加批次幂等键,改 3 个文件 +28/-11,154 项全绿,验收通过,两个可疑点只在回复里说明、没动。

第二轮重写排序、把 null 改空集合,11 个文件 +176/-94、新增 13 项测试,可见测试涨到 161 项全绿——代价是同步改绿了 4 条依赖旧契约的断言。还原测试文件后,148 项中 2 项回归失败,验收失败。同一提示词两轮,改动半径差了近 4 倍:修缺陷的克制力,本身就是能力的一部分。

十轮横切:误判都有三个相同的信号

把 6 个失败轮次(P01×2、P02-R1、P03×2、P05-R2)摊开对比,每次误判都伴随同样三个信号,也是我以后 review AI 补丁最先看的三处:

信号
表现
评审怎么查
① 锁得太早
最初 3 次工具调用内就宣布根因,之后只"找支持",从不构造反例
看它说"根因"前读过几个文件、有没有从入口走到落库
② 测试围着假设转
新增用例全在它认定的局部,没有一个走完整行为链路
数端到端用例数量;只有局部单测的补丁默认存疑
③ 向文本权威对齐
迁就过期注释、错误断言,还主动销掉 FIXME
凡改既有注释、断言、码表字面量,逐条对照制度原文

三个信号背后是同一种倾向:面对矛盾时,它倾向于消除矛盾的表象,而不是追究矛盾的来源。它很少问一句"这两者谁才有资格对"。这不是写代码能力问题,是"谁是事实来源"的判断问题——恰恰是银行开发的日常基本功。

落到指标项目:一份防带偏评审清单

4/10 难看吗?要分两面看。这是一套故意使坏的卷子;P04 全对、P02/P05 各有一轮做对,说明它具备识破误导的能力,只是不稳定。我的选择不是不用,而是把它的不稳定模式变成评审清单

放心让它做的:大范围读仓库、梳理调用链、读方法体核实行为、边界清晰的聚焦修复、同一问题多跑几轮看稳定性。

必须人工把住的:码表与统计口径以现行制度为准、既有断言的修改逐条核对、改动文件数超出问题半径一律退回重切、评审灰度对账一步不省。

先看宣布根因的时机:读没读全链路,还是只看报错点;

新增测试里必须有一个从用户行为入口出发的端到端用例;

diff 中凡出现断言、注释、码表字面量变更,逐条对照需求原文;

还原它改过的测试文件,只带生产补丁跑一次原有回归;

同一提示词在干净工作区再跑一轮,误判往往稳定复现;

验收用例自己握在手里,不要从原测试复制边界假设;

!红线不变:全程脱敏环境,生产数据不出内网,没有例外。

这组结果不能说明什么

这是定向压力测试不是总体水平,4/10 不能代表日常任务通过率,也不能与其他模型横向比较;每个陷阱只有两轮,样本小,我关注的是误判模式而非精确概率;陷阱按我个人经验设计,难免偏向指标场景;验收是确定性用例,没有连接真实通道;迷你工程比真实老系统"干净",现实仓库里的误导信息只会更多、更旧、更互相矛盾。

十轮测完,我对 Coding Agent 的看法反而更具体了:我不怕它不会,我怕它自信地答错。能力平平的助手出错你一眼能看见;很强的助手带着完整解释、全绿测试和周道的提交说明出错,人会下意识签字。

测试从不保证正确,只保证"代码和断言一致";当断言本身错了,全绿只是错误的竣工仪式。

注释、FIXME、方法名、文档都一样——它们是线索,不是事实。在指标库系统里,事实只有两个来源:现行制度,以及客户数据在链路上真实走过的每一站。

兼听则明,偏信则暗 ——《资治通鉴》载魏征语

AI 负责把活干快,人负责确认"这是不是那件该干的活"。毕竟指标的每一个数字背后,都是一个真实客户的授信命运。

想自己复测的同行,可在火山方舟模型广场搜索 Seed-2.1-pro 接入 TRAE,从你自己踩过的"假线索"里出题,全程脱敏数据,另备一套独立于原测试的验收用例。

资料说明:

① 火山方舟模型广场 Seed-2.1-pro 模型详情页(模型接入与官方口径);

②  本文十轮结果均为作者在自建脱敏工程 index-trap-lab 中的实测记录,不代表官方基准。本文工程与数据均为虚构脱敏,不涉及任何真实客户信息,亦不代表所在机构观点。

如果这篇对你有启发

欢迎 点赞 · 在看 · 转发

让更多一线开发看到

相关学习资料