SDCon 全球软件技术大会上,阿里云通义灵码团队分享了关于 AI 辅助编码的最新研究与实践,随着 AIGC 技术的发展,软件研发领域将迎来智能化的新高度,助力 DevOps 流程优化,提升研发效率和研发幸福感。
大家好,我是来自阿里云通义灵码团队的张昕东。很高兴和各位同仁做这次分享,分享的主题是人机协同趋势与效果提升实践。我们所做的模型提升和功能开发是为了促进人机在研发领域的协同,而当今的人机协同现状又决定了我们应该做哪些效果优化。
因此,我这次分享主要会从三个方面来陈述,首先讲一下背景,现在大模型 AI 辅助编码非常火爆,产品层出不穷,我想先从企业对研发效能的需求讲起,谈谈 AIGC 对软件研发的根本性影响;我们现在所处的人机协同阶段和通义灵码团队在这种协同模式下所做的一些效果提升努力;最后再展望一下未来,相信很多我们口中的未来都即将实现。
通义灵码官网:通义灵码_智能编码助手_AI编程-阿里云
1. AIGC 对软件研发的影响
我们团队一直在研发效能和研发智能化领域探索,关注企业的开发效率与幸福感,结合近期的 AIGC 浪潮与大模型能力,我们总结了三个 AIGC 对企业研发效能的核心影响。
第一点是人员技能,客观讲,人员技能在任何企业任何领域都是影响企业效能的核心因素,在技术研发领域尤为突出。一位资深的开发人员效率可能十倍于一名初级程序员,缺乏经验或编码生疏的开发者可能会花更多地时间思考代码写法,寻找缺陷根因,而现如今的大模型,结合海量高质量的研发知识数据,能够很轻松地拉平初级工程师的水平到中级或中高级,对于更加资深的开发者,AIGC 能帮助更快地定位修复问题,并且资深开发者也会更善于利用大模型,提升自己的全栈和架构能力。
第二点是协同消耗,术业有专攻,完成一个具体的需求或任务时,有些同学负责产品设计、有些负责前端、后端,还有负责测试的同学保障质量,100 人日的活来 100 个人一天是干不完的,并且各个角色之间的信息传达是有信息熵损失的,个体之间的协同消耗进而会放大到整个组织的效能损耗。在 AIGC 时代,我们期望 AI 能进化成一个超级个体,Agent 或 Multi-agent,一些单点的任务或独立的缺陷由 AI Agent 互相配合处理,机器与机器之间的交流会更加高效,人类为了与AI交流也会沉淀出更详实的需求文档和上下文。
第三点是成本控制,成本一直是企业考虑的重中之重,一个需求不可能投入无限的成本。对于一线开发来说,成本主要在协作沟通和代码编写上。有调研显示,开发者一天有七成的代码编写时间是在做事务性劳动,如增删改查。如果能将这部分生产力解放出来,让企业成员投入更多的时间到创新事物和业务思考上,从而降低企业整体的研发成本,提升企业核心竞争力。
总结下来,企业软件研发主要有四大挑战,而挑战本身恰恰也是智能化的机遇。前两点个体效率,协作效率我就不再赘述了,期望大模型的 Copilot 模式和 Agent 能力能极大程度提升企业一线研发的个体效率,并且用 7*24 小时的响应能力和标准的管理流程,提升协作效率。
第三点研发效率怎么理解呢,比如现在本地运行代码报错了如何解决呢?一般大家还是习惯把报错内容粘贴到搜索引擎里,搜索引擎会提供一些技术博客,解决经验文章,甚至复杂点的问题需要去翻阅 PR 或 Issue 里面的聊天记录,然后带着潜在的解决方案返回 IDE 做修改,这个过程可能需要往复好几遍才能解决。并且有些报错需要结合代码或代码库来看,内嵌在 IDE 中的编码助手能很好地解决这个问题,通过统一的自然语言交互方式,用户可以足不出户地咨询和解决问题。
以通义灵码为例,灵码在报错信息里嵌入了 Lingma 图标,只要点击,即可自动采集报错堆栈和对应的代码上下文,组装好 Prompt 后发给模型,生成出来的修复代码也能通过复制,插入,新建文件等方式应用到代码库中,极大程度地提升了研发效率,当然其他补全问答等功能也大幅提升了研发效率。
最后一点就是企业最关注的数字资产了,我们常常听到有人抱怨屎山代码,祖传代码,我相信每一段代码在被编写出来的时候都不是屎山(至少在写这段代码的人眼里)。那为什么我们的代码,我们的工程会慢慢腐化呢?主要还是企业优质的代码和文档没有沉淀下来,开发规范和运维手段没有同步,如今的 AIGC 浪潮正好能促使企业重视无形资产,管理好企业知识信息库,然后通过 SFT,RAG 等方式将知识赋予给大模型,形成正向循环。
总的来说呢,我们认为大模型时代,主要给软件研发带来了两大改变。
第一点是研发知识传递形态的改变,目前企业内的知识更多的是口口相传,老带新,新人接手项目后主要通过口述和看代码注释,运维脚本甚至是故障排错的方式去熟悉,即使熟悉了某个代码,整个企业里的二方包、组件库,实用工具,内部平台都需要花大精力去掌握。大模型的介入,使整个 DevOps 流程,工作流程更加顺滑,企业将知识通过 SFT、RAG 等方式注入到大模型的知识库,让一线工作人员和管理者都用上大模型问答和编码,整个软件研发生命周期生成出来的新代码、新文档、新知识又会回流到企业知识库中,形成了正向循环,释放出研发效能的红利。
第二个改变是 AIGC 带来了新的人机协同模式,主要分为三个阶段,第一个阶段 Copilot,这也是近年来非常热的词,这个模型侧重于人机高频协同,解决单点任务,大模型被当作工具来提升人类的效率。
第二个阶段是 Agent,这也是老生常谈的名词了,主要侧重于单点任务的完成,解决一些复杂的需要多角色协同的独立的任务;第三个阶段是 Facilitator,他是面向目标的,和前一个阶段相比,Agent 阶段完成目标的主语是人,而这个阶段主语是 AI,AI 能够拥有复杂信息整合,分析的能力,拥有一定的决策能力,比如让 AI 去做一个淘宝商城,或者去提升一个企业的研发效能,它能够更好地定位目标,找到路径,自我驱动,解决问题,人类更多的是负责创新,定义目标和配合纠偏。
讲到到这 3 种模式,我感觉特别像人类历史进化发展的不同阶段,人和 AI 的关系就好比中央主权意志和人民群体的关系,在远古时代,部落需要生存,部落首领给成员指派单点任务,如打猎,采集浆果,寻找雨天避难所,在生产力不发达的远古神话时期,茹毛饮血,朝不保夕,部落高频地与环境交互,度过春夏秋冬每一个难关;社会来到周朝、秦朝、以及后来的一些封建王朝,中央集权,疆域版图扩大,交通和生产力飞速发展,这些王朝通过分封制、郡县制等方式管理地方,能够发动多个郡县地区完成诸如修建长城,贯通京杭大运河的壮举,这离不开生产力的发展和人民群众内部的协同与智慧;那么问题就来了,部落和夏朝这种远古时期与后面的周秦汉唐差了什么呢?差了一个中间商呀。开个玩笑,实际上我认为最有体感的就是成熟文字的出现,让信息长期存储和传递有了可能,就好比模型的计算单元拥有了更稳定的存储和更长的记忆,另外交通的发展,制度的更新,使信息传递更快,就好比模型的推理速度有了质的飞跃。
段,类似于经济全球化,人类命运共同体,人类经济全球分工,科技无国界共享,进而探索星辰宇宙。
那么反观如今研发领域的人机协同模式发展阶段呢,我觉得主要得从四个因素来考虑。
首先是输入长度限制了更多上下文的加入,即使是最先进的长上下文模型,也无法每次问答都把所有的研发知识都输入给模型,并且过长的上下文也会稀释关键信息,影响性能,这在代码补全这种性能敏感的场景是不可接受的。
提到推理速度,除了代码补全这种即时高频响应的场景,还有在做 Agent 的时候,多轮交互,自动迭代的过程中,也是需要模型尽快推理产出当前阶段输出的。
模型出现幻觉和错误输出是难免的,当错误概率超过一定阈值就会影响体验,断崖式降低开发效率,使 AI 辅助变成一种负担。另外在 Multi-Agent 迭代的过程中,如果某个 Agent 出错不按照预期的格式输出,也是非常致命的,如果重试多次仍无法解决的话,会导致流程失败。
第四点协同惯性,有人的因素,也有模型的问题。一方面,人类还处于从个人编程到辅助编程的适应阶段,调研一下就会发现,现如今还有很多人抵触 AI 的介入,可能也是 AI 的不稳定引起的,需要用更高质量的模型来冲破心墙枷锁。
另一方面,人类还无法给到模型完整的上下文和清楚的需求,即使是学习过 Prompt 写法的人,或者是从业人员。一句话需求,一段代码找缺陷,是常常发生的事情,AI 也是有苦难言。用户希望 AI 是一个心有灵犀的活动,对代码库和遇到的现象了如指掌,人类的使用习惯与 AI 的输入需求的矛盾点便是我们要做的努力。但有时候即使有心,Prompt 也不好组织,以我最近遇到的缺陷为例,由于现象太过复杂,人类的推理也真假参半,在定位到缺陷之前,我很难组织好给到 AI 的 Prompt。
结合上述的一些难点,局限,诉求和挑战,我们认为如今现阶段的人机协同模式还处于 Copilot 模式的高速发展期,类似的插件和工具如雨后春笋,而 Agent 模式已经初见端倪,最近解决真实问题的 SWE-bench 榜单上 lingma-agent 拿到了榜首,这个榜单名次轮替得很快,相信几个月内就能突破 50%,Agent + 研发这个领域目前学术界进展比较快,各家公司也都有一些 demo 场景取得了进展,相信在不久的将来,模型突破一定阈值,会立马有商业化的产品出来,实实在在赋能给每个开发者。
2. 通义灵码效果提升实践
准备这个 PPT 的时候,我在想怎么把这些效果优化手段串起来。浅浅分析一下,现在的大模型能力很强毋庸置疑,但是在解决现实问题的时候好像又差强人意,大模型强在丰富的经验知识,看过的代码比整个公司所有人写的都多,弱又弱在人类和他交互的时候,很难对齐上下文,影响了效果。这就好比老中医问诊,老中医经验丰富,但是门诊时间就是这短短的几分钟,他要通过望闻问切的手段了解患者的背景和诉求,并给出解决方案。这就需要我们在做工具的时候,想法设法给足上下文,提升诊疗效果。
传统的中医诊疗手段分为“望闻问切”,人机交互也是类似。
望顾名思义就是让模型眼观六路,从提示词中得到足够的输入,既然做不到把所有背景信息和问答放到所有输入,便人为干预地抽取一些有价值地浓缩信息给到模型。
闻则是让模型更加贴合用户的习惯,有时候也不是生成得不对,只是过于大众,解决不了当前用户特定上下文的问题,需要对症下药。
用户的需求可能只有一句话比较模糊,这是因为用户平常和人沟通惯了,期待对方会结合理解给出追问从而明确需求,大模型也是这样,对于用户的 query 要反复揣摩,韦编三绝说的是孔子多次阅读竹简,这连接竹简的绳都磨断了,这像不像勤奋老实的大模型通过追问,自问的方式揣摩用户意图?
切这块我是想引申出未来 Multi-Agent,AI 程序员的交互模式的,拥有了长记忆和长上下文后的 Agent 能够跳脱出如今的一些限制,大幅提升效果。为什么叫晴空一鹤呢?一是想表达跳脱限制,神陆跃马,效果飞升之意,二是晴空一鹤出自的是晴空一鹤排云上,未来 AI 程序员一定是运行在云上的,希望人类程序员有一天能从事务性任务中解脱出来,放心地交给 AI。
第一点,望。不是忘记的忘,但是幻觉很像是模型忘记了什么。代码生成中的幻觉非常常见,即使是人类程序员在编写一段代码的时候,也不能只看单个文件的前后文,一定是阅读理解相关的文件,以及配合 IDE 语法分析后给出的代码元素提示来编码。如果只是给代码的前后文的话,真不能怪 AI 出现幻觉,属于是巧妇难为无米之炊。
解决方案主要就是在推理阶段将相关的代码引用定义通过提示词传达给模型,当然这个过程还有相似代码的库内 RAG 以及二三方包组件定义的采集。这边主要将一下代码库内的跨文件感知,首先是在线部分。我们在 lingma-agent 进程中实现了一套类型追踪图索引和毫秒级的关联信息抽取。比如上面一段代码,需要将 subList 中 ODS 层的对象里的值抽取出来,赋值给 DWD 层的数据结构 report。利用提前索引好的 TTG,以及正在编写位置的上下文,我们能推理出想要补全正确这段代码,需要告诉模型两个数据对象各有哪些字段,这中间还有一些细节,比如识别 lombok 的注解,区分字段和方法的公开性。由于现实情况比较复杂,一段代码引用的文件也很多,我们还会基于一些特征,比如 import 语句,被引用方法的出入参对象等信息来减少模型幻觉。
这些方法其实很类似我们以前做 LSIF,用于文件的定义和引用间的跳转。我们复用了很多之前的研究成果,做了一套在线跨文件感知系统。
这套在线跨文件感知系统,不仅仅用于代码补全,任何代码生成类的任务都用的上。比如想要生成出准确可编译的单元测试,我们需要采集当前这段代码的出入参对象数据结构,之前要 mock 哪些字段和 assert 哪些信息;需要知道方法内部引用了外部的什么方法,可见度是怎么样的,是静态的还是普通的,如何去 mock;需要知道当前代码库的测试框架是什么,引用了哪些三方包 Mockito 还是 SpingBoot Test,是 Junit5 还是 Junit4;当然最重要的一步,还是采集一些库内的单测样本来学习。而这些信息都是通过 Lingma 进程里的跨文件感知系统完成的。
为了让在线推理阶段更加准确,在 SFT 过程中让模型学习到同样的提示词和跨文件场景是很有必要的。
通义灵码内部构建了一套比较完整通用的离线跨文件数据集构建引擎。他的特点是数据结构比较通用,类似一种中间语言,所有的语言后处理都是一套流程,不同的语言只需要适配一层解析器即可,接入成本较低。并且所有的解析过程是免编译和文件级别独立的,牺牲了一些动态特性的解析,但是速度足够快,因此只要机器足够,并发可以打很高,我们自己处理的时候平常下来大概是每小时 2000 万个文件。
整个流程大概是先做常规的数据清洗和去重,然后我们会做一个元信息索引,把代码文件里的类定义、方法定义、字段定义、文件定义、模块定义等解析并提前存下来,这里的元信息结构体和存储粒度因不同语言而异,唯一的要求就是每个元信息索引的文件名在解析引用过程中能够成功定位到,以及元信息里面的定义 id 是完全唯一的。元信息的解析在某些语言场景下要分为两步,以 Java 的继承为例,子类需要将父类的公开方法和字段移植到自身的元信息中,并且判断是否重载,这样能够避免在跨文件关联时需要循环查询元信息定义。
接下来的跨文件还原步骤便是利用元信息索引,将当前代码文件的外部方法引用和外部方法的定义关联起来,构造出大规模的跨文件还原数据集。这种语言通用的数据结构体在不同任务中都发挥着极大的作用,效果方面跨文件补全数据集准确率从 21% 提升到了 77%,Java 单元测试编译通过率从原先的 4.9% 提升到了 25.6%,并且语言解析和多语言支持的迭代速度也大幅提升,解决了大量计算资源和时间。
接下来将我们让 AI 贴合用户使用习惯所做的努力。首先是触发时机,AI 生成的不准必然被人诟病,但是比准确性更重要的是不对用户造成打扰,即使生成的可能是对的。我们分析了 50 余种不同 IDE 端的场景,尽量避免在不必要的场景下触发大模型补全,因为我们现在常用的交互是那种 ghost text 的 inline 生成,后面大段的代码会由于触发补全而被顶到后面,所以这是对用户很重要的一点。另外根据编码经验,我们在不同代码上下文和不同 IDE 做了不同的延迟策略,以及通过动态下发实验特性来找到用户最能接受的 debounce。触发长度除了提供给用户的倾向性设置以外,还会通过前序的采纳行为,击键速度,代码上下文去用小模型学习推理,以及对于特定补全难度较大的文件类型也会规则化地去做限制。
第二块是生成粒度,用户期望生成大段代码,并且模型生成的内容一定程度上是对的,就真的是用户所期望的吗?其实不然,所谓 Copilot,就是用户希望模型副驾驶给到必要且克制的帮助,落地到代码层面,就是模型生成的粒度需要符合用户的编码习惯,过长的生成一是更容易出现错误,脱离用户预期,二是阅读的成本也陡然增高,一个屏幕一般是 80 行代码,对于普通的用户来说,超过 8 行就会阅读困难,不倾向于采纳。
因此利用上文提到的通用离线数据所分析出来的代码区块信息,在模型 SFT 的过程中采样代码区块,模拟补全,让模型学习到用户期望的生成粒度。
同样问答任务也有很多需要贴合用户使用习惯或者团队代码库的习惯,一种方式是 few shot 的方式,比如 commit message 生成的时从用户代码库采集一些 message 样例来适应语言和格式规范,当然也会遇到一些及其简单敷衍的 message,比如 fix,update,模型也要避免去模仿,尽量在贴合用户习惯的基础上给予正确的引导和生成。注释生成也是同理。
第二种是通过追问引导词来提供便捷的方式让用户纠正模型的问答,对于不同的指令性任务通义灵码都给予了不同的追问选项,如代码解释场景,灵码会优先生成百字左右简短的概要性描述,如果用户期望模型回答得更加具体,可以点击 In Detail 追问按钮来互动。
第三种是用户自定义的需求,类似自定义提示词。比如在单测生成过程中给予很多企业代码库单测规范和命名规范的输入,可以在输入框里面补充,灵码近期上线的 feature 也会记住这些自定义指令,以此来更加贴合用户的操作和使用习惯。
我们的产品设计思路还是倾向于减少设置选项,通过插件端和模型去嗅探用户的行为和使用习惯,避免对用户造成过多打扰。
望和闻主要观察的是表面的现象和解决简单的问题。而代码场景的问题很多时候依赖对代码库的洞察,需要利用更进一步:问的手段。这里的问分为两部分,一个是问模型细化需求,一个是问本地环境,召回相关文件。库内检索也称为 workspace,整个流程是这样的,根据用户的问题加上获取到的当前代码库的文件目录树,通过模型分析,比如指代消歧,输出细化后的用户需求,以及一些关键词。然后利用本地向量库做一层相关文件召回,以及检索见过的合并与重排,组装最终的 Prompt 请求大模型。
整个库内 RAG 的过程是纯本地的,我们在用户本地构建了向量检索库,能够保证用户代码的绝对安全。
除了库内 RAG,还有企业知识库的检索召回增强,逻辑类似。
说到企业知识库检索,这也是企业用户的强诉求,大家都觉得 SFT 太贵,不能做的很频繁,而且可能影响原来效果。RAG 成了比较流行的解决方案。项目管理场景下有很多专有名词需要录入给模型,模型也需要学习到需求、任务的拆分规范,任务指派逻辑,开发场景的诉求是最多的,比如开发规范,单测规范,企业内部二方包组件库,在补全和问答场景下都希望利用好 RAG。测试和运维也有一些诉求,比如需要模型理解自研的测试框架,内部运维手册等。
现在检索增强的手段热度比较高,大有遇事不决上 RAG 的趋势,但是 RAG 也不是万能的,他对于数据质量,数据格式比较高,并且影响性能,特别是补全场景,这一块还有许多值得探索和标准化的工作。
通义灵码的企业级检索方案如图所示,我们会给用户提供企业管理后台用于上传和管理知识文档,灵码提供了数据分块,代码解析,召回重排,向量化等通用能力,给予企业 RAG 和 SFT 的能力。
最后一步切,我认为是研发领域的大杀器了,也是之前提到的人机协同模式的下一步,大家寄予厚望的 Agent。
Agent 的概念很广,目前主要是学术届的研究先行,产品落地角度我觉得会先以单 Agent 或两个 Agent 互相配合的模式去逐步演进。产品化依赖比较稳定的输出,准确的效果,较低的成本和切实的痛点。
目前看来单测生成的 Agent,修复缺陷的 Agent,以及生成 Demo 应用的 Agent 会率先得以应用。比如说这领域比较流行的 SWE-bench 数据集,也是通义灵码刚赢的榜首的数据集,主要侧重于数行代码修改的缺陷修复以及需求开发,目前 SWE-bench light 数据集已经能够达到 33% 的解决率,相信在未来几个月内能够突破50%,50%-80% 可能有一个风水岭,一定程度上能催生出解决实际问题的产品大家敬请期待。
相比于通用问题的多 Agent 解决,其实在 Copilot 类型的插件背后已经有一些 Agent 能力在实践了,比如刚才提到的库内 RAG 问答就是一个比较简单的单 Agent 场景,其他比如测试用例生成 Agent,即通过不断运行和解决编译报错的能力,提升测试用例的编译通过率和覆盖率。还有缺陷修复的 Agent,都在不久的将来能来大家体验。近期阿里云也宣发了 AI 程序员的概念,我们内部通过多 Agent 实现了一个简单的获取巴黎奥运会赛程并做成网站的小 demo,未来已来,敬请期待。
3. 未来 AIGC 研发赋能展望
朱熹曾说过:读书有三到,谓心到、眼到、口到。我们刚才讲了很多插件端的努力,属于是眼到和口到,而三到之中,心到最急,心既到矣,眼口岂不到乎?核心还是模型。
通义灵码是基于通义大模型基座,利用高质量的开源数据以及阿里云自身沉淀的文档与 API/SDK 数据,在上层训练了代码补全、研发问答等多个研发垂直大模型。然后再是上层插件层、应用层利用问答意图识别,习惯学习,prompt 工程,跨文件感知强化等能力,得到了灵码这款智能编码插件。通义的 qwen2 模型目前已经取得了 SOTA,但还是有很多提升的空间,我们的目标是更快,更长,更便宜,如果上下文能再长一些,推理速度能再快一些,很多场景下就不需要做 RAG 和相关文件信息裁剪了,补全的采纳率,生成占比,问答的准确率,Agent 的有效率都能大幅度提升。
相信在我们上层应用诉求的推动下,模型会提升得更快。
我们在“爱斯基模”的同时,也不会停止对灵码应用层的打磨。各家厂商的基模,训练数据差距不会那么大,很多时候比拼的是研发领域的软件工程经验,谁最能抓住开发者的痛点,并结合大模型去解决它,谁就能赢得中国上千万开发者的青睐。我做研发智能化领域做了很多年,记得是 2018 年的时候,我们就尝试把 AST 的端到端路径做向量化,然后去解决一下缺陷定位、代码质量之类的任务,当时也只是一些初步的探索,能比 SOTA 提升 0.05 左右的 F1 值,半年后我们看到很多代码向量化的论文面世,比如 Code2Vec,北大的 TreeCNN。
通义灵码的前身是阿里云智能编码插件,当时应该是 2020 年,我们用 GPT2 搭建了代码补全模型,并且通过大规模数据处理打造了代码搜索和文档搜索服务,至今也还能在 JetBrains IDE 体验到,最近 FSE 的 Industry Track 我们也有代码搜索相关的论文发表,直到去年大模型的爆火,通义灵码赶上了风口,目前支持 JetBrains 全家桶、VS Code 以及 Visual Studio,未来又更多的端侧会提供给大家。不论扩展多少 IDE,我们始终把核心服务放在本地的灵码进程中,比如跨文件感知索引,问答会话管理,还有 Agent 调度流程,向量检索,代码分析等服务,这样提升了通义灵码多端的一致性和研发效率。
软件工程领域发展了五十多年,智能编码方兴未艾,诗经里说,陟彼高冈,我马玄黄。不得不承认,在大模型助力下,软件研发智能化来到了新的高度,未来非常可期。近一两年,智能编码不会局限于 IDE 插件以及 Copilot 形态的人机协同模式,代码大模型的能力配合 Copilot,Agent 的形态,能够助力整个 DevOps 研发流程,很荣幸和各位同仁们正在见证和创造这个历史,一起致力提升研发幸福感。