FixAgent:一款自动化debug的多Agent应用,有效提升模型20% debug能力

技术

picture.image

FixAgent:一款自动化debug的多Agent应用,有效提升模型20% debug能力

发布时间:2024 年 04 月 26 日

Agent应用

为了简化软件调试这一繁琐过程,包括故障定位和修复生成,人们投入了巨大的努力。近期,大型语言模型(LLMs)在自动化调试领域展现出巨大潜力。尽管如此,我们发现传统及基于 LLM 的调试工具面临三大挑战:故障定位的不精确性会波及后续的修复工作;在处理复杂逻辑错误方面的不足;以及对程序上下文的忽略。针对这些问题,我们首次提出了一个自动化、集成的调试框架——FixAgent,它通过 LLM 代理的协同作用实现。FixAgent 能够一站式完成漏洞的定位、修复和分析。我们洞察到,LLMs 可以借鉴人类开发者在调试过程中采用的通用软件工程原则,例如橡皮鸭调试法,以促进对程序功能和逻辑错误的深入理解。因此,我们设计了三种受橡皮鸭调试法启发的机制来应对这些挑战:代理的专业化与协同、关键变量追踪以及程序上下文理解,这些机制要求 LLMs 提供清晰的解释,并引导它们关注程序逻辑的关键信息。在广泛使用的 QuixBugs 数据集上的测试显示,FixAgent 成功修复了 80 个漏洞中的 79 个,其中 9 个是之前未被修复的。即便在没有漏洞位置信息和低于 0.6%的采样次数的条件下,它在 CodeFlaws 上修复的缺陷数量也比最佳修复工具多 1.9 倍。平均来说,与使用不同 LLMs 的基础模型相比,FixAgent 在合理和正确修复方面提升了大约 20%,证明了我们设计的成效。此外,FixAgent 的正确率高达 97.26%,这表明它可能有能力解决现有方法中存在的过拟合问题。

背景

前面读了一篇LLM +Code Review的水文,今天我一怒之下,再读一篇Code Review方向文章。

这次这篇可不是水文了,非常详细、系统的介绍了如何打造一款专门用于自动化程序审查和修复的多Agent系统(MAS)。

码农们应该都知道,他们一般要花费50%以上的时间来调试他们的代码,所以如果有一款自动化调试工具的话,软件开发工作的效率和成本将会大大降低。

而自动化调试(编程)一般包括两个阶段:故障定位(Fault Localization,FL)和自动化程序修复(Automated Program Repair,APR):

  • • 故障定位是指精准识别错误代码,并列出可疑代码。传统的故障定位一般是通过统计分析来定位故障源头,效果很大程度上依赖于人工编写的测试案例。
  • • 自动化程序修复(APR)是指生成正确的代码来替换有问题的代码块。传统的方法通过事先定义好的修复模式来提供一些补丁、或者通过一些规则执行基于人工测试案例来合成补丁。这种方法的局限性在于,可用的补丁数量非常少,需要大量的定制化工作来适应不同编程语言。

随着大语言模型的兴起,大型语言模型(LLM)被认为是处理编码相关任务,包括调试在内的最有效的新型方法。也有研究表明,直接应用LLM可以显著超越现有的先进APR技术。其他基于LLM的FL和APR研究也获得了充满希望的结果。

然而,无论是传统还是基于LLM的调试工具,都还是存在一些局限性:

  • • 1、故障定位不完善,APR的修复效果很大程度上受限于FL的定位效果,而实际上,现有的FL技术效果非常有限,这也导致整个系统的可用性极大的降低。
  • • 2、在复杂逻辑错误时,效果欠佳。尽管大语言模型有很强的逻辑理解能力,但是在修复复杂的逻辑错误上,仍然效果欠佳。这往往是因为调试时一个逻辑推理的过程,这对于依赖模式识别而不是真正的思考的模型来说,是很困难的一件事。
  • • 3、缺乏上下文背景。调试的时候,往往需要理解程序代码的目的和上下文代码,包括预期的输出和潜在的影响。大多数LLM都是基于文件级的源代码训练的,缺乏分析依赖关系的能力。另外,基于LLM的APR一般只考虑代码和测试结果,不考虑如变量作用域、函数定义和外部库等信息丰富的程序上下文,这也会影响大语言模型的效果。

鉴于以上问题,作者提出了FixAgent,一款多Agent协作的集成自动化调试框架

什么是FixAgent?

picture.image

上图展示了FixAgent的整体架构,由三个Agent专家组成,分别承担了:错误定位、补丁生成、修复后review的角色。此外,还包括一个额外的Agent Crafter,负责生成超出了人工编写测试集范畴的测试,用来解决一些过拟合问题。如果生成的补丁不可行,会利用包含失败信息的反馈重新采用修复。

FixAgent有三大核心机制:

  • • 专业的多Agent协作:首先将两个LLM Agent作为专业的错误定位器和补丁生成器,然后再引入第三个LLM Agent来分析错误和修复。每个Agent都采用了小黄鸭调试法来详细的解释工作过程。这种协作不仅可以在未知错误位置的情况下,提供带解释的程序修复,还能纠正错误定位器的疏漏。比如:修复超出定位器识别范围的代码语句。
  • • 中间变量追踪:FixAgent会引导每个Agent在错误程序的关键位置追踪关键变量。并讨论这种追踪如何指导FixAgent完成任务。这样的设计好处在于可以使Agent沿着逻辑执行步骤分析代码,提供更加具体的错误解释。
  • • 构建程序上下文:根据程序本身和依赖关系构建代码块的上下文,并将错误和代码一起发送给FixAgent,程序本身一般包括:功能描述、输入输出及其示例、变量作用域等。鼓励Agent在分析错误的时候,充分考虑上下文,以便更深入的理解错误出现的原因。

传统方法的缺陷

bug定位方法的缺陷

bug定位方法的缺陷包括定位错误或者Bug遗漏两个方面,这些问题会影响后续的修复工作。

Bug定位错误

首先,FL工具可能会错误地将正确的代码段判定为错误源头,这不仅导致生成的修复补丁毫无必要,还可能引入新的错误。Defects4J V1.2.0 也只能实现将少于22.3%和46.3%的真实错误语句分别排在最可疑的前1位和前5位。这一结果远未达到令人满意的程度,基于此类FL结果所建立的补丁空间可能存在问题。大多数自动化程序修复(APR)工具仅限于用APR生成的代码段替换已识别的代码段,这背后往往基于一个不切实际的假设——完美的故障定位是容易实现的,而这一假设实际上会影响调试的效果。

Bug定位遗漏

另外,理论上说,传统的FL工具会漏掉很多错误。

  • • 首先,大多数FL和APR工具都假设每段代码只有一个错误,这样假设的原因主要有:
  • • 1)识别跨多行的错误并在多个非连续的位置进行修改的难度比较大;
  • • 2)APR工具通常依赖于基于谱系的FL技术,该技术只能定位单行错误
  • • 其次,基于错误案例的FL技术无法识别由于缺少语句导致的错误。这些方法通过计算某个语句的执行次数来进行故障定位,因此在发现缺失条件时会遇到困难。

然而,现实世界中的错误是复杂且多样化的,可能涉及多行或缺少行的问题,这就需要更精细的调试方法来应对故障定位不完善的风险。

复杂逻辑缺陷的修复

大语言模型的出现,给软件自动化调试方向带来了一线希望,单个LLM既可以独立完成软件代码的调试和修复任务。最近《Debugbench: Evaluating debugging capability of large language models》这篇文章对LLM的调试进行了比较深入的探讨和研究。这篇文章里提到,最先的LLM模型GPT4在LeetCode的代码修复水平与人类水平齐平。但是,在实际过程中,LLM在解决逻辑错误方面仍然有一些缺陷。

picture.image

上图展示了一个存在逻辑缺陷的Bug的代码片段,以及GPT4尝试进行修复的过程。GPT4的修复结果可能能够通过大多数测试案例,但是在测试一个边缘测试案例的时候,仍然会有一些问题:即在只有一根管道连接三所房子的情况下,水箱和水龙头的数量计算时。

解决这个问题所需要的逻辑推理能力远远超过了语言理解,所以对于LLM而言仍然是比较难的。所以单靠简单的LLM来应对复杂软件调试仍然是不够的。

缺乏上下文

代码调试不仅需要对代码的语法有比较深入的理解,还需要理解代码的设计语义目的。但是,传统的自动化修复工具往往只关注代码本身和测试及结果,忽略了代码的上下文:比如预期的功能、数据流向、预期效果等。下图展示了一个典型的需要上下文指导的程序修复过程。

picture.image

FixAgent的技术架构

picture.image

前面介绍了FixAgent的基础架构(如上图所示),下面对FixAgent架构的关键机制做详细介绍:

专业Agent协作机制

FixAgent包括三个关键Agent:错误定位Agent、补丁生成Agent、修复后审查Agent。

  • • Bug定位器复杂识别出代码中的错误语句,甚至发现缺失的代码行,并在程序中注明“// missing this line causes a bug”。
  • • 补丁生成Agent用来针对Bug生成可行、正确的修复代码。
  • • 修复后审查Agent则分析原有代码的bug和修复补丁的合理性。

这三个Agent按顺序协同工作,将各自的处理结果传递给下一环节,共同构建出完整的代码修复过程。

每个AGent包括三个部分:角色定位、程序描述、操作指令

  • • 每个Agent根据对应的设计要求,给予清晰的角色定位。角色定位包括专家身份认证和Agent任务描述。专家身份认证定义了Agent的角色,例如“You are an expert in identifying specific faulty code elements.”。任务描述则明确了具体的任务目标,比如“Your task is to fix buggy code snippets”。
  • • 角色定位后,是程序描述,比如:错误代码、失败测试案例、代码上下文、以及上游Agent的输出(比如:向修复后审查Agent提供由Bug定位器定位的Bug位置和补丁生成器生成的修复代码)。
  • • 接着就是操作指令,比如,补丁生成器的任务是返回一个带有解释的补丁,并以设计好的格式输出。这个过程中,需要执行一系列步骤:理解上下文、针对失败案例进行分析、最小化的修改来生成补丁、对已识别的错误语句进行复核。

下游Agent依赖上游Agent的输出,同时也会对上游的结果产生影响。比如补丁生成器生成了一个合理的补丁,并且这个补丁改变了已经识别出错误的代码语句,故障定位器就会需要根据这些变化调整对应的结果。同样的,修复后审查Agent可能会提出一个更好的补丁来优化补丁生成器的代码,或提供更加精确的定位信息。

另外,FixAgent设计允许同时并行处理多个代码程序,最终呈现给人类用户的答案是汇聚了多个Agent的协作成功,包括:已经识别出的错误语句、生成的补丁代码、修复后的错误分析。

精准跟踪中间变量

FixAgent指导每个Agent针对失败的测试案例追踪并对比程序中的关键中间变量值与预期结果。每个Agent都需要在回答中清晰展示中间变量追踪过程,并解释这个过程如何辅助生成答案。这个设计的灵感是来自小黄鸭调试法,通过解释代码来增强代码debug的效果。这个思路与CoT的理念有异曲同工之妙,即将复杂问题细化为多个小问题,这样可以提高LLM的推理能力,即便是简单的加入“think it step by step”,也可以带来明显的效果改善。

这个设计还可以提升LLM决策过程中的透明度,使得开发者可以看到Agent推理答案的完整路径,为人机交互提供了更多信息。例如,在处理动态规划问题时,开发者可以指示模型特别关注状态转移方程和边界条件。即便Agent最终未能生成正确的修复方案,其思考路径也能为开发者提供宝贵的启示。这种透明的解释过程也有助于增强开发者对模型的信任。

上下文构建

FixAgent在构建上下文时,重点考虑两个关键要素:需求和依赖。

首先,对于文档比较完整的程序代码,利用程序的功能描述、输入/输出格式、精度需求等。如果程序实现的是一个通用算法,但是没有明确的文档说明,则会使用一个通用大语言模型(不一定是FixAgent)根据算法名称来描述这个算法,用来作为算法的需求说明。

然后,分析出错程序的依赖关系,并提取出相关的依赖文件代码。这些提取出的代码放在程序代码的前面,这样可以保证LLM能先处理依赖代码,再处理出错程序,提高处理效率。

其他设计要点

测试用例生成

FixAgent在上述三个Agent之外,还设计了一个Agent(名为:工匠,Crafter),专门负责生成超出人工编写测试用例范围的测试案例。这个Agent在生成了补丁代码后启动,目的是为了解决过拟合问题,避免出现生成的补丁能通过测试代码,但是不具备泛化能力。

基于反馈的迭代重采样

Debug的过程中,难免会出现错误,为了降低错误修复的发生率,FixAgent引入了一种基于反馈的设计理念。当FixAgent生成了一个错误补丁时,会通过迭代重采样的方式,重新生成一个新补丁。这个设计理念旨在与先前的自动化程序修复(APR)工具保持一致,这些工具通常会尝试数千个候选补丁。每次只输入一个补丁及其反馈信息进行重采样,避免了大型语言模型(LLM)令牌窗口太大的风险。最终,经过错误修复分析验证的最佳补丁将提供给人类用户。此外,用户也可以请求查看任何Agent的具体响应,包括相应的解答和解释。

效果评估

在整个评估过程中,作者讨论了三个关键问题,分别是:

  • • RQ1:FixAgent与最先进的自动化程序修复系统相比,效果如何?
  • • RQ2:FixAgent使用不同的大语言模型效果差距如何?
  • • RQ3:设计的各个模块,对FixAgent的贡献如何?

RQ1:FixAgent与最先进的自动化程序修复系统相比,效果如何?

作者对比了FixAgent与Codeflaws、QuixBugs三个数据集,在C、Java、Python程序上的比较。由于不同的自动化程序修复工具采用了不同的Bug定位方法,所以根据不同的Bug定位定位方法分开讨论。但是FixAgent是一个端到端的解决方案,不需要知道Bug的位置,所以没有把FixAgent和其他LLM提供Bug定位的结果对比。

picture.image

如上图所示:FixAgent在Codeflaws和QuixBugs上的3982个错误中合理地修复了2780个,与所有基线相比表现最佳 。特别是在Codeflaws数据集上,FixAgent显著优于现有的APR方法和LLM。它产生的合理补丁是最佳APR方法GenProg的1.9倍 。与APR方法中正确修复最多错误的CoCoNuT相比,它的正确率提高了57.42%。

下图展示了一个由FixAgent独特修复的QuixBugs中的示例错误。该Bug错误地返回了一个空数组,但期望的是一个嵌套的空数组。传统的和基于学习的APR很难修复它,因为它需要添加多行编辑,这是简单修改无法实现的,并且超出了典型的错误修复模板。由于QuixBugs只包含程序的非常简短的描述,调试方法必须从程序中的其他返回语句或失败信息(如果有的话)中抽象出期望的返回格式,这需要对程序进行全面的概述和深入分析。实际上,即使是FixAgent也是在原始失败测试触发返回一个空集嵌套数组的情况下生成了正确的补丁。请注意,与基础模型GPT4相比,FixAgent在QuixBugs中修复了四个额外的错误。

picture.image

RQ2:FixAgent使用不同的大语言模型效果差距如何?

picture.image

作者对采用不同的基础大语言模型的FixAgent在ConDefects数据集上进行了测试。如上表,FixAgent-[Model]为仅替换不同的基础LLM,其他设计均未发生变化。x / y 表示y个bug里面,修复了x个。

尽管更大的LLM意味着更好的性能,但是FixAgent-Deepseeker-Coder的效果超越了FixAgent-ChatGPT和FixAgent-Gemini ,后面两个模型的规模实际上比Deepseeker更大。尽管DeepSeek-Coder和CodeLlama在模型规模上相似,但FixAgent-DeepSeek-Coder的性能显著超过了FixAgent-CodeLlama,这可能源于两者在编码能力上的本质差异。DeepSeek-Coder在解决编程竞赛方面的表现远远超过了CodeLlama。鉴

还评估了FixAgent如何提升其基础模型在Codeflaws上的性能,如表III所示。为了提高效率,在此评估中随机抽取了300个缺陷进行分析。与基础模型相比,FixAgent在合理修补的缺陷上多修补了17至35个,正确修复的缺陷上多修复了12至33个。在所有比较的组合中,FixAgent在GPT4上提升最为显著,正确和合理修补的缺陷分别增加了33个和35个。总体来看,引入FixAgent平均可以使原始LLM的性能提高20%。这表明,这样的多Agent设计,** 以一种非侵入式(无需训练、微调)的方式增强了LLM的debug能力** 。

picture.image

RQ3:设计的各个模块,对FixAgent的贡献如何?

下表中每一行都代表去掉一个设计元素,导致正确或合理生成的补丁数量的减少。

picture.image

结果发现,上下文构建对FixAgent的贡献是最明显的 ,缺少上下文会使正确修复的缺陷数量减少122个。这突显了上下文在调试中的重要性,因为它们有助于理解潜在的问题领域,推断预期的功能,并应用适当的修复措施。这种下降趋势也说明了为什么即使没有完美的故障定位,LLM仍然显著优于APR工具。

其次是变量跟踪设计 。如果没有变量跟踪,FixAgent生成的合理和正确补丁数量分别减少了37个和38个。

最不具贡献的设计是反馈支持的重采样。尽管额外的反馈提供了更多信息,但它扩大了对话窗口,模型必须将其有限的注意力分配给这些新信息,可能会忽略其他关键细节。

FixAgent的局限性

提升空间

虽然FixAgent显著提升了多个大型语言模型(LLM)的调试表现,但其效能本质上受限于底层模型的固有能力 。实际上,FixAgent无法根本性地改变LLM的内在能力 ,只能是激发LLM原有的潜力。例如,FixAgent通过在Codeflaws上合理且正确地修复了额外的26个和17个缺陷,从而提升了Gemini的性能,但即便如此,其表现仍然不及直接应用GPT4。因此,对于那些天生调试能力受限的模型来说,即便在使用FixAgent后有所进步,也不会实现性能上的大幅飞跃。提升的上限最终受限于LLM自身的能力。因此,我们选择GPT4作为默认的底层模型,原因在于它在广泛的认识任务中展现出了先进的性能[63],尽管FixAgent也能与任何LLM兼容。

外部测试输出的计算

FixAgent引入了额外的Agent来生成测试输入,以减轻过拟合问题,但它无法直接计算相应的预期输出。这主要是因为LLM固有的概率机制,使得它们在准确回答计算问题上面临挑战。解决类似于数学推理的问题是LLM所面临的重大挑战之一。FixAgent无法克服它们的内在局限,因此必须引入额外的信息,比如正确的代码或手动计算的答案,以获得生成输入的输出并构成完整的测试用例。

测评的有限性

由于无法获得模型训练的数据集,所以大语言模型的训练数据集可能与测评数据集存在重合。

针对这一问题,作者通过三个步骤来缓解:

  • • 1)选择了最近收集的数据集(ConDefects)进行评估,该数据集被认为可以减轻LLM的数据泄露问题,因为它是在模型发布后才收集的。
  • • 2)使用的其他两个数据集被公认为不属于训练数据的一部分,因为它们在GitHub上的星数较少,且它们专注于经典算法(QuixBugs)和编程作业(Codeflaws),这些并不属于更大型的现实世界项目。
  • • 3)FixAgent框架与底层LLM相比显著提高了调试效率,其中的提升与数据泄露问题无关。

外部威胁主要在于评估数据集,其中FixAgent的优越性可能无法推广到其他数据集,尤其是那些使用较少、开源代码数据较少的编程语言,这些语言对LLM的训练来说是一个挑战。为了解决这个问题,FixAgent与包括通用LLM和APR方法在内的高级基线进行了比较,涵盖了三个数据集的五个基准,覆盖了三种编程语言。实验表明,FixAgent在所有这些基准中都展现了其有效性。

picture.image

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论