发布时间:2024 年 03 月 25 日
Agent 软件工程 程序修复
自动化程序修复技术异军突起,有效减轻了软件缺陷对系统稳定性和用户体验的负面影响。本文首次提出了 RepairAgent,这是一种基于大型语言模型(LLM)的自主代理,用于应对程序修复的挑战。RepairAgent 打破常规,不再依赖固定提示或反馈循环,而是将 LLM 视为一个能够主动规划和执行修复动作的智能体,通过调用恰当的工具来解决漏洞。它灵活地在收集漏洞信息、准备修复材料和验证修复方案之间切换,并根据已有信息和过往修复尝试的反馈来选择使用哪些工具。RepairAgent 的核心贡献在于一套实用的程序修复工具、一种能让 LLM 与这些工具动态互动的提示格式,以及一个指导代理调用工具的有限状态机。在 Defects4J 数据集上的测试显示,RepairAgent 能独立修复 164 个漏洞,包括之前技术无法解决的 39 个。与 LLM 的互动平均消耗 27 万令牌/漏洞,按 OpenAI GPT-3.5 模型的现行收费标准,每个漏洞的成本约为 14 美分。这项研究开创性地将基于 LLM 的自主代理应用于程序修复领域,为软件工程领域的代理技术发展开辟了新路径。
RepairAgent,是第一个基于大型语言模型(LLM)的自动化程序修复自主代理。
将LLM视为一个能够规划和执行动作以实现修复缺陷目标的自主代理。为此,我们为LLM配备了一套特定于缺陷修复的工具,模型可以调用这些工具与代码库进行交互,方式类似于人类开发者。
例如,RepairAgent具有工具可以通过读取特定代码行来提取关于缺陷的信息,通过搜索代码库来收集修复材料,并通过应用补丁和执行测试用例来提出和验证修复方案。
重要的是,我们没有硬编码如何以及何时使用这些工具,而是让LLM根据先前收集的信息和之前修复尝试的反馈自主决定下一步调用哪个工具。
我们的方法由三个关键组件支持:
首先,一个通用的LLM,如GPT-3.5,我们用动态更新的提示反复查询它。我们提出了一种新颖的提示格式,引导LLM完成缺陷修复过程,并根据LLM调用的命令和之前命令执行的结果进行更新。
其次,一套LLM可以调用的工具,以与代码库交互。我们提出了一套14种工具,旨在涵盖人类开发者在修复缺陷时会采取的不同步骤,如读取特定代码行、搜索代码库和应用补丁。
第三,一个中间件,协调LLM和工具之间的通信。我们提出了通过有限状态机引导工具调用的新技术,并为可能不正确的LLM输出提供了启发式解释。RepairAgent的迭代循环将持续进行,直到代理声明已找到合适的修复方案,或直到迭代预算耗尽。
上图是 RepairAgent 方法的概览,该方法由三个组件组成:一个LLM代理、一套工具,以及一个协调两者之间通信的中间件。
对于一个要修复的bug,中间件会用一个包含任务信息和使用提供的工具执行任务的指令的提示来初始化LLM Agent。LLM通过建议调用其中一个可用工具来进行响应,中间件解析这个调用然后执行该工具。工具的输出随后被整合到下一个LLM调用的提示中,这个过程会迭代进行,直到缺陷被修复或预定义的预算耗尽。
每一次迭代又被成为一个循环(Cycle)。一个循环代表与LLM代理进行一轮交互,其中包括以下步骤:
- • 查询代理
- • 后处理响应
- • 执行代理建议的命令
- • 根据命令的输出更新动态提示 在每个循环中,都会与LLM进行一次交互。模型的输入根据LLM在前几个循环中调用的工具(命令)及其结果进行更新。我们称模型输入为动态提示。
为了引导LLM Agent 高效准确的使用工具,作者定义了一个有限状态机,限制在给定时间点可用的工具。
之所以这么做的原因是在没有这种指导的早期实验中,LLM代理经常在无目的的探索中迷失方向。
上图正好展示了我们设计的有限状态机,旨在模仿人类开发者在修复缺陷时经历的状态。每个状态与Agent可用的一组工具相关联。重要的是,代理可以随时通过使用工具在状态之间自由转换。也就是说,尽管提供了指导,状态机并不强制执行工具调用的严格顺序。
提示的状态描述部分向Agent通报其当前状态:
- • Bug理解:代理从这个状态开始,在这里它可以收集与失败的测试用例和缺陷位置相关的信息。一旦代理理解了缺陷,它就会提出一个假设来描述缺陷的性质和背后的原因。在整个修复过程中,代理可能会反驳早期的假设并提出新的假设。在提出假设之后,代理将自动切换到下一个状态。
- • 收集修复Bug信息:在这个状态下,代理收集有助于为假设表达的缺陷提出修复方案的信息,例如,通过搜索特定的修复材料或阅读可能相关的代码。一旦代理收集了足够的信息尝试修复,它可以转换到下一个状态。
- • 尝试修复Bug:在这个状态下,代理尝试根据其当前假设和收集的信息来修复缺陷。每次修复尝试都会修改代码库,并通过执行测试用例来验证。如果必要,代理可以回到之前的某个状态来建立新的假设或收集额外的信息。
上图是作者在RepairAgent中使用到的所有工具,总体分为四大类:
- • 读取和抽取代码
- • 搜索和生成代码
- • 测试代码
- • 控制流
为了评估 RepairAgent的效果,作者提出了三个方面的问题:
- • RQ1 RepairAgent在修复现实世界中的缺陷方面有多有效?
- • RQ2 这种方法的成本是什么?
- • RQ3 LLM代理如何使用可用的工具?
有效性
上面这个表总结了RepairAgent在修复Defects4J中的835个缺陷方面的有效性。
该方法为186个缺陷生成了可行的修复方案。虽然这些修复方案不一定正确,但它们通过了所有测试用例,并可能为开发者提供关于应该更改什么的线索。
RepairAgent为164个缺陷生成了正确的修复方案,这些方案在语义上与开发者提供的补丁一致。能够修复来自不同项目的缺陷表明该方法可以泛化到多个领域的代码库。
此外,RepairAgent还为不同复杂度级别的缺陷创建了修复方案,修复了115个单行缺陷、46个多行(单文件)缺陷和3个多文件缺陷。
成本
我们衡量RepairAgent强加的三种成本:
- • 修复一个缺陷所需的时间。-
- • 查询LLM所消耗的token数量,这对于商业模型(如本文中使用的GPT-3.5)和自托管模型都很重要,在自托管模型中,token的数量决定了计算成本。
- • 与token消耗相关的货币成本,基于OpenAI截至2024年3月的定价。
修复一个bug花费的时间的中位数是920秒,已修复(870秒)和未修复缺陷之间的变化很小。已修复的bug没有表现出更低的修复时间。这是由于RepairAgent的自主特性,其中修复过程会持续进行,直到调用目标完成命令或迭代预算耗尽。
其中也出现了几个异常值,其中修复缺陷的尝试需要多个小时。RepairAgent花费总时间的99%在工具执行上,主要是运行测试。通过仅执行选定的测试用例,未来可以降低这一成本。
分析LLM所施加的成本,我们发现中位消耗大约为270,000个令牌,相当于大约14美分(美元)。已修复缺陷消耗的令牌数量(21,000)明显低于未修复缺陷(315,000)。这种差异是因为代理继续为尚未修复的缺陷提取额外信息,通过操作如读取更多代码行或执行广泛的搜索,使提示达到饱和状态。
虽然RepairAgent在修复Defects4J中的缺陷时显示出了有希望的结果,但我们承认存在几个潜在的有效性威胁和固有的局限性:
- • (i)数据泄露:由于我们在GPT-3.5上进行评估,而其训练数据并不为公众所知,LLM可能在训练期间已经接触过Java项目的部分内容。虽然我们认识到这一风险,但我们的方法并不完全依赖于了解一个缺陷,而是依赖于收集修复缺陷的信息的能力。我们还注意到,最接近的竞争对手ChatRepair也使用GPT-3.5,因此面临同样的风险。
- • (ii)缺少测试用例:Defects4J为每个缺陷至少有一个失败的测试用例,这在现实世界的使用场景中可能并非如此。在未来的工作中,评估RepairAgent在没有预先可用的错误揭示测试用例的缺陷上的表现将是有趣的。
- • (iii)故障定位:不准确或不精确的故障定位可能导致次优的修复建议或错误的诊断。
- • (iv)LLM的非确定性输出:LLM固有的非确定性特性可能导致连续两次运行RepairAgent时产生不同的结果。我们在评估的大量缺陷上降低了这一风险。此外,我们确保所有与LLM的交互的日志可用,以确保可重现性。
Arxiv[1]
if like_this_article():
do_action('点赞')
do_action('再看')
add_wx_friend('iamxxn886')
if like_all_arxiv_articles():
go_to_link('https://github.com/HuggingAGI/HuggingArxiv') star_github_repo(''https://github.com/HuggingAGI/HuggingArxiv')
引用链接
[1] Arxiv: https://arxiv.org/abs/2403.17134
