基于ChatGPT的自协作代码生成

互联不一般哥 2024-03-19 04:34:43

Self-collaboration Code Generation via ChatGPT

Yihong Dong∗ , Xue Jiang∗ , Zhi Jin† , Ge Li† Key Laboratory of High Confidence Software Technologies (Peking University), Ministry of Education; Institute of Software, Peking University, Beijing, China

引用

Dong Y, Jiang X, Jin Z, et al. Self-collaboration Code Generation via ChatGPT[J]. arXiv preprint arXiv:2304.07590, 2023.

论文:https://arxiv.org/abs/2304.07590

摘要

本研究提出了一种自协作框架,使大型语言模型ChatGPT能够应对复杂的代码生成任务。该框架首先为三个不同角色的大型语言模型分配任务,包括分析员、程序员、测试员,然后通过软件开发方法规定了这些角色之间的交互方式。通过所提出的自协作框架,相较于ChatGPT3.5,实验结果显示在四种不同基准测试中,Pass@1的性能提高了29.9%至47.1%。

1 引言

代码生成旨在生成以某种规范的形式表示的满足人类需求的代码。成功的代码生成可以提高软件开发的效率和质量,甚至引起社会生产方式的变革。因此,代码生成一直是人工智能、自然语言处理、软件工程等领域的重要研究热点。近年来,代码生成技术在学术界和工业界都取得了长足的发展。特别地,LLMs在代码生成任务上取得了优异的性能,表现出了很好的应用潜力。

将团队协作理论应用于代码生成的研究的原理是:先训练不同模型执行对应子任务,然后用联合训练增强相互理解。这种训练方法成本非常高,且缺乏相关的训练数据。虽然经过足够的训练,让LLM在软件开发的各个阶段都可以出色地完成任务,方便后续分配,根据人类命令做出调整,开发模型交互的潜力,但却依赖于人类程序员的专业知识,依然耗时耗力。

为此,我们提出了一个自我协同框架,旨在引导LLMs相互协作,从而处理复杂的需求并提高代码生成的性能。该框架分为分工和协作两个部分,两者均以角色指令为主导。首先,角色指令通过将特定的角色和职责分配给LLM来实现分工。该策略使LLM能够站在角色的角度思考和处理任务,将原有的LLM转变为领域专家。其次,角色指令控制着角色之间的互动,让彼此孤立的角色组成虚拟团队,方便彼此的工作。

为了促进高效的协作,我们将软件开发方法 (SDM) 引入到自我协同框架中。SDM提供了一套清晰定义的阶段、原则和实践,用于有效地组织和管理团队,最终控制开发复杂性和提高软件质量。在SDM之后,我们实例化了一个由三个角色 (即分析师、编码者和测试者) 组成的基本团队。这些角色依附于一个工作流,其中分析、编码和测试的各个阶段依次执行,每个阶段向其前身提供反馈。具体来说,分析师分解需求,并制定高层计划来指导编码员。然后,编码员根据计划或测试人员的反馈创建或改进代码。同时,测试人员根据编码员的结果编写测试报告,并记录测试过程中发现的任何问题。我们使用3个ChatGPT代理,通过角色指令分别扮演这3个角色,然后在自我协同框架的指导下协作处理代码生成任务。

本文的主要贡献如下:

提出了一种基于角色指导的自协作框架,允许LLM之间相互协作以生成满足复杂需求的代码。在SDM之后,我们实例化了一个基本团队,该团队仅由三个ChatGPT角色 (即分析师、编码者和测试者) 组成,分别负责软件开发过程中的各个阶段。基于自我协同框架,由ChatGPT ( GPT-3.5 ) 组建的虚拟团队在多个代码生成基准测试集上都能达到先进水平,甚至超过GPT-4。在一些选定的真实场景中,自我协同代码生成对于直接代码生成具有挑战性的复杂代码生成任务表现出显著的有效性。

2 技术介绍

自协作框架原理我们的自我协同框架由分工 ( DOL ) 和协作两部分组成,如图1 (左)所示。给定一个需求x,我们提出使用LLMs执行自我协同来生成输出y。任务定义为T:x→y。

图1:自我协同代码生成框架及其实例

2.1 DOL任务分配

DOL任务分配是指,根据任务分配角色指令,然后将对应的任务传递给角色。具体过程如下:

任务定义为y,x为输入,y为输出

1. 将解决任务的过程分解为不同阶段,由角色负责;

2. LLM按照格式生成角色指令,根据具体任务分配角色;

3. 将具体任务传递给对应角色。

众所周知,LLMs对上下文是敏感的,因为它们被训练来根据先前的标记来预测后续的标记。因此,通过指导语或提示语来控制LLM的产生是比较普遍的。为了实现分工,我们制定了一种特定类型的指令,将角色和职责分配给LLM,我们称之为角色指令。具体来说,我们要求LLM扮演一个与其职责有很强相关性的特定角色。此外,我们需要传达详细的任务,即这个角色应该履行的职责。一般而言,指导语中描述的任务越详细,LLM的表现越符合你的预期。一个可能不需要勾勒角色职责的情况是,当分工足够普遍时,在现实中可以找到匹配的角色。

通过角色扮演,我们可以有效地将LLM置于特定的领域内,从而激发其在该领域内的专长。我们的经验证据表明,这种角色扮演的方法比在没有预先定义的上下文环境的情况下直接让LLM参与任务产生了更好的结果。因此,角色扮演可以作为一种有效的工具来提高LLM在特定任务中的表现。

2.2 共享黑板协作

在DOL部分将角色分配给LLMs后,随着阶段的推进,角色与其他角色进行交互输出,细化工作,保证输出结果y的准确性和周到性。在协作部分,我们专注于促进不同角色之间的有效交互,以确保它们相互增强彼此的工作。共享黑板协作是指,不同角色间,将输出的信息共享。具体过程如下:

1. 初始化共享黑板,方便LLM之间交换信息

2. 不断交流协作,在阶段角色与之前阶段的角色合作输出,代表之前的所有阶段的输出。协作过程可用下述公式表达:

3. 输出y可由更新函数得:

2.3 Instance实例化

我们将SDM中经典的瀑布模型[彼得森等, 2009]引入到自我协同框架中,使得代码生成的团队协作更加高效。具体来说,我们设计了一个由分析、编码和测试三个阶段组成的简化瀑布模型作为自我协同代码生成的实例。本实例的工作流程遵循瀑布模型从一个阶段流向下一个阶段,如果发现问题则返回到上一阶段进行细化。因此,我们建立了一个基本团队,由分析师、编码员和测试人员组成,负责分析、编码和测试阶段,如图1 (右)所示。这三个不同的角色领域分派了以下任务:

分析员。分析师的目标是制定一个高级的计划,并专注于指导编译器编写程序,而不是研究实现细节。在给定需求x的情况下,分析师将x分解为几个易于解决的子任务,便于划分功能单元,并制定一个高层计划,概述实施的主要步骤。

编码者。作为这个团队的中心角色,编码者在整个工作流程中接收来自分析师的计划或者来自分析员的测试报告。因此,我们给编码者分配了两个职责:1 )编写满足指定需求的代码,遵循分析师提供的计划。2 )修复或精化代码,考虑测试人员反馈的测试报告的反馈。

测试员。测试员获取编码员编写的代码,随后编写包含功能性、可读性和可维护性等各个方面的测试报告。我们主张采用模型模拟测试过程并生成测试报告的做法,而不是先生成测试用例,再通过执行来手动测试代码,这样既方便了交互,又避免了外部的影响。

我们为LLMs (以ChatGPT为例)定制了角色指令来扮演这三个角色。编码器的角色指令示例如图2所示。在本例中,角色指令不仅包括角色描述(角色与职责),还包括团队描述和用户需求,它们将共同作用来初始化ChatGPT代理,从而设置ChatGPT的行为。此外,交互发生在负责连续两个阶段的角色之间,我们将最大交互限制为n。我们只在阶段St编码时更新输出yt,当达到n或者测试人员确认yt没有问题时,此工作流终止。

图2:自我协同框架实例中的编码员角色指令示例。

2.4 案例说明

1. 简单任务:基本操作

1)分析员:分解任务 + 制定high-level计划

2)程序员:按照计划生成对应代码

3)测试员:检验代码的功能性和边缘测试情况,反馈错误让程序员修改

2. 复杂任务:游戏开发

我们的框架能够满足所有游戏逻辑,保障了精确的角色控制,设置正确的碰撞检测,必要的游戏内容加载和适当的图像缩放。此外,可以发现出现了没有直接规定但是符合常识的游戏逻辑,比如炸弹掉落至底部后会被重置位置。而单个LLM只能生成脚本的粗略草稿。

3 实验评估

3.1 实验设置

1、基准。我们对四个代码生成基准程序进行了全面的评估,以证明我们的自我协同方法的有效性。MBPP包含427个人工验证的Python编程任务,涵盖编程基础知识、标准库功能等。每个任务由一个NL描述、一个代码解决方案和3个自动化测试用例组成。HumanEval由164个手写编程任务组成,由OpenAI提出。每个任务包括一个函数签名、NL描述、函数体和若干个单元测试(平均每个任务7.7个)。MBPP - ET和HumanEval - ET是MBPP和HumanEval的扩展版本,每个任务增加100多个测试用例。该更新版本包含了边缘测试用例,相比于原始基准测试,增强了代码评估的可靠性。

2、设置与基线。在本文中,我们使用了两种流行的代码生成设置:( 1 ) NL +签名+公共测试用例。该设置是为了与其他代码生成方法进行比较而设置的,它提供了一个NL描述、函数签名和公共测试用例作为输入提示。在这个设置中,我们将我们的自我协同方法与针对代码 ( Alpha Code、Incoder、Code Gee X、Code Gen、Code X ) 定制的LLMs、先前最先进的方法 ( CodeX + CodeT ) 和通用型LLMs ( ChatGPT 和 GPT – 4 ) 进行了比较。由于商业限制,部分对比模型不可用。因此,我们参考了他们在原文中报道的结果。( 2 ) NL - only。这种设置专门使用NL描述作为输入提示,更符合现实世界的发展情景。在这个设定中,我们进一步探索了除了前面的基准测试之外的一些复杂的代码生成任务。

在所有实验中,我们通过其API调用ChatGPT ( GPT-3.5 ),即gpt - 3.5 - turbo。我们使用" 0301 "版本的gpt - 3.5 - turbo作为我们的基础模型,以尽量减少模型意外变化影响结果的风险。为了提高LLM输出的稳定性,将解码温度设置为0。除非另有说明,我们使用NL - only设置进行代码生成,并将LLM之间的最大交互次数限制为4。

3、度量。我们采用Pass @ k的无偏变体,通过执行测试用例来衡量top - k生成代码的功能正确性,可以表示为:

主要结果

自我协同方法相对于其他方法的主要结果如表1所示。实验结果表明,我们的自我协同框架显著提高了基础LLMs的性能。值得注意的是,即使使用一个简单的3人团队(包括分析人员、编码人员和测试人员),基于ChatGPT ( GPT-3.5 )的自我协同代码生成在4个代码生成基准上都取得了最好的性能,甚至超过了GPT - 4。与ChatGPT ( GPT-3.5 )相比,我们的框架提供的改进是显著的,相对增幅从29.9 %到34.6 %不等。值得注意的是,在与扩展测试用例相关的数据集HumanEval - ET和MBPP - ET上,自我协同代码生成产生了更显著的改进。这表明我们的框架可以有效地辅助LLM生成可靠的代码。这种增强可能归因于协作团队能够考虑更广泛的边界条件和解决常见的bug。

表1:自我协同与其他方法(其设置为: NL +签名+公开测试用例)的比较。

角色扮演效应

我们考察了仅依赖NL描述的代码生成。在这种设定下,我们评估了我们的自我协同框架实例化的初级团队中每个ChatGPT角色的表现,如表2所示。实验结果表明,不管是双角色团队还是三角色团队,组建团队后相比于只使用编码者角色,绩效都有大幅提升。编码者-分析师-测试者团队在Human Eval和Human Eval - ET基准上取得了最好的结果,相对改进分别为40.8 %和47.1 %。相比之下,编译器-测试人员团队在MBPP和MBPPET测试集上获得了最高的性能,相对提升幅度分别为36.7 %和39.4 %。分析师在MBPP和MBPP - ET基准上的次优表现可能归因于MBPP相对基本的规划任务,不需要规划(随着问题变得越来越复杂,规划是一个有影响力的部分)。此外,MBPP中的一些任务具有不同于传统人类编码实践和认知过程的输入和输出特征,并且它们缺乏输入中提供的任何提示。这些可以很容易地通过解雇团队中的分析师和提供输入输出格式化技巧来解决。

表2:每个ChatGPT角色在自协作代码生成中的有效性。

为了进一步验证角色扮演策略的有效性,我们与两个没有角色扮演的基线进行了对比分析:指令(零样本),其中包含删除角色扮演成分的指令,以及少量样本提示,这提供了在每个阶段演示LLMs任务的例子。详细的说明和提示见附录B.1。结果如表3所示,表明角色扮演法基本上在没有角色扮演的情况下超过基线。我们认为,角色扮演型LLMs表现更好的可能原因是,它提供了一个特定的情境,约束了LLMs的生成空间,使其在场景的约束范围内进行推理,产生与该角色中的LLM可能具有的视角相一致的反应。因此,角色扮演不是直接提高LLM的能力,而是激发LLM的潜在能力。此外,在两个没有角色扮演的场景中,我们观察到指令(零样本)略优于少样本提示。我们确定了两个可能导致这一观察的潜在因素。首先,由于样例选择的有限性,少量的提示可能会使LLMs对人类意图的理解产生偏差。其次,在小样本提示中使用的长提示(约为实验中指令长度的14倍)会阻碍LLMs对相关信息的有效提取。

表3:角色扮演对自协作代码生成的影响

交互作用的影响

为了评估交互对自协作代码生成的影响,我们在实验中控制了MI,结果如表4所示。MI值等于零表示参与的角色之间完全没有相互作用。这意味着,特定编码员生成的代码并没有从其他角色那里得到任何形式的反馈,因此结果与雇佣相同一个编码员。MI值从0增加到1,性能提升最大。这一结果表明,对于这4个基准测试,我们的方法在两轮(也就是说,一轮互动)内解决了大部分任务。MI值在第一轮之后的持续增加会带来收益递减的改善;然而,仍然观察到一致的增强。这表明更复杂的任务正在经历持续的改进。一般而言,MI值越高,结果越好。尽管如此,由于最大令牌数的限制,我们的探索仅限于1 ~ 4轮的MI值。

表4:最大交互(MI)对自协作代码生成的影响。

案例研究

我们进行了案例研究,以定性地评估我们的方法的性能。我们的研究主要集中在两类情形。第一种是相对简单的功能级代码生成要求,源自Humaneval基准。第二个涉及更复杂的现实需求,具体是在Python中要求模型创建一个游戏,这是一个类似于用户故事的高级需求。

Humaneval Benchmark实例分析:我们在HumanEval基准上对自我协同代码生成进行评估。图4给出了一个描述这种评估的案例研究,它说明了我们的方法的工作流程和性能。1 .分析师进行全面分析并制定计划以满足整套需求。对于这个需求,分析人员首先将其分解为几个容易解决的子任务,然后基于子任务给出一些高层的方案。2 .编码者根据给定的需求、设计的分解和高层计划来实现一个代码。显而易见,该实现代码中的每个子功能和步骤与分解和高级计划几乎一一对应。3 .测试人员为实现的代码编写详细的测试报告,识别其中的错误。在该测试报告中,测试人员认为实现的代码可能导致从列表中删除重复的元素,从而可能导致一些边缘测试用例的失败。因此,建议从实现的代码中删除行' lst = list(set(lst)) '。4 .编码员随后根据测试报告中提供的反馈对代码进行完善。在修订后的代码中,将测试报告中的推荐考虑在内,同时删除行' lst = list(set(lst)) '。5 .测试人员对修改后的代码进行评估,确认没有问题存在,所有测试均通过,从而结束代码生成过程。

图3:HumanEval基准测试案例分析。省略了角色的输入,关注工作流的进程。

复杂任务案例研究:我们将自我协同代码生成应用到一个复杂游戏开发需求中,案例分析如图5所示。自我协同方法在不需要人工干预的情况下生成一个满足所有需求的工作游戏。首先,我们的方法实现了正确的游戏逻辑,其中鼠标点击开始游戏,鼠标从起点控制字符,通过所有障碍物,并避免掉落炸弹到达终点。游戏设计确保了精确的字符控制,获胜/失败条件的反馈,以及与障碍物和炸弹的正确碰撞检测。第二,遵守了需求中概述的规范,包括开始和结束点的可视化表示,必要的游戏资产加载,以及图像的适当缩放。第三,也注意到了一些要求中没有提及,但完全符合常识的游戏逻辑,如"炸弹从屏幕顶部落下,击中底部时重置位置"。相比之下,直接生成的结果是Python脚本的粗略草稿。该脚本不包括需求中要求的所有功能。即使我们手动输入指令"继续添加功能",直到ChatGPT认为所有功能都被覆盖为止,实际上仍然不能很好地满足这一要求。

图4:真实场景中复杂任务的案例研究。红色标记,加上后置处理,用来表示特定的对象。

转述:王凌杰

0 阅读:0

互联不一般哥

简介:感谢大家的关注