SWE-BENCH: Can Language Models Resolve Real-world Github Issues?
SWE-BENCH: Can Language Models Resolve Real-world Github Issues?
- 作者/机构: Carlos E. Jimenez* 1,2, John Yang* 1,2, Alexander Wettig1,2, Shunyu Yao1,2, Kexin Pei3, Ofir Press1,2, Karthik Narasimhan1,2
- 机构: 1普林斯顿大学 2普林斯顿语言与智能 3芝加哥大学
A1 主要贡献
语言模型(LMs)的发展速度已经超越了我们有效评估它们的能力,但为了其未来发展,研究其能力的前沿至关重要。本文发现,现实世界的软件工程是一个丰富、可持续且具有挑战性的测试平台,可用于评估下一代语言模型。
核心问题与研究目标:现有的编程基准测试(如HumanEval)大多涉及几行代码就能解决的自包含问题,未能反映真实软件工程的复杂性。真实的软件工程任务,如修复一个bug,可能需要导航大型代码库、理解不同文件中的函数交互或在复杂代码中发现微小错误。为了解决这一评估差距,本文旨在创建一个更真实、更具挑战性的基准,以反映语言模型在现实世界软件工程任务中的应用能力。
主要贡献与创新点:
1. 推出SWE-bench基准:本文引入了一个名为SWE-bench的评估框架,包含从12个流行的Python代码库中提取的2,294个真实的软件工程问题。这些问题源于真实的GitHub issue及其对应的拉取请求(pull request)。
- 任务定义:给定一个代码库和一份待解决问题的描述,语言模型的任务是编辑代码库以解决该问题。解决方案以补丁(patch)文件的形式生成,并通过代码库的测试框架进行评估。
- 任务特点:解决SWE-bench中的问题通常需要理解和协调跨多个函数、类甚至文件的变更,要求模型具备与执行环境交互、处理超长上下文和进行复杂推理的能力,远超传统代码生成任务。
-
评估前沿模型的能力:本文评估了多个最先进的语言模型在SWE-bench上的表现,发现它们除了最简单的问题外都无法解决。表现最好的模型Claude 2仅能解决1.96%的问题,揭示了当前模型在真实软件工程任务上的巨大挑战。
-
发布训练数据集和模型:
- SWE-bench-train数据集:发布了一个包含来自37个代码库的19,000个非测试任务实例的训练数据集,旨在推动开放模型在这一挑战性领域的发展。
- SWE-Llama模型:基于CodeLlama模型,利用SWE-bench-train微调并发布了两个模型:SWE-Llama 7b和13b。在某些设置下,SWE-Llama 13b的性能可与Claude 2相媲美,并能处理超过10万个token的上下文。
SWE-bench的推进代表了向更实用、更智能、更自主的语言模型迈出的重要一步。
A3 背景知识与设计原则
SWE-bench是一个基准测试,其特点是包含了来自流行代码库的GitHub issues(报告错误或请求新功能)以及为解决这些问题而对代码库进行更改的pull requests。任务是生成一个能够解决给定issue并通过与该issue相关测试的pull request。
2.1 基准构建
三阶段流水线构建高质量任务。GitHub是软件开发的丰富数据源,但其中的代码库、issues和pull requests可能存在噪音、临时性或文档维护不善。为了大规模地找到高质量的任务实例,我们采用了如下的三阶段流水线。
第一阶段:代码库选择与数据抓取。我们首先从GitHub上12个流行的开源Python代码库收集pull requests(PRs),总共产生了约90,000个PRs。我们专注于流行的代码库,因为它们通常维护得更好,有明确的贡献者指南,并且有更好的测试覆盖率。每个PR都有一个由其基础提交(base commit)指定的关联代码库。
第二阶段:基于属性的筛选。我们通过选择满足以下两个条件的已合并PRs来创建候选任务:(1)解决了一个GitHub issue;(2)对代码库的测试文件进行了更改,这表明用户可能贡献了测试来检查issue是否已解决。
第三阶段:基于执行的筛选。对于每个候选任务,我们应用PR的测试内容,并在应用PR的其他内容之前和之后记录相关的测试结果。我们筛选掉那些没有至少一个测试状态从失败变为通过(以下称为fail-to-pass测试)的任务实例。我们还筛选掉那些导致安装或运行时错误的实例。
筛选结果与数据集特征。通过这几个筛选阶段,最初的90,000个PR被筛选至构成SWE-bench的2,294个任务实例。图3展示了这些任务实例在不同代码库中的最终分布情况,表1则突出了SWE-bench任务实例的关键特征。我们特别指出,这些代码库规模庞大,包含数千个文件,而参考的pull requests通常会同时对多个文件进行更改。关于SWE-bench构建流水线的技术细节在附录A中讨论,更多数据集统计信息见附录A.5。
2.2 任务制定
模型输入。模型会收到一份issue的文本描述和一个完整的代码库。然后,模型的任务是对代码库进行编辑以解决该issue。在实践中,我们将编辑表示为补丁文件(patch files),这些文件指定了为解决issue需要修改代码库中的哪些行。
评估指标。为了评估一个提议的解决方案,我们使用unix的patch程序将生成的补丁应用到代码库上,然后执行与该任务实例相关的单元测试和系统测试。如果补丁成功应用并且所有这些测试都通过,我们认为提议的解决方案成功解决了该issue。我们基准测试的指标是已解决任务实例的百分比。更多技术细节见附录A.4。
2.3 SWE-BENCH的特点
与传统基准的对比。NLP中的传统基准通常只涉及短的输入和输出序列,并且考虑的是专为该基准创建的某种程度上“人为”的问题。相比之下,SWE-bench的现实构建环境赋予了数据集独特的属性,我们将在下面讨论。
真实世界的软件工程任务。由于SWE-bench中的每个任务实例都包含一个庞大而复杂的代码库和对相关问题的描述,因此解决SWE-bench需要展示经验丰富的软件工程师所拥有的复杂技能和知识,而这些在传统的代码生成基准中并不常被评估。
可持续更新。我们的收集过程可以轻松应用于GitHub上的任何Python代码库,并且需要最少的人工干预。因此,我们可以通过持续提供新的任务实例来扩展SWE-bench,并在模型训练日期之后创建的issues上评估语言模型,这确保了解决方案不包含在它们的训练语料库中。
多样化的长输入。Issue描述通常很长且详细(平均195个单词),代码库通常包含成千上万个文件。解决SWE-bench需要在海量上下文中识别出为解决问题需要编辑的相对较少的代码行。
稳健的评估。对于每个任务实例,至少有一个用于测试参考解决方案的fail-to-pass测试,并且40%的实例至少有两个fail-to-pass测试。这些测试评估模型是否解决了issue中的问题。此外,平均还有51个额外的测试运行,以检查先前功能是否得到妥善维护。
跨上下文代码编辑。与先前可能将编辑范围限制在单个函数或类(例如,【8,Evaluating large language models trained on code,2021,arXiv】;【5,Multipl-e: A scalable and extensible approach to benchmarking neural code generation,2022,ICLR】)或提供填空式补全(例如,【41,Codexglue: A machine learning benchmark dataset for code understanding and generation,2021,CoRR】;【15,Incoder: A generative model for code infilling and synthesis,2023,ICLR】)的设置不同,SWE-bench不提供此类明确指导。我们的基准不仅要求模型生成简短的代码片段,还挑战模型在大型代码库的多个位置生成修订。SWE-bench的参考解决方案平均编辑1.7个文件、3.0个函数,以及32.8行代码(增加或删除)。
解决方案的广泛空间。代码库规模的代码编辑任务可以作为一个公平的竞争平台,用于比较从检索和长上下文模型到决策智能体等各种方法,这些智能体可以在代码中进行推理和行动。SWE-bench还允许创造性自由,因为模型可以生成可能偏离参考PR的新颖解决方案。
A2 方法细节
3 SWE-LLAMA:为SWE-BENCH微调CODELLAMA
开放模型的重要性。在SWE-bench上对开放模型与专有模型进行基准测试非常重要。在撰写本文时,只有CodeLlama模型(【52,Code llama: Open foundation models for code,2023,arXiv】)能够处理所需的超长上下文。然而,我们观察到现成的CodeLlama变体无法遵循详细指令来生成代码库范围的代码编辑,并且通常输出占位符响应或不相关的代码。为了更好地评估这些模型的能力,我们对70亿和130亿参数的CodeLlama-Python模型进行了监督式微调。由此产生的模型是专门的代码库编辑器,可以在消费级硬件上运行并解决GitHub issues。
表1:描述SWE-bench任务实例不同属性的平均值和最大值。统计数据是未按代码库分组计算的微观平均值。
训练数据。我们遵循我们的数据收集程序,从另外37个流行的Python包代码库中收集了19,000个issue-PR对。与第2.1节不同,我们不要求pull requests贡献测试更改。这使我们能够创建一个更大的训练集用于监督式微调。为消除数据污染的风险,训练数据中的代码库集合与评估基准中包含的代码库集合是不相交的。
训练细节。给定指令、来自GitHub的issue文本和相关代码文件作为提示,我们微调SWE-Llama以生成解决给定issue的补丁(即“黄金补丁”)。为了提高内存效率,我们仅使用LoRA Hu等人(2022)【23,LoRA: Low-rank adaptation of large language models,2022,ICLR】微调注意力子层的权重,并排除了超过30,000个token的训练序列,从而将训练语料库的有效大小减少到10,000个实例。更多细节在附录B中提供。
4 实验设置
本节我们解释了如何构建输入以运行SWE-bench评估。此外,我们回顾了本工作中评估的模型。
4.1 基于检索的方法
上下文选择问题。SWE-bench实例提供一个issue描述和一个代码库作为模型输入。虽然issue描述通常很短(如表1所示,平均195个单词),但代码库包含的token数量(平均438K行)远超通常可以放入LM上下文窗口的范围。那么问题来了,究竟如何选择相关的上下文提供给模型呢?
两种上下文设置。为了解决我们基线的这个问题,我们简单地使用一个通用的检索系统来选择要作为上下文插入的文件。具体来说,我们在两种相关上下文设置下评估模型:1)稀疏检索和2)预言式(oracle)检索。
稀疏检索。由于非常长的键和查询长度,以及使用自然语言查询检索代码文档的特殊设置,密集检索方法不适用于我们的场景。因此,我们选择使用BM25检索(【51,The probabilistic relevance framework: Bm25 and beyond,2009,Foundations and Trends in Information Retrieval】)来为每个任务实例检索相关文件作为上下文。我们试验了三种不同的最大上下文限制,并简单地检索尽可能多的文件以适应指定的限制。我们评估了每个模型在其上下文窗口内的所有限制,并报告了最佳性能。从观察来看,模型在最短的上下文窗口上表现最好,如表2所示。
“预言式”检索。为了分析目的,我们还考虑了一种设置,即我们“检索”由GitHub上解决该问题的参考补丁编辑过的文件。这种“预言式”设置不太现实,因为致力于解决问题的工程师可能事先不知道需要修改哪些文件。此外,这种设置也不一定全面,因为仅编辑的文件可能不包含理解软件与代码库未见部分交互时行为所需的所有上下文。
检索效果对比。我们将BM25检索结果与“预言式”检索设置的结果进行比较,如表3所示。我们观察到,在大约40%的实例中,对于27,000-token的上下文限制,BM25检索到了预言式文件的超集。然而,在近一半使用27,000-token限制的实例中,它没有检索到“预言式”上下文中的任何文件。
4.2 输入格式
构建模型输入。一旦使用上述两种方法之一选择了检索到的文件,我们就构建模型的输入,它包括任务指令、issue文本、检索到的文件和文档,最后是一个示例补丁文件和生成补丁文件的提示。实例示例和此格式的更多细节在附录D中提供。
4.3 模型
适用模型的选择。由于需要处理长序列,目前只有少数模型适用于SWE-bench。因此,我们评估了ChatGPT-3.5 (gpt-3.5-turbo-16k-0613)、GPT-4 (gpt-4-32k-0613)、Claude 2和SWE-Llama,它们的上下文限制如表4所示。
表2:使用BM25检索时,不同最大上下文长度下模型的解决率。
表3:对于不同的最大上下文长度,BM25相对于预言式文件的召回率。
表4:我们比较了不同的上下文长度和“预言式”检索设置所覆盖的比例。因此,上下文长度较短的模型天生处于劣势。注意,token长度的描述是一个相对非标准的度量(例如,Llama-tokenized序列平均比等效的GPT-4 tokenized序列长42%)。
A4 实验环境
-
数据集:
- 评估集 (SWE-bench): 包含2,294个软件工程问题,源自12个流行的Python GitHub代码库。每个问题都包含一个真实的用户issue和对应的代码库快照,任务是生成代码补丁来解决问题。
- 训练集 (SWE-bench-train): 包含19,000个issue-PR对,源自另外37个Python代码库,用于微调开放模型。
-
模型架构:
- 专有模型: ChatGPT-3.5 (gpt-3.5-turbo-16k-0613, 16k上下文)、GPT-4 (gpt-4-32k-0613, 32k上下文)、Claude 2 (100k上下文)。
- 开源模型: SWE-Llama 7b和13b,是基于CodeLlama-Python模型,使用LoRA方法进行监督式微调。
-
硬件配置:
- 训练: SWE-Llama 7b在4块NVIDIA A100 GPU上训练了20小时;SWE-Llama 13b在8块NVIDIA A100 GPU上训练了47小时。
- 推理: 未明确指定,但提到微调后的模型可在消费级硬件上运行。
-
软件配置:
- 代码库: 12个流行的Python项目,如django, scikit-learn, matplotlib等。
- 评估框架: 使用Unix的
patch程序应用模型生成的补丁,并通过每个代码库自带的测试框架(如pytest, tox)来验证解决方案的正确性。 - 训练框架: 使用DeepSpeed Ulysses和Flash Attention来支持长上下文训练。
A4 实验结果
我们报告了使用不同检索机制和提示风格的模型的实验结果,然后对模型性能和任务难度进行了一些分析和洞察。
整体性能:我们在表5中总结了使用BM25检索的模型性能。总体而言,模型在解决问题上都遇到了显著困难。表现最好的模型Claude 2仅能解决1.96%的问题。为了分析检索器对整体系统结果的重要性,我们在附录表18中展示了“预言式”检索的结果。在该设置下,Claude 2能够使用“预言式”检索器解决4.8%的问题。
表5:我们使用第4节描述的BM25检索器对不同模型进行比较。*由于预算限制,我们在SWE-bench的25%随机子集上评估GPT-4。
不同代码库的难度差异:如图4所示,按代码库分解性能时,所有模型在不同代码库上的表现趋势相似。尽管如此,每个模型解决的问题并不一定有大量重叠。例如,在“预言式”设置中,Claude 2和SWE-Llama 13b表现相当,分别解决了110和91个实例。然而,在这些实例中,Claude 2仅解决了SWE-Llama解决实例的42%。这可能与issue中存在图片有关(即![image][https://...])。某些代码库自然会有更多带有图片的实例;例如,matplotlib 32%和seaborn 10%的实例在其issue文本中包含嵌入图片,而所有实例中只有2%。解决这些实例可能需要多模态LM或某种外部工具来处理图片。
难度与上下文长度相关:聊天模型可能在长代码序列上进行了预训练,但通常被要求在有限的上下文框架下生成较短的代码片段。如图5所示,我们看到随着总上下文长度的增加,Claude 2的性能显著下降;这种行为在其他模型中也观察到。在我们的评估设置中,模型看到了大量可能与解决手头问题不直接相关的代码,它们似乎经常在定位需要更新的问题代码时遇到困难。这一结果证实了其他研究,即模型会被额外上下文分散注意力,并可能对目标序列的相对位置敏感(【38,Lost in the middle: How language models use long contexts,2023b,arXiv】)。即使增加BM25的最大上下文大小会提高相对于预言式文件的召回率,性能仍然下降,如表2所示,因为模型在定位问题代码方面效率低下。
“预言式-折叠”上下文消融实验:为了进一步研究这一点,我们对“预言式”检索上下文进行了一项输入消融实验,称为“预言式-折叠”,其中检索到的文件被完全折叠,除了真实pull request实际编辑的行(带有±15行的缓冲区),结果如表6所示。在这种设置下,我们看到性能有所提高,GPT-4从1.3%跃升至3.4%,Claude 2从4.8%增至5.9%。
表6:我们展示了“预言式”-折叠检索设置的结果,该设置使用预言式文件,但折叠了PR未直接修改的代码(保留±15行)。
难度与issue解决日期无关:在表7中,我们展示了在“预言式”检索设置下,按日期(2023年前后创建的PR)划分的模型结果。我们发现,对于大多数模型,这个日期前后的性能差异很小,除了GPT-4。我们认为这个结果在很大程度上是积极的,因为它表明尽管模型可能接触过某个代码库的某些版本,但它们不太可能通过简单生成代码库的较新版本来“作弊”解决问题。
表7:我们比较了2023年前后任务实例在“预言式”检索设置下的性能。大多数模型表现差异不大。*由于预算限制,GPT-4在SWE-bench任务的25%随机子集上进行评估,这可能会影响性能。
微调模型对上下文分布变化的敏感性:微调模型SWE-Llama 7b和13b在使用BM25检索的上下文时表现出奇地差。由于这些模型是使用“预言式”检索作为上下文进行微调的,我们怀疑这种上下文的转变使得模型难以可靠地执行。例如,SWE-Llama被训练来编辑作为上下文包含的每个文件,而在BM25设置中,上下文中提供的许多文件预计不会被更改。
生成补丁比生成整个文件更容易:模型通常在标准代码文件上进行训练,很少见到补丁文件。我们通常将任务设定为让模型生成补丁文件,而不是用它们提议的更改重新创建整个文件,因为补丁文件通常是文件更改的更高效表示。如表5所示,我们观察到模型在生成格式良好的补丁文件方面仍然存在困难。因此,我们尝试让模型重新生成整个文件以解决问题。在这种设置下,我们发现模型通常比生成补丁文件时表现更差;例如,在“预言式”检索的主要表格中,Claude 2得分为2.2%,而生成补丁为4.8%。即使在控制实例长度的情况下,在按输入token数较短的一半任务实例上生成,Claude 2的得分为3.9%,而生成补丁为7.8%。
语言模型倾向于生成更短、更简单的编辑:模型生成的补丁文件倾向于比它们各自的黄金补丁添加和删除更少的行。如表8所示,与平均的黄金补丁相比,模型生成的成功应用的补丁文件总长度不到黄金编辑补丁文件的一半(74.5行对30.1行),并且很少编辑超过一个文件。
表8:“预言式”检索设置下,成功应用的补召中模型生成补丁的平均编辑情况。对于每个模型特定的任务实例,我们计算了相应黄金补丁的相同统计数据。Avg Gold显示了在每个模型各自黄金补丁上宏观平均的统计数据。All Gold显示了所有黄金补丁的统计数据,不以模型性能为条件。
.1 SWE-LLAMA生成结果的定性分析
案例分析。我们选择了SWE-Llama和Claude 2的11个生成结果,以更好地理解在“预言式”检索设置下任务的质量和生成的补丁。这里我们讨论一个来自SWE-Llama的例子和我们的总体发现,其他例子的深入分析见附录F。
案例:sphinx-doc sphinx-8713。我们来考虑来自Sphinx文档生成器的任务实例sphinx-doc sphinx-8713,如图6所示。问题指出,当配置设置napoleon.use_param为True时,Sphinx的napoleon扩展没有正确格式化文档关键词“Other Parameters”。问题文本进一步提供了疑似问题源代码的详细代码片段,以及用于复现错误的代码示例和与包版本相关的附加信息。对于这个特定实例,模型没有解决任务,未能通过黄金解决方案解决的一些测试。
模型表现分析。在“预言式”检索设置中,模型输入提供了这个issue文本以及一些指令、黄金补丁编辑的文件的全部内容,以及我们期望答案所采用的diff格式示例。模型总输入包含1,558行上下文或20,882个token。比较黄金补丁和模型的补丁,我们发现一个明显的错误。虽然模型编辑了正确的函数_parse_other_parameters_section(位于sphinx/ext/napoleon/docstring.py的第684行),但它将函数的行为更改为好像napoleon.use_param总是True,而不是先检查配置设置并复制_parse_parameters_section的行为,就像黄金补丁那样。在测试中,test_parameters_with_class_reference直接比较了使用napoleon_use_param设置为False的配置生成的文档,这立即捕获了模型的错误。
总体趋势。通过比较我们考虑的所有示例的结果,我们注意到一些显著的行为趋势。模型倾向于编写原始的Python代码,并且不利用现有的第三方库或代码库的其余部分来解决问题。模型的生成也反映了一种“贪婪”的解决问题方法,很少考虑代码风格或代码库可能反映的逻辑约束(例如,使用相对导入而不是绝对导入)。相比之下,我们观察到许多黄金补丁会进行结构性改进,覆盖代码库更广的范围;这些编辑不仅解决了问题,还预见并解决了潜在的未来问题。
A5 结论
局限性与未来方向。首先,SWE-bench的任务实例均为Python语言;我们希望应用SWE-bench的任务实例收集流程,将其覆盖范围扩展到更多的编程语言和领域。其次,我们的实验旨在为这项任务建立最简单、最直接方法的基线;我们不打算将未来的方法论限制在同一种方法上,并鼓励未来的工作研究不同的方法(例如,基于智能体的方法、工具增强的LM)。最后,虽然这项工作使用基于执行的代码测试来评估模型,但仅依赖这种方法不足以保证模型生成结果的可靠性能,因为我们发现LM的自动代码生成在全面性、效率或可读性方面,常常不如人类编写的解决方案。
结论。现实世界软件开发过程的复杂性远不止代码补全。通过借鉴开源协作流程,SWE-bench忠实地反映了真实世界的编码环境。这种更真实的环境鼓励创造性的解决方案,这些方案可以在开源软件开发中立即应用。我们希望这个基准和我们的其他贡献,能在未来开发更实用、更智能、更自主的语言模型方面,成为宝贵的资产。
A6 附录
A 基准测试细节
本节补充了第2节的内容,对数据收集、基于执行的验证和评估程序进行了更技术性和细致的总结,并对任务实例进行了更全面的描述。
A.1 高层次概述
Pull request抓取。我们从2023年8月下载量排名前5000的PyPI库列表中,选出前100个包,识别每个库对应的开源GitHub代码库,验证哪些包的许可证允许自由使用软件,并通过GitHub开发者API收集这些代码库的所有PR。我们选择从流量大的代码库中获取问题,因为广泛使用通常意味着代码库有详尽的文档、结构化的开源开发指南以及可用的、格式良好的代码。
任务实例构建。我们从满足三个条件的PR构建候选任务实例。首先,PR的状态必须是“Merged”(已合并)。这表示PR相关的代码更改已被接受并并入其父代码库。其次,该PR解决其代码库中的一个或多个issue。Issue根据其在GitHub中的规范用法定义,是用于跟踪软件项目中的错误、增强功能或任何一般开发目标的数字工单。我们扫描PR的标题、正文和提交信息以查找链接的issue(例如,“fixes #24”)。第三,PR必须引入一个或多个新测试。当PR的代码更改编辑了包含测试相关关键字(如“test”、“testing”)的文件路径时,就算作一个新测试。
候选任务实例的转换。满足这些标准的PR随后被转换为候选任务实例,如图7所示。代码库C由代码库的所有者/名称标识符和pull request的基础提交(base commit)确定。从这些信息中恢复实际的代码库很简单。我们创建原始GitHub代码库的镜像,每个镜像都唯一地由所有者/名称标识。克隆代码库对应的镜像并检出(checkout)基础提交,即可得到PR前的代码库C。问题陈述P是所有相关issue的标题和描述以及在PR初始提交时间戳之前编写的任何后续评论的集合,以避免解决方案细节的泄露。PR的代码更改被分为测试补丁(test patch)和黄金补丁(gold patch)δ。T由测试补丁中编辑的文件中的所有测试组成。如图7所示,T和δ都存储为补丁文件。关于解析PR和语义数据的更多细节在附录A.2中。
基于执行的验证。我们通过执行来验证任务实例的可用性。对于每个候选任务,我们首先定义一个虚拟环境作为执行上下文,然后在应用任何补丁之前安装C,最后在应用解决方案δ之前和之后各运行一次T。如果验证过程中的任何步骤失败,候选任务将从最终数据集中移除。此外,为确保解决方案δ并非微不足道,我们比较解决方案前后的验证日志,检查T中是否存在一个或多个测试的状态从失败变为通过。最后,我们排除了那些测试调用了在解决方案δ中首次引入的新创建的函数或类的任务实例。因为命名这些结构通常是一个随意的过程,并且通常在问题陈述中没有明确指定,解决这类测试即使对人类开发者来说也可能是一项不可能的任务。关于执行上下文、代码库安装、从日志中确定测试状态等信息在附录A.3中。
持续更新。SWE-bench的收集过程可以轻松扩展到任何开源代码库,从而可以轻松、低维护地扩展到新的编程语言和代码领域。这种设计也为SWE-bench提供了时间上的稳健性;随着时间的推移,新的语言模型在更新的源代码上训练并发布,SWE-bench可以简单地更新,以基于任何LM训练日期之后创建的PR生成新的任务实例。
A.2 构建过程
任务实例字段。我们讨论将pull request对象转换为候选任务实例的更多细节。宏观上,此转换的主要目标是获取构建代码库C、问题陈述P、单元测试T和解决方案δ组件所需的相关信息。为此,一个SWE-bench任务实例包含表9中呈现的字段,这些字段共同对应四个任务实例模块。
表9:SWE-bench任务实例对象各字段的描述。关于各字段如何收集的细节请参见§ A.2。
问题陈述(Problem Statement)。每个任务实例的问题陈述P可以直接通过problem_statement字段获得。问题陈述是所有issues的首次评论以及在PR初始提交日期之前创建的这些issues的任何评论的集合。我们从PR的标题、正文和提交信息中抓取issues。在连接这些组件的文本数据后,我们首先移除任何Markdown风格的评论,然后在剩余文本中查找对issue编号的引用(一个井号#后跟一个数字),并检查issue编号引用之前的词是否包含在一组表明该issue由PR解决的关键字中(例如“closes”、“fixes”、“resolves”)。找到的issues记录在issue_numbers字段中,然后发出单独的网络请求来检索每个issue的数据。为了形成问题陈述,每个issue的标题和正文被加在一起,然后与下一个issue的连接(如果有多个的话)。也正是在这一步中,创建并收集了hints_text字段,它来自PR评论区中在PR初始提交之前创建的评论文本。这种收集方法的直觉是,这样的PR评论可能包含关于如何完成手头问题的自然语言和伪代码建议。本文中提出的实验没有使用hints_text,但我们相信这些信息对于未来的研究可能很有趣。
代码库(Codebase)。代码库C的内容并未以纯文本形式存储在每个任务实例中。相反,任务实例通过repo和base_commit字段包含了对相关代码库的引用。这两个字段在原始PR的数据中都可用。为了使从这两个元素中检索代码库C的过程可复现且可靠,我们创建了原始代码库的镜像。构成评估和微调数据的代码库镜像被收集并以SWE-bench GitHub组织的名义开源。由于原始代码库的代码可能会受到作者控制之外的提交和编辑历史变化的影响,我们选择创建一个镜像代码库,以确保后续对代码库的修改不会因相关基础提交的损坏或删除而潜在地使任务实例无法使用。此外,我们创建镜像而不是克隆并存储代码库的最新版本。这是因为镜像保留了原始的提交哈希、历史、分支和标签,作为原始代码库技术细节的忠实和完整历史。镜像不保留原始代码库的星标、关注者、issues或pull requests。
镜像创建与使用。我们在收集任务实例的当天之后创建了代码库的镜像。镜像保留了原始代码库的“owner/name”标识,只是“/”字符被转换为“_”以符合GitHub的命名约定。有了这个基础设施,检索任务实例的代码库就变得很简单。首先,可以使用repo从SWE-bench组织克隆正确的镜像。其次,在镜像的本地副本中,检出(checking out)base_commit将使代码库重置为代码库C。要处理来自同一代码库的另一个任务实例,使用git版本控制来自动移除与当前任务实例相关的任何修改,然后再检出下一个任务实例的base_commit。
解决方案与测试补丁(Solution, Test Patches)。解决方案δ和测试T源自PR的文件更改数据,即diff。如2.1节所述,原始diff以及解决方案δ和测试T都表示为一个.patch文件,这是一种高效指定基于行的文本文件转换的格式。一般来说,.patch文件结构为一个块列表,每个块由一个头部和一或多个hunk组成,共同对应单个文件的更改。头部包含指定文件路径和行号的元数据,而对目标文件的实际修改则编码为以“+”和“-”为前缀的多行,以指示添加和删除。为了创建测试T,我们首先识别补丁中的每个唯一块,然后挑选并汇集文件路径包含测试相关关键字(如“tests”、“testing”)的块。剩余的块合并形成解决方案δ。我们通过将两个补丁应用于相应的代码库C并运行测试来验证用于正确解析T和δ的脚本的稳健性;然后我们检查结果是否重现了基础PR的diff数据的行为。解决方案δ保存为patch字段,而测试T保存为test_patch字段。
剩余字段(Remaining Fields)。created_at字段是一个时间戳,指定了基础PR的创建时间。我们保留了原始数据中的created_at字段,并用此字段进行模型性能的时间分析。version字段是一个字符串,对应于PR发布时代码库的发布版本。根据可用性和每种方法所需的工作量,我们通过直接从源代码中检索信息、在本地构建代码库并调用代码将版本显示到标准输出,或将created_at字段与代码库网页上的发布版本时间线进行比较来创建version字段。我们为代码库的每个版本创建了可执行上下文,这在§A.3中有更详细的讨论。
A.3 基于执行的验证
验证流程。在从代码库中筛选出所有PR并将其转换为候选任务实例后,下一步是通过执行来验证每个任务实例的可用性。此过程分为三个步骤。首先,我们为代码库的每个发布版本创建可执行上下文。其次,我们检查解决方案δ和测试T是否可以成功地在代码库C上应用、安装和运行。最后,我们检查每个任务实例的执行日志,以验证一组特定的行为,确保任务对于模型评估是可用和公平的。
可执行上下文。我们选择为每个发布版本创建可执行上下文,这是在试验了不同粒度的虚拟环境定义后决定的。定义任务实例特定的上下文最有利于确保端到端的安装成功,但代价是繁琐的手工制作。另一方面,基于代码库最新版本的代码库特定上下文通常定义过于粗糙,与旧版本的要求不兼容。我们发现,发布版本是捕捉一部分任务实例依赖需求的一个很好的代理,它在安装成功和手动工作量之间取得了可管理的平衡。我们通过检查每个版本的最新任务实例的代码库来手动创建每个可执行上下文。根据源代码和通常在代码库的README和CONTRIBUTING指南中找到的文档,我们找出Python版本、必要的依赖项和安装命令。
验证引擎。验证引擎的目的是验证候选任务实例。具体来说,此步骤首先检查解决方案δ和测试T是否可以应用于代码库C,其次,代码库是否可以在相应的虚拟环境中正确安装和运行。为此,我们逐个代码库进行验证,对每个代码库的任务实例集,执行以下过程:
- 根据每个版本的最新任务实例创建conda环境作为可执行上下文。
- 按版本对任务实例进行分组。
- 遍历每个任务实例组,对于每个任务实例,在相应的conda环境中执行以下操作:(a) 移除任何文件更改并检出任务实例的基础提交。这将代码库设置为C。(b) 运行安装命令来实例化代码库C。(c) 将测试补丁T应用于代码库C。(d) 运行从测试补丁T确定的测试脚本,生成测试结果日志
logpre。(e) 将解决方案δ补丁应用于带有测试T的代码库C。(f) 再次运行(d)部分的测试脚本,生成测试结果日志logpost。
测试命令。测试命令由代码库使用的测试框架(例如pytest, tox)和附加在T中指定的路径组成。测试命令将运行每个文件路径内容中指定的所有测试。如果(a)到(f)中的任何步骤失败,则候选任务实例将被丢弃。在不同代码库中略有差异,我们观察到此步骤通常会移除一半的候选任务实例。
检查验证日志。最后,我们检查验证引擎创建的日志logpre和logpost的特定属性。首先,为了防止任意命名选择,我们检查logpre中的ImportError和AttributeError出现情况,这可能表明存在与依赖命名相关的错误,这些错误既微不足道又几乎不可能正确解决。为此,我们从考虑中移除了所有在其logpre中存在此类错误的任务实例。接下来,我们比较测试结果,以检查任务实例是否重要,即至少有一个或多个测试在应用解决方案δ之前状态为失败,之后状态为通过。为了检查这一点,我们首先定义了几个特定于代码库的解析器,将logpre和logpost转换为测试$t_i \in T$到状态$s \in [\text{fail},\text{pass}]$的映射。给定这两个数据结构,我们然后检查是否存在至少一个$t_i$其状态从失败变为通过。如果没有找到这样的测试,则该任务实例将从考虑中移除。
最终筛选。如果一个任务实例满足这两个标准,那么它就会被包含在评估数据集中。表10显示了在构建过程和基于执行的验证步骤中,有多少任务实例被移除的摘要。我们将所有最终确定的任务实例保存到一个.json文件中,该文件是开源的,可供下载。
缓存测试结果。除了任务实例,我们还创建了一个包含真实测试结果的相应文件夹。对于每个任务实例,我们从它们各自的logpre和logpost的测试到状态映射中,创建一个测试结果数据结构,其键为FAIL TO FAIL、FAIL TO PASS、PASS TO FAIL和PASS TO PASS,值为测试列表。通过“缓存”这些结果,我们消除了在评估时重新运行解决方案δ的需要(尽管重新运行是一个可用选项)。我们使用这个数据结构来验证任务完成情况,如A.4节所述。
表10:在构建和验证过程的各个阶段完成后,保留的候选任务实例数量的统计数据。
A.4 评估流程
评估流程概述。我们在图8中提供了评估流程的可视化。评估流程根据解决方案δ的行为对模型生成的ˆδ .patch进行评分。在更细粒度的层面上,评估流程可以分解为四个独立的步骤,如图8中编号的步骤所示。首先,代码库和问题陈述对LM是可见的并提供给它;然后LM生成一个.patch预测ˆδ。在评估步骤中,对目标任务实例的每个预测执行以下步骤:
- 移除任何文件更改并检出(checkout)任务实例的基础提交。这会将代码库设置为C。
- 激活与任务实例版本对应的可执行上下文。
- 运行安装命令来实例化代码库C。
- 将测试补丁T应用于代码库C。
- 将预测补丁ˆδ应用于带有测试T的代码库C。
- 如果上一步失败,我们尝试自动修复预测补丁ˆδ并重新应用它。
- 运行从测试补丁T确定的测试脚本,生成测试结果日志
logδˆ。
流程可靠性与失败处理。由于在任务实例验证过程中进行了验证,步骤1到4可以可靠地不失败。如果应用预测补丁(步骤5)失败,我们尝试通过移除不必要的上下文行并重新计算头部值来修复预测补丁文件(步骤6)。如果修复后的补丁再次失败或运行测试命令(步骤7)失败,则该预测将自动获得0分。假设这些步骤成功,输出日志logδˆ可以通过§ A.3中介绍的相应的、特定于代码库的解析器转换为一个测试到状态的映射,结构与真实情况相同。
评估指标计算。为了确定任务是否完成,我们将从logˆ解析出的测试到状态映射与真实测试结果数据结构中对应FAIL TO PASS和PASS TO PASS键的测试列表进行比较。确定任务完成很简单;我们检查所有FAIL TO PASS和PASS TO PASS的测试是否都被找到并且在评估的测试到状态映射中状态为通过。如果一个测试缺失或状态不为通过,则认为其状态为失败。正如在主论文中定义和使用的那样,如果FAIL TO PASS和PASS TO PASS的所有测试都通过,则任务被认为是已解决的。
A.5 评估测试集特征
扩展统计数据。我们提供了表1的扩展形式,即表11,其中包含了特定于代码库的统计数据。表12展示了从代码库文档中提取的每个代码库的简要描述以及代码库相关的开源许可证。相关的许可证都允许对原始库源代码进行非商业性使用,只要原始许可证中的权限得到遵守和保留。除了表1中呈现的原始统计数据,我们还引入了三个新值。δ # Lines Added(δ # 添加行数)和δ # Lines Removed(δ # 删除行数)加起来等于δ Lines Edited(δ 编辑行数)。“Added”指的是引入的新行数,而“Removed”是解决方案中删除的已有行。|T| (Pass to Pass) 统计数据指的是在验证流程中应用解决方案δ之前通过的测试数量。与用于描述问题陈述P并确定修订是否解决了问题的fail to pass测试不同,pass to pass测试被包含进来以确保修订不会破坏或违反任何现有的预期行为。这些测试是在§ A.3中讨论的验证日志检查阶段提取的。我们注意到,fail to fail测试和pass to fail测试在评估中不被考虑,这些统计数据未在上述表格中反映。
任务实例问题类别。为了更好地了解SWE-bench任务实例所包含的问题类型,我们对每个任务实例的issue(由issue_numbers字段标识)进行了简单分析。对于每个issue,我们检查元数据,特别是标签(tags),以描述PR所贡献的类型。表13对我们发现的所有issue中的2,289个标签进行了分组并展示了几个例子。虽然绝大多数issue与错误修复相关,但SWE-bench的任务实例与多种代码更改相关,其目的超出了调试和错误纠正。
属性分布。在图9中,我们展示了表1中介绍的属性的累积分布函数图。从这些图中,我们可以看到,中位数的SWE-bench任务实例的问题描述有140个词,发生在一个包含近1900个文件和40万行代码的代码库中。相应的参考解决方案δ通常会编辑一个文件内的一个函数,更改约15行代码,并有一个fail to pass测试来验证更改的正确性,同时还有51个pass to pass测试来检查现有行为是否被保留。
表11:按代码库分组的SWE-bench任务实例不同属性的平均数。除了表1中呈现的统计数据,我们还引入了三个新值:δ # Lines Added, δ # Lines Removed, 和 |T | (Pass to Pass)。
表12:所有提取了任务实例的GitHub代码库的摘要和许可证。
补丁修复率。我们展示了表14,其中呈现了每个模型为多少任务实例生成了补丁(总共2294个),其中有多少补丁成功应用,以及成功应用的补丁中有多少需要经过附录A.4中介绍的补丁修复程序。我们发现,修复过的补丁在成功应用的SWE-Llama补丁中占比较小,这表明SWE-Llama的微调过程对生成格式良好的补丁有积极影响。对于闭源模型,成功应用的补丁较少,而在那些成功应用的补丁中,需要后期修复的比例更大,这表明模型在补丁生成和结构化输出方面仍然存在困难。
表13:与SWE-bench任务实例的issue相关的标签类别。
表14:在评估期间,每个[模型,检索设置]组合为2,294个任务实例生成了多少补丁、成功应用了多少以及需要后期修复才能成功应用的统计数据。GPT-4 BM25 27k和“Oracle”设置在25%的子集上运行。GPT-4 “Oracle”-collapsed设置在完整的SWE-bench测试集上运行。
A.6 开发集特征
开发集的提供。除了评估测试集,我们还提供了一个开发集,用于在最终测试集上运行之前评估模型和调整超参数。我们沿用之前的表格和图表风格,呈现了类似的统计数据来描述这225个开发任务实例(略多于主评估集的10%),这些实例是从6个许可证允许此类使用的开源代码库中收集的。开发集的收集遵循了与主评估集完全相同的方法和筛选器。除了已有的步骤,我们还筛选开发集,保留2019年1月1日之后创建的任务实例。与表12类似,在表15中,我们简要总结了6个选定代码库的用途和许可证。
开发集标签。参照表13,我们还在表16中列出了与开发集任务相关的标签,再次展示了任务类型的多样性和覆盖范围,不仅仅是修复bug。与主评估任务相比,我们还可以看到一些标签(例如,“Crash :collision:”、“io”)指的是开发集中代码库特有的问题。
开发集统计数据。参照表1,我们在表17中呈现了开发集中6个代码库的相同代码库特定平均统计数据。在整个开发集中,每个任务实例平均有19.9个/中位数2个F2P测试。平均有171.3个/中位数79.0个P2P测试,每个任务实例总共有平均191.2个/中位数101.0个测试。
表15:所有提取了开发任务实例的GitHub代码库的摘要和许可证。
表16:与SWE-bench开发任务实例的issue相关的标签类别。
B SWE-LLAMA训练的附加细节
B.1 训练细节
优化。我们使用LoRA(【23,LoRA: Low-rank adaptation of large language models,2022,ICLR】)进行微调,参数设置为r = 16,α = 16,dropout = 0.05,作用于每个注意力子层的查询、键、值和输出投影矩阵。我们使用6e-4的学习率和每个梯度步骤32个序列的批大小进行训练,最多训练4个epoch。在训练期间,我们每50步保存一次检查点,训练结束后,根据在一个包含100个实例的保留验证集上的验证损失选择最佳检查点。SWE-Llama 7b使用CodeLlama-Python 7b初始化,并在4个NVIDIA A100上训练了20小时。SWE-Llama 13b使用CodeLlama-Python 13b初始化,并在8个NVIDIA A100上训练了47小时。我们使用DeepSpeed Ulysses(【24,Deepspeed ulysses: System optimizations for enabling training of extreme long sequence transformer models,2023,arXiv】)和Flash Attention(【9,Flashattention: Fast and memory- ´ efficient exact attention with io-awareness,2022,NeurIPS】)来支持长上下文训练。
C 附加结果
C.1 “预言式”检索结果
使用“预言式”检索的结果。使用第4.1节中描述的“预言式”检索方法,我们在表18中展示了总体性能结果。自然地,仅提供参考解决方案的pull request编辑过的文件,与噪声较大的BM25检索设置相比,模型性能有所提高。
C.2 评估测试集
分代码库性能。我们提供了模型性能的逐个代码库分解,见表19,该表对应于主论文中的图4。正如主论文中所讨论的,不同代码库的性能差异很大。
C.3 GPT-4评估子集结果
GPT-4子集结果。在本节中,我们展示了在表20中对GPT-4进行测试的25%随机子集的表5统计数据。由于子集的选择是随机的,我们发现% Resolved和% Apply率与主要结果一致,并没有显著偏向于比一般评估集更简单或更困难。
表17:为开发数据集中代码库的SWE-bench任务实例按代码库分组的平均数。这里也显示了与表11中相同的统计数据。
表18:我们使用第4节中描述的BM25和预言式检索设置对模型进行比较。主要结果表,即表5,仅呈现了使用BM25时不同模型的结果。*由于预算限制,我们仅在“Oracle”和BM25 27K检索器设置下,在SWE-bench的25%随机子集上评估GPT-4。
C.4 扩展的时间分析
按年份分析。在本节中,我们对按年份解决的任务实例进行了扩展的时间分析,该分析遵循主论文评估部分表7中所示的分析。在表21中,我们展示了在“Oracle”检索设置下,模型在6个不同时间分区(按问题创建年份分组)的% Resolved统计数据。从表中可以明显看出,模型性能与年份之间没有一致的相关性,这支持了我们的结论:尽管模型可能在其预训练数据集中看到过旧版本的代码,但在SWE-bench中理解和实施修复是一项需要理解的困难任务,无法通过记忆观察到的数据来可行或一致地完成。
C.5 F2P, P2P比率分析
细粒度结果分析。在主论文的结果中,我们展示了“% Resolved”统计数据,该数据表明不同模型完全解决了多少任务实例。在本节中,我们对那些1. 模型的补丁生成成功应用但2. 任务实例未被解决的任务实例的差距提供了更细粒度的洞察。假设补丁成功应用,我们在表22中定义了6种情况,完全捕捉了基于F2P和P2P测试的通过/失败结果的所有可能结果的分布。除了已确立的“Resolved”结果,我们引入了五个新术语。“Breaking Resolved”指的是当问题的期望行为已实现(所有F2P测试通过),但并非所有先前的行为都得到维护(并非所有P2P测试通过)。“Partially Resolved”指的是当代码库的先前行为得到维护(所有P2P测试通过),但期望的行为未完全实现(并非所有F2P测试通过)。“Work in Progress”情况是当期望的行为未完全实现(并非所有F2P测试通过)且代码库的先前行为未得到维护(并非所有P2P测试通过)。“No-Op”是指代码更改对原始代码库没有任何影响;先前行为得到维护(所有P2P测试通过),但问题完全未解决(0个F2P测试通过)。最后,如果问题未解决(0个F2P测试通过)且先前的工作行为被破坏(一些P2P测试失败),代码库的状态变得更糟,我们将其定义为“Regression”。
结果分类。在表23中,我们根据这六种情况对成功应用的补丁生成进行了分类。我们发现,在未“Resolved”的问题中,模型提出的大多数补丁生成并未解决相应任务实例的任何一个F2P测试用例(“No-Op”和“Regression”)。在这些情况的子集中,大多数(60%到70%)是No-Op,而模型在其余情况下破坏了现有行为。
部分通过案例分析。通常,模型生成通过部分而非全部测试的情况(“Breaking Resolved”、“Partially Resolved”、“Work in Progress”)相对于其他三类问题,累计起来只占一个较小的子集。通过对其中几个案例的人工检查,很明显模型展示了对任务要求的某些理解。然而,由于基线方法对代码库的视图有限,不包括文件间依赖和函数关系等信息,许多这些任务实例失败的原因是,一个正确解决当前问题的更改没有考虑到使用并受该更改实体影响的其他模块。我们在F节中包含了几个直接突出这些缺点的案例研究。总的来说,这些结果不仅突出了SWE-bench的难度,也指出了通过执行环境提供反馈的潜在价值,这将允许模型针对现有测试运行修复,然后决定是继续编辑还是提交补丁进行审查。
C.6 补丁生成扩展分析
所有生成补丁的统计。在本节中,我们根据表8中列出的指标,量化补丁生成的各个方面的统计数据。在表24中,我们为所有模型在oracle检索设置下的所有补丁生成重新计算了这些值,无论补丁是否成功应用。
表19:SWE-bench中每个代码库的模型解决率(% Resolved)。
表20:我们使用第4节中描述的BM25和oracle检索设置,在SWE-bench的25%随机子集(574个实例)上对模型进行比较,仅在“oracle”和BM25 27K检索器设置下。这与表5中提到的GPT-4评估的子集相同。与表5和表18中百分比的差异以下标形式包含。
发现。在所有指标上,我们发现不同模型的补丁生成在大小上更接近于平均黄金编辑的特征。虽然一些模型生成的代码行数仍相对于相应的黄金编辑较少(例如,Claude-2、ChatGPT-3.5、GPT-4),但SWE-Llama模型的编辑在大多数方面平均更长。当同时考虑表8和表24时,很明显,模型在生成更长的输出序列作为格式正确的补丁方面存在困难。对这类情况的进一步检查,如我们在F节的案例研究中所示,表明幻觉、遵守现有代码风格/结构以及正确引用长距离依赖是较长生成中更常出现的常见错误。
C.7 软件工程度量
初步评估。我们进行了初步评估,探索使用软件工程度量来评估集成在复杂代码库中的大型代码块的效率和复杂性。与传统NLP基准中流行并被代码生成采纳的用于评估流畅度和表面形式相似性的语义相似性评分函数不同,像圈复杂度(【44,A complexity measure,1976,IEEE Transactions on Software Engineering】)和Halstead复杂度度量(【20,Elements of Software Science,1977,Elsevier】)这样的度量是基于逻辑抽象(例如,抽象语法树)和软件原则,以标量值量化代码的复杂性、效率和可读性。补丁生成和SWE-bench评估日志是软件工程度量和静态分析器可以轻松应用的丰富信息来源。与小型的代码竞赛基准不同,在那些基准中,由于目标功能范围微小,软件工程度量的见解没有意义,而SWE-bench的任务足够复杂,从业者可以使用这些工具获得关于补丁生成更改的复杂性及其对代码库其余部分影响的结构良好、严谨和广泛的反馈信号。
探索性工作。我们在此处包含了我们的探索性工作,展示了软件工程度量如何能够可靠地捕捉代码质量的特征,以及比较两个补丁的这些统计数据如何能够提供关于模型能力的自动观察。我们使用Radon包,一个用于直接从源代码计算不同软件工程度量的库。
表21:我们在此表中展示了一个扩展的时间分析,显示了在“Oracle”检索设置下,不同模型在不同截止日期划分的任务实例的解决率(% resolved)。Year列指的是在指定日历年内创建的任务子集。在Total列中,我们列出了属于该年份的任务数量。25%列是GPT-4评估子集的相同信息。其余特定于模型的列包含% resolved度量。
表22:我们展示了成功应用并执行的补丁生成的6种可能结果。这些结果通过通过的F2P和P2P测试数量来区分。
度量应用。我们特别关注在“Oracle”检索设置下,psf/requests代码库中成功应用的Claude 2补丁预测,该代码库是几个模型表现最好的,如图4所示。对于每个预测,我们将补丁应用于代码库,然后计算修改后函数的圈复杂度和Halstead复杂度分数。圈复杂度量化了函数的控制流,计算通过源代码的独立执行路径数量(【44,A complexity measure,1976,IEEE Transactions on Software Engineering】)。较高的圈复杂度分数表明函数更复杂,有更高的缺陷可能性,并且通常意味着难以维护。Halstead复杂度计算程序中操作符和操作数的数量(【20,Elements of Software Science,1977,Elsevier】)。对于每个预测,我们也对相应的黄金补丁执行相同的步骤。
案例研究。我们发现软件工程度量为模型性能提供了自动的定性见解。考虑图10中的这个简单案例研究。虽然模型补丁预测(左)的代码行数更少(6行而不是11行),修改的文件更少(1个而不是2个),但模型的编辑在一个相对复杂且广泛使用的HTTPAdapter类中放置了一个条件。这引入了两个新的潜在执行结果,将HTTPAdapter的圈复杂度从3提高到5。相比之下,虽然更长,参考解决方案导入了模块内依赖,修改了get_connection中一个逻辑上更简单的函数,并定义了一个新的错误类型InvalidProxyURL来捕捉issue中描述的新bug。
D 附加实验细节
D.1 检索细节
稀疏检索。在检索过程中,我们对文档做了一个微小的增强,即在文件内容前加上它们的文件路径,以便更好地根据issue中可能直接提到的文件名进行检索。
表23:根据表22中定义的案例,对成功应用的模型生成进行分类。如前所述,GPT-4在SWE-bench的25%子集(574个实例)上进行了评估。
表24:在oracle检索设置下,所有模型生成的补丁(包括未成功应用的补丁)的平均编辑情况。对于每个模型特定的任务实例,我们计算了相应黄金补丁的相同统计数据。
预言式检索。预言式检索的文件路径直接从参考解决方案的补丁文件中提取,不包括测试文件。
D.2 推理设置
单次生成与贪婪解码。由于生成相对昂贵,我们每个实例只生成一个补丁文件。遵循代码生成评估中Pass@1的先例(【8,Evaluating large language models trained on code,2021,arXiv】;【52,Code llama: Open foundation models for code,2023,arXiv】),我们对所有模型都简单地使用贪婪解码。
D.3 提示模板示例
通用模板。模型被提示使用以下通用模板,根据所用模型的不同略有变化。
你将获得一个部分代码库和一个解释待解决问题的issue陈述。
<issue>
{ISSUE TEXT}
</issue>
<code>
[start of README.md]
{README.md text}
[end of README.md]
[start of file_1.py]
{file_1.py text}
[end of file_1.py]
...
</code>
这是一个补丁文件的示例。它包含了对代码库的更改。它指定了文件名、每次更改的行号,以及删除和添加的行。单个补丁文件可以包含对多个文件的更改。
<patch>
--- a/file.py
+++ b/file.py
@@ -1,27 +1,35 @@
def euclidean(a, b):
- while b:
- a, b = b, a % b
- return a
+ if b == 0:
+ return a
+ return euclidean(b, a % b)
def bresenham(x0, y0, x1, y1): points = [] dx = abs(x1 - x0) dy = abs(y1 - y0) sx = 1 if x0 < x1 else -1 sy = 1 if y0 < y1 else -1 err = dx - dy
+ x, y = x0, y0
+ sx = -1 if x0 > x1 else 1
+ sy = -1 if y0 > y1 else 1 while True: points.append((x0, y0)) if x0 == x1 and y0 == y1: break e2 = 2 * err if e2 > -dy:
+ if dx > dy:
+ err = dx / 2.0
+ while x != x1:
+ points.append((x, y)) err -= dy x0 += sx if e2 < dx: err += dx y0 += sy
+ if err < 0:
+ y += sy
+ err += dx
+ x += sx
+ else:
+ err = dy / 2.0
+ while y != y1:
+ points.append((x, y))
+ err -= dx
+ if err < 0:
+ x += sx
+ err += dy
+ y += sy
+ points.append((x, y)) return points
</patch>
我需要你通过生成一个我可以直接使用git apply应用到这个仓库的单个补丁文件来解决所提供的问题。请以如上所示的格式响应一个单个补丁文件。
请在下方回应:
实验发现。使用稍多或稍少指令或示例的实验似乎对整体性能没有实质性影响,除了第5节中陈述的实验发现。
E 社会影响
AI安全问题。随着对代码的推理成为许多LM能力的基础技能,一个机器自动化软件工程的潜在未来引发了许多重要问题,并对AI安全(【18,AI safety subproblems for software engineering researchers,2023,arXiv】)具有重要的潜在影响。解决诸如如何确保AI生成的代码忠于人类意图,以及当代码智能体误解人类目标并执行任务时应设置何种护栏等问题至关重要。为了在一个受控的环境中观察这些问题并找到解决方案,我们希望SWE-bench可以作为设计安全、稳健措施以实现对齐、可验证和安全的AI驱动软件工程的试验场。
F SWE-LLAMA生成结果的深入分析
定性分析。在本节中,我们遵循第5.1节的风格,提供了五个来自Claude 2和SWE-Llama(Oracle检索设置)生成结果的额外定性分析。
Claude 2案例研究。Claude 2的定性研究见表25和表26。表27、28和29是Claude 2未能正确解决的任务实例。
SWE-Llama案例研究。SWE-Llama的定性研究涵盖了表30、31、32、33、34。在表30、31和32中,我们展示了SWE-Llama 13b正确解决的任务实例。在表33和34中,我们展示了两个SWE-Llama 13b未能正确解决问题的任务实例,指出了模型可能在某些推理和生成技能上不够熟练以完成任务。
主要观察。我们在这些部分所做的观察证实了主论文中提出的观点,即模型在处理多行和多文件更改时往往会遇到困难,当所需的修复相对较短时表现更佳,并且需要帮助以有效的方式理解代码库。
表25 (Claude 2, scikit-learn, 成功):问题是HuberRegressor在处理布尔类型输入时会抛出TypeError。模型通过添加一个自定义的_validate_data函数来转换数据类型,成功解决了问题。然而,黄金补丁提供了一个更简洁、更符合代码库风格的解决方案,即在调用check_X_y函数时直接传递dtype参数。这表明模型倾向于编写原始的Python代码,而不是利用代码库中已有的方法。
表26 (Claude 2, matplotlib, 成功):问题是pyplot.subplots的sharex和sharey参数不接受0和1作为布尔值。模型的解决方案是直接在条件判断中加入对1和0的检查。黄金补丁则通过修改API的check_in_list函数的允许值列表来解决,这种方式更具可维护性,并且还清理了相关的警告信息,显示了人类开发者对代码未来可维护性的考虑。
表27 (Claude 2, astropy, 失败):问题是ascii.qdp模块假定QDP文件中的命令都是大写的,而实际上QDP本身是大小写不敏感的。模型尝试直接修改_command_re正则表达式,将其改为小写。然而,这个正则表达式被用于构建更复杂的模式,简单的修改导致了错误。黄金补丁通过在re.compile时添加re.IGNORECASE标志来解决问题,这是一个更根本和正确的修复,同时还处理了其他地方的大小写问题。这表明模型难以理解代码中变量和逻辑的深层依赖关系。
表31 (SWE-Llama 13B, requests, 成功):问题是在Python 2.7.2中,使用Unicode方法名(如u'POST')会导致UnicodeDecodeError。问题描述中甚至给出了修复建议。SWE-Llama的解决方案是在prepare_request函数中将方法名强制转换为字符串str(request.method).upper()。这个方案通过了所有测试。然而,黄金补丁使用了项目内部的兼容性函数builtin_str(method),这更符合代码库的编码规范,并可能处理了更多边缘情况。这个例子说明,模型即使能功能性地解决问题,其解决方案在代码风格和鲁棒性上可能不如人类开发者。
表32 (SWE-Llama 13B, django, 成功):问题是希望通过一个上下文变量show_save_and_add_another来控制“保存并添加另一个”按钮的显示,类似于其他按钮的实现方式。这个问题描述比较抽象,没有提供具体的代码定位信息。SWE-Llama成功地定位到了submit_row函数中相关的字典键,并直接在布尔逻辑中添加了对新上下文变量的检查 context.get('show_save_and_add_another', True)。这个解决方案非常简洁高效。相比之下,黄金补丁的修改更复杂,它引入了一个新的局部变量can_save_and_add_another来封装整个逻辑,这更符合代码库的现有风格。这个案例展示了模型有时能找到新颖且有效的解决方案,但也再次凸显了模型在遵循代码库编码风格方面的不足。
表34 (SWE-Llama 13B, scikit-learn, 失败):问题是KernelPCA使用rbf核时,每次运行结果的符号不一致,导致结果不具有确定性。模型生成的补丁是在_fit_transform函数中返回K / self.lambdas_,这是一个完全错误的修改,不仅没有解决问题,还破坏了原有的功能,导致多个已有的测试(Pass to Pass)失败。黄金补丁的解决方案是导入并使用svd_flip函数来翻转特征向量的符号,从而确保输出的确定性。这个例子表明,模型在不理解算法原理的情况下,很难进行正确的修复,并且其修改可能会对代码库的其他部分产生意想不到的负面影响,凸显了理解代码全局依赖性的重要性。
💬 评论讨论
欢迎在这里分享您的想法和见解!