最近在看老师给的LLM-Agent论文,在这记录一下
给定一个自然语言意图或者周围代码的上下文来进行补全or编辑通常针对小范围内的代码
但是软件工程的“Outer loop”涉及到整个代码库,可能存在外部api的调用和依赖(牵一发而动全身)be like:
因此,为了达到这样的效果, 我们需要一个系统来自动生成编辑的派生规范
codeplan综合了一个多步骤计划来解决储存库级别的编码任务
如图,codeplan的输入是一个储存库,a task with seed specifications(确定任务的起点和任务需求) expressed through a natural language instruction or a set of initial code edits, a correctness oracle and an LLM.
CodePlan构建了一个平面图,每个节点表示LLM需要执行的代码编辑任务,CodePlan监视代码编辑,并且自适应的扩展计划图,merge模块把LLM生成的代码合并到储存库当中
Oracle对储存库分析,没错的话标记为完成任务,否则生成错误报告,用于执行下一轮计划生成和执行的种子规范
在软件工程中,"Oracle" 是一个术语,指的是用于验证软件系统行为是否正确的标准或机制。它作为一个参考点或期望结果,用于判断软件的输出是否符合预期。
codeplan优于Oracle引导的修复技术
算法维护的核心数据结构是一个规划图G,这是一个多个根节点的有向无环图
规划图里每个节点是一个元组
AdaptivePlanAndExecute函数:主要的work-horse:迭代的掉选每个待决策节点并进行处理,workflow如下:
静态分析组件:
适应性规划和计划执行
PlanGraph
ExtractCodeFragment
GetSpatialContext
GetTemporalContext
-----以上都是基于之前的静态分析(不用LLM),然后基于此去make prompt-----
MakePrompt
1. 自适应规划(Adaptive Planning):在确定受影响的代码块后,CodePlan 通过创建变更义务(change obligations)来保持依赖代码与变更的一致性。这是一个迭代的过程。
2. 计划图(PlanGraph):计划图 P = (,) 是一个有向无环图,其中是一组义务(obligations),每个义务是一个三元组 ⟨, , ⟩,其中 B 是一个代码块,I 是一条指令,status 表示待处理或已完成。中的边记录了源义务和目标义务之间的原因和依赖关系。换句话说,边标签标识了在表1中的变更可能影响规则(change may-impact rule)中的哪个 Rel 子句导致创建目标义务。
3. 提取代码片段(ExtractCodeFragment):提取代码块 B 的代码时,简单地提取代码是次优的,因为它会丢失上下文信息。ExtractCodeFragment 函数接受代码块所属的整个类,保留 B 的完整代码,并只保留类的声明和其他类成员的声明。这样做是有用的,因为类和其他成员的名称和类型为 LLM 提供了额外的上下文信息。LLM 经常需要进行多个同时变更。例如,在一些案例研究中,LLM 需要添加字段声明,将参数传递给构造函数,并在构造函数中使用它来初始化字段。将周围代码的草图作为代码片段提供给 LLM 可以让 LLM 在正确的位置进行这些变更。代码片段提取逻辑通过遍历 AST 并“折叠”掉草图的子树(例如方法体)来实现。正如在第1节中所述,即使存在多个同时变更,这种草图表示也允许我们将 LLM 生成的代码放回 AST 中而不产生歧义。
4. 获取空间上下文(GetSpatialContext):CodePlan 中的空间上下文指的是代码块在代码库中的排列和关系,有助于理解类、函数、变量和模块的结构和交互。它对于进行准确的代码更改至关重要。CodePlan 利用依赖图提取空间上下文,将代码表示为节点,将它们之间的关系表示为边。这个图使得 CodePlan 能够遍历代码库,识别相关的代码块,并保持对它们的空间上下文的意识。因此,在生成代码编辑时,依赖图使得 CodePlan 能够进行与代码的空间组织一致的、具有上下文意识的代码修改,提高了代码编辑能力的准确性和可靠性。
5. 获取时间上下文(GetTemporalContext):计划图记录了所有变更义务及其相互依赖关系。提取时间上下文是通过线性化从计划图的根节点到目标节点的所有路径来实现的。每个变更是一对变更前和变更后的代码片段。时间上下文还说明了连接目标节点与其前置节点的“原因”(记录为边标签)。例如,如果节点 A 与 B 通过一个 CalledBy 边相连,则 B 的时间上下文是 A 的变更前/后代码片段和一条说明“B 调用 A”的语句,这有助于 LLM 理解最新的时间变更(对 A 的变更)与当前义务(对 B 的变更)之间的因果关系。
6. 计划执行(Plan Execution):CodePlan 迭代地选择计划图中的一个待处理节点,并调用一个 LLM 来完成变更义务。
7. 构建提示(MakePrompt):在提取要编辑的代码片段以及相关的空间和时间上下文后,我们构建一个提示(prompt)传递给 LLM,其结构如下所示。首先是任务特定的说明(Task Instructions),然后是到目前为止在代码库中进行的与要编辑的片段相关的编辑(Earlier Code Changes),接下来是说明每个出现在 Earlier Code Changes 中片段与要编辑的片段的关系(Causes for Change),然后是相关代码块(Related Code)和要编辑的代码片段(Code to be Changed Next)。通过编辑“Code to be Changed Next”并生成“Changed Code”,根据“Task Instructions”对“Code to be Changed Next”进行编辑,使其与“Earlier Code Changes”、“Causes for Change”和“Related Code”一致。如果不需要进行任何更改,则输出“No changes.”。
8. Oracle 和计划迭代(Oracle and Plan Iterations):当计划图中的所有节点都被标记为完成且不再添加新节点时,一个存储库级别的代码编辑迭代就完成了。如图2所示,在存储库上调用 Oracle。如果 Oracle 标记出任何错误(例如构建错误),则将错误位置和诊断信息作为下一次迭代的种子变更添加,并且自适应规划再次开始。如果 Oracle 没有标记出任何错误,CodePlan 终止执行。