作者 | 妙净
作者周婷婷(花名:妙净,微信: weekendingting)是阿里淘系技术部高级前端技术专家,视觉稿智能生成代码 imgcook平台负责人,阿里经济体前端智能化方向重要成员,来自业内首个 AI 前端团队淘系前端之 F(x) Team 。去年我们聚焦在设计稿智能生成前端视图代码领域,但对于逻辑代码的智能生成也一直在不断尝试,本文介绍逻辑代码自动生成相关技术概述。
程序生成介绍
如何有效地提高软件开发的效率和质量,一直是软件工程领域关心的问题。其中, 程序自动生成技术被认为是提高软件开发自动化程度和最终质量的重要方法,受到学术界和工业界的广泛关注。程序自动生成技术指利用某些技术自动地为软件生成源代码,达到根据用户的需求机器自动编程的目的。极大程度地减轻了程序员的开发负担, 使得程序员可以更加关注于业务价值赋能。
学术界上,程序生成和代码补全是程序综合 (program synthesis) 的重要分支, 其目的在于辅助甚至代替程序员编写程序。回到(前)端代码的自动生成,分为视图代码和逻辑代码,视图代码方面目前有类似基于设计稿自动生成 的方案(如 imgcook),本文重点讲述逻辑代码的生成技术方案,常见的生成逻辑代码的方案包含基于可视化编排生成、基于输入输出样例生成、基于代码语料生成补全、基于功能描述生成等。
逻辑代码自动生成技术方案对比
基于可视化编排生成代码
可视化编程也发展了几十年,其指的是借助于一些组件化的集成代码可视化平台,一些不具备专业代码技能和开发经验的“小白”人群也能自主组织或参与应用开发,从而把代码开发由一项程序员专属职能扩充到更广泛的人群。它主要是让程序设计人员利用软件本身所提供的各种控件,像搭积木式地构造应用程序的各种界面,可视化编排更适合界面视图代码的生成;目前国外主打可视化编程、低代码编程的平台至少有十几家,其中最具代表性的可以说是 OutSystems、Mendix、Salesforce 等几家,而且对于逻辑代码的可视化方案也有很多,如下:
以上类 blockly 等适合比较简单的逻辑编程,目前在少儿编程领域的应用不错;一但逻辑复杂,搭建出来的积木也无比庞大。这样的系统在大公司,如果给业务方直接用,太复杂需要有业务方有编程思维,和 PRD 思维完全不一样,门槛较高;如果给编程人员用,只是用图形来代替了编程语言,编程人员因熟悉编程语言和相关调试等配套环境,可视化搭建的方式生成代码反而更低效。
其他主流的逻辑代码的可视化的编排(如下图),其优势是突出“可视化流”和复用逻辑节点带来的高效,主体是大的流程,输入输出、逻辑处理等细节包含在每个节点的具体表单规则中。此方案适合复用逻辑非常多的场景,复用的逻辑可抽象成流程图中的“节点”。节点的复用颗粒度大小是比较关键的,颗粒度大到一个功能服务,就是业务流程编排了,业务流程复用度比较高的场景是比较适合让业务方直接所见所得编排使用的;颗粒度小到表达式级别可以看做是业务逻辑编排,太小颗粒度是需要使用者有编程思维,颗粒度太小的可视化对于有编程思维的同学来说,可能直接写代码效率更高。
小结:可视化逻辑编排生成代码,其本身编排的成本还是比较高的复杂情况下甚至比手写代码编程还要高,但在逻辑复用度非常高的场景是比较适合的,可抽象成可复用的逻辑节点去编排,节点的复用可以带来部分提效。
基于输入输出样例生成代码
基于输入输出的样例,自动推导出生成的逻辑程序,也叫 PBE(Programming by Examples),其是 2016 年微软在论文中提到的一种程序综合(program synthesis)的技术方案。现实中比较成功的应用案例是,Excel 中有个功能相信大家应该熟悉,那就是自动填表(FlashFill),其可以根据几个样例快速的生成表格项公式,比如下图中,根据 第一列的 2 和 4,就可以填充偶数;第二列根据 50 和 40 自动推导出等差数列公式的结果。
以更复杂的情况为例:
上述的公式更复杂,现实情况,FlashFill 的正确率只有不到 50%,后来又在 PBE 的基础上提出了 NPBE(neural programming by example) 完成 Excel 中的 45 种字符串操作。NPBE 的意图是让模型从输入输出字符串中学习相关的特征, 并用学到的特征来归纳正确的程序。一般 PBE 的技术实现过程如下:
基于代码语料生成补全代码
随着 github 的开源代码的积累和深度学习的兴起,基于源代码理解(程序理解)应用比较多,如代码补全推荐、还有根据代码猜测代码片段的意思。
代码补全
常用的集成开发环境中往往整合了代码补全工具, 一般限于关键字或基于语法的提示,比如在 tsx 文件中 this. 会提示出当前可用的类属性、方法;现有的智能代码补全工具通常基于模型性能考虑一般简单基于静态词频统计。
比如我们共建团队同事基于 javascript 代码语料库,对比 GPT2 之后,采用 n-gram 概率模型开发的代码补全插件。详见这里,其主要流程图如下:
代码意图生成
代码意图生成指根据代码内容可以推测出代码的功能作用,业界较知名的开源的模型和服务有 code2vec 和 code2seq,其中 code2vec 是做 code summarization(代码功能概要);code2seq 做 code captioning (代码功能说明)。
code2vec 示例说明如下:根据左侧的代码片段,分析代码的功能是,90.93%的概率是 contains,右侧是对代码的分析可视化过程,重点关注节点间的连线粗细,粗细用来表示决策的信息权重大小。
code2seq 示例说明如下:根据左侧的代码片段,分析此代码的功能说明是,save bitmap to file,右侧是对代码的分析可视化过程,同样重点关注节点间的连线粗细,粗细用来表示决策的信息权重大小。
其内部模型详见:其目前开源的模型数据集大小为 billon 级,也开源了基于 typeScript 的模型。
基于功能描述生成代码
大家通常利用自然语言来描述程序功能, 从自然语言描述到程序的自动生成是极具挑战性的问题。自然语言文本和程序的多样性、文本的二义性以及代码的复杂结构, 使得建立文本和代码的联系成为一个难题。目前业内有些探索,包括 NL2SQL、NL2TFTTT、基于功能描述生成代码。
NL2SQL
Natural Language to SQL (NL2SQL) 是CUI(Conversation User Interface)的新兴研究热点,其研究目的是将用户输入的自然语言转为可用的 SQL 语句,提高用户查询数据的效率,现在阻碍大数据价值变现的最大难题就是访问数据门槛太高,依赖数据库管理员写复杂的SQL,而且中文的表述更加多样复杂。此领域的国内外的研究非常多,目前 NL2SQL 领域比较有名的数据集是英文版的数据集,包括 WikiSQL、Spider、CoSQL 等。
NL2SQL 典型的三层架构如下:
NL2SQL分为User query interface、Processing Unit、Database三部分,其中 Processing Unit 是整个架构的中心,也是语义解析的核心所在,打通了 User 与 Database 的交互通道,囊括智能分词、实体识别、知识检索等各个技术要点。在上述研究中,Processing Unit 的内部算法也在逐步朝着深度学习的方向进展。下面是中文 NL2SQL 大赛中冠军团队提出的 M-SQL 模型,在中文数据集上达到了92%的准确率。
业内 Google的 Analyza 采用的是语义解析和规则的方式构建的,这种方式可控性较强,但是需要人工维护一些规则进去。端到端的方案则是通过深度学习方法,采用 encoder-decoder 的方式进行 NL2SQL 的实现,整个算法系统分成了对 SQL 几个子句的识别,包括SELECT clause、WHERE clause,有时候还有 group by、limit 等操作符。每个部分还会涉及到很多细节,比如表识别、属性识别等。不同算法都是在这样的框架体系下,在细节的地方做一些改动和优化,以取得一个比较好的效果。虽然端到端的 NL2SQL 方案能够减少人力维护的成本,但是只有在 wikisql 这种简单场景下有一定效果,针对相对复杂一点的 spider 或 cosql 场景来说,准确率非常低,达不到商业应用的要求。公司内部的数小蜜团队实际业务落地的方案与Google的Analyza方案很相似,同时也已经对端到端 nl2sql 方案进行了研究和实现,下一步期望能够将 NL2SQL 和语义规则解析的方式融合到一起,来解决复杂场景的需求。【此部分结论来源内部小蜜团队】
NL2TFTTT
IFTTT 是什么?意思是 if this then that;是一个新生的网络服务平台,通过其他不同平台的条件来决定是否执行下一条命令。例如「如果明天下雨,请今天通知我」「当有人在 Facebook 标记有你在内的相片时,就自动将那张照片备份到iPhone 的照片相册中」,这满足了用户把 A 服务内容串连到 B 服务的需求,并且用户不用自己动手, IFTTT 可以自动帮忙完成上述动作。
功能示例
如下面的应用:
- 如果明天下雨,请发通知给我
- 请每天 7 点给我一份天气预报
其设置如下:
NL2IFTTT 就是通过自然语言生成如上 If-This-Then-That(IFTTT) 代码,IFTTT 程序相对于常用的编程语言来说, 其结构更加简单, 也更容易学习其结构规则。IFTTT 基于任务的条件触发,类似极简版编程语言,即:“若 XXX 进行 YYY 行为,执行 ZZZ”。每一个可以触发或者作为任务的网站叫做一个 Channel,触发的条件叫做 Trigger,之后执行的任务叫做 Action,综合上面的一套流程叫做 Recipe。
NL2IFTTT 方面的实践,2016年, Liu等人提出了一种隐注意力机制, 该机制可以有效地学习自然语言中哪些词对触发器的预测更重要, 哪些词对动作的预测更加重要。同年,Beltagy 等人将IFTTT 程序生成问题当做语义分析问题。
数据集示例
微软有一份开源 IFTTT 样本集,其样本数据类似如下,可以方便大家更好地理解 NL2IFTTT 的问题定义:
NL2Code-TranX
相比于根据自然语言的功能描述生成 TFTTT 代码 和 SQL,根据自然语言的需求描述直接生成 python、java、javascript 这类的编程语言逻辑代码的难度要大的多。至今(2020.8)暂时没有找到相关的在线可用服务,目前找到卡内基梅隆大学有相关研究产出 TranX,其可以做到单一功能描述 生成 表达式级的代码,类似根据单行代码注释生成相应代码,见其网站 demo 如下。
TranX 功能示例
TranX 数据集示例
{
"intent": "Sending http headers with python",
"rewritten_intent": "sending http headers to `client`",
"snippet": "client.send('HTTP/1.0 200 OK\\r\\n')",
"question_id": 8315209
},
{
"intent": "Python -Remove Time from Datetime String",
"rewritten_intent": "Format a datetime string `when` to extract date only",
"snippet": "then = datetime.datetime.strptime(when, '%Y-%m-%d').date()",
"question_id": 26153795
},
{
"intent": "How do I split a multi-line string into multiple lines?",
"rewritten_intent": "split a multi-line string `inputString` into separate strings",
"snippet": "inputString.split('\\n')",
"question_id": 172439
},
{
"intent": "How do I split a multi-line string into multiple lines?",
"rewritten_intent": "Split a multi-line string ` a \\n b \\r\\n c ` by new line character `\\n`",
"snippet": "' a \\n b \\r\\n c '.split('\\n')",
"question_id": 172439
},
TranX 模型示例
编程语言有严格的语法,它不能容忍拼写错误和语法错误;此模型针对 AST 构建基于 Tree 的新型模型,能完整表达编程语言的所有语法;同时相应的语料标注是非常昂贵和耗时的,标记样本的有限可用性是监督模型的瓶颈,此模型引入了 STRUCTVAE,一种用于半监督的自动编码模型,它可以从有限的样本中学习,又能从现成未标记的 NL 语言中学习。论文模型详见:
NL2Code-debuild
功能描述生成代码的应用,还有基于 GPT3.0 的 debuild 平台;目前演示网站关闭,从宣传出来效果图来看,可以看到从功能描述生成代码新的可能性。其可以根据布局语言描述生成相应的布局代码;也能根据简单高阶的功能描述生成相应的代码。
debuild 功能示例
debuild 模型示例
此平台使用的模型基于 openAI 的 GPT3.0,作者表示自然语言生成代码基于 GPT3.0 让之前不敢想象的的 50 年之后能生成代码 提前到 5 年内,目前的版本还是偏试验,更多是偏简单功能描述生成高阶(封装好的组件库、模块库等)的功能代码,感兴趣的可以试试 GPT 3.0。
总结
总体来看,基于深度学习为自动生成程序代码是趋势,但当前利用深度学习技术的程序生成和代码补全还处于起步阶段。利用深度学习生成程序代码和代码补全与传统方法相比有了较大的提升, 而程序生成技术还没有用于工业化。其主要面临着以下的挑战:
1) 训练语料的质量参差不齐。常见工作中, 用来训练深度学习模型的语料大致可以分为两类:一类是基于 DSL 人为构造出的程序; 另一类是从开源社区, 如 GitHub 等网站上爬取的项目。基于 DSL 的程序往往语法较为简单, 程序长度较短, 易于训练和测试, 但同时, 针对 DSL 设计的模型也难以推广到其他语言上使用; 而开源社区上爬取的项目虽然更接近于实际软件开发, 但是也难以保证代码的质量——低质量、不规范的代码会给神经网络带来额外的噪声, 而使用不同编程规范的代码则会使神经网络模型在训练和预测时产生混淆。如何获取统一规范的高质量程序语料库也是一项挑战。
2)用户自定义逻辑生成后的泛化能力弱。程序代码尤其是逻辑代码,很多都是业务闭域的逻辑代码,需要很多业务闭域的逻辑物料,需要对现有业务的所依赖的各种服务进行高阶理解,新业务需要结合自己的物料库训练自己的模型;但往往全新的业务依赖的服务也需要从 0 开始开发,新物料的提供比较难,除非所有依赖的服务是不可拆分的原子服务。所以逻辑模型的普适性比较难,但方法论可以相互借鉴有普适性。
3)功能描述和程序代码信息不对称。如果真的做到极致,功能描述由 PRD 直接提供,可直接生成相应的程序代码;需求描述比一般的功能描述还要高阶和抽象化,需求描述->功能描述-代码描述->程序代码,这其中每一个环节都会有很多信息损耗,这些损耗目前都是程序员通过业务经验和编程经验补上的。功能描述直接生成代码解决办法大致有 3 种,一种是端到端的从需求描述并生成 AST,如果 PD 对软件功能的描述足够精确,相当于创造了一个更高级的描述语言; 第二种是后面的链路部分(功能描述-代码描述->程序代码)封装抽象成颗粒度较大的功能块描述,目前大部分是基于这种的可行性比较高(如 NL2TFTTT 等);第三种是真正深入到每一个业务域中,上述每一个环节都让模型去一层层理解最后逐步生成代码,语料库的质量和最后的模型准确度效果是比较大的挑战。
为了减轻程序员的开发负担, 提高软件开发的自动化程度, 提高软件开发的效率和质量, 学界和工业界都不断尝试研究程序自动生成技术。随着深度学习技术的快速发展, 相信在将来, 越来越多的重复性的程序开发将由机器代替, 程序员将更关注于底层架构和上层业务价值赋能。
对于逻辑代码的智能生成,目前阿里前端智能化团队内部正在进行各维度的试验,欢迎交流。
参考资料
- 设计稿生成代码平台
- 程序理解:现状与未来
- 基于深度学习的程序生成与补全技术研究进展
- 无代码工具的自动化概念概述
- code2vec
- code2seq
- 根据功能描述生成函数级代码
- https://urialon.cswp.cs.technion.ac.il/wp-content/uploads/sites/83/2018/12/code2vec-popl19.pdf
- https://openreview.net/pdf?id=H1gKYo09tX
- https://dl.acm.org/doi/10.1145/2950290.2950334
- 根据自然语言生成代码demo debuild
- debuild 中文 pr 版
- https://debuild.co/
关注「Alibaba F2E」
把握阿里巴巴前端新动向