由Google研究科学家Petros Maniatis和Daniel Tarlow发布
软件并非一步到位创造出来的,它是逐步改进的,一点一点地改进——编辑、运行单元测试、修复构建错误、处理代码审查、再次编辑、满足linter的要求,修复更多错误——直到最终变得足够好并合并到代码仓库中。软件工程不是孤立的过程,而是人类开发人员、代码审查人员、错误报告人员、软件架构师和工具(如编译器、单元测试、linter和静态分析器)之间的对话。
今天,我们介绍DIDACT(Dynamic Integrated Developer ACTivity),这是一种用于培训大型机器学习(ML)模型进行软件开发的方法。 DIDACT的新颖之处在于,它使用软件开发过程作为模型的训练数据源,而不仅仅是该过程的精细终态——完成的代码。通过向模型展示开发人员在工作时看到的上下文以及他们采取的行动,模型学习软件开发的动态,更符合开发人员花费时间的方式。我们利用Google软件开发的工具来扩大开发人员活动数据的数量和多样性,超越了以前的工作。结果在两个方面非常有前途:对专业软件开发人员的有用性,以及作为植入ML模型的一般软件开发技能的潜在基础。
DIDACT是一个多任务模型,训练开发活动,包括编辑、调试、修复和代码审查。 |
我们构建并内部部署了三个DIDACT工具,评论解决(我们最近宣布)、构建修复和提示预测,每个工具都集成在开发工作流程的不同阶段。所有这三个工具都得到了数千名内部开发人员的热烈反馈。我们认为这是有用性的终极测试:专业开发人员是否利用这些工具来提高生产力?
最令人兴奋的是,我们展示了DIDACT是通向一种通用开发者辅助代理的第一步。我们展示了经过训练的模型可以通过提示开发人员活动的前缀以各种令人惊讶的方式使用,并通过链接多个预测来展开更长的活动轨迹。我们相信DIDACT为开发能够在软件开发过程中通用辅助的代理铺平了一个有前途的道路。
软件工程过程中的数据宝库
Google的软件工程工具链将与代码相关的每个操作存储为工具和开发人员之间交互的日志,并已经这样做了几十年。原则上,人们可以使用这个记录来详细重现“软件工程视频”中Google代码库的关键情节,一步一步地——一次代码编辑、编译、评论、变量重命名等等。
Google的代码位于monorepo中,一个包含所有工具和系统的代码单一存储库。软件开发人员通常会在由名为“Clients in the Cloud(CitC)”的系统管理的本地写时复制工作区中尝试代码更改。当开发人员准备将一组代码更改打包到特定目的(例如修复错误)时,它们会在Critique中创建一个变更列表(CL),这是Google的代码审查系统。与其他类型的代码审查系统一样,开发人员与同行审查人员就功能和样式进行对话。随着对话的进行,开发人员编辑他们的CL以解决审查人员的意见。最终,审查人员宣布“LGTM!”(“看起来不错”),CL被合并到代码库中。
当然,除了与代码审查人员的对话之外,开发人员还与其他大量软件工程工具进行“对话”,例如编译器、测试框架、linter、静态分析器、fuzzer等。
开发软件所涉及到的复杂活动的插图:开发人员的小动作,与代码审查人员的互动以及使用编译器等工具。 |
软件工程的多任务模型
DIDACT利用工程师和工具之间的交互来支持ML模型,从而协助Google开发人员在追求其软件工程任务时,建议或增强开发人员在上下文中采取的行动。为此,我们定义了许多有关单个开发人员活动的任务:修复破碎的构建、预测代码审查评论、处理代码审查评论、重命名变量、编辑文件等。我们对每个活动使用一个共同的形式化:它需要一些状态(代码文件),一些意图(特定于活动的注释,例如代码审查评论或编译器错误),并产生一个操作(执行任务所需的操作)。这个操作就像一个小型编程语言,可以扩展到新添加的活动中。它涵盖了编辑、添加注释、重命名变量、标记带有错误的代码等内容。我们将这种语言称为DevScript。
DIDACT模型受到任务、代码片段以及与该任务相关的注释的促进,并产生开发操作(例如编辑或评论)。 |
这种状态-意图-操作形式使我们能够以一般方式捕获许多不同的任务。更重要的是,DevScript是一种简洁的表达复杂操作的方式,无需输出整个状态(原始代码),因为它在动作发生之后就会变得如此;这使得模型更加高效和可解释。例如,重命名可能会触及文件中的数十个地方,但是模型可以预测单个重命名操作。
ML同行程序员
DIDACT在个人辅助任务方面表现出色。例如,下面我们展示DIDACT在功能大部分完成后进行代码清理。它查看代码以及代码审查人员的最终评论(在动画中标记为“human”),并预测编辑以解决这些评论(渲染为差异)。
给定一段初始代码和代码审查人员附加到该段代码的评论,DIDACT的预提交清理任务会生成编辑(插入和删除文本),以解决这些评论。 |
DIDACT的多模态特性也带来了一些令人惊讶的功能,类似于随着规模的增长而出现的行为。其中之一是历史增强,可以通过提示启用。知道开发人员最近做了什么可以使模型更好地猜测开发人员接下来应该做什么。
历史增强代码补全的示例。 |
一个典型的任务是历史增强代码补全。在下图中,开发人员添加了一个新的函数参数(1),并将光标移到文档(2)中。在开发人员编辑的历史和光标位置的条件下,模型通过正确预测新参数的文档字符串条目(3)来完成该行。
多次链接迭代的编辑预测示例。 |
在更强大的历史增强任务中,模型可以选择下一步要编辑的位置,以一种历史上一致的方式。如果开发人员删除了一个函数参数(1),该模型可以使用历史记录正确预测文档字符串的更新(2),以删除已删除的参数(而不是人工将光标放置在那里),并以语法(以及——可以说——语义)正确的方式更新函数中的语句(3)。有了历史记录,模型可以明确地决定如何正确地继续“编辑视频”。没有历史记录,模型不知道缺少的函数参数是故意的(因为开发人员正在进行更长时间的编辑以删除它)还是意外的(在这种情况下,模型应该重新添加它以修复问题)。
模型还可以更进一步。例如,我们从一个空白文件开始,要求模型连续预测下一个编辑,直到它编写了一个完整的代码文件。令人惊讶的是,模型以一种对开发人员来说似乎自然的方式逐步开发代码:它首先创建了一个完全工作的框架,包括导入、标志和一个基本的主函数。然后逐步添加新的功能,如从文件中读取和写入结果,并添加了功能以根据用户提供的正则表达式过滤一些行,这需要在文件中进行更改,例如添加新的标志。
结论
DIDACT将Google的软件开发过程转化为ML开发助手的培训演示,并使用这些演示来训练模型以交互式地与工具和代码审阅者一起逐步构建代码。这些创新已经在每天使用的Google开发人员工具中发挥着作用。DIDACT方法补充了Google和其他地方所取得的大型语言模型所取得的巨大进展,朝着使软件工程师的劳动更轻松、提高生产力和提高工作质量的技术方向迈进。
致谢
这项工作是Google Research、Google Core Systems and Experiences和DeepMind多年合作的结果。我们要感谢我们的同事Jacob Austin、Pascal Lamblin、Pierre-Antoine Manzagol和Daniel Zheng,他们是这个项目的关键推动者。没有Alphabet的合作伙伴(Peter Choy、Henryk Michalewski、Subhodeep Moitra、Malgorzata Salawa、Vaibhav Tulsyan和Manushree Vijayvergiya)以及许多收集数据、确定任务、构建产品、制定策略、宣传和帮助我们执行这个议程的人(Ankur Agarwal、Paige Bailey、Marc Brockschmidt、Rodrigo Damazio Bovendorp、Satish Chandra、Savinee Dancs、Denis Davydenko、Matt Frazier、Alexander Frömmgen、Nimesh Ghelani、Chris Gorgolewski、Chenjie Gu、Vincent Hellendoorn、Franjo Ivančić、Marko Ivanković、Emily Johnston、Luka Kalinovcic、Lera Kharatyan、Jessica Ko、Markus Kusano、Kathy Nix、Christian Perez、Sara Qu、Marc Rasi、Marcus Revaj、Ballie Sandhu、Michael Sloan、Tom Small、Gabriela Surita、Maxim Tabachnyk、David Tattersall、Sara Toth、Kevin Villela、Sara Wiltberger以及Donald Duo Zhao)以及我们极具支持性的领导层(Martín Abadi、Joelle Barral、Jeff Dean、Madhura Dudhgaonkar、Douglas Eck、Zoubin Ghahramani、Hugo Larochelle、Chandu Thekkath和Niranjan Tulpule)的支持,这项工作不可能实现。感谢你们!