你有没有想过,程序员为什么总是害怕改代码?
因为代码里藏着各种各样的坑。空指针、缓冲区溢出、路径穿越,这些听起来专业的词,背后其实都是真实世界里发生过的灾难。2017年FFmpeg的一个漏洞,就是因为处理PNG图片时少算了一个字节的空间,导致程序在写入字符串结束符的时候,直接越界写到了不该写的地方。这类问题每年都在各种软件里反复出现,而专门用来揪出这些问题的工具,叫做**静态分析检查器**。
> 静态分析:不需要真正运行程序,只靠分析代码结构和逻辑,就能发现潜在缺陷的技术。
>
> 检查器(Checker):静态分析工具里负责识别某一类特定缺陷模式的组件,比如专门揪空指针问题的检查器,专门揪内存泄漏的检查器。
写检查器这件事,一直是专家的活。你得懂编译原理,懂程序分析的理论,还得对目标缺陷有深刻理解,才能写出一个既能抓到真实问题又不会疯狂报错的检查器。这几年大家都在琢磨,能不能让AI agent(也就是能自主使用工具、读写代码、运行程序的智能体)来干这个活。东华大学、上海交通大学、北京大学等机构的研究者们,就做了这么一件事:他们造了一个专门用来考验AI agent能不能写出靠谱检查器的考场,叫**CheckerBench**。
结果挺打脸的。21种模型和工具组合跑了三遍,平均及格率只有32.30%,就算是表现最好的组合,也只拿到了45.33%的分数。换句话说,就算是目前最强的AI agent配置,十个任务里也有超过五个做不好。
现有的测试,为什么测不出真实水平
在聊这篇论文具体做了什么之前,得先搞清楚一件事:为什么我们需要一个新的测试标准?
过去评估AI写代码能力的benchmark(基准测试集),大多长这个样子:给你一个GitHub issue,再给你对应的代码仓库,让你生成一个patch(补丁),然后用人类写的测试用例检验这个补丁对不对。这套流程对评估"修bug"这件事挺好用的,但它根本不是为"写检查器"这个任务设计的。
打个比方。你家马桶堵了,叫来一个水管工,他直接拿管钳伸进去把堵塞物掏出来,这是修复——对应到代码上就是打补丁。但如果你想要的是一套能自动检测"以后马桶会不会堵"的监测系统,这完全是另一回事,水管工得去理解堵塞的成因,设计一套通用的预警机制,还要保证这套机制不会在马桶正常工作时乱报警。前者是一次性的修补,后者是可复用的诊断能力。
如果继续用patch生成的方式测试agent写检查器的能力,会发生什么?agent很可能走捷径,直接针对这一个bug写一段"一次性打补丁式"的伪装检查器,它能让这个特定的漏洞消失,却完全不具备识别同类其他问题的能力。论文里管这个问题叫"不是以检查器为中心"(not checker-centric)。
这还只是第一个问题。论文作者总结了现有评测体系里的四个硬伤。
第一个硬伤前面说过了,不是以检查器为中心,测的是打补丁而不是写出可复用的分析规则。
第二个硬伤是交互深度不够。很多现有的测试把整个流程拆成了"看例子-总结规律-写检查器-验证"这样固定的几个阶段,agent只能在每个格子里规规矩矩地填答案,不能真正像人类专家那样反复试错、调整思路、推翻重来。但现实里,写一个靠谱的检查器往往需要几十次的"写代码-编译-运行-看报错-改代码"的循环,固定阶段的流程根本模拟不了这种长周期的摸索过程。
第三个硬伤是脚手架不统一。同一个AI模型,换一套工具框架(论文里叫harness,也就是agent运行所依赖的工具调度环境),得分可能完全不一样。如果评测标准每个团队都各搞一套,那根本没法横向比较谁的方法更强。
第四个硬伤,也是最隐蔽的一个:现有的评价指标只看补丁打没打对,测试套件过没过,完全不关心检查器本身的质量,比如它的误报率高不高,能不能精准定位到问题代码,跟目标分析工具兼不兼容。
这四个问题堆在一起,意味着在CheckerBench出现之前,没人能说清楚"AI agent到底会不会写检查器"这个问题。
CheckerBench:300道真实世界的考题
搞清楚了问题所在,CheckerBench要解决的事情就很明确了:造一套真正能衡量"从零写出一个靠谱检查器"这件事的测试集。
这套测试集的规模是300个任务,来自297个真实的CVE漏洞记录,横跨167个代码仓库、85种CWE缺陷类型、5种编程语言生态。
> CVE:Common Vulnerabilities and Exposures的缩写,是业界通用的安全漏洞编号系统,每一个CVE编号对应一个被正式记录在案的真实软件漏洞。
>
> CWE:Common Weakness Enumeration的缩写,是对缺陷"类型"的分类体系,比如"越界写"是一种CWE,"空指针解引用"是另一种CWE,一个CVE漏洞通常可以归到某个CWE类别下。
每一个任务都包含了漏洞代码的"病前"版本和"病后"(修复后)版本,一套配好环境的分析工具,还有一个检查器的脚手架代码,agent需要在这个基础上把检查器真正写出来并调通。
为什么要同时给"病前"和"病后"两个版本?因为一个靠谱的检查器必须具备区分能力:它得在有漏洞的版本上报警,在修复后的版本上保持沉默。如果检查器对两个版本都报警,或者对两个版本都不报警,那它压根没学会识别这个漏洞的本质特征,只是在瞎蒙。
这300个任务里,C/C++语言占了159个(53%),剩下的141个分布在Java、Python、JavaScript、Go四种语言里。C/C++用的是Clang Static Analyzer(简称CSA,一个业界常用的开源静态分析工具),其他四种语言用的是CodeQL(GitHub出品的代码查询分析引擎)。仓库方面,Linux内核贡献了107个任务,占了三分之一还多,FFmpeg、ImageMagick等知名开源项目各贡献了几个。缺陷类型上,CWE-787(越界写)是出现频率最高的,占了8.7%,其次是CWE-125(越界读)和CWE-22(路径穿越)。
这套数据集不是随便拼凑的,它的构建走了一条六阶段的流水线。第一阶段从国家漏洞数据库里收集CVE记录,关联到真实的修复提交,保留能追溯到漏洞代码和修复代码的实例。第二阶段重建可执行的分析环境,把两个版本的代码分别放进隔离的工作区,恢复出编译数据库,确保能真正跑起来而不是摆设。第三阶段把补丁、受影响的函数、编译命令、一个最简化的检查器模板打包成任务包,这个包里故意隐藏了CVE和CWE的标签,防止agent抄近道直接查漏洞类型来蒙混过关。
第四阶段是构建独立验证器,这一步很关键,这个验证器在整个收集过程里是完全冻结、不可见的,agent写完检查器之后,验证器会独立重新编译并运行它,不接受agent自己汇报的"我测过了,没问题"这种说法。第五阶段是人工引导的试跑采样,每个任务允许一次初始尝试加最多两次重试,人类专家会根据失败证据修改提示,但不会直接告诉agent答案。第六阶段是质量控制和去重,确保最终保留下来的任务都是真实可靠、不重复的。
这套流水线背后有一个很朴素的想法:你没法指望随便抓一堆CVE编号就能拼出一个好的测试集,每一个任务都得经过真正的编译验证、差异分析验证,才能保证它测的是agent的真实能力,而不是碰运气。
CheckerLab:一套不许作弊的裁判系统
有了考题,还得有裁判。论文第二个重要贡献是**CheckerLab**,一个独立的评测框架。
它的核心设计思路是把"交互式写检查器"和"独立评判检查器好坏"彻底分开。agent在一个隔离的工作区里干活,拿到任务说明、修复补丁、代码上下文、分析工具,还有一套共享的技能库(里面有分析工具的API文档、官方检查器示例,但绝对不包含这道题的标准答案)。agent有50分钟的生成时间,在这段时间内可以反复编译、反复扫描、反复调整,直到觉得自己写完了为止。
一旦agent提交,游戏规则就变了。CheckerLab会把提交的检查器源码冻结下来,在一个全新的、干净的环境里重新编译、重新运行。这一步非常关键,意味着agent汇报的"我验证过了,能用"完全不算数,必须靠独立重建的结果说话。
这就好比考试交卷之后,阅卷老师不会相信你草稿纸上写的"我检查过了,这道题肯定对",而是要重新演算一遍你的答案,这是唯一能防止自说自话的办法。如果不这样做,agent完全可能在自己的沙盒环境里投机取巧,比如硬编码只针对这一道题的特征,而不是真正理解缺陷的本质。
验证通过之后,还有一轮针对误报的大规模扫描,这个扫描用的代码既不是漏洞版本也不是修复版本,而是项目的主干分支代码(mainline)。扫描出来的警告,最多抽样10条,交给Claude Opus 5这个AI模型当裁判,评判这些警告是真的问题(true positive)还是误报(false positive)。同一个裁判模型还会在另外两个独立的环节里,分别打分检查器的"过程质量"(agent解题的思路是否严谨合理)和"语义质量"(检查器是不是真正抓住了缺陷的核心机制,而不是碰巧蒙对了)。
论文还专门做了一轮人工验证,抽样200个判断让三位人类标注员独立打分,跟AI裁判的结果对比,算出的一致性系数(Cohen's κ)是0.85,这个数值在统计学里通常被认为是"几乎完美一致"的区间,说明用AI当裁判这件事本身是靠谱的。
最后,只有同时满足以下所有条件的检查器才算"通过":正常生成完成(没有超时崩溃)、独立编译成功、漏洞版本和修复版本的诊断结果确实有对比度、报警位置确实落在补丁修改过的函数里、误报率低于阈值、过程质量和语义质量都达标。任何一项不达标,哪怕其他方面再优秀,也算失败。这是一种**不可替代性**的设计:
高过程分不能替代低语义分,低误报率不能替代编译失败。
这套严苛规则导致了一个很有意思的现象。GPT-5.6配合Hermes Agent这套组合,编译成功率(Build)高达100%,意味着代码写出来完全跑得通,但真正的差异化检测成功率(Diff. SR)只有64%,最终的综合及格率(Pass@1)只有23%。能编译不等于能用,能用不等于真的理解了缺陷。这中间的落差,恰恰说明了这套严苛的多维度评判标准的价值所在。
实验结果:没有一个模型能独占鳌头
论文团队测试了7个主流大模型,GPT-5.6、Claude Opus 4.8、Qwen3.8-Max、DeepSeek-V4-Pro、GLM-5.2、Kimi-K3、HY4-Preview,搭配3种agent运行框架:OpenCode、Claude Code、Hermes Agent。21种组合,每种组合跑3次独立重复实验,取平均值。
| 模型/框架 | Pass@1 | 编译成功率 | 误报率 |
|---|---|---|---|
| Claude Opus 4.8 / OpenCode | **45.33%** | 81.67% | 13.79% |
| DeepSeek-V4-Pro / OpenCode | 39.67% | **92.67%** | **2.56%** |
| GPT-5.6 / Hermes Agent | 23.00% | 100.00% | 0.00% |
| 21种组合平均 | 32.30% | 78.25% | 3.88% |
表现最好的组合是Claude Opus 4.8配OpenCode框架,拿到45.33%的及格率,但这意味着超过一半的任务依然失败。更有意思的是,没有任何一个配置能在"写出能用的检查器""误报率低"这两件事上同时拿第一,Claude Opus 4.8虽然及格率最高,但它的误报率(13.79%)明显高于DeepSeek-V4-Pro(2.56%),这说明它可能更倾向于"宁可报错也不漏报"的策略,付出的代价是精确度打了折扣。
框架的影响也很显著。同样是Kimi-K3模型,配OpenCode能拿到38.67%,换成Hermes Agent直接掉到24.33%,相差超过14个百分点。这说明同一个大脑,换一套手脚(工具调用方式),表现天差地别。这就像同一个大厨,给他一套趁手的厨具和给他一套别扭的厨具,做出来的菜味道能差出一个档次。工具链本身的设计,直接决定了模型的真实实力能发挥出来多少。
语言层面的表现差异也很突出。所有模型在C/C++任务上的表现都是最差的,哪怕是表现最好的DeepSeek-V4-Pro,在C/C++上也只有24.53%的及格率。这159个C/C++任务用的是CSA分析框架,要求agent深度理解Clang编译器的内部机制,远比CodeQL覆盖的四种语言难啃。相对的,Claude Opus 4.8在Python任务上能拿到65.36%,DeepSeek-V4-Pro在Go上拿到53.33%,这说明不同模型各有所长,没有一个全能冠军。
研究团队还专门拿这套方法和已有的同类系统**KNighter**做了对比。KNighter是此前专门针对Linux内核检查器合成的一个系统,用的是固定模板加分阶段提示的方式。研究团队用同一套底层模型(DeepSeek-V4-Pro),在KNighter原本的61个Linux内核任务上做对比:他们的方法(配合共享技能库)拿到80.3%的有效检查器生成率,KNighter本身是67.2%,而如果把技能库拿掉,这个数字会暴跌到29.5%。这组对比说明了两件事:共享的、可复用的经验指导对agent的表现影响巨大;而这套方法并不是针对CheckerBench这道题特制的,换到别人的考场上依然管用。
时间和容错的边界在哪里
论文还做了几组很扎实的敏感性分析,让结果更立体。
一组是时间预算的敏感性测试。研究者把同一批实验轨迹,按10分钟、20分钟、30分钟、40分钟、50分钟几个时间截点重新切片,看agent在更短的时间限制下能完成多少。结果是整体及格率从10分钟时的7.24%,涨到30分钟时的26.05%,再到50分钟时的32.30%,但涨幅是递减的,40分钟就已经能拿到50分钟时93%的效果,最后10分钟只多贡献了2.25个百分点。这意味着给agent更多时间确实有用,但边际收益在快速衰减,就像挤海绵里的水,前面几把能挤出一大摊,后面怎么挤也只有几滴。
另一组是误报阈值的敏感性测试。研究者把判定检查器"及格"所需的误报率上限,从10%一路调到50%,看看整体及格率会不会大幅波动。结果显示非常稳定,从10%时的32.05%到50%时的32.83%,只变化了0.78个百分点。这说明误报率这道门槛的具体取值,并不是决定实验结论的关键变量,整个评测体系的结论是稳健的,不是靠调整某个阈值凑出来的。
研究团队还专门分析了4360次失败的运行记录,拆解出了最主要的几类失败原因。时间预算耗尽占25.32%,是最大的失败来源。合成过程本身不够严谨占22.96%。语义建模不到位占21.15%,论文里举了一个很典型的例子:agent本来计划做路径敏感分析(一种更精细的程序分析技术,能跟踪代码执行的具体路径),结果最后写出来的实际上只是简单的语法树匹配,完全漏掉了加锁、控制流条件这些关键细节。误报过多占15.96%。这几类加起来,占了失败原因的八成以上,说明agent在写检查器这件事上,卡住的不是"会不会用工具",而是"能不能真正理解缺陷背后的程序逻辑"。
这就好比一个实习医生,拿到病历知道该查哪些指标,流程走得一板一眼,但真正看片子判断病情的时候,还是抓不住关键线索。工具会用,流程熟悉,离真正的专业判断力还有距离。
论文最后还做了一轮关于数据污染的严谨审计,查证这300个任务里有没有可能被模型在训练阶段"背过"。他们检查了CVE的公开日期和每个模型的发布日期,发现绝大多数任务的CVE公开时间都早于对应模型的发布时间,只有极少数例外(比如Claude Opus 4.8对应的3个任务)。他们还设计了一组对照实验,把100个任务的代码做了"语义不变的改写"(保留逻辑和API调用,但替换掉变量名和注释里可能暴露身份的线索),对比模型在原始版本和改写版本上的表现差异。结果显示,Claude Opus 4.8和DeepSeek-V4-Pro在能"认出"原始漏洞身份的样本和"认不出"的样本之间,正确率只差了2到3个百分点,而识别率本身却下降了7到9个百分点。这说明就算模型可能见过某个漏洞的公开信息,这种"见过"带来的表现提升是有限的,真正决定能不能写出好检查器的,还是理解和分析能力本身。
写在后面
读完这篇论文,印象最深的一点是,及格线定得非常朴素,就是"检查器真的能在有漏洞的代码上报警,在修好的代码上闭嘴",但即便是这么朴素的标准,21种顶尖模型组合里,平均通过率也只有三成出头。这说明我们现在喊得很响的"AI agent自主开发能力",距离真正接管一个需要深度程序理解的专业任务,还有相当长的路要走。
有一个细节我觉得特别值得单独拎出来说:编译成功率和最终及格率之间的巨大落差。GPT-5.6配Hermes Agent编译成功率是100%,但最终及格率只有23%。这意味着agent写出来的代码"看起来能跑"的比例,远远高于"真正理解了问题"的比例。这种落差提醒我们,评价AI编程能力的时候,光看代码能不能跑起来是远远不够的,还得看它是不是真正抓住了问题的本质。
还有一点让我挺触动的是,这篇论文里提到的那个失败原因分析:最大的失败来源不是工具用不好,也不是编译出错,而是"计划里的精细分析,落地变成了粗糙的模式匹配"。这跟人类专家写代码时常犯的毛病其实很像,设计阶段想得很周全,真正动手实现的时候图省事,偷工减料。AI agent暴露出来的问题,某种程度上也是人类工程师的问题,只是被放大、被量化了而已。
这篇论文没有回答的一个问题是:如果给agent更长的时间,比如几个小时而不是50分钟,这个及格率曲线最终会收敛在哪里?论文里的时间敏感性实验显示收益递减得很快,但那只测到了50分钟为止。如果真给足够长的时间,AI agent到底能不能靠纯粹的试错把这件事做到接近人类专家的水平,还是说会卡在某个无法突破的天花板上?这个问题,恐怕要等下一篇论文来回答了。
Q&A
Q1:CheckerBench是什么?
A:CheckerBench是一套专门测试AI agent能不能独立写出静态代码分析检查器的评测基准,包含300个真实任务,来自297个CVE漏洞、167个代码仓库、85种缺陷类型,覆盖五种编程语言。
Q2:为什么现有的编程AI测试集不能直接拿来测检查器合成能力?
A:现有测试集大多只看agent能不能打出一个补丁修好某个具体bug,而写检查器要求的是生成一套可复用的通用缺陷识别规则,二者目标完全不同,前者测不出后者的真实能力。
Q3:目前最强的AI agent配置在CheckerBench上表现如何?
A:21种模型和工具组合跑三轮测试,平均及格率只有32.30%,表现最好的Claude Opus 4.8配OpenCode组合也只拿到45.33%,超过一半任务依然做不好。







