SWE-CI:基于持续集成评估智能体代码库维护能力
引言
自动化软件工程一直是人工智能领域的核心目标。近年来,大语言模型(LLM)的突破性进展为这一目标提供了强劲动力 —— 从代码补全、测试生成到端到端程序修复,基于大语言模型的智能体已在多项基准测试中展现出可与人类开发者相匹敌的能力。编码基准测试的同步发展在这一进程中起到了关键作用,既提供了严谨的能力评估手段,也指明了清晰的研究方向。
在代码生成层面,HumanEval、MBPP 和 LiveCodeBench 确立了单文件代码合成的评估范式。在代码库层面,SWE-bench 提出了'Issue 到 PR'范式,要求模型在完整代码库环境中生成补丁。在智能体交互层面,Terminal-bench 和 τ-bench 进一步将评估范围拓展至终端操作与多轮工具使用。这些工作共同构建了一个多粒度、多场景的代码智能评估体系。
尽管这一评估体系具备广度与深度,但其底层范式仍存在一个根本性局限:现有基准几乎只关注评估智能体编写功能正确代码的能力。然而在现实场景中,成功的软件很少是一蹴而就的,它是长期维护的结果。Lehman 定律表明,软件质量会随着维护过程自然下降;而经典文献早已证实,维护活动占软件整个生命周期成本的 60%~80%。因此,亟需设计新的基准,以有效衡量模型对代码的长期维护能力。
这种能力长期未被纳入评估,其根源在于主流基准测试范式本身。从 HumanEval、LiveCodeBench 到 SWE‑bench 和 Terminal‑Bench,现有基准普遍采用快照式评估流程:智能体接收一次完整的需求,并给出一次性解决方案。在这种范式下,一个写出硬编码、脆弱修复的智能体,与一个写出整洁、可扩展代码的智能体,可能都能通过同一套测试用例——二者在可维护性上的差异完全无法体现。只有当代码库需要持续演进时(新需求出现、接口变更、模块必须扩展),这种差异才会显现。此时,早期设计决策带来的代价会不断累积。一个经常生成结构糟糕代码的智能体,会发现后续修改越来越困难,最终无法跟上迭代节奏。由此得到一个关键结论:智能体的代码维护能力,只能通过长期演进过程来体现——在持续的代码变更中,历史决策的后果会不断累积并显现。
基于这一认识,我们提出 SWE‑CI(软件工程–持续集成),这是一个用于评估智能体在长期代码演进过程中维护代码能力的全新基准。SWE‑CI 包含 100 个任务,每个任务均来自真实代码库,由一个起始提交和一个目标提交定义,平均覆盖 233 天的真实演进历史与 71 次连续提交。SWE‑CI 采用架构师–程序员双智能体评估机制:从起始提交开始,智能体执行持续集成循环,迭代生成需求、修改源码并运行测试,最终目标是通过与目标提交相关的所有测试。SWE‑CI 提出 EvoScore(演进得分)作为代理指标:它衡量智能体在后续代码修改中的功能正确性,因此早期决策更利于后续演进的智能体会获得更高分数,而不断累积技术债的智能体则会表现持续下降。
评估框架与方法
任务形式化
我们首先为智能体编码任务建立统一的形式化定义。设 $t$ 表示单个单元测试,$T = {t_1, t_2, \dots, t_{|T|}}$ 为我们关注的所有测试构成的集合。设 $C$ 为代码库空间,$R$ 为需求空间。我们进一步定义两个函数:
requireT : C × C → R,该函数用于识别两个代码库之间关于测试集 $T$ 的功能差异,并据此生成需求文档。codeT : R × C → C,该函数根据给定需求对代码库进行修改,并返回更新后的代码库。
基于上述定义,我们发现目前许多主流的代码基准测试都遵循基于快照的评估范式。在该范式中,需求仅依赖于基础代码库 $c_0$ 与'标准答案'代码库 $c^$,即 $r \equiv \text{requireT}(c_0, c^)$。在实际使用中,基准维护者会预先生成需求 $r$ 并将其保存为提示词,因此基准使用者无需重新生成。然而,本文转而考虑基于演进的评估范式。该范式中的需求是从当前代码库动态推导而来:$r_i = \text{requireT}(c_i, c^*)$,代码库也随之更新:$c_{i+1} = \text{codeT}(c_i, r_i)$。这一迭代循环保证了早期修改产生的影响会传递到后续迭代中,从而使得智能体的长期决策质量可被观测。

归一化变更
大多数基准测试将通过所有测试用例作为功能正确性的黄金标准。然而在软件工程中,部分功能需要长期规划与增量开发,而非一次性实现。此外,在代码演进过程中,原本通过的测试很容易被意外破坏——这种现象被称为回归(regression)。因此,我们需要一种更细粒度的指标来反映代码库 $c$ 的当前状态,而非简单的'通过/失败'二值判断。为此,我们提出归一化变更(normalized change)。
设 $n(c)$ 为代码库 $c$ 通过的测试用例数量: $$ n(c) = \sum_{t\in T} \mathcal{I}(t, c) $$ 其中指示函数 $\mathcal{I}(t, c)$ 当且仅当单元测试 $t$ 在代码库 $c$ 上通过时取值为 1,否则为 0。







