1. 这场重测到底测了什么
先把背景交代清楚。SWE-bench Pro 是 SWE-bench 系列里难度更高的一档评测集,它把任务从"改一个函数"升级到"跨文件、跨模块地完成一个真实仓库里的工程改动",并且要求提交的补丁能通过仓库原有的测试套件。换句话说,它考的不是模型能不能写出一段看起来对的代码,而是它能不能像一个真正的工程师那样,读懂一个陌生仓库、定位问题、做出最小改动、并且不把别的地方搞坏。
这次重测的对象是 7 个模型,覆盖了当前编程 Agent 领域里讨论度最高的一批:GLM-5.2、DeepSeek-V4-Pro,以及另外几个同量级的选手。评测方式不是让模型单轮输出一段代码,而是给它一个 Agent 循环——可以读文件、可以跑命令、可以看报错、可以反复修改,直到它自己认为任务完成。这个设定很关键,因为它更接近真实使用场景:现在没人真的指望模型一次写对,大家用的是 Agent,是让它自己迭代。
结果就是标题里那句话:7 个模型,6 个不及格。
我先说清楚"不及格"的定义,不然这个结论容易被误读。这里的及格线不是"完全解决",而是"在限定轮次内,提交的补丁能通过目标测试,且不引入回归"。按这个标准,只有 1 个模型越过了线,其余 6 个要么补丁跑不过测试,要么改对了目标却弄坏了别的测试,要么干脆在定位阶段就迷路了,反复在无关文件里打转直到轮次耗尽。
这个结果乍看很吓人,但如果你真的用编程 Agent 干过活,会发现它其实符合直觉。SWE-bench Pro 的题目大多来自真实开源仓库的历史 issue,仓库规模动辄几万行,模块之间耦合很深。人类工程师接手这种任务,第一件事是git log看最近改了什么,第二件事是git show看相关提交的 diff,靠这两步快速建立上下文。而大部分模型在 Agent 循环里,恰恰缺的就是这个"先看历史再动手"的习惯。
所以这篇东西我想聊的不是"哪个模型第一",而是把这 7 个模型的失败模式拆开看,讲讲它们在 SWE-bench Pro 这类任务上到底卡在哪,以及如果你自己要用编程 Agent 干活,应该怎么绕开这些坑。这些结论对选型有用,对写 prompt 有用,对搭自己的 Agent 工作流更有用。
2. 为什么 SWE-bench Pro 这么难
2.1 它和普通代码评测的本质区别
普通的代码生成评测,比如 HumanEval 那一类,本质是"给定函数签名和 docstring,写出函数体"。这种任务的信息是完整的、封闭的,模型只需要做局部推理。SWE-bench Pro 完全不是这个逻辑,它给的是一个仓库快照加一段自然语言描述的 issue,模型要自己去找"问题在哪"。
这个差别有多大?打个比方。普通评测像是给你一道填空题,题干和空都在你面前;SWE-bench Pro 像是把你扔进一个陌生的大楼,告诉你"三楼某个房间的灯不亮",让你自己找配电箱、找线路、找是哪根线断了,修完还得保证整栋楼不停电。前者考的是知识,后者考的是工程判断。
具体到技术层面,SWE-bench Pro 的难点集中在三个地方。第一是上下文规模,仓库文件多、单文件长,模型不可能全读进来,必须做检索和取舍。第二是隐式约束,很多改动要求你遵守仓库既有的代码风格、抽象层次、错误处理约定,这些不会写在 issue 里,得从周围代码里推断。第三是回归风险,你改的地方可能被别的模块依赖,改完目标测试过了,但别的测试挂了,一样算失败。
2.2 Agent 循环放大了哪些问题
理论上 Agent 循环应该缓解难度,因为模型可以试错。但实测下来,循环反而放大了几个问题。
最明显的是上下文污染。Agent 每跑一次命令、每读一个文件,都会往上下文里塞内容。跑到十几轮之后,上下文里堆满了报错日志、文件片段、失败的尝试,模型开始"忘记"最初的任务目标,或者被中间某次误导性的报错带偏。我见过最典型的情况是:模型第一次跑测试失败,报错指向 A 文件,它就去改 A,改完还是失败,报错指向 B,它又去改 B,改到第五轮已经完全不记得 issue 原本要解决什么了。
第二个是过早收敛。有些模型在找到一处"看起来相关"的代码后,就急着输出补丁,不再验证。它可能改对了症状,没改到病根。SWE-bench Pro 的测试往往覆盖边界情况,这种"治标"的补丁很容易在隐藏测试上翻车。
第三个是工具使用不当。这是我觉得最值得说的。git show、git log、git blame这些命令,在真实工程里是定位问题的利器,但很多模型在 Agent 循环里根本不会主动用,或者用了但不会解读输出。它们更倾向于直接grep关键词,然后一头扎进第一个匹配的文件。这个习惯在简单任务上没问题,在复杂仓库里就是灾难。
2.3 及格线背后的工程含义
我把及格线定成"通过目标测试且无回归",有人可能觉得太严。但你想,如果一个编程 Agent 改完代码,目标测试过了,却让另外三个测试挂了,你敢把它提交的补丁合进主干吗?不敢。那这个补丁在工程意义上就是没价值的。
这也是为什么 6 个模型不及格这件事,虽然数字难看,但反映的是真实差距。能过线的那个模型,未必是"最聪明"的,但它大概率是最守规矩的——知道先看历史、知道改完要跑全量测试、知道改动要最小化。这些不是智力问题,是工作流问题。而工作流,恰恰是可以通过 prompt 和工具设计去优化的。
3. 七个模型的失败模式拆解
3.1 定位阶段的三种迷路方式
我把 6 个不及格模型的失败过程回放了一遍,定位阶段的迷路方式基本能归成三类。
第一类是关键词直冲。模型拿到 issue 描述,提取几个关键词,直接grep,然后打开第一个命中的文件开始改。问题是 issue 里的词往往是用户视角的描述,跟代码里的命名对不上。比如 issue 说"登录后跳转错误",代码里可能叫redirect_after_auth,也可能叫post_login_navigation,还可能藏在某个中间件的配置里。grep 命中率低,模型就开始瞎猜。
第二类是过度阅读。跟第一类相反,这类模型很谨慎,把相关目录下的文件一个个读过去,读了二十个文件还没动手。上下文被塞满,等到真要改的时候,关键信息已经被淹没。这种失败方式在轮次受限的评测里特别吃亏,因为时间都花在读上了。
第三类是历史盲区。这是最可惜的一类。模型其实定位到了大致范围,但它不看git log,不知道这块代码最近被改过,不知道有个相关的 PR 刚合并。如果它看一眼提交历史,可能立刻就明白问题出在哪。git show一个最近的提交,diff 里往往就藏着答案,但大部分模型不会主动做这一步。
3.2 修改阶段的典型错误
定位对了,改的时候照样能翻车。我总结了几种高频错误。
改动范围失控是最常见的。模型为了"确保修好",顺手把周围的代码也重构了一遍,改了命名、调了格式、挪了函数位置。结果目标测试过了,但依赖这些函数签名的其他模块全挂了。SWE-bench Pro 特别看重最小改动,你动得越多,回归风险越大。
忽略隐式契约也很致命。仓库里可能有个约定:所有数据库操作必须走某个封装层,不能直接调底层驱动。这个约定不会写在 issue 里,但测试会检查。模型如果直接调了底层,功能上没问题,测试照样挂。
还有错误处理缺失。真实仓库对异常路径往往有明确要求,比如某个函数必须抛特定类型的异常,或者必须返回特定结构的错误对象。模型写的补丁在正常路径上跑得通,一遇到边界输入就崩,隐藏测试就是专门测这些的。
3.3 验证阶段的偷懒行为
验证阶段的问题,说白了就是不跑全量测试。
很多模型改完代码,只跑 issue 里提到的那一个测试,过了就宣布完成。它不知道仓库里还有几百个测试,其中可能就有被它改挂的。这个行为在 Agent 循环里特别常见,因为跑全量测试慢,模型倾向于走捷径。
更隐蔽的一种是测试通过但没验证语义。有些测试写得比较宽松,只要不抛异常就算过。模型钻了这个空子,写了个"能跑但不解决问题"的补丁。这种在评测里会被更严格的隐藏测试抓出来,在真实工作里就是埋雷。
3.4 唯一及格的那个做对了什么
说完成功的反面,说说唯一过线的那个。我观察下来,它做对的核心就一件事:把工程习惯固化进了工作流。
它拿到任务后的第一步不是 grep,是git log --oneline -20看最近提交,然后针对可疑的提交跑git show看 diff。这一步帮它快速建立了"这块代码最近发生了什么"的上下文。第二步它才去读相关文件,而且读的时候带着假设去读,不是漫无目的地翻。第三步改完之后,它跑的不是单个测试,是相关模块的整个测试目录。最后一步,它会把 diff 再 review 一遍,确认没有多余改动。
这套流程没有任何魔法,就是人类工程师的常规操作。它之所以能过线,是因为它没有偷懒。这也说明一个事:在当前阶段,编程 Agent 的上限很大程度上取决于工作流设计,而不是模型本身的代码能力。
4. 编程 Agent 的实操工作流该怎么搭
4.1 先看历史再看代码
如果你自己要用编程 Agent 干活,第一条建议就是强制它先看 git 历史。
具体怎么做?在系统 prompt 里明确写:在阅读任何源文件之前,必须先执行git log --oneline -30,找出与任务描述相关的提交,对每个可疑提交执行git show <hash>,把 diff 内容作为定位依据。这个约束看起来死板,但实测有效。
为什么有效?因为 issue 描述的是"现象",而最近的提交记录的是"变化"。bug 往往就藏在最近的变化里。你让模型先看变化,等于给它划定了搜索范围,比让它在大仓库里瞎逛高效得多。git show输出的 diff 还有个好处:它天然展示了"改动前后的对比",模型很容易从中理解代码的意图。
提示:
git log的条数别设太大,20 到 30 条足够。太多会污染上下文,太少可能漏掉关键提交。如果仓库提交很频繁,可以加--since限定时间范围。
4.2 用 git show 建立上下文锚点
git show这个命令值得单独讲,因为它是被严重低估的定位工具。
它的价值在于,一个提交的 diff 同时包含了三样东西:改了哪些文件、每个文件改了什么、改动周围的上下文。这三样合起来,就是一个天然的"代码地图"。模型看完一个相关提交的 diff,基本能知道这个功能的入口在哪、数据怎么流动、边界在哪。
我自己的做法是,在 Agent 的工具集里把git show包装成一个"高优先级工具",并在 prompt 里说明:当你定位到某个功能相关的文件时,先对这个文件跑git log --oneline -- <file>,看看它最近被哪些提交改过,再挑最相关的提交git show。这个组合拳打下来,定位准确率能明显提升。
4.3 最小改动原则的落地方法
最小改动说起来容易,让模型做到难。我的办法是在 prompt 里给出可操作的判定标准,而不是抽象的原则。
比如这样写:你的补丁应该满足——新增或修改的行数不超过 30 行;不改变任何已有函数的签名;不调整任何与任务无关的代码格式;如果必须新增函数,优先放在已有相关函数附近。这些是硬指标,模型更容易遵守。
另外,改完之后让它自己输出一份"改动清单",逐条说明每一处改动对应 issue 的哪个部分。如果某处改动说不清对应关系,那就是多余的,应该删掉。这个自检步骤能砍掉不少画蛇添足的改动。
4.4 验证环节不能省的三步
验证环节我建议固化成三步,缺一不可。
第一步,跑 issue 直接相关的测试,确认目标达成。第二步,跑改动文件所在模块的完整测试目录,抓回归。第三步,如果仓库有 lint 或类型检查,也跑一遍,因为很多仓库的 CI 会卡这些。
这三步里,第二步最容易被跳过,也最重要。跑模块级测试比跑全量快,又能覆盖大部分回归风险,性价比最高。如果模块测试也过不了,说明改动影响面比预想的大,得回去重新评估。
注意:有些仓库的测试有执行顺序依赖,单独跑某个测试文件可能过,全量跑就挂。所以条件允许的话,最终验证还是跑一次全量,哪怕慢一点。
5. 模型选型的现实考量
5.1 GLM-5.2 和 DeepSeek-V4-Pro 的差异
这两个是热词里被提得最多的,我结合这次重测的观察说说差异。
GLM-5.2 在长上下文保持上表现更稳。Agent 跑到十几轮之后,它还能记得最初的任务目标,不太容易被中间的错误信息带偏。这个特性在 SWE-bench Pro 这种多轮任务里很值钱。它的短板是有时候过于保守,定位到了也不敢下手改,轮次容易耗在犹豫上。
DeepSeek-V4-Pro 在代码理解深度上更强,读一个复杂函数能准确说出它的职责和边界,写出来的补丁质量也高。但它对上下文的"抗污染"能力稍弱,轮次一多容易迷失。用它的策略应该是尽量压缩轮次,把关键信息在前期一次性喂足,别让它自己慢慢摸索。
这两个没有绝对优劣,看你的任务类型。任务链条长、需要反复试错的,GLM-5.2 更合适;任务相对聚焦、需要深度理解的,DeepSeek-V4-Pro 更合适。
5.2 别只看榜单分数
我特别想提醒一句:别只看榜单分数选模型。
SWE-bench Pro 的分数受工作流影响极大。同一个模型,换个 Agent 框架、换套 prompt,分数可能差十几个点。你看到的榜单,是别人用他们的工作流跑出来的,未必代表你用同样的模型能达到同样效果。
更靠谱的做法是,拿你自己仓库里的真实 issue 做个小测试集,五到十个就够,让候选模型在你的工作流下跑一遍,看通过率和改动质量。这个测试花不了多少时间,但结论比任何榜单都可信。
5.3 成本与轮次的平衡
还有个现实问题:轮次就是成本。
Agent 每多跑一轮,就多一次模型调用、多一次工具执行。SWE-bench Pro 这种任务,跑满轮次的成本不低。所以选型时不能只看"能不能做对",还要看"几轮能做对"。
我的经验是,一个模型如果在前 5 轮还没定位到正确文件,后面基本就是瞎耗,不如直接终止重来。与其让它跑到 30 轮碰运气,不如设个轮次上限,超了就换策略或者换模型。这个上限设多少,取决于你的任务复杂度和成本预算,一般 10 到 15 轮是个合理的起点。
6. 常见问题与排查技巧
6.1 模型反复改同一个地方却改不对
这是最高频的问题。模型盯着一个文件改来改去,每次报错都指向那里,但就是改不对。
排查思路:先看它是不是误解了报错。很多报错的根因不在报错指向的文件,而在调用方。让模型往上追一层调用栈,看看是谁传了错误的参数进来。如果追上去还是不对,再让它看这个文件的git log,确认最近的改动有没有引入问题。
一个实用技巧是,让模型在改之前先"复述"它理解的 bug 成因,写成一句话。如果这句话说不通,说明它还没定位对,别急着改。
6.2 补丁过了目标测试但挂了别的测试
这是回归问题,说明改动影响面没评估清楚。
排查思路:先看挂掉的测试和改动文件有没有依赖关系。如果有,说明改动破坏了某个契约,得调整改法。如果没有直接依赖,可能是测试之间有共享状态,改动间接影响了它。这种情况让模型跑一次git diff看自己的改动,逐行问"这行会不会影响别的地方"。
预防办法是在 prompt 里要求:改动前先搜索这个函数/类的所有引用点,确认改动不会破坏调用方。这一步多花点时间,能省掉后面大量返工。
6.3 Agent 跑着跑着就"忘了"任务
上下文污染导致的迷失。表现是模型开始做一些跟任务无关的操作,或者重复已经做过的步骤。
排查思路:看上下文里是不是堆了太多失败尝试的日志。如果是,考虑在每轮结束后做一次"上下文压缩",把已经排除的可能性总结成一句话,把冗长的报错日志删掉,只保留关键信息。
另一个办法是定期"重述任务"。每跑五轮,让模型重新读一遍原始 issue 描述,确认自己还在正确的方向上。这个动作成本很低,但能有效防止跑偏。
6.4 工具调用报错或返回空
Agent 依赖工具,工具出问题整个流程就断。
常见原因有几个:命令拼写错误、路径不对、权限不足、依赖没装。排查时先让模型把完整的命令和报错原样输出,别让它自己"解释",很多时候一看命令就知道错在哪。
还有个隐蔽原因是输出太长被截断。比如git log不加限制,输出几千行,工具返回被截断,模型只看到一半。解决办法是给所有可能输出很长的命令加限制,比如git log --oneline -20、grep -m 10。
6.5 快速排查对照表
| 现象 | 最可能的原因 | 优先排查动作 |
|---|---|---|
| 反复改同一处不生效 | 误解报错根因 | 往上追一层调用栈 |
| 目标测试过、其他测试挂 | 改动引入回归 | 检查函数引用点 |
| 跑着跑着跑偏 | 上下文污染 | 压缩上下文、重述任务 |
| 工具返回空或报错 | 命令或环境问题 | 原样输出命令和报错 |
| 定位阶段耗时过长 | 没用 git 历史 | 强制先跑 git log |
| 补丁能跑但不解决问题 | 治标不治本 | 检查是否覆盖边界情况 |
7. 我踩过的几个坑
最后分享几个我自己在搭编程 Agent 工作流时踩过的坑,都是花钱花时间换来的。
第一个坑是过度信任模型的自我评估。早期我让模型改完自己判断"是否完成",结果它经常在没跑测试的情况下就说完成了。后来我改成强制它必须贴出测试命令和输出,没有输出就不算完成。这个改动之后,虚假完成率降了一大截。
第二个坑是prompt 写得太抽象。"请做最小改动"这种话模型理解不了,它不知道什么叫最小。改成"改动行数不超过 30 行、不改函数签名"之后,效果立竿见影。给模型定规矩,一定要给可量化、可验证的标准。
第三个坑是忽略了仓库的测试基础设施。有些仓库的测试需要特定环境变量、特定数据库、特定 fixture,模型不知道这些,跑测试就报环境错误,然后它开始"修"环境问题,越修越乱。后来我在 prompt 里加了一段仓库特定的测试说明,告诉它怎么正确跑测试,这类问题基本消失了。
第四个坑是轮次上限设太高。我一开始设 30 轮,想着给模型充分的机会。结果发现超过 15 轮之后,模型基本在做无用功,纯粹烧钱。现在我把上限压到 12 轮,超了就终止,让它带着当前状态汇报,我人工介入。这样整体成本反而更低,因为省下的轮次够我重跑好几次。
这套东西没有一劳永逸的配置,仓库在变、模型在变、任务在变,工作流也得跟着调。但核心逻辑是稳定的:让 Agent 像工程师一样先看历史、再做最小改动、最后跑全量验证。把这三件事固化成流程,比换任何模型都管用。