SWE Refactor Bench:智能体说"迁移做完了",你信吗?

如果你让一个编程智能体把仓库里的 Maven 全换成 Gradle,它跑完之后告诉你"搞定了,测试全绿"——你敢直接合并吗?

说实话,我的第一反应是:测试全绿不就行了?但这篇论文戳破了一个我之前真没仔细想过的盲区:迁移任务的起点,本来就是全绿的。原仓库的测试套件在原实现上当然全过,智能体只要把代码原样抄一遍、或者包一层壳,测试照样全绿。行为测试能证明"没把东西弄坏",但永远证明不了"迁移真的发生了"。论文给这个失效模式起了个名字,叫 Blindness(盲视)

核心摘要

SWE Refactor Bench 是一个专门考"整仓技术栈迁移"的基准:20 个真实开源基础设施仓库(SQLite、zlib、libsodium、GraphHopper 这个量级),覆盖语言、框架、平台、构建工具链四类技术债,每个任务给智能体 6 到 30 小时自主工作。它的杀手锏是一套三阶段评测协议——先用 Migration Audit 硬闸门验证"迁移到底发生没有",再用 13 万个固定行为检查验证"行为一点没变",最后放 6 个独立编码智能体当"破坏者",各拿一小时专门找固定测试没想到的行为差异。结果相当惨烈:8 个前沿模型、26 种配置、520 次运行,只有 28 次全部通关(5.4%),最强的 claude-opus-5 也只拿到 47.0 分(满分 100)。这篇论文的价值不在刷榜,在于它把"迁移评测"这件事本身的评测方法论问题讲透了,值得每个做 Agent 评测的人细读。

论文信息

  • 标题:SWE Refactor Bench: Can Coding Agents Complete a Long-Horizon, Whole-Repository Stack Migration?
  • 作者:Deyao Hong、Yizhe Chi、Wenyi Li(共同一作)、Xiaoqiu Wang、Mingju Gao、Kaisen Yang、Bingxiang He、Youjie Zheng、Calvin Xiao、Qinhuai Na(通讯)
  • 机构:Navers Lab, Einsia.AI;清华大学
  • 时间:2026 年 8 月 24 日,arXiv:2608.23564
  • 主页:https://lab.einsia.ai/swe-refactor-bench

🎯 问题动机:为什么现有基准答不了这个问题

SWE-bench 那套范式大家都熟:真实仓库、真实 issue、一个"红转绿"的测试信号——补丁前测试挂、补丁后测试过,这个跳变本身就是"活干完了"的证据。后续的一大票基准,SWE-EVO、SWE-CI、Terminal-Bench,无非是把这个范式拉长到更大的时间尺度。

但整仓迁移这个任务家族,天生没有"红转绿"信号。起点是全绿的,终点也应该是全绿的。论文把这个观察形式化得很干净:一个迁移任务是个六元组 \(\tau=(R_A,\;\Sigma_A\rightarrow\Sigma_B,\;\mathcal{O},\;\mathcal{I},\;E,\;B)\),其中 \(R_A\) 是源栈 \(\Sigma_A\) 上的原始仓库,\(\Sigma_B\) 是目标栈,\(\mathcal{O}\) 是构建产物的可观测接口。一次成功的迁移要同时满足两个条件:

\[\Sigma_B \text{ 构建出交付产物,且 } \Sigma_A \text{ 从仓库和构建闭包中消失} \tag{迁移条件}\]
\[\mathcal{O}(R_S) = \mathcal{O}(R_A) \tag{保持条件}\]

问题来了。行为测试套件 \(T\)\(\mathcal{O}\) 的有限子集,而原始仓库 \(R_A\) 在任何 \(T\) 上的得分恒等于 1——这是构造出来的,不是巧合。也就是说,智能体交一个"空 diff"(原样奉还)就能在任何行为套件上拿满分,同时连迁移条件的一个字都没满足。奖励的最大值,端端正正地坐在一份零工作量的提交上。而且加再多测试也没用,因为迁移后仓库要过的每个用例,原仓库本来就过。

这个洞不在测试够不够密,而在评测仪器看的东西不对。堵它的唯一办法,是在行为之外再加一个握有否决权的检查。

🏗️ 基准设计:先定技术债,再找仓库

任务构造的顺序挺讲究的——不是"挑个仓库再想让它改什么",而是反过来:先定下一个维护者会承认"早该还了"的技术债,再去找那个"这次迁移就是全部工作"的项目。入库有三条硬要求:旧栈必须是承重墙(拆掉它要动设计,不是改几行 import);可观测接口必须有真实下游消费者(C ABI、HTTP API、安装树都算);原系统必须能反复构建运行(这是差分测试的前提)。

20 个任务分四类技术债,盘子不小:

技术债类别 代表性迁移 任务数 源码规模 时间预算(小时) Stage I 判据 Stage II 检查数
语言重写 C→Rust、C→Java、Go→Zig 7 4.4k–39.8k 12–30 60 59,771
框架重写 Flask→Starlette、Gin→chi、Vue→React 7 0.8k–94.8k 6–16 39 55,852
平台移植 POSIX→wasm32-wasi、Node→V8 realm 3 16.0k–358.0k 6–10 19 6,725
构建工具链 Autotools→CMake、Maven→Gradle 3 19.0k–78.8k 6 18 7,770
合计 20 个上游项目 20 867,062 行 6–30 262 130,118

智能体拿到的是一个离线容器:原始仓库、两套工具链、一份指令,没有网络。评测侧的三样东西——审计判据、固定测试、验证智能体——活在另一个从不挂载给它的镜像里。不是"它没读",是"对它而言不存在"。

图2:任务执行与隐藏评测的隔离

图2:左边是智能体能看到的世界——源栈上的真实仓库(1K–500K 行代码、6–30 小时预算),自己走完理解、规划、重写、构建调试、对照验证五步;右边是它永远碰不到的三阶段隐藏评测。底部那句话很戳人:"差一点过不等于差一点及格——一个漏网的 bug 就足以让整个重写被打回。"

🔧 三阶段协议:三道闸门各管一件事

图1:SWE Refactor Bench 总览

图1:顶部对比了旧范式(单阶段行为测试,没有迁移审计,原样提交也能全绿)和本基准的三阶段串联;左下是四类技术债的任务覆盖;右下是 8 个前沿模型各自最强配置的综合得分——最高的 claude-opus-5 也只有 47 分。

Stage I:Migration Audit(迁移审计)。只问一件事:迁移真的发生了吗?判据写成针对这个仓库的提问,由评判模型(gpt-5.6-sol)读着两棵源码树逐条裁决,每个否定结论必须引用可复查的证据,每条判据独立评三次取多数。任何一条不过,直接否决。这是乘法项,不是加分项——迁移没发生,行为再对也是零分。

Stage II:Behavioural Tests(行为测试)。13 万个检查全部从原始系统的真实运行中录制而来:同样的调用打在 \(R_A\) 上,录下来的输出就是标准答案。平均每个任务六千多个检查,错一个就是零分,全清才能进 Stage III。为什么这么狠?因为这类任务问的是"能不能做即插即用的替换"——一千次调用错一次的库不是替换品,那个失败的测试背后站着一个真实的下游消费者。

Stage III:Agentic Verification(智能体验证)。前两关检查的是"出题人事先想到的",这一关检查"没想到的"。6 个独立编码智能体,各拿一小时,手里同时握着原始和迁移后的两棵源码树,专门找固定套件没覆盖的行为差异。5 个各负责一个指定方向(C ABI、路由集合、安装树等),第 6 个不设限。交上来的不能是报告,只能是可执行的反例——在原系统上通过、在提交上失败,先对参考实现跑绿,再复现三次,flake 的不算。这实质上是差分测试的智能体形态。

计分公式把三层证据拧在一起:

\[S(\tau)=\underbrace{\mathbf{1}[g=\textsf{pass}]}_{\text{Stage I:否决}}\cdot\underbrace{\mathbf{1}[r_i=1\;\forall i]}_{\text{Stage II:全有或全无}}\cdot\Bigl(0.4+\underbrace{0.6\cdot\tfrac{s}{6}}_{\text{Stage III:按验证者计}}\Bigr)\in\{0\}\cup[0.4,1]\]

其中 \(s\) 是 6 个验证者中没找到反例的个数。Stage III 占 0.6 的权重且线性计分,理由很坦率:6 个验证者都没找到反例,比 1 个没找到更可信,但这是证据强度的问题,不是等价性证明——线性分数记录的正是这个"程度"。

📊 实验结果:漏斗窄得吓人,而且窄在三个不同的地方

实验配置:8 个前沿模型(claude-opus-5、claude-sonnet-5、gpt-5.6-luna、gpt-5.6-sol、kimi-k3、qwen3.8-max、dsv4-flash、glm-5.2),GPT 系用 Codex 脚手架、其余用 Claude Code,共 26 种模型-努力档配置,每种配置把 20 个任务各跑一次,520 次计分运行。

漏斗数据:340 次(65.4%)过了 Stage I,118 次(22.7%)过了全部固定检查,两者都过的 88 次进入 Stage III,最终 28 次(5.4%)活着走出来。20 个任务里 13 个没有任何模型解出来过。全体 520 次的平均得分只有 13.44,但进入 Stage III 的 88 次平均 79.43——难度全在"走到第三关"这件事本身。

最强配置排行榜(每模型取最佳行):

模型 脚手架 努力档 行为通过率 得分(/100) 单任务成本(美元)
Claude Opus 5 Claude Code xhigh 92.8% 47.0 74.9
GPT-5.6 Sol Codex max 84.1% 28.5 143.5
Kimi K3 Claude Code max 93.9% 19.5 28.9
Claude Sonnet 5 Claude Code medium 73.9% 15.0 11.9
GPT-5.6 Luna Codex max 89.1% 10.5 2.8
Qwen 3.8 Max Claude Code max 74.7% 10.0 14.5
DeepSeek V4 Flash Claude Code max 90.7% 7.0 4.3
GLM 5.2 Claude Code max 85.2% 6.5 17.5

有几个细节值得咂摸。Kimi K3 的行为通过率 93.9% 是全场最高,比 Opus 还高,但得分只有 19.5——行为分高不等于迁移过关。GPT-5.6 Sol 的 max 档烧到 143.5 美元一个任务,是 Opus xhigh 的近两倍,得分却低了快 20 个点,性价比相当难看。还有三个模型(gpt-5.6-luna、dsv4-flash、glm-5.2)一个 accepted 都没有,但各自都有几次"固定检查全过"的运行——只看行为测试,它们会带着几个"满分"登上 leaderboard;在三阶段协议下,它们什么都没解出来。这就是 Blindness 不是理论担忧、而是真实发生的直接证据。

🔬 三个行为发现,一个比一个扎心

发现一:"把代码写对"和"把迁移做完"是两种能力,而且智能体在两个相反的方向上各栽各的。30 次运行靠"不迁移"保住了全部行为检查(散布在 8 个模型中的 7 个),只有 Stage I 拦得住;另一边是八倍于此的 252 次——迁移真做了,行为真砸了,只有 Stage II 拦得住。两道闸门谁也替代不了谁:光有 Stage II 会奖励"啥也不干",光有 Stage I 会奖励"破坏性施工"。

最典型的是 lang01(cmark,C→Rust):6 次过了 Stage I,5 次固定检查全过——这 5 次全是 Blindness。它们把原 C 代码的控制流逐句直译成 Rust,手动内存管理原封不动搬过来,所有权压根没重新设计。这次迁移本来要买的就是 Rust 的安全性,结果没买到。行为测试表达不了这个,因为问题从来不在行为上。

图4:Stage I 必须分辨的四类提交

图4:四类典型提交。左上"啥也没改"——C 代码原样拷贝只换后缀;右上"包壳没改"——Rust 侧只是个 extern "C" 转发壳,原 C 还在干活;左下"改了一半"——部分函数真重写了,剩下的还在回调旧实现;右下"改错了"——真重写了但行为变了。前两类能过所有行为测试,只有读机理的仪器才拦得住。

发现二:最后 1% 是鬼门关。340 次真完成迁移的运行里,91% 把固定套件过了一半,58% 到达 99%,36% 到达 99.9%——但只有 26% 做到一个不错。140 次运行在 99% 到 100% 之间咽气,中位数只差 12.5 个检查,18 次只差 1 个。而且这些"只差一个"不是随机的:fw03(conduit,Vue→React)上四个不同模型全部卡在 21768/21769,挂的是同一个检查——原版用 hash 路由,访问根路径会落在 /#/,React 版停在 /,所有书签和分享链接全废;build03(PyCryptodome,setuptools→Meson)上五个模型全部卡在 380/381——构建产物 wheel 的 METADATA 长描述是 0 个字符,发出去 PyPI 项目页就是一片空白。哪个都不是吹毛求疵,全是生产事故级别的回归。

发现三:过了全部固定检查,也只走完三分之二。88 次满分提交里,60 次(68.2%)被验证者在一小时内抓出反例,平均每次提交只挡得住 6 个验证者中的 3.94 个。被攻破还来得很快:找到反例的中位时间 17 分钟,而"幸存"的中位耗时是 32.8 分钟。Stage III 抓到的差异确实是出题人想不到的——比如 fw04(ChartMuseum,Gin→chi)里 Gin 会在第一个空格或分号处截断 Content-Type,迁移版只在分号处截断,boundary 前多一个空格的请求就被分发到完全不同的处理器;lang05(go-yaml,Go→Zig)里 Go 的 time.Parse 居然接受逗号当小数点,于是一个带逗号的时间戳在原版是 !!timestamp、在 Zig 版成了 !!str。这种东西,写测试的人怎么会事先想到?

🧪 能力画像:四类迁移,瓶颈各在不同关卡

类别 Stage I 通过率 Stage II 通过率 Stage III 存活率 被接受数 得分
构建工具链 80.8% 54.0% 17.6% 6 31.4
平台移植 57.7% 37.8% 23.5% 4 17.2
框架重写 72.5% 18.9% 56.0% 14 12.0
语言重写 54.9% 12.0% 33.3% 4 5.6

注意一个反直觉的错位:构建工具链在前两关最容易(80.8%、54.0%),第三关存活率却全场最低(17.6%);框架重写反过来,Stage II 只有 18.9% 能过,但过了的里头 56% 扛住了验证者。作者的解释挺合理:工具链和平台类任务动的是"怎么构建、跑在哪",产品代码基本不动,所以容易过固定测试,但 Stage III 专找套件之外的行为差异,一抓一个准;框架和语言类任务动的是产品代码本体,固定测试在 Stage II 就把大多数提交筛死了。28 个被接受的迁移里 14 个来自框架类——难关过得早,反而说明剩下的都是硬功夫。

⚖️ 评测本身靠谱吗?作者自证了一轮

这套协议的前提是"仪器可信",作者花了整整一节自证:

Stage I 的评判稳不稳?3,536 条判据裁决里 96.3% 三次采样完全一致,只有 131 条 2:1 分裂。但别小看这 131 条——Stage I 要求条条通过,340 次过关运行里有 35 次(10.3%)至少有一条判据是靠 2:1 勉强过的。不搞多数投票,过关数会从 340 掉到 305。三次采样多数制在这里是承重的,不是仪式感。跟人类标注对比:两位外部研究者在 6 个任务 156 次运行上独立标注,人机一致率 89.7%(κ=0.795),且 16 处分歧里 14 处是机器偏严——错误的方向是让分更难拿,不是更好拿。评判模型也没有偏袒自家:gpt-5.6-sol 对 GPT 系提交的通过率 57.1%,比对别人家的 72.5% 还低。

6 个验证者是不是摆设?两个 claude-opus-5 验证者的攻破率是 55.7% 和 53.4%,其余四个只有 21.6% 到 26.1%。同一模型换 prompt、换努力档,攻破率只动两个点;换模型,动三十个点。这暴露了一个挺深的事实:报告的分数既是提交的性质,也是这个评审团的性质——把最强的两个验证者撤了,accepted 会从 28 涨到 46。所谓"被接受的迁移"不是"被证明正确的迁移",而是"扛住了我们能派出的最强对手的迁移"。随着模型变强,同一批 20 个任务会被打得越来越严。至于串通问题,数据反而指向反方向:验证者对自家模型提交的攻破率 33.9%,对别人家 34.3%,几乎一样。

🤔 我的判断

这篇论文最值钱的不是那个 47 分的 leaderboard,而是它把"行为保持型任务"的评测方法论问题形式化清楚了。Blindness 这个概念——奖励最大值坐在零工作量提交上、且加密测试无用——其实适用于一大类任务:重构、依赖升级、性能优化(行为不变的优化),凡是"起点即全绿"的任务家族全都中招。之前做 Agent 评测的时候我也隐约觉得"测试全绿"这个信号越来越不够用了,但没像这篇这样把"为什么不够用"证明得这么干净:\(\mathrm{rate}(R_A;T)=1\) 对任意 \(T\) 成立,一行公式把问题钉死了。

当然也要泼点冷水。20 个任务的规模决定了统计噪声不小——每个配置每任务只跑一次,Opus 5 的 xhigh 档 47.0 分和 max 档 31.0 分之间的差距,很可能有相当部分是运气而非努力档的真实效应,论文没给置信区间。Stage I 用模型当判官,虽然做了三采样多数制和人类一致性校验,但 262 条判据的编写质量本身没法完全自证,"Rust 是否真的重新设计了所有权"这种判据的裁决边界其实是模糊的——作者自己报告的 14 例"机器偏严"分歧就是证据。另外 Stage III 的分数绑定在"当前最强验证者"身上,这个设计诚实是诚实,但也意味着分数跨时间不可比,leaderboard 的长期维护会是个麻烦。

工程上的启发倒是立竿见影。如果你在用编码智能体做依赖升级、框架替换这类"行为应保持不变"的活,别只看测试绿不绿:加一个"旧栈是否真的消失"的静态审计(哪怕是 grep 级别的),再加一个拿原系统当参照的差分 fuzzing,成本不高,拦住的恰恰是最危险的那类假完成。而对于做 Agent 评测的人,这篇论文立了个新标准:评测协议本身需要被当作研究对象来对待,三阶段、否决权、可执行反例,这套组合拳大概率会被后续的迁移类基准直接抄走。

最后说一句有点悲观的观察:58% 的真迁移能到 99%,但只有 26% 能到 100%,而那最后一步恰好是"生产事故和正常发布"的分界线。编程智能体现在最缺的不是"写得更多",而是"收得了尾"。这个 benchmark 估计会在榜上硬挺很久。


觉得有启发的话,欢迎点赞、在看、转发。跟进最新AI前沿,关注我