网站建设中的技术问题,国内wordpress最好的主题,如何加强网站管理的队伍建设,网站 新媒体建设情况背景#xff1a;
最近在做代码大语言模型生成项目代码的课题。代码生成现在大部分的工作是在做即时代码生成#xff0c;这个有点类似代码智能提示#xff0c;只不过生成的可能是一段片段代码#xff1b;然而对于整个项目代码的生成做的团队并不多#xff0c;原因大致如下…背景
最近在做代码大语言模型生成项目代码的课题。代码生成现在大部分的工作是在做即时代码生成这个有点类似代码智能提示只不过生成的可能是一段片段代码然而对于整个项目代码的生成做的团队并不多原因大致如下
1.项目代码比较复杂关联的代码文件较多文件关系、类关系、方法关系理清楚比较难
2.项目代码涉及的代码量较大也就是上下文较多要让模型理解整个项目抓住重点很难
3.项目代码量大所以要求模型允许输入的token长度较长
4.项目代码生成如何抽象问题、设计任务、构建语料训练模型诗歌难题
也正是因为上面的几个问题点导致虽然项目代码的生成是一个很有商业价值的方向但却找不到合适的解决方案。今天介绍的这篇文章《CodePlan: Repository-level Coding using LLMs and Planning》并未提出项目代码生成方案但是提出了项目代码增删改后如何自动的把应用到发生改变的代码片段自动修改的方案。
这个方案的解决思路如下 1.对项目代码做依赖关系的分析构建代码方法依赖关系数
2.定义了代码片段可能的几种变化行为并针对行为定义了几种actiom
3.当代码片段发生变化把信息结构化成prompt输入LLMLLM根据prompt触发对应项目修改action
4.循环3直到所有的代码依赖全部更新完为止 依赖图的示例带有关系作为边缘标签的注释 代码片段可能的几种变化行为和对应actiom prompt模版
论文翻译
软件工程活动如包迁移、修复来自静态分析或测试的错误报告以及向代码库添加类型注释或其他规范涉及广泛编辑整个代码存储库。我们将这些活动形式化为存储库级别的编码任务。 最近的工具如GitHub Copilot由大型语言模型LLMs驱动已成功提供了高质量的解决方案来解决局部编码问题。存储库级别的编码任务更加复杂无法直接使用LLMs解决因为存储库内的代码相互依赖整个存储库可能太大无法适应提示。我们将存储库级别的编码视为一个规划问题并提出了一个任务不可知的框架称为CodePlan来解决它。CodePlan合成了一系列多步编辑计划其中每一步都会调用存储库中的代码位置上的LLM上下文来自整个存储库、先前的代码更改和任务特定的说明。CodePlan基于一种新颖的增量依赖分析、变更可能影响分析和自适应规划算法的组合。 我们在两个存储库级别的任务上评估了CodePlan的有效性包迁移C#和临时代码编辑Python。每个任务在多个代码存储库上进行评估每个存储库都需要对许多文件进行相互依赖的更改2至97个文件之间。以LLMs自动化处理这种复杂程度的编码任务以前尚未实现。我们的结果显示与基线相比CodePlan与实际情况更匹配。CodePlan能够使5/6个存储库通过有效性检查例如无错误地构建和进行正确的代码编辑而基线没有规划但具有与CodePlan相同类型的上下文信息无法使任何存储库通过这些检查。我们将在https://aka.ms/CodePlan发布我们的数据和评估脚本。 CCS概念• 计算方法学→不确定性下的规划• 软件及其工程→软件维护工具软件演进自动编程。
附加关键词和短语自动编码、存储库、LLMs、静态分析、计划、一系列编辑。
**1 引言**
大型语言模型LLMs的卓越生成能力[24, 28, 30, 35, 57, 73]开辟了自动化编码任务的新途径。基于LLMs构建的工具如Amazon Code Whisperer [14]、GitHub Copilot [38]和Replit [66]现在广泛用于根据自然语言意图和周围代码的上下文完成代码并根据自然语言指令执行代码编辑[78]。这些编辑通常针对代码的小区域例如完成或编辑当前行或整个方法的主体。 虽然这些工具有助于软件工程的“内循环”其中开发人员在编辑器中编码并编辑小代码区域但在软件工程的“外循环”中有一些涉及整个代码存储库的任务。例如如果我们的代码存储库使用了一个名为的库而库的API从版本更改为版本1我们需要迁移我们的代码存储库以正确调用修改后的版本。这样的迁移任务不仅涉及对调用库相关API的存储库的所有区域进行编辑还涉及对存储库的区域跨文件边界进行编辑这些区域在更新后的代码上具有传递性的语法和语义依赖关系。 这在图1中进行了说明图中显示了复杂数库的API变化。我们的任务是根据这个变化迁移我们的代码存储库。图3的左侧显示了我们的代码存储库的相关部分其中使用了复杂数库。具体来说文件Create.cs具有方法func该方法调用库中的create_complex方法而Process.cs具有方法process该方法调用func。 我们可以将图1中的任务描述和func的主体传递给LLM以生成func的修改后代码如图3的右侧所示。如图所示LLM已正确编辑了对create_complex API的调用以使其返回Complex类型的对象而不是两个浮点数值的元组。请注意此编辑导致方法func的签名发生了变化-它现在返回Complex类型的对象。这需要对调用方法func的调用者进行更改例如图3左下角显示的文件Process.cs中的process方法。如果没有对process方法的合适更改我们的代码将无法构建图3右下角显示了将存储库置于一致状态以使其构建无错误的process方法的适当更改。 问题阐述。上述迁移任务代表了一类任务涉及为各种目的编辑整个代码存储库例如修复静态分析或测试中的错误报告修复有缺陷的编码模式重构或添加类型注释或其他规范。每个这类任务都涉及一组种子规范例如图1中所示的规范这些规范是代码编辑任务的起点。这些种子规范通常会触发代码的其他编辑要求而这些要求需要在代码存储库中的依赖关系中传播以执行存储库中的其他编辑以完成编码任务。通常这种跨依赖传播的编辑工作是手动完成的。
我们的目标是构建一个存储库级别的编码系统该系统可以自动为像图3中的process方法所需的派生规范生成规范以将存储库置于有效状态。这里有效性是相对于一个“oracle”来定义的可以实例化为多种强制存储库级别正确条件的方式例如无错误地构建、通过静态分析、通过类型系统或一组测试或通过验证工具。我们定义一个由LLM驱动的存储库级别编码任务如下 提出的解决方案。在本文中我们提出了一种通过将LLM驱动的存储库级别编码视为一个规划问题来计算派生规范的方法。自动规划[37, 67]旨在解决多步问题其中每一步执行多个替代操作之一以达到目标状态。它在许多领域广泛应用如运动规划[47]、自动驾驶[39]、机器人学[44]和定理证明[26]。 我们提出了一个称为CodePlan的任务不可知框架它合成一个多步计划来解决存储库级别编码任务。如图2所示CodePlan的输入包括一个存储库、一个通过自然语言指令或一组初始代码编辑表达的任务种子规范、一个正确性oracle和一个LLM。CodePlan构建一个计划图图中的每个节点标识LLM需要履行的代码编辑义务并且一条边表示目标节点需要在源节点之后履行。CodePlan监视代码编辑并自适应扩展计划图。编辑Δ来自任务描述而编辑Δ则根据一种新颖的增量依赖分析、变更可能影响分析和自适应规划算法的组合来确定和上下文化。合并块将LLM生成的代码合并到存储库中。一旦计划中的所有步骤都完成存储库将由oracle进行分析。如果oracle验证存储库则任务完成。如果发现错误则将错误报告用作下一轮计划生成和执行的种子规范。 再次考虑图1中指定的示例API迁移任务在图3的代码上。CodePlan使用图1中的指令作为种子规范执行方法func的编辑。通过分析图3(a)到(b)之间的代码更改它将更改分类为逃逸更改因为它影响到方法func的签名。变更可能影响分析确定func的调用者可能会受到影响因此自适应规划算法使用调用者-被调用者依赖关系推断出一个派生规范以编辑调用func的方法process。种子和派生更改都通过为LLM创建适当的提示来执行生成的代码存储库通过oracle即构建无错误。请注意这只是一个简单的示例只有一次更改传播。在实践中派生更改本身可能需要传递其他更改而CodePlan处理这种情况。 提出的解决方案。 与我们的规划方法相比一个更简单的替代方案是使用oracle来推断派生规范。例如在图3中进行种子更改后构建系统可以找到process方法中的错误。这有重要的限制。首先即使它们导致行为更改也不是所有更改都会引发构建错误例如将返回值从True更改为False而不更改返回类型。其次构建系统在代码中断时对因果关系毫不关心。例如如果根据种子规范更改了覆盖方法的签名那么相应的虚拟方法需要进行类似的更改。然而构建系统在运行在存储库的中间、不一致快照时指责覆盖方法不符合虚拟方法。试图天真地修复构建错误会导致回滚种子更改。CodePlan的静态分析和规划组件克服了这些限制。我们在实验中将CodePlan与使用构建系统迭代识别破坏性更改并使用LLM修复它们的基线进行了比较。我们的定量和定性结果表明CodePlan优于这种以oracle为导向的修复技术。 贡献。 就我们所知迄今为止还没有明确识别和解决LLM对存储库进行的代码编辑的影响以及系统地规划一系列相互依赖的编辑的问题这是自动化存储库级别编码任务的问题。 在存储库级别编码任务的领域中已经发现两种上下文对于提示LLMs非常有用1空间上下文通过静态分析[9, 34, 51, 59, 61, 70, 71, 77]或检索[81, 85]提供跨文件信息给模型和2时间上下文以历史编辑为条件对仓库中的预测进行调整[23, 40, 64, 76]。由于CodePlan监视代码更改并维护存储库范围的依赖关系图因此我们在一个统一的框架中提供了这两种形式的上下文。现有技术假定开发人员提供下一个编辑位置并且不考虑编辑对依赖代码的影响。相反通过推断每个更改的影响CodePlan将更改传播到依赖代码从而自动化存储库级别编码任务通过一系列编辑的方式。 总结一下我们在本文中做出了以下贡献 我们首次正式化了使用LLMs自动化存储库级别编码任务的问题这需要分析代码更改的影响并在整个存储库中传播它们。目前没有系统且可扩展的解决方案来解决这个问题。我们将存储库级别编码视为一个规划问题并设计了一个称为CodePlan的任务不可知框架该框架基于增量依赖分析、变更可能影响分析和自适应规划算法的新颖组合。CodePlan合成了由LLM执行的多步编辑链计划。我们使用gpt-4-32k模型在两个存储库级别编码任务上进行了实验C#存储库的包迁移和Python存储库的时间代码编辑。我们将其与使用oracleC#的构建系统和Python的静态类型检查器来识别派生编辑规范与CodePlan中使用的规划相反的基线进行比较。我们在基线中使用了与CodePlan相同的上下文化方法。我们的结果表明与基线相比CodePlan与实际情况更匹配。CodePlan能够使5/6个存储库通过有效性检查而基线无法使任何存储库通过。除了2个专有存储库外我们将在https://aka.ms/CodePlan发布我们的数据和评估脚本。
**2 设计**
在本节中我们首先概述用于自动化存储库级别编码任务的CodePlan算法第2.1节。然后我们介绍了CodePlan的静态分析第2.2节和自适应规划以及计划执行第2.3节组件。 **算法 1CodePlan算法用于自动化存储库级别编码任务。Cyan和Orchid中的数据结构和函数在第2.2和第2.3节中有解释。**
CodePlan算法算法1接受四个输入 存储库的源代码 。任务的一组种子编辑规范 Δ。一个oracle Θ。一个LLM 。 该算法的核心数据结构是一个计划图 它是一个具有多个根节点的有向无环图第4行。计划图中的每个节点都是一个元组 〈, , 〉其中 是存储库 中的一块代码即代码位置的序列 是编辑指令类似于图1中所示的示例 要么是 要么是 。 CodePlan算法还维护一个依赖图 第5行。图4说明了依赖图的结构。我们将在第2.2.1节中详细讨论它。目前只需知道依赖图 表示存储库 中代码块之间的语法和语义依赖关系。 在第6-9行的循环中直到 Δ 不为空为止执行。第7行调用 InitializePlanGraph 函数第11-13行该函数将 Δ 中的所有更改添加为计划图的根节点。每个编辑 规范包括一个代码块 和一个编辑指令 。对于根节点状态设置为 pending第13行。然后在第8行调用 AdaptivePlanAndExecute 函数该函数执行计划更新依赖图以反映每个代码更改并根据需要扩展计划。一旦计划图完全执行就会在存储库上运行 oracle Θ。它返回错误位置和诊断消息这些将形成下一轮的 Δ。如果存储库通过了oracle的检查则返回一个空集合并且CodePlan算法终止。现在让我们讨论 AdaptivePlanAndExecute这是主要的工作流程。它迭代地选择每个待处理节点并处理它。处理带有编辑规范的待处理节点其中包含块 和编辑指令 涉及以下五个步骤 第一步第19行是提取要编辑的代码片段。简单地提取块 的代码会丢失与周围代码的关系信息。另一方面保留整个文件占用了提示空间并且通常是不必要的。我们发现当一个块属于一个类时周围的上下文最有帮助。对于这样的块我们绘制包含类。也就是说除了块 的代码之外我们还保留了包围类及其成员的声明。正如我们稍后讨论的那样这种草图表示还有助于更容易地将LLM的输出合并到源代码文件中。 第二步第21行是收集编辑的上下文。编辑的上下文第38-41行包括a空间上下文其中包含与块 相关的代码例如从块 中调用的方法以及b时间上下文其中包含导致需要编辑块 的先前编辑。时间上下文由从计划图的根节点到 的路径上的编辑形成。 第三步第23-24行使用第一步中提取的片段、编辑规范中的指令 和第二步中提取的上下文构建编辑的提示并使用提示调用LLM以获取编辑后的代码片段。 第四步第26-28行将编辑后的代码合并回存储库。由于代码已更新许多依赖关系如调用者-被调用者、类层次结构等可能需要更改因此这一步还会更新依赖图 。 第五和最后一步第30-35行进行自适应规划以传播当前编辑对依赖代码块的影响。这涉及对编辑块中的更改进行分类并根据更改类型选择在依赖图中遍历和定位受影响块的正确依赖关系。例如如果当前块 中方法 的编辑涉及到方法签名的更新那么所有调用者 都会受到影响图3的情况。对于每个受影响的块 ′ 和依赖关系 rel该关系将块 与 ′ 连接到依赖图中我们得到一对 〈′, rel〉。如果计划图中存在 ′ 的节点并且它处于待处理状态则我们将从 到 ′ 的边添加到计划图中标记为 rel。否则该边将添加到新创建的 ′ 节点中第34行。块 被标记为已完成第31行。 **2.2 静态分析组件**
现在我们关注CodePlan中使用的静态分析组件。我们将涵盖算法1中带有青色背景的所有数据结构和函数。 **2.2.1 增量依赖分析**
LLM可以提供一个代码片段和编辑指令来进行编辑。虽然LLM可以准确执行所需的编辑但分析编辑对存储库其余部分的影响超出了LLM调用的范围。我们认为静态分析非常适合完成这项工作并提出了相应的增量依赖分析。 **DependencyGraph.** 依赖分析[12]用于跟踪代码元素之间的语法和语义关系。在我们的情况下我们对导入语句、方法、类、字段声明和语句之间的关系感兴趣不包括仅在封闭方法内部定义的局部变量。正式地说依赖图 (,)其中 是一组节点代表上述的代码块 是一组带有标签的边边的标签表示边的源节点和目标节点之间的关系。图4说明了我们跟踪的所有关系这些关系作为标记的边。这些关系包括1语法关系ParentOf 和 ChildOf、Construct 和 ConstructedBy表示代码块 与在语法上包围 的块 之间的关系特殊情况是构造函数和其封闭类之间的关系由 Construct 和 ConstructedBy 表示2导入关系Imports 和 ImportedBy表示导入语句与使用导入模块的语句之间的关系3继承关系BaseClassOf 和 DerivedClassOf表示类与其超类之间的关系4方法重写关系Overrides 和 OverriddenBy表示重写方法与被重写方法之间的关系5方法调用关系Calls 和 CalledBy表示语句与其调用的方法之间的关系6对象实例化关系Instantiates 和 InstantiatedBy表示语句与创建该对象的构造函数之间的关系7字段使用关系Uses 和 UsedBy表示语句与使用的字段声明之间的关系。 **ConstructDependencyGraph.** 依赖关系是通过静态分析跨存储库中的源代码生成的。我们将存储库的源代码表示为抽象语法树AST的森林并在AST子树之间添加依赖关系边缘。文件级别的分析用于派生语法和导入关系。所有其他关系需要跨类、跨过程的分析可以跨越文件边界。特别是我们使用类层次分析[32]来派生语义关系。 **ClassifyChanges.** 正如在第2.1节中讨论的CodePlan在第四步中将LLM生成的代码合并到存储库中。通过对比前后的代码我们对代码更改进行分类。表1第一列和第二列给出了原子更改的类型及其标签。广义上更改分为修改、添加和删除更改进一步根据更改的构造进行分类。我们区分方法体和方法签名的更改。同样我们区分类声明的更改、其构造函数的更改或其字段的更改。还会识别导入语句或使用导入的语句的更改。这些都是原子更改。LLM可以在给定的代码片段中进行多个同时编辑导致多个原子更改所有这些更改都由ClassifyChanges函数识别。 **UpdateDependencyGraph.** 当LLM生成的代码合并时与更改站点的代码相关的依赖关系会重新分析。表1第三列根据ClassifyChanges推断的标签提供了更新依赖图 D 到 D 的规则。对于修改更改我们重新计算已更改代码的关系但不包括构造函数。构造函数与其封闭类之间有一个语法关系不需要重新计算。对于添加更改为添加的代码创建新的节点和边缘。与语法关系对应的边缘以直接方式创建。如果更改同时添加了一个元素导入、方法、字段或类及其使用我们将在分析使用它的语句之前为添加的元素创建一个节点。方法的添加需要特殊处理如表中所示如果添加了一个重写方法 C.M那么如果调用是在类型为 C 的接收对象上发出的则将与匹配的被重写方法 B.M 的Calls/CalledBy边缘重定向到C.M。删除重写方法需要与表1中所述的类似处理。所有其他删除更改需要按照表中所述删除节点和边缘。 **2.2.2 变更可能影响分析**
在第五步中CodePlan通过LLM进行的代码更改来识别可能受到影响的代码块。让 Rel(D, B, rel) 表示通过关系 rel 在依赖图 D 中与块 B 相连接的代码块的集合。让 D 和 D 分别表示在表1中的更新之前和之后的依赖图。 **获取受影响的块。** 表1的最后一列告诉我们如何为每种更改类型识别受影响的代码块。当编辑方法 M 的主体时我们执行逃逸分析 [22, 29] 来确定是否已受到调用 M 的调用者中可访问的任何对象逃逸对象的更改影响。如果是的话M 的调用者通过 Rel(D, M, CalledBy) 识别被标识为受影响的块。否则更改局限于该方法没有受影响的块。如果编辑了方法的签名则通过继承层次结构中的方法重写关系与之相关的调用者和方法受到影响。签名更改本身可能会影响 Overrides 和 OverridenBy 关系例如Override 访问修饰符的添加或删除。因此在更新后的依赖图 D 中通过这些关系相关的块也被视为受影响如表1所示带有 MMS 标签的行。当修改类 C 的字段 F 时使用 F 的语句、C 的构造函数以及 C 的子类/超类都会受到影响。当修改类时按照 D 和 D实例化它以及其子类/超类的方法都会受到影响。对构造函数的修改具有类似的规则只是这种更改不会更改继承关系因此只需要 D。当修改导入语句 I 时使用导入模块的语句会受到影响。
添加和删除更改比修改更改要简单它们的规则设计沿用了上面讨论的原则。出于篇幅考虑我们不会逐步解释每个更改。我们假设代码中不会使用新添加的类或导入。因此添加它们不会导致任何受影响的块。在我们的实验中我们发现表1中的规则已经足够了。但是如果需要CodePlan可以轻松配置以适应表1中规则的变化。
**2.3 自适应规划和计划执行**
现在我们讨论来自算法1的Orchid背景中的数据结构和函数。
**2.3.1 自适应规划**。在使用GetAffectedBlocks识别受影响块之后CodePlan创建需要使用LLM解决的更改义务以使依赖代码与更改保持一致。如第2.1节所讨论的这是一个迭代过程。
**PlanGraph。** 计划图P (, )是一个带有一组义务的有向无环图每个义务都是三元组 〈, , 〉其中B是一个块I是一条指令状态要么是待定的要么是已完成的。中的边记录了原因源义务和目标义务之间的块之间的依赖关系。换句话说边的标签标识了在表1中的更改可能影响规则中的哪个Rel子句导致创建目标义务。
**ExtractCodeFragment。** 如第2.1节的第一步所讨论的仅提取块B的代码是次优的因为它会丢失上下文。ExtractCodeFragment函数获取代码块所属的整个类保留B的完整代码并仅保留类和其他类成员的声明。我们发现这很有用因为类和其他成员的名称和类型为LLM提供了额外的上下文。通常情况下LLM需要同时进行多个更改。例如在我们的一些案例研究中LLM必须添加字段声明将参数传递给构造函数并在构造函数中使用它来初始化字段。通过将周围代码的草图作为代码片段提供给LLMLLM可以在正确的位置进行这些更改。代码片段提取逻辑通过遍历AST并“折叠”掉已经草绘的子树例如方法主体来实现。如第1节所述即使存在多个同时更改这种草绘表示也允许我们将LLM生成的代码放回AST而不会产生歧义。
**GetSpatialContext。** CodePlan中的空间上下文是指代码块在代码库中的排列和关系有助于理解类、函数、变量和模块的结构和相互作用。它对于进行准确的代码更改非常关键。CodePlan利用依赖图来提取空间上下文将代码表示为节点以及它们之间关系的边。这个图使CodePlan能够遍历代码库识别相关的代码块并保持对它们的空间上下文的意识。因此在生成代码编辑时依赖图使CodePlan能够进行上下文感知的代码修改使其与代码的空间组织保持一致从而增强了其代码编辑能力的准确性和可靠性。
**GetTemporalContext。** 计划图记录了所有更改义务及其相互依赖关系。通过将计划图的根节点到目标节点的所有路径线性化可以提取时间上下文。每个更改都是在更改之前和之后的代码片段的一对。时间上下文还说明了“原因”记录为边标签将目标节点与其前驱节点连接起来。例如如果节点A通过CalledBy边连接到B那么B的时间上下文是A的前后片段以及一条说“B调用A”的语句这有助于LLM理解最新时间更改对A进行更改与当前义务对B进行更改之间的因果关系。
**2.3.2 计划执行。** CodePlan迭代选择计划图中的待定节点并调用LLM来履行更改义务。
**MakePrompt。** 在提取了要编辑的代码片段以及相关的空间和时间上下文之后我们构建了一个要传递给LLM的提示其结构如下。我们首先使用特定于任务的说明p1然后列出到目前为止在存储库中进行的与要编辑的片段相关的编辑p2。接下来的部分p3说明了p2中出现的每个片段与要编辑的片段的关系。然后是空间上下文p4和要编辑的片段p5。 **Oracle和计划迭代。** 一旦计划图中的所有节点都标记为已完成并且没有添加新节点就完成了一次存储库级别的代码编辑迭代。如图2所示对存储库调用了Oracle。如果Oracle标记了任何错误例如构建错误则将错误位置和诊断消息添加为下一次迭代的种子更改然后自适应规划再次开始。如果Oracle没有标记任何错误CodePlan终止。
**3 实施**
在本节中我们提供了构成我们方法核心的实施组件的详细概述。
**依赖图构建。** CodePlan方法的核心是依赖图它有助于表示代码块之间复杂的关系。为了从代码存储库构建这个依赖图我们采用了系统性的方法。最初我们解析存储库中的所有代码文件利用tree-sitter库[25]生成类似AST的结构。这种结构化表示简化了代码库中各种基本代码块的识别。例如图5示例了tree-sitter生成的C#代码片段的AST结构。代码块在不同的级别进行识别包括类、方法、导入语句和非类表达式。例如在图5中以class_declaration节点为根的子树对应于SyncSubscriberTest类。 **C#中的关系识别。** 在C#存储库的背景下在依赖图中建立边涉及到在AST内部仔细跟踪关系。我们为图4中列出的每种关系类型制定了自定义逻辑包括重要的连接如调用者-被调用者、覆盖-被覆盖、基类-派生类等。为了说明对于调用者/被调用者关系我们在AST中查找invocation_expression节点。随后我们处理这些节点下的子树以解析关键细节如目标类和调用方法的名称。有了这些信息我们在启动方法调用的代码块和目标类中的相应方法块之间创建Calls/CalledBy关系链接。虽然我们为这些关系实施了自定义逻辑但值得注意的是由于其固有的灵活性也可以将用于C#的其他依赖关系分析工具如C#的语言服务器LSP[5]、CodeQL [2]或类似解决方案集成到我们的系统中。
**Python中的关系识别。** 对于Python存储库我们使用Jedi [4] - 一种静态分析工具用于在整个代码库中查找符号的引用和声明。这些功能被用来识别依赖图中的关系如调用者-被调用者、覆盖-被覆盖和基类-派生类。
**集成GPT-4进行代码编辑。** CodePlan充分利用了GPT-4 [57]的卓越能力来有效地执行代码编辑。在为编辑模型构建输入数据时我们仔细提供了时间上下文、空间上下文和实际要编辑的代码以代码片段的形式提供。这些代码片段表示包含编辑位置的类或方法并以草图表示如第2.1节所述。这种草图表示确保了模型为每个编辑位置提供了丰富的上下文从而显著提高了生成的编辑的质量和准确性。
**语言可扩展性。** 尽管我们当前的实现能够有效支持C#和Python存储库但扩展到其他编程语言的存储库是一项简单的工作。它主要涉及创建具有图4中识别的关系的依赖图并将其整合到CodePlan框架中从而使其能够无缝适应各种编程语言。
**4 实验设计**
在本节中我们将解释我们如何进行实验来测试CodePlan。我们将首先讨论我们使用的不同数据集。然后我们将讨论我们比较CodePlan的方法这些方法类似于我们的参考点。最后我们将解释我们如何测量结果以查看CodePlan与其他方法相比的性能如何。 **4.1 数据集**
在我们的实验中我们利用了多样化的数据集代表了各种复杂性和规模的代码存储库。这些数据集使我们能够在不同的现实世界情境中全面评估CodePlan的性能。 **内部存储库Int-1和Int-2。** 这些存储库是专有的属于一家大型产品公司。它们的特点是规模庞大、模式复杂并且代表了生产级别的代码库。我们主要关注的任务是将这些存储库从传统的日志框架迁移到现代的日志框架。这个迁移涉及到非平凡的更改包括使用日志工厂创建特定于服务的日志记录器、通过调用链传递日志记录器、管理类层次结构、在不同范围类、方法等存储日志记录器引用以及在静态和非静态类/方法中处理日志记录器。这两个生产存储库Int-1和Int-2之所以被选择是因为它们具有不同的编码风格和设计模式为我们的评估提供了全面的内部数据集。 **外部存储库公共GitHub。** 我们还考虑了来自GitHub的外部存储库以丰富我们的数据集。选择这些存储库是为了代表两种不同的编码任务迁移和时间编辑后面会讨论。 **迁移任务。** 这个任务涉及迁移API或解决代码库中的不兼容性变化。示例包括更新依赖项、适应外部库的更改或与新的编码标准对齐代码。它的复杂性在于通常需要在许多代码文件和依赖项之间进行一致的更新。为了选择这些存储库我们搜索包含与各种迁移API、框架等相关的提交和拉取请求的存储库。我们筛选了至少包含50个文件的存储库。 **时间编辑任务。** 这个任务涉及在给定一些初始代码更改的情况下编排一系列代码更改。许多代码更改都可以被描述为时间编辑包括重构或添加/删除功能。一个时间编辑任务由一组初始编辑通常由用户进行以及由种子编辑引发的派生编辑组成。工具的任务是从初始编辑集中推断出派生编辑。一个示例的时间编辑任务可以是初始编辑是向方法添加一个参数派生编辑是更改调用此方法的所有地方。我们从GitHub上的公共存储库中的提交中识别时间编辑任务。我们考虑具有宽松许可证的Python存储库按星级排序过滤掉与文档/教程相关的存储库并从候选存储库中选择至少有10,000个星星的存储库在2021年11月1日之后进行多个相关更改的提交。 **源/目标/预测存储库。** 为了收集迁移和时间编辑任务的代码更改我们获取了GitHub上提交之前和之后的文件。我们将这些称为源存储库提交前和目标存储库提交后。分析这些更改允许我们通过手动检查识别种子更改。例如在从NUnit迁移到XUnit时种子编辑之一涉及将Console.WriteLine替换为写入ITestOutputHelper对象。有了源存储库和种子更改说明CodePlan的任务是对源存储库进行必要的更改从而产生我们称之为预测存储库的结果。如果CodePlan成功执行了所有更改那么预测存储库应该与目标存储库匹配为我们对其在这些不同代码库中的性能与实际情况进行了充分的评估提供了坚实的基础。 **预处理源和目标存储库。** 在处理大型存储库时常常会有多个开发人员贡献代码导致个人偏好驱动的各种编码风格。例如一个开发人员可能会始终使用this限定符来引用类成员而另一个则可能不会。当CodePlan通过LLM提示执行更改时它往往会在整个代码库中建立统一的风格这可能包括强制使用this限定符的一致性等等。虽然这些更改确保了功能等效性但在将预测存储库与目标存储库进行比较时它们可能会影响评估指标。为了解决这些问题我们在源和目标存储库上进行了手动预处理步骤。这个预处理旨在在存储 表2 总结了迁移C#和时间编辑Python存储库的数据集统计信息包括存储库的名称内部和外部以及各种关键统计信息包括它们的大小、代码更改和其他相关指标 文件数目每个存储库中的文件总数。代码行数所有文件中的代码行数之和。已更改文件数在源存储库和目标存储库之间发生更改的文件数。种子更改数初始编辑的数量通常被认为是代码更改的起点。派生更改数跟随初始种子更改的后续编辑的数量。差异大小源存储库和目标存储库之间不同的行数。种子编辑的大小当种子编辑直接在代码上进行时表示初始编辑的行数。当种子编辑通过LLM指令进行时它表示指令文本的大小。提示模板大小这个数字表示CodePlan使用的LLM提示模板的大小。相同的模板适用于所有迁移存储库任务另一个类似的模板用于所有时间编辑存储库任务。 这些指标不仅提供了数据集特征的全面概述而且突出了使用CodePlan相对于手动过程的显著优势特别是对于大型存储库。在手动情景中需要人工费力地识别依赖更改并实现每个修改。值得注意的是“差异大小”和“种子编辑的大小”等指标提供了有关所需开发工作的见解。另外值得注意的是为CodePlan制定LLM指令所需的工作量明显少于手动进行所有代码更改所需的大量工作。这些指标共同展示了CodePlan在不同代码库上的高效性和有效性强调了它在简化开发工作流程并节省宝贵开发人员时间方面的潜力。 4.2 神谕和基线神谕。回想一下我们对存储库级编码任务的定义是围绕满足可以确定解决方案有效性的神谕。在我们的实验中我们考虑了两个特定的神谕实例。对于C#迁移任务我们将神谕定义为通过C#构建工具而没有任何错误。对于时间编辑方案我们使用Pyright [6]这是Python的静态检查器作为神谕。 神谕引导修复。这两个神谕都以代码库作为输入并可以输出代码库中的错误列表。这自然地导致了对我们任务的基线方法的制定我们将其称为神谕引导修复。这些都是简单的反应性方法在每个步骤中我们尝试纠正神谕标记的错误。对于C#迁移场景基线是构建修复对于时间编辑基线是Pyright修复根据使用的神谕而定。 神谕引导修复包括以下步骤 初始编辑该过程从应用初始种子编辑到代码库开始。构建和错误检测在种子编辑之后我们调用神谕以检测由于种子编辑而在代码库中产生的错误。错误消息分析然后解析神谕生成的错误消息以精确定位错误在代码中的位置。LLM修补随后将错误消息以及标记位置的代码片段传递给LLM。LLM利用其代码生成能力为确定的错误生成修补程序或修复。为了与CodePlan进行公平比较我们在神谕引导修复中使用了我们的实现用于空间和时间上下文的提取。也就是说CodePlan和神谕引导修复之间的主要区别在于CodePlan使用自适应规划而神谕引导修复使用神谕生成的诊断信息。需要注意的是神谕引导修复方法是一种反应性方法缺乏全面的“更改可能影响分析”。这意味着它们可能不会彻底评估所提议的代码更改对代码库的其他部分可能产生的影响。因此在处理复杂的编码任务时此类方法生成的修复可能不完整或不正确。 替代编辑模型Coeditor [76]。CodePlan默认利用LLM的文本和代码处理能力以在提供适当上下文的情况下对代码片段进行本地编辑。然而从理论上讲CodePlan的增量依赖分析、更 改可能影响分析和自适应规划组件可以与任何能够根据提供的意图进行局部编辑的工具或模型结合使用。Coeditor [76]是一个经过微调的基于变压器的模型可以编辑代码片段同时考虑到在存储库中之前进行的编辑。这样的模型非常适合时间编辑任务其中我们需要从一组种子编辑中进行一系列编辑其中每个编辑依赖于前面一组编辑的某个子集。实际上Coeditor在[76]中对时间编辑任务进行了评估。为了展示我们的分析和规划的通用性我们评估了我们的方法在时间编辑情境中的性能将gpt-4-32k替换为Coeditor作为编辑模型。4.3 评估指标 我们研究中采用的评估指标旨在评估CodePlan或基线如何有效地在整个代码存储库中传播更改以及每个更改的正确性。为了实现这一目标我们依赖于两个关键指标块指标和编辑指标。 块指标。块指标帮助我们了解CodePlan准确识别需要修改的代码块的能力。这些指标包括 匹配块这些是存在于源存储库中、已在目标存储库中进行了编辑并且还在预测存储库中进行了编辑的代码块。基本上这些是CodePlan成功识别需要更改的块。 未命中块未命中块是指存在于源存储库中、已在目标存储库中进行了编辑但在预测存储库中未进行编辑的代码块。换句话说这些是CodePlan在应该进行修改的情况下未能修改的块。 虚假块虚假块是指在源存储库中找到的代码块在目标存储库中未进行编辑但在预测存储库中被CodePlan错误地进行了编辑。这代表了CodePlan不必要地进行的编辑。 理想情况下匹配块数量高未命中块和虚假块数量低。 编辑指标。虽然块指标评估代码块的识别但编辑指标深入探讨了CodePlan所做修改的正确性。这些指标包括 Levenshtein距离Levenshtein距离度量了预测存储库和目标存储库之间文件级别的编辑距离。它计算了将一个文件转换为另一个文件所需的更改次数。较高的Levenshtein距离表示CodePlan未正确地对存储库进行了更改。 Diff BLEU通常我们使用BLEU [58]这是自然语言处理中常见的指标来衡量文本相似性。然而在应用于我们的任务时BLEU可能会产生过高的相似性分数因为特定任务的代码编辑通常只涉及文件的一小部分。为解决这个问题我们计算Diff BLEU一种修改后的BLEU分数表示为BLUE(DIFF(源存储库文件目标存储库文件)DIFF(源存储库文件预测存储库文件))。在这里DIFF计算两个文件之间的差异diff hunks。Diff BLEU的独特之处在于它专注于比较预测和目标存储库之间代码的修改部分同时忽略常见的代码。当预测和目标存储库中的修改精确匹配时Diff BLEU得分为1.0表示在处理代码修改方面具有高度的正确性和一致性。 总之这些评估指标全面评估了CodePlan的性能无论是在识别需要修改的代码块方面还是在确保所做的修改是正确的方面。
5 结果与分析 在本节中我们提供实证结果以回答以下研究问题 RQ1CodePlan能够多么有效地定位并进行所需更改以自动化存储库级别的编码任务 RQ2时间和空间上下文对于CodePlan的性能有多重要 RQ3使CodePlan在解决复杂的编码任务中胜过基线的关键区别是什么 5.1 RQ1CodePlan能够多么有效地定位并进行所需更改以自动化存储库级别的编码任务 动机。在现代软件工程背景下研究问题涉及到CodePlan框架在自动化存储库级别编码任务中的有效性这是至关重要的。有几个关键动机驱使这一问题的重要性 存储库级别任务的复杂性软件工程活动如包迁移和时间代码编辑通常超越了局部代码更改的范围。存储库级别编码任务涉及对整个代码库的广泛修改。这种复杂性需要新的方法来确保效率和正确性。现实世界的相关性在实践中软件存储库经常需要进行大规模的更改。例如包迁移涉及跨多个文件和依赖项更新依赖项而时间代码编辑需要跟踪和管理不断演化的代码库。这些任务不仅耗时而且在手动操作时容易出错。与基线的比较评估CodePlan与基线方法的比较至关重要。基线方法如“Oracle-Guided Repair”在软件开发中很常见但在处理存储库级别任务时可能缺乏效率。将CodePlan与基线进行评估提供了衡量其有效性的基准并突出了其表现出色的领域。我们还研究了在执行不同编辑模型时我们的系统的行为通过在时间编辑任务上评估Coeditor和CodePlan的组合。大规模存储库研究不仅涉及孤立的编码问题还评估了CodePlan在大型内部和外部存储库上的性能。这个广泛的范围确保了框架在各种复杂的实际场景下的有效性得到了测试。 实验设置。 为了研究CodePlan能够多么有效地定位并进行所需更改以自动化存储库级别任务我们在4.1中描述的任务的上下文中对其进行评估。对于C#迁移和时间编辑任务我们从没有进行任何编辑的源状态的存储库开始。我们在源状态的基础上应用种子编辑此时CodePlan或正在评估的基线接管整个存储库的编辑。在C#迁移的情况下使用合适的提示自行执行种子编辑而对于时间编辑我们为每个存储库任务存储种子编辑并将其应用为补丁。CodePlan在种子编辑后对存储库进行增量依赖分析识别可能受其影响的代码并计划下一步要进行的编辑使用合适的提示查询LLM并将结果合并到存储库中。CodePlan会不断进行迭代直到发现没有更多的站点需要进行编辑。 有时由于大型语言模型LLM响应的固有变异性可能需要多次迭代。我们启动第一次迭代称为“Iter 1”以使用LLM启动代码编辑过程。然而LLM响应的偶尔不准确可能会引入错误的代码更改和后 续的构建错误。为了解决这些挑战第二次迭代称为“Iter 2”变得重要。在此阶段CodePlan积极识别并确认前一次迭代中的任何构建错误并重新与LLM交互以获取更精确的响应以纠正初始错误。 与CodePlan一起我们还在相同的存储库上评估了一系列基线。对于C#迁移我们评估了Build-Repair对于时间编辑我们评估了Pyright-Repair其设置如4.2中所述。Pyright-Strict-Repair是一个变种其中我们使用启用了严格模式的Pyright工具。在所有修复基线中我们提供与CodePlan中相同的上下文时间和空间唯一的区别是本地化的编辑位置是使用oracle进行的。我们还评估了在时间编辑任务中使用Coeditor而不是gpt-4-32k作为编辑模型的情况如4.2中所述。在所有Coeditor基线中上下文化是根据[76]中的进行的下一个编辑站点的本地化是通过CodePlan或使用oracle进行的。 我们评估所有这些方法在多大程度上能够通过匹配、未命中和虚假块指标定位要编辑的站点以及整体修改的正确性如4.3中所述。我们还确定了方法执行结束后存储库的状态是否通过了有效性检查即是否满足了oracle并根据基本事实进行了正确的编辑。 结果讨论。 表3中的实验结果展示了CodePlan框架在自动化存储库级别编码任务方面的有效性。 在内部专有存储库的C#迁移任务的背景下结果表提供了两种方法的性能全面视图CodePlan和Build-Repair。值得注意的是CodePlan在几个关键方面表现出色。它在“匹配块”方面表现出色为“Int-1日志”和“Int-2日志”数据集均实现了151个匹配块和438个匹配块的完美结果。这表明CodePlan在准确识别和处理预期代码更改方面具有卓越的精度。此外CodePlan令人印象深刻地展现了零“未命中块”确保没有忽略任何关键代码修改从而最小化了功能问题的风险。同样值得注意的是“虚假块”的缺失。
**CodePlan vs. Build-Repair**
比较分析显示了为什么Build-Repair落后于CodePlan。导致其性能差距的一个关键因素是它依赖于“构建错误位置”作为代码更正的指示器。构建错误通常会标出错误检测到的位置但它们不一定与实际所需的修复位置相符。例如错误可能表现为派生类的重写函数签名不匹配但修复是在基类的虚函数签名中需要的这会导致Build-Repair错误地解释为校正位置。此外在编译器优化的上下文中构建过程可能会遮盖后续的错误仅在特定时间显示选定的错误。这可能导致不正确的校正位置的识别阻碍正确更改的传播进一步加剧了CodePlan和Build-Repair之间性能差距。这些限制突显了Build-Repair在准确定位和处理复杂迁移任务中的代码修改时所面临的挑战。 **多次迭代**
正如在CodePlan的“Iter 1”后的“Int-1”数据集中所示处理大型语言模型LLM响应固有变异性的需求从而需要多次迭代来处理。为了纠正这一点“Iter 2”发挥着重要作用。在此阶段CodePlan识别并承认了前一次迭代中的构建错误并重新与LLM合作以获取更准确的响应以纠正初始错误。值得注意的是在两次迭代之间的块度量没有发生变化因为CodePlan正确地识别了需要纠正的块并与LLM进行了修改。然而“Iter 1”中的LLM校正是错误的导致Levenshtein距离度量较低。在代码编辑阶段这种迭代的改进过程显著有助于减轻LLM输出偶尔不准确的影响。 **在Ext-1上的性能**
在“Ext-1”数据集上CodePlan与基线方法Build-Repair的比较中我们观察到它们性能上的显著差异。CodePlan成功地识别并更新了58个代码块实现了1.00的完美DiffBLEU分数表明它进行了与目标存储库相同的更改。相比之下基线方法Build-Repair则未能识别六个块并生成了八个构建错误。这种差异突显了Build-Repair的一个关键局限性——缺乏全面的“更改可能影响分析”功能。具体来说Build-Repair未能更新用于初始化新添加的ITestOutputHelper _output类成员的构造函数块。这个遗漏没有被注意到因为缺少初始化没有触发构建错误从而对调用者产生了级联影响。相比之下CodePlan成功处理了新添加的ITestOutputHelper _output类成员的初始化。这一成就要归功于其强大的更改可能影响分析它准确识别了在添加新字段时对构造函数块的必要修改。因此CodePlan无缝地更新了构造函数和所有其调用者避免了任何遗漏的块或构建错误。这一发现突显了CodePlan的高级规划能力的重要性这确保了对存储库级别的代码编辑采用更全面和准确的方法。 **CodePlan vs Pyright-Repair**
在时间编辑任务的背景下我们可以看到CodePlan能够成功地识别所有的派生编辑位置其中两个存储库T-2T-3中的效果几乎完美第三个存储库T-1中也几乎成功。与此形成对比的是基线Pyright-Repair方法它未能识别两个存储库T-2T-3中的任何派生编辑。我们发现在严格模式下使用Pyright可以提供更好的结果但仅在一个存储库T-1中表现得与CodePlan一样好。总体而言我们观察到Pyright-Repair基线在识别编辑位置方面不足。这也反映在DiffBLEU得分一直较高和Levenshtein距离L.D.一直较低的情况下。请注意由于LLM不一定执行与基本事实相同的确切编辑因此DiffBLEU和L.D.可能不会具有完美的1.0和0值。 对于T-2和T-3我们看到Pyright-Repair基线无法识别任何派生编辑。在这两个存储库中一旦进行了种子编辑Pyright就不会标记代码库中的任何错误。在T-2的情况下种子编辑涉及向方法添加参数如图6所示。然而此新参数还被分配了默认值。由于此参数存在默认值Pyright不会标记此方法的所有调用站点为错误。但是在基本事实中开发人员会跟进并编辑调用站点。CodePlan的更改可能影响分析将这些调用站点标识为“可能需要”编辑因此我们将它们传递给LLM以进行编辑。 在T-3的情况下种子编辑涉及修改函数的主体如图6所示。在种子编辑之后该方法现在期望传递给它 的字典包含一个新的键“api_endpoint”。Pyright不会在此阶段标记代码库中的任何错误但很明显这可能需要对send_request的调用者进行修改。CodePlan确实检测到了这一点并能够在基本事实中的10个派生站点中识别和进行编辑。 在这两种情况下我们事先不知道是否需要编辑调用站点。因此CodePlan将此决策留给LLM根据上下文来决定。相比之下Oracle-Guided基线甚至没有检测到可能需要更改。这可以归因于像Pyright或Build之类的Oracle的事实它们旨在检测错误因此只会标记违反某些规则的代码。这与在整个存储库中传播更改的任务不一致因为在许多情况下可能需要传播更改但具有更改的存储库不会违反Oracle的任何规则。 **Coeditor评估**
在比较CodePlan和Coeditor-CodePlan时我们可以看到它在T-1上表现得相当好但在T-2和T-3上略逊一筹每个存储库中都错过了一个编辑站点。尽管这两种方法都使用相同的分析和规划但CodePlan中的本地编辑与基本事实更一致与Coeditor-CodePlan在T-2和T-3中展示的更低的DiffBLEU分数和更高的L.D.反映出这一点。这可能是由于gpt-4-32k和Coeditor之间的上下文理解能力差异所致。例如选择将方法的参数实例化而不是向调用者添加参数可能意味着错过了由于调用者签名更改而产生的编辑。作为一个更强大的模型gpt-4-32k更擅长理解时间编辑的上下文因此它所做的编辑与基本事实更加一致与Coeditor相比。这也在T-2上比较Pyright-Strict-Repair和Coeditor-Pyright-Strict-Repair时观察到Coeditor的错误本地编辑导致了编辑站点的丢失以及更差的DiffBLEU和L.D。 总之实验结果证明了CodePlan的基于规划的方法在自动化存储库级别编码任务方面非常有效相比传统的基线方法它具有更好的匹配度、完整性和精度。其处理大规模存储库内复杂编码任务的能力标志着自动化软件工程活动的重大进展。
**RQ2: 时态和空间背景对于CodePlan的性能有多重要** **动机** RQ2的动机在于认识到大型语言模型LLMs需要时态和空间背景来提供准确和与上下文相关的代码更新建议。时态上下文对于理解代码更改的顺序和时机至关重要使LLMs能够提出与代码的演变相一致并保持一致性的建议。没有这种时态意识LLMs可能会提供与早期或后续修改相冲突的解决方案导致代码错误。另一方面空间上下文使LLMs能够理解代码库不同部分之间的复杂关系和依赖关系。这种理解对于确定需要更新的位置以及确保以保持代码功能的方式应用它们至关重要。因此调查这些上下文在CodePlan的规划中的重要性对于有效地利用LLMs来自动执行存储库级别的编码任务至关重要。 **实验设置** 为了评估CodePlan的规划中时态和空间上下文的重要性RQ2采用了特定的实验设置。回顾一下CodePlan通过维护与先前相关的更改列表来跟踪时态上下文并将这些上下文随后纳入到大型语言模型LLM提示中。 为了衡量这些上下文的重要性进行了一项受控实验。在这个实验中故意停止跟踪时态变化不提供时态或空间上下文给LLM在代码修改任务期间。这使得可以详细研究当这些基本上下文被省略时LLM响应受到了怎样的影响。该实验还包括了对各种指标的全面评估包括代码一致性块度量和代码正确性Diff BLEU和Levenshtein距离。通过比较带有时态和空间上下文的CodePlan设置与没有这些上下文的实验设置之间的结果研究旨在准确量化这些上下文在CodePlan的规划过程中的重要性以及它们对自动化代码修改质量的影响。 **结果讨论** 关于时态和空间上下文对于CodePlan的规划的重要性的结果RQ2揭示了关键的见解。如表2所示当不考虑时态上下文时在代码修改过程中错过的块明显增加。这增加归因于大型语言模型LLM由于缺乏时态上下文而未对某些代码块进行必要的更改因为它无法理解在没有时态上下文的情况下需要这些修改。 图8中的一个示例说明了这个问题。在这种情况下根据派生类的方法的签名更改需要在基类的虚方法中进行校正。然而由于LLM缺乏时态上下文它没有关于派生类的方法的信息导致它认为不需要对基类方法进行任何更改。这突显了时态上下文在理解代码依赖关系和确保准确更新方面的关键作用。 此外图9提供了另一个示例说明了缺乏时态上下文如何影响代码修改过程。在这种情况下需要在“Startup”方法内的“Create-Service”调用中添加“Context”参数。然而由于LLM缺乏时态上下文它不知道“CreateService”的签名更改因此无法识别需要更新所有调用者的需求。这个遗漏导致了整个代码库中的许多未能更新的内容。
**关于时态和空间上下文的重要性的观察** 还需要强调另一个重要的观察结果即在缺乏足够的空间上下文时虚假块的数量增加。这种现象发生是因为在缺乏足够的空间上下文的情况下大型语言模型LLM可能会错误地感知缺失的代码元素并尝试创建它们导致虚假代码块的生成。 图10中的一个示例说明了这个问题。在这种情况下任务是通过将日志调用从旧的日志框架迁移到新的框架来修改“AuthorizeUser”方法。然而由于缺乏空间上下文无法指定“GetUserSubscription”方法和“CurrentUser”属性的存在LLM试图创建这些元素。因此不仅解决了日志迁移问题LLM还引入了不必要的代码块比如创建“GetUserSubscription”方法和将“CurrentUser”添加为类级对象。 这一观察结果强调了空间上下文在引导LLM理解代码结构和关系方面的关键作用。提供全面的空间上下文有助于防止生成多余的代码块并确保代码修改精确且与预期的更改一致。 总之实验结果强调了时态和空间上下文在CodePlan的规划中的基本性质。由于缺乏时态和空间上下文而导致的错过和虚假更新的增加强调了通过这些上下文向LLM提供对代码演化和依赖关系的全面理解对于确保准确和有效的代码修改的重要性。
**5.3 RQ3CodePlan在解决复杂编码任务方面的关键差异因素是什么** **动机** RQ3旨在揭示CodePlan相对于基线方法在处理复杂编码任务方面表现出色的关键不同之处。这个研究问题的动机在于需要识别和理解有助于CodePlan表现出众的具体因素。考虑到编码任务日益复杂尤其是存储库级别的任务因此有必要明确指出将CodePlan与传统方法区分开的具体方面。通过识别这些不同之处研究旨在揭示CodePlan的优势和优点为解决复杂编码任务所带来的挑战提供有价值的见解。 **实验设置** 这里的主要关注点是定性分析。在使用CodePlan和基线方法进行代码修改后通过手工检查结果进行仔细分析。这包括详细检查每种方法所做的更改目的是深入了解决策过程和代码修改的微妙差异。通过手动检查和比较这些更改研究旨在发现微妙但关键的差异以阐明CodePlan在处理复杂编码任务时的优势和基础机制。这种定性方法提供了全面了解CodePlan在这一背景下表现出色以及它与传统方法的不同之处的理解。 **结果讨论** **CodePlan的战略规划和上下文感知**
CodePlan在处理复杂编码任务方面的卓越表现可以归因于其强大的功能尤其是其增量分析和更改可能影响分析。这些功能使其与像Build-Repair这样的基线方法区分开来后者主要侧重于保持语法正确性而忽略了关键的上下文细节。为了说明这一点让我们深入研究图11所示的存储库Ext-1中的一个示例在这个示例中CodePlan的任务是将Console.WriteLine方法迁移到ITestOutputHelper.WriteLine。这个迁移涉及到一系列的更改1到4如图11所描述。这些级联更改始于引入ITestOutputHelper _output作为类级成员这是通过LLM更新完成的。 在这种情况下CodePlan的更改可能影响分析在发挥作用方面无价。它认识到添加新字段需要修改构造函数以确保正确初始化。因此CodePlan安排了必要的构造函数修改。因此构造函数Subscriber(...)被正确更新为接受ITestOutputHelper作为参数并初始化类成员_output。这反过来导致了存储库中的一系列更改如图11中的步骤1到4所解释的那样。 这个例子说明了CodePlan如何根据上下文和上下文感知的能力有序地对存储库进行更改得益于它的更改影响分析和引入时态上下文的能力。相比之下仅依赖于语法正确性的Build-Repair无法检测到Subscriber构造函数中的修改需要修改。由于遵守了所有语法规则它不会触发构建错误因此无法在图4中所示的步骤2到4中执行更改而只会执行步骤1中概述的修改导致代码更新不完整。 CodePlan的独特优势在于它对代码关系的全面理解和精心规划确保了代码库的完整性和功能在复杂编码任务中得以保持。这种定性分析突显了CodePlan在处理复杂编码任务时的微妙方法如何胜过基线方法。 **增量分析与依赖图的关系维护**
CodePlan在应对复杂编码任务时的卓越表现归功于其增量分析该分析有效地将更改与底层的依赖图关联起来。与静态代码快照不同静态代码快照可能导致依赖关系的不完整表示我们的增量分析方法确保了依赖图内部的关系保持到受影响的代码块被修改之前。 考虑一个调用者函数进行重命名的情况。传统的静态快照可能难以保持调用者-被调用者关系因为在它们的视图中调用者已经被重命名。然而CodePlan的增量分析介入保持调用者
**7 相关工作** **用于编码任务的大型语言模型 (LLMs)** 已经训练了大量的LLMs [10, 19, 21, 24, 28, 30, 35, 57, 73–75, 80]这些模型是基于大规模的源代码和自然语言文本语料库的。它们已被用于完成各种编码任务。一些示例包括程序合成 [50, 56]、程序修复 [11, 43, 79]、漏洞修补 [60]、推断程序不变式 [62]、测试生成 [69] 和多任务评估 [72]。然而这些研究是在从其存储库中提取的策划示例上进行的并且旨在通过独立调用LLM来完成。我们考虑的是一类不同的任务这些任务是在代码存储库的规模上提出的LLM在不同的示例上被多次调用这些示例是相互依赖的。我们在存储库范围内监视每个LLM调用的结果以确定未来的代码更改义务以使存储库达到一致状态例如存储库不包含构建或运行时错误。 **自动化规划** 自动化规划 [37, 67] 是人工智能领域中一个广泛研究的主题。在线规划 [67] 用于在不知道动作效果并且无法预先列举状态空间的情况下。它需要监视动作和计划扩展。在我们的情况下编辑动作由LLM执行其结果不能在预先知道之前预测并且状态空间是无界的。因此我们的自适应规划是一种在线算法我们通过静态分析监视动作并扩展计划。在另一方面[42] 使用LLM从自然语言意图中推导出一个计划然后生成代码以解决复杂的编码问题而[86]执行前瞻规划树搜索来引导代码LLM的标记级解码。我们的工作中的规划是基于分析依赖关系和LLM对代码存储库进行更改的影响而进行的。 **代码更改分析** 静态分析用于确保软件质量。每当代码发生更改时重新计算分析结果都是昂贵的。增量程序分析领域提供了一些技术可以仅重新计算受更改影响的分析结果。针对数据流分析 [18, 68]、指针分析 [84]、符号执行 [63]、错误检测 [52] 和类型分析 [27] 已经开发了专门的算法。程序差异分析 [16, 46, 48] 和更改影响分析 [17, 41] 确定了两个程序版本之间的差异以及更改对程序的其余部分的影响。已经研究了更改对回归测试 [65]、分析重构 [33] 和协助代码审查 [13, 36] 的影响。我们分析由LLM生成的代码逐步更新了语法例如父子关系和依赖关系例如调用者-被调用者关系。我们进一步分析了这些更改对相关代码块的可能影响并创建了由LLM执行的更改义务。 **空间和时间上下文化** 如介绍中所讨论的LLMs受益于从存储库中的其他文件和过去的编辑中提取的相关上下文。我们通过跟踪代码更改和依赖关系提供了这两种信息给LLM。 **学习编辑模式** 已经开发了许多方法用于从过去的编辑或提交中学习编辑模式包括重写规则 [31]、错误修复 [15, 20]、类型更改 [45]、API迁移 [49, 82] 和编辑的神经表示 [83]。诸如 [53] 和 [54] 的方法从用户提供的示例中合成上下文感知的编辑脚本并将它们应用于新的上下文中。其他方法观察IDE中的用户操作以自动化重复的编辑 [55] 和与时间相关的编辑序列 [87]。我们不打算学习编辑模式也不假设编辑之间存在相似性。我们的重点是识别LLM进行的代码更改的影响并引导LLM进行必要的其他更改。 **8 结论和未来工作** 在本文中我们介绍了CodePlan这是一个新颖的框架旨在解决存储库级别编码任务的挑战这些任务涉及到跨大规模和相互依赖的代码库的广泛更改。CodePlan利用增量依赖分析、更改可能影响分析和自适应规划通过Large Language Models引导多步编辑。我们在不同复杂性和大小的各种代码存储库上评估了CodePlan包括内部专有存储库和C#和Python的公共GitHub存储库用于迁移和时间编辑任务。我们的结果表明CodePlan优于基线方法与实际情况更加一致。总之CodePlan提供了一种有望自动化复杂的存储库级别编码任务的方法既提高了生产率又提高了准确性。它在解决这些挑战方面的成功为高效和可靠的软件工程实践开辟了新的可能性。 虽然CodePlan表现出了巨大的潜力但还有许多未来研究和改进的方向。首先我们的目标是将其适用于更广泛的编程语言和代码工件范围包括配置文件、元数据和外部依赖项以提供更全面的存储库级别编辑解决方案。此外我们计划进一步定制CodePlan的更改可能影响分析。这可以通过通过基于规则的方法或更高级的机器学习技术将任务特定的影响分析规则纳入其中以微调其特定的编码任务的编辑决策。此外我们将解决处理动态依赖关系的挑战例如数据流依赖关系、复杂的动态调度通过虚拟函数和动态转换、算法依赖关系例如输入列表预计要排序和各种执行依赖关系例如多线程和分布式处理以使CodePlan在更广泛的软件工程任务中更加灵活。