霍格沃兹测试开发学社

《Python测试开发进阶训练营》(随到随学!)
2023年第2期《Python全栈开发与自动化测试班》(开班在即)
报名联系weixin/qq:2314507862

Claude Opus 5 把 SWE-bench 刷到 97%,为什么你的回归集还是拦不住它

关注 霍格沃兹软件测试开发 公众号,回复「资料」, 领取人工智能测试开发技术合集

九月第一周,AI 编程圈密集放了几发。

其中最扎眼的一条:Anthropic 的 Claude Opus 5 在 SWE-bench Verified 上跑到 97.00%,登顶,同时价格砍到上一代的一半左右。行业综述把这一波总结成一句话——AI 编程正在从「Always-On Agent」跨进「生产级控制面 + 资深工程评估」的双轨阶段。

另一边,市占数据也在同步变化。有统计口径显示,90% 的开发者已经离不开 Agent 类工具,Claude Code 以 39% 的使用率排在第一。

这两条消息放在一起看,很多人第一反应是「模型又变强了」。但如果你是做质量的,应该看到另一个信号:

当一个公开基准的头部成绩逼近 97%,这个基准就已经不再区分强弱了。

而这件事,和你手上那套常年全绿的回归集,是同一个病。

一、先说清楚 SWE-bench Verified 是什么
SWE-bench 的做法是从真实开源仓库里捞 issue,把 issue 描述和当时的代码库快照交给模型,让它提交补丁,最后用该项目自带的测试套件判定是否通过。Verified 是其中经过人工复核、确认「题目可解、判定可靠」的子集,所以分数含金量比原始榜单高。

它的价值在于「真实」:不是人造算法题,是真实仓库里的真实缺陷。

它的局限也在于「真实」:这些 issue 已经被人工筛过一遍,筛掉了描述不清、依赖缺失、无法复现的部分。

换句话说,SWE-bench Verified 是一个已经被清理干净的真实世界。

二、97% 之后,基准失去了分辨率

97% 意味着什么?意味着剩下 3% 的题目,可能是标注争议、可能是极端长尾、也可能就是模型确实做不出来。但从选型的角度看,你已经无法用这个分数区分「谁更能干」了——95% 和 97% 在你的项目里可能表现为完全一样的能力。

这就是基准饱和。饱和之后,行业会做两件事:一是造更难的尺子(所以这一波同时出现了 4 个新基准),二是把评估重心从「能不能做出来」挪到「做出来之后能不能被控制」。

所谓「生产级控制面」,说的就是后者:不是让 Agent 一直在线自己跑,而是给它加上权限边界、审批闸口、可回放的执行轨迹和风险分级放行。

所谓「资深工程评估」,说的是评估方式本身在升级:不再只看最终答案对不对,而是看它的解法是不是一个资深工程师会认可的解法。

这两个词听起来很新,但做质量的人应该有强烈的既视感——这就是我们这些年一直在干的事,只不过对象从人换成了模型。

三、五强混战背后,测试的位置在往前挪
再看市占。90% 的开发者已经在用 Agent 写代码,Claude Code 39% 排第一,剩下的份额被几家分掉。

这个数据对测试团队的实际含义不是「该学哪个工具」,而是:进入代码库的代码,已经有相当比例不是人手写的了。

代码产出速度上去之后,质量债不会消失,只会往后堆。堆到哪个环节爆,取决于你的验证入口设在哪。如果验证入口还停在「提测之后跑一遍回归」,那你实际上是在用去年的产能,审今年的产量。

所以这一波发布里最值得注意的,不是 97% 这个数字,而是评估这件事被提到了和生成同等重要的位置。

下面把镜头转回你自己手上那套回归集。

人工智能技术学习交流群
伙伴们,对AI测试、大模型评测、质量保障感兴趣吗?我们建了一个 「人工智能测试开发交流群」,专门用来探讨相关技术、分享资料、互通有无。无论你是正在实践还是好奇探索,都欢迎扫码加入,一起抱团成长!期待与你交流!👇

image

四、你的回归集全绿,和 97% 是同一种沉默
先问一个问题:你的回归集最近一次「全绿」是什么时候?

再问一个更难的问题:它全绿,是因为代码没问题,还是因为它根本抓不到问题?

这两句话在报告上长得一模一样,都是那个令人安心的绿色。区别在于,前者是质量信号,后者只是装饰品。

基准饱和和回归集全绿,本质是同一个现象:指标到达了天花板,于是失去了分辨率。 一个 97% 的基准分不出模型强弱,一个 100% 的通过率分不出版本好坏。

而通过率一旦失去分辨率,团队接下来会做的事几乎是可以预测的——继续加用例。用例从 800 条加到 3000 条,通过率还是 100%,跑得还更慢了。

问题不在数量。

五、公开基准与自建回归集,口径差在哪
很多人看到 97% 会下意识做个推论:模型这么强,那我把活交给它,我的回归集应该能兜住。

这个推论有漏洞。两边的口径根本不一样:

维度
SWE-bench Verified
你的回归集
任务边界
单仓库、单 issue、代码快照已冻结
跨服务、跨版本、依赖真实数据与配置
需求完整度
issue 描述经过人工复核,可解性已确认
需求文档常常缺失,一半规则是口头约定
判定来源
项目自带测试套件,作者写的
断言由你自己写,覆盖到什么程度没人复核过
失败代价
刷分,无生产后果
线上事故,有人要写复盘

第三行是最要命的。SWE-bench 用的是别人为了保障自己项目而写的测试,那套测试是被真实缺陷打磨过的。而你回归集里的断言,很多是为了「让这条用例能跑通」而写的,它从来没被验证过能不能抓到 bug。

所以正确的理解是:公开基准告诉你模型的能力上限,你的回归集决定你的风险下限。前者不能替代后者,97% 也不会替你兜底。

六、通过率失效之后,该量什么
既然通过率没有分辨率了,就得换一个有分辨率的指标。

答案是:缺陷检出率。

不问「我的用例通过了多少」,改问「如果我往里塞一个已知缺陷,我的用例能抓到几个」。

这个思路在学术上叫变异测试(mutation testing):往被测代码里注入一批语义上明确错误的小改动,每个改动叫一个变异体,然后跑测试集。被至少一条用例判失败的变异体叫「被杀死」,全程没被发现的叫「存活」。

检出率 = 被杀死的变异体 / 注入的变异体总数。

这个指标的好处是它天然有分辨率:它不会因为你的用例多就自动变高,只会因为你的断言真的写到了点子上才变高。

七、给你的回归集做一次体检
不需要一上来就引入 mutmut 这类完整框架。先用一个手写清单,把最容易漏的缺陷类型各注入一个,就能看出回归集的真实水位。

mutation_probe.py

思路:往被测代码注入已知缺陷 → 跑回归集 → 统计有多少被抓到

import subprocess, shutil, pathlib, json

MUTATIONS = [
# (文件, 原始片段, 变异片段, 缺陷类型)
("service/refund.py", "if amount <= balance:", "if amount < balance:",
"边界 off-by-one"),
("service/refund.py", "status = 'REFUNDED'", "status = 'SUCCESS'",
"状态枚举错写"),
("service/coupon.py", "return round(total, 2)", "return total",
"金额精度丢失"),
("dao/order.py", "ORDER BY created_at DESC", "ORDER BY created_at",
"排序方向反转"),
("service/notify.py", "if retry_count < 3:", "if retry_count < 30:",
"重试上限失效"),
("service/auth.py", "if token.exp < now:", "if token.exp > now:",
"过期判定取反"),
]

def run_probe(root: pathlib.Path, pytest_target: str):
results = []
for rel, old, new, kind in MUTATIONS:
f = root / rel
backup = f.read_text(encoding="utf-8")
if old not in backup:
results.append({"kind": kind, "status": "SKIP", "reason": "锚点未命中"})
continue
try:
f.write_text(backup.replace(old, new, 1), encoding="utf-8")
p = subprocess.run(
["python", "-m", "pytest", pytest_target, "-x", "-q", "--no-header"],
capture_output=True, text=True, timeout=600,
)
# 用例失败 = 变异体被杀死 = 回归集有效
killed = p.returncode != 0
results.append({
"kind": kind,
"status": "KILLED" if killed else "SURVIVED",
"file": rel,
})
finally:
f.write_text(backup, encoding="utf-8") # 必须还原,别把变异提交上去
return results

if name == "main":
res = run_probe(pathlib.Path("."), "tests/regression")
valid = [r for r in res if r["status"] in ("KILLED", "SURVIVED")]
killed = [r for r in valid if r["status"] == "KILLED"]
rate = len(killed) / len(valid) if valid else 0
print(json.dumps({
"检出率": f"{rate:.0%}",
"存活变异体": [r["kind"] for r in valid if r["status"] == "SURVIVED"],
"跳过": [r["kind"] for r in res if r["status"] == "SKIP"],
}, ensure_ascii=False, indent=2))
三个实现上的注意点:

第一,锚点要唯一。replace(old, new, 1) 只替换第一处,如果这段代码在文件里出现多次,你注入的可能不是你以为的那个位置。跑之前先 grep 确认唯一性。

第二,finally 里的还原不能省。 这段脚本会真实改写源码,中途 Ctrl+C 或者超时抛出,都可能把变异留在工作区。更稳妥的做法是在一份 git worktree 副本里跑,主工作区根本不碰。

第三,-x 是刻意的。 遇到第一个失败就停,因为你只需要知道「有没有被抓到」,不需要知道「被抓到几次」。这能让一轮体检快好几倍。

八、检出率怎么用:三条判定线
跑出来之后,别看总分就完事,要看存活清单。

检出率 ≥ 80%:回归集是有效的质量信号,可以支撑风险分级放行。
60%—80%:能挡住大部分问题,但存活的那几类就是下一次线上事故的候选。把存活变异体按缺陷类型归类,优先补断言,而不是补用例。
< 60%:这套回归集目前的主要作用是让人安心。这时候再往里加用例,只会让它跑得更慢、绿得更假。
存活清单的价值远大于那个百分比。「边界 off-by-one 存活」这句话,直接告诉你断言写的是 assert result == expected 而不是 assert result <= limit;「排序方向反转存活」告诉你根本没有用例校验返回顺序。

这两条信息,比「通过率 100%」有用一万倍。

推荐学习
测试智能体与智能化测试平台公开课,从Web/App/接口测试智能体,再到智能体工具Opencode,爱测智能化测试平台,手把手带你掌握AI智能体与智能化测试平台!

👉 扫码进群,报名学习!

image

九、写在最后
97% 是个漂亮的数字,但它同时也是一个提醒:当指标到达天花板,它就不再是指标,而是遮羞布。

模型的基准会饱和,你的回归集也会饱和。区别在于,基准饱和了行业会去造新的尺子,而回归集饱和了,大多数团队会选择继续加用例。

真正该做的动作只有一个——定期去验证你的测试到底能不能抓到 bug。

这件事没人给你打分,也不会上榜单。但线上出事的时候,它是唯一真正拦住过你的东西。

我们在做 AI 测试开发的工程化落地,如果你想看看变异注入这套东西在真实项目里怎么跑起来,留言区聊聊。

关于我们
霍格沃兹测试开发学社,隶属于 测吧(北京)科技有限公司,是一个面向软件测试爱好者的技术交流社区。

学社围绕现代软件测试工程体系展开,内容涵盖软件测试入门、自动化测试、性能测试、接口测试、测试开发、全栈测试,以及人工智能测试与 AI 在测试工程中的应用实践。

我们关注测试工程能力的系统化建设,包括 Python 自动化测试、Java 自动化测试、Web 与 App 自动化、持续集成与质量体系建设,同时探索 AI 驱动的测试设计、用例生成、自动化执行与质量分析方法,沉淀可复用、可落地的测试开发工程经验。

在技术社区与工程实践之外,学社还参与测试工程人才培养体系建设,面向高校提供测试实训平台与实践支持,组织开展 “火焰杯” 软件测试相关技术赛事,并探索以能力为导向的人才培养模式,包括高校学员先学习、就业后付款的实践路径。

同时,学社结合真实行业需求,为在职测试工程师与高潜学员提供名企大厂 1v1 私教服务,用于个性化能力提升与工程实践指导。

posted @ 2026-09-11 11:10  霍格沃兹测试开发学社  阅读(14)  评论(0)    收藏  举报