测试全绿,评审翻车:为什么「功能过了」不等于「代码能合入」
你有没有经历过这种事:辛辛苦苦把 bug 修好了,单测全绿,提 PR 想发版,结果维护者一句「哦,方案不错,但你这样改会破坏旧接口」——打回。
这就是代码 agent 每天面对的世界的缩小版。
过去两年,衡量代码 agent 好不好用,几乎只看一个指标:能不能让测试变绿。SWE-bench 就是从真实 GitHub issue 出发,让 agent 修 bug,修完跑功能测试,过了就算赢。SWE-agent、OpenHands、Agentless 这些知名 agent 都是这么评出来的。
但仔细一想,这个口径很怪。真实世界里的「修好一个 bug」,从来不是「测试过了」就行——代码评审那关才是决定补丁能不能合入的裁判。评审会说什么?
- 「这样改会把旧 API 弄坏,保持向后兼容」
- 「这个函数原来抛布尔值代表两种情况,你别把语义改了」
- 「咱仓库的约定是异常集中处理,别在业务层 try-catch」
这些要求,功能测试根本测不出来。
今天这篇论文,就是来补这个洞的。
TL;DR
SWE-Gate 从 75 个开源 Python 仓库的真实代码评审意见里,提炼出「验收约束」(比如向后兼容、保留异常语义、遵守仓库约定),做成 303 个修复任务。它把评测拆成两个独立的维度——功能正确 和 约束服从——每个实例都配了两套测试。
结论很扎心:644 个通过功能测试的补丁里,有 221 个(34.3%)违反了评审约束。也就是说,光看功能测试,代码 agent 的真实水平会被系统性高估三分之一。
问题出在哪:SWE-bench 只测了「半个答对」
先说清楚 SWE-bench 这类评测为什么流行:
- 它从真实 issue–PR 配对里取材,够真实;
- 有可执行的功能测试,够客观;
- 修 bug 是明确的「考试题」,够清晰。
但它有个天生的盲区:一个补丁能合入,除了修好 bug,还要过代码评审。评审里提的那些要求——向后兼容、保留异常语义、遵守项目惯例——在 SWE-bench 的评分体系里完全不存在。
论文里举了个非常生动的例子:Pydantic(Python 那个数据校验库)有一个 PR 想给额外的字段序列化加开关。初版实现功能是完全对的——该隐藏的字段确实被隐藏了。但评审说:「你改的是公共接口,这是破坏性变更,不行。」最后加了个 extras_ser_exclude_if 参数才合进去。
看见了没有?「功能修好了」和「功能修好且过了评审」是两码事。论文管前者叫 functional success,管后者叫 joint success——中间隔着的,就是那些「隐藏失败」(hidden failure)。
SWE-Gate 怎么做:把「评审意见」变成「可执行测试」
思路挺有巧思,跟多数评测都不一样——它不是造更难的题,而是换一个维度出题。
第 1 步:从真实评审里抽「约束种子」
他们抓了大量有丰富评审历史的开源 Python 项目的合并 PR,把每一条评审意见拆成「原子建议」:
- 评审人指出的问题是什么
- 要求改什么
- 理由是什么
然后过滤掉那些没法验证的(风格建议、格式问题、措辞修改这些全不要),最后让 LLM 逐条判断:这条约束,能不能构造一个「功能修好了但违反它」的反例补丁? 能,才收。
第 2 步:把约束「移植」到别的仓库
每条约束种子要找一批兼容的仓库再造题——这叫「约束优先构造」,受 SWE-Mirror(用跨仓库转移来防作弊)启发。比如「数据校验类约束」就优先放进数据处理生态的项目里。
迁移不是拍脑袋:每一步都要跑给代码看。
第 3 步:同一个问题,出两套测试
每个实例最终长这样:
- bug 补丁:往干净仓库里注入一个真实可复现的缺陷;
- 功能测试:测「issue 修复了没」;
- 约束测试:测「评审约束满足没」;
- 非合规补丁:能过功能测试、但违反约束(证明两维确实可分);
- 黄金补丁:两套测试都过(证明约束不是不可达的苛求)。
这四个补丁互相咬合,构成一个「校验矩阵」:干净仓库过功能测试 → 注入 bug 后挂 → 非合规补丁过功能挂约束 → 黄金补丁全过。任何一环对不上,这个实例直接淘汰。
最后还有三道质检:Docker 容器里全矩阵验证、LLM 语义审查(对照那些「复述功能」「描述剧透隐藏测试」等失败模式)、人工终审。
结果:34.3% 的「修好了」其实是「没通过」
投入 4 个能力梯度不同的模型,用同一个 agent 框架(Mini-SWE-Agent,最多 100 步交互)跑全部 303 题:
| 模型 | 功能成功率 FSR | 功能成功中违反约束的比例 |
|---|---|---|
| GPT-5.5 | 74.9% | 29.5% → 约束跟随率 70.5% |
| DeepSeek-V4-Flash | 66.7% | ~35.6% |
| GPT-5.4-mini | 61.7% | ~35.8% |
| GPT-4o-mini | 9.2% | 53.6% |
合计:644 个功能成功,423 个真正过评审,221 个隐藏失败,HFR = 34.3%。
注意看最强的 GPT-5.5:功能成功率快 75%,却在 227 个「修好了」的补丁里有 67 个过不了约束关。 越能干的模型,看起来越「够好」,实际上照样在评审这关摔跟头。
哪些约束最容易翻车?按类别看:
- 最难的:范围泛化(CFR 46.3–63.0%)、资源生命周期清理(53.8–62.5%)、编码转义(51.1–55.9%)——这些要求「覆盖面超出眼前这个 bug」或者「资源和异常要管到收尾」,agent 很容易只做表面功夫;
- 相对容易的:哨兵区分(74.2–81.6%)、参数顺序保留(75.0–79.6%)——跟语法风格接近,模型学得会。
一个意外的发现:把约束告诉它,也不全管用
论文还做了对照组:一半题目把「评审约束」用自然语言明确写给 agent 看(+C),另一半不给(−C)。
结果很有意思:
- 给了约束:联合成功率(JSR)四个模型全涨——GPT-5.5 从 41.3% 涨到 52.8%(+11.5 个百分点);约束跟随率整体提升 10.2–25.6 个百分点;
- 但功能成功率反而微降:有的模型掉了 3.3–9.9 个百分点。
为什么?论文给的解释很实在:多一条验收标准,补丁就更复杂。agent 把精力分到「满足约束」上,可能就不会去选那条最稳妥的最小功能改法。这说明约束信息是「知道比不知道强」,但它不是免费的——这是复杂度和成功率的真实权衡。
泼冷水:这基准有什么局限
第一,只有 Python。75 个仓库全是 Python 生态。评审约束跟语言强相关——比如类型系统强的语言,「错误语义」这类约束的可测性会完全不一样。后续能不能扩展到 Java/TS 是一大看点。
第二,黄金补丁是合成的,不是真实合入记录。虽然过了全矩阵校验,但它是 LLM 按照「同过两套测试」的标准造出来的,跟历史上维护者真实接受的补丁可能有系统性差异(可能偏简单)。
第三,约束类别分布不均。50.2% 的实例落在「错误语义」这一类,模型在这一类的表现会过度影响总分。
第四,真实评审里还有很多无法写成测试的要求——代码风格、可维护性、团队惯例。SWE-Gate 只测了「能被测试的那类约束」。论文自己也承认,这是未来要补的题。
这事对你有啥用
- 你在用代码 agent 提效:SWE-bench 的高分不等于「能直接合入」。给 agent 的 prompt 里明确写清楚验收约束(兼容性、异常语义、仓库约定),实测能把联合成功率拉上去——但当心它会让 agent 变「保守」,自己多盯一眼核心改动。
- 你在做 agent 评测:SWE-Gate 的双维度协议(功能 × 约束、分开报、给反例补丁证明可分)是可移植的打法,可以直接借鉴到内部评测里。
- 你在跟 agent 协作:下次它跟你说「测试都过了」,可以问一句:「评审会怎么看你这个补丁?」
参考来源
- 论文:SWE-Gate: Passing Functional Tests Is Not Enough for Software Engineering Agents(arXiv:2609.04167)
- 代码与数据:github.com/DeepSoftwareAnalytics/SWE-Gate
浙公网安备 33010602011771号