部门的中后台项目使用AI生成中后台前端代码也有大半年的时间了,已应用到4个业务后台,累计78个页面。
AI生成的代码中,只有对接的api接口文件以及typescript类型声明是100%准确的,每次生成能节省10几分钟的对接时间,不过生成的页面功能、组件、交互逻辑还是差强人意。本文就针对“差强人意”的这部分内容进行讨论:为什么会导致这样的结果?未来可以通过什么方式可以去解决?
在开始阅读本文之前,可以先阅读一下我之前的一篇文章:【AI框架langchain实战】,了解一下生成代码的基本流程。https://mp.weixin.qq.com/s/36LDqIlBsAHpoM7M5aBtjA
以一个通用的中后台页面为例,大概可以分开3个部分,查询区域、表格区域、表单区域。
这里可以看一下实际对应的页面:
如果是这样一个最基础【增删改查】页面,那么生成出来基本上90%的内容都是符合我们规范且可用的代码。这样简单的页面,在不同系统的数量占比大概是40% - 70%。
虽然中后台的页面基本都长一个样,但是不同的需求,页面逻辑还是有差异的,这部分差异,就是生成的一个复杂点。
目前生成的代码大概可以分为接口、类型声明、组件、配置文件、展示页面,结构大概如下图所示(使用tcf开发过项目的小伙伴应该都比较熟悉)。
重点关注一下index.vue这个文件,它包含了查询和删除的逻辑以及新增/编辑的入口。熟悉vue3的同学都知道,一个.vue文件大概可以分为3大块,【script setup】、【template】、【style】。下图是我按查询、新增&编辑、删除划分的代码区域分布图。
第一版的AI生成代码,是还没有去掉无用的代码的。假设用户只需要开发一个报表(无增删功能),但实际上还生成了无用的增删改的代码,用户需要手动删掉6、7处的代码块。对于熟悉的同学,可能2 - 3分钟就删掉了,但还是会感觉体验很差。
现阶段最简单的处理方式是,按【查询】、【删除】、【新增&编辑】这几个维度去划分代码块,然后给【删除】、【新增&编辑】打上标识。如果用户只希望生成【查询】的代码,就正则匹配把其他无用的代码块删掉。
因为后续还要增加更多组件和功能,比如上传组件、图表组件、tab组件等。这种情况下,代码“做减法”涉及到的逻辑很多,而且不够直观,更别谈有些逻辑还是互斥的了。
换一个思路,从零开始“做加法”,把每个功能的代码块,用一个唯一的key做标识。比如,
接着把所有的key放到一个集合中,当AI识别出功能或者用户手动选择对应的功能时,就从大的集合中去匹配key,生成一个小的集合。
比如,最终页面需要的功能是【查询】、【折线图】,那对应的key集合就为:query_import_xxx、query_logic_xxx、query_template_xxx、line_template_xxx。
最后,再把匹配集合中的key剔除掉,得到的就是“纯净版”的代码了。
或许大家有听说过一些网站,输入url或者提供截图,就能生成一个“一模一样”的网站。各大营销号纷纷贩卖焦虑,前端已死。但就目前而言,这些所谓的生成式的网站,生成的代码和真正项目代码之间的区别就是像是手机模型和手机的区别。看上去一模一样,但其实生成的网站既没有逻辑,也很难进行二次开发,只能用于纯展示。
但我们要生成的代码,是要和我们规范的代码是要完全一致的。所以在我们的AI工具中,AI承担的更多是匹配转换的工作,而不是基于它本身的理解去生成。
举个例子,你给AI设定一个精通ant design vue和typescript的前端专家角色,然后让他去生成一个日期范围选择组件,那么就会得到以下的代码。
const queryDate = ref([])
<a-range-picker v-model:value="queryDate"/>
渲染的出来的效果为:
看起来没有问题。但是!会有3个局限性。
1、我们日期组件规范中是带有快捷选择的。
2、我们默认输入和输出是YYYY-MM-DD的格式,而<a-range-picker v-model:value="queryDate"/>是带时区的日期对象。
3、<a-range-picker v-model:value="queryDate"/>渲染输出的是一个数组,而我们要传给后端的,是起始时间和结束时间这2个字符串。
基于以上3点,我们是无法直接使用antd的原生组件的,而是基于它再二次封装。
模块化和组件化是提升生成准确率的一大关键。最好只给出不超过3种选择,让AI在不同场景下做对应的匹配。比如,替换函数或组件种的某个变量或条件。
这种匹配替换使用正则或者AST也可以实现,但是逻辑的复杂度、考虑的边界条件、代码量都会很庞大(项目中也有用到AST做一部分简单的操作,后面会提到)。
而使用AI的prompt只需几句就可以达到一样的效果。
总结了以上各个生成方案的优缺点之后,梳理了第2版的代码生成时序图。
总共分为 2 步:
1、爬取api文档生成typescript类型声明和api接口函数。
2、选择要生成的页面模板(图表、列表)和组件。
步骤一,爬取api文档生成typescript类型声明和api接口对接。
相对1.0版本改善的点:
1、做了一个便捷化的操作,预设了我们中后台所有的地址,不需要开发每次生成的时候再去找了。
2、左侧加了预览代码的窗口,可以预览生成的文件。当后端接口和字段改动较为大时,可以重新生成整块替换,不需要再一个个字段复制和对比了。
步骤二,选择要生成的页面模板(图表、列表)和组件
对1.0版本改善的点:
1、自动识别需不需要勾选【增改】、【删】的功能。如果识别不准确,用户也可以手动处理。
2、文件可以直接保存到项目目录下,无需解压和复制。
“不准确”的场景分为以下几种:
1、返回的内容格式不固定
之前测试效果的时候,前后换过几种大模型,如gpt3.5-turbo、gpt4、kimi-v1-32k。同样的prompt会因为用的模型不一样,有些会生成带markdown标签的代码,有些则生成纯代码。有时候还会因为传入的上下文内容和prompt示例差距过大,导致生成不一样的markdown标签。
所以,对于AI返回的代码,最后输出时,都要去掉这些markdown标签。目前处理方式比较简单,直接匹配typescript、html、vue等markdown标签,实现过程就不展开说了。
2、返回的代码不按照示例来
在给的示例模板内容过多时,AI很难每一项都精确识别。
以上的对比图是为了大家更好地理解而截取出来的一小部分内容。实际上,生成的内容这么简短的话,AI是可以准确地识别的。但是一个页面那么多,如果拆成很细的模块的去调AI,不仅要写的逻辑多,而且调api的费用也会高很多。
这种小修小改就可以结合AST处理,目前用的库是【ts-morph】。
这样就可以拿到代码片段中所有的函数名,然后再将它重命名插入对应的位置即可。
3、无法处理逻辑分开多段代码的组件
再以我们之前提到的日期组件为例。前面也说到,antd的日期组件入参是一个数组,但是后端要求的是开始时间和结束时间2个字符串。
总的来说,以目前的AI技术,让它自主创造生产可用的业务代码还是比较难实现的。但是,让AI基于我们封装好的组件再根据不同业务场景进行匹配和转换,是能稳定产出符合规范的代码的。
后续也会把一些流程固定的业务场景抽成对应的模板,将以前手动复制页面/模块代码的模式逐步替换成AI匹配替换。
扫码关注 了解更多