AIGC 和低代码结合应用全栈研发实践总结
发布时间:2024-11-14 23:44:09点击:
一、背景
电商供应链的系统建设一般偏向于数据管理类型,但此类系统建设有一个很明显的问题就是前后端开发的沟通成本较高(相对研发成本而言),特别是一些简单加减字段的诉求沟通成本甚至达到 50% 以上,如何将这部分沟通成本降低下来,并保证高质量的交付成为目前亟待解决的问题。
经过对需求和系统页面进行分析,我们得出如下数据:
通过以上数据分析,一句话总结如下:
需求简单,但是沟通成本大 !!!
二、思考
请注意这里讲沟通成本大是相对于简单需求的开发成本而言,单独看绝对投入其实还好,但是需求数量大,也会造成很大的资源浪费,我们希望探索出更高效的需求交付方式。
对于解决沟通问题其实有很多解决思路,其中有一个比较有效的方式就是目前得物技术正在推进的 Mooncake,它的好处是已经和发布系统打通,代码部署后所有接口和出入参等描述信息都会上传到 Mooncake,做到了统一以文档方式满足接口出入参说明诉求,执行后确实有不错的收益,但还存在两个主要问题:
在当前背景下,解决这个沟通问题的思路最简单的做法,就是让服务端一个角色全栈搞定前后端开发,为什么是服务端全栈而不是前端全栈,因为中后台系统的服务端的工作相对复杂,通过需求数据分析发现,服务端投入工时是远高于前端的,另外一个原因就是需求前端部分相对简单很多。
三、具象思考
谈到全栈,肯定不是直接把前端的工作直接交给服务端去做,得物研发目前的工作压力还是不小的,所以需要一种低成本的全栈方案,让服务端快速上手。
低成本的前提就是把复杂的前端开发变得简单,初期我们考虑了三条路进行简化:
低代码代替源码开发的好处是可以让服务端在配置一两个页面后快速掌握配置技能,它的认知成本会降低,学习周期也会缩短,下面这张图更方便大家理解。
另外,在知乎看到一张非常有趣的图,虽然有点夸张成分,但是感觉可以帮助大家理解低代码的学习成本。
各个大厂其实在低代码领域已经做的非常成熟,给大家列举两个做的比较好的,大家可以去深度了解:
我们选择 Amis 作为基础的渲染引擎,主要原因如下:
另外关于和 AIGC 的结合,其实重点要看低代码在服务端全栈的场景下,可以帮助解决哪些问题?比如脚本编辑、UI布局等,这些一定会成为痛点,下面我们将随着问题的展开逐渐应用起来。
四、对低代码的“吐槽”
谈及我们的方案之前,我首先要“吐槽”下低代码/零代码,很多人说不好用,甚至有人说低代码是“行业毒瘤”,我翻看了很多这方面的总结性文章,以及自己的经验,理性总结如下:
五、主要方案设计
该篇文章我不会过多介绍低代码配置实现的原理,想了解的可以 Google 下。
我们整个全栈方案有一个统一的名字叫Wizard,包括渲染引擎、组件、在线配置、发布流程、AI 答疑、AI Proto 等一系列工具,接下来会捡重点介绍。
特殊说明:我们初期全栈覆盖的页面类型主要聚焦在 CQUD,并没有扩散,核心原因就是目前此类页面占比较高(72%),并且交互形式足够收敛,页面类型收敛可以将全栈的成本降低很多,后期可以根据必要性选择性覆盖更多的页面类型。
另外对于上面提到的前 4 个槽点,没什么好的方法,大家只能认清事实,持续的去做,针对第 5 点,我们确实有一些方向,分享出来给大家参考,总结来讲主要是三步:简化组件配置、高效初始化页面、智能答疑。
简化组件配置
得物的基础组件建设得益于 Antd 和 ProComponents,好的东西当然直接复用,我们的主要工作是还原得物的设计标准以及业务组件的沉淀,组件注册到Wizard中最重要的是要将复杂属性转换成 JSON 配置可实现,比如接口请求、表单联动、数据格式化等,如果这一步做不好,会导致部分功能被阉割,所以这部分我们花费了比较大的精力去设计实现,以表单联动举例,效果参考如下动图:
选择显示姓名,姓名展示,选择禁用密码,姓名隐藏,密码禁用
源码实现:
import React, { useState } from 'react';import { Radio, Form, Input } from 'antd';type FieldType = {username?: string;password?: string;type?: number;};const App: React.FC = () => {const [form] = Form.useForm();const [hiddenName, setHiddenName] = useState(false);const [diablePassword, setDiablePassword] = useState(false);const onValuesChangeHandler = (values: FieldType) => {setHiddenName(values.type !== 1);setDiablePassword(values.type === 2);};return (<Formform={form}name="basic"labelCol={{ span: 8 }}wrapperCol={{ span: 16 }}style={{ maxWidth: 600 }}notallow={onValuesChangeHandler}autoComplete="off"><Form.Item<FieldType> wrapperCol={{ offset: 8, span: 16 }}><Radio.Group><Radio value={1}>显示姓名</Radio><Radio value={2}>禁用密码</Radio></Radio.Group></Form.Item>{!hiddenName && (<Form.Item<FieldType> label="姓名"><Input /></Form.Item>)}<Form.Item<FieldType> label="密码"><Input.Password disabled={diablePassword} /></Form.Item></Form>);};export default App;
Wizard配置实现:
{"type": "form","body": [{"type": "radio","name": "type","label": "类型","options": [{"label": "显示姓名","value": 1},{"label": "禁用密码","value": 2}]},{"type": "text","name": "username","label": "姓名","visibleOn": "${type == 1}"},{"type": "password","name": "password","label": "密码","disabledOn": "${type == 2}"}] }
上面右侧Wizard代码示例中,type 代表组件类型,同级的其他属性代表组件API,body 属于通用属性用于子元素设置,认真看会发现,Wizard针对禁用和显隐设置增加了 disabledOn 和 visibleOn 两个属性,并且支持写简单的表达式,类似这种标准属性的实现,是所有的组件统一去实现的(所有的组件都会支持 visibleOn ,所有的表单类组件都支持 disabledOn ),表达式是引擎统一实现的能力,为了更好的支持配置化,Wizard引擎还支持数据域、数据链、模版、数据映射、表达式、函数、行为等。
另外一种比较复杂的情况,是组件自带的数据请求能力,比如 ProTable、Select、Form 等,而且一般都需要解决数据处理问题,比如数据查询前需要对入参进行格式调整,拿到数据之后需要对出参进行格式校准等,是非常常见的操作,我们统一设计了 api 配置,除了满足基本的 url、method、data 设置外,还支持请求前后的 adaptor 设置,当然这里就是我们说的需要写脚本的地方,而且目前是整个Wizard唯一需要写脚本的地方,这部分还是有点复杂的,但是我们这里借助于 AI 的能力实现了只需要配置当前数据格式和你需要转换的数据格式,就可以生成转换脚本,并且支持对函数进行快速测试,如果没问题即可回填到配置中,以更低的成本实现脚本输出。
下面这个示例,相信大家一看左右结构就能明白研发同学的数据转换意图,此处不再啰嗦。
其实这种功能在有了大模型之后,实现变得很简单,我们只需要设计一个合理的 prompts 即可,虽然输出的脚本有时候有点啰嗦,但是准确率和稳定性还是比较高的。
//adaptor 脚本生成 prompts你扮演一个纯函数代码生成器,你负责生成数据格式转换代码,你负责接收函数出入参的文本指令,根据要求生成javascript 代码,取变量值和给变量赋值的时候请做好容错处理,处理容错时不要提前返回undefined或null帮我生成一个 js 函数: function formatData(payload, response, api) {},要求最终返回处理好的数据response参数: ${before},返回数据为: ${target},只输出函数代码,不要输出其他无关的东西,函数代码中的注释保持中文输出,其他无关信息不要输出输出代码缩进2个空格
高效初始化页面
俗话说“万事开头难”,页面 0 到 1 的成本如何降到尽可能的低是我们一直比较追求的,因为这样可以有效降低全栈门槛,我们主要通过以下三个手段:
通过页面模版初始化
针对 CQUD 的场景,我们沉淀了比较多的示例,基本能够涵盖系统需要的常见交互和功能诉求,这是最基础的做法,全栈同学选择模版创建后,修改下配置即可满足页面诉求。
通过接口元数据生成页面
上面提到得物的 Mooncake 平台,沉淀了得物所有接口的出入参信息,Wizard可以做到选择接口和页面类型生成页面描述,根据接口类型,可以选择生成列表、表单、详情,可以复制到页面中成为页面主体或者一部分。
根据 PRD 描述快速生成页面
这个是为了解决上面“吐槽”中提到的第5点,很多 PRD 对于中后台需求描述过于简单,没有草图说明,即使有草图,对于全栈同学也不一定知道怎么还原 UI,Wizard训练了自有的大模型(关于模型训练,后面章节会介绍),做到对Wizard足够了解,可以结合 PRD 描述快速生成页面效果(插件WizardProto),我们只需要提供标准的描述页面功能的文本格式,产品同学按照格式填空书写页面结构即可,具体实现细节参考 :大模型在产品原型生成中的应用实践 ,这里放一个视频用于说明使用方式:
,时长00:30
通过 Proto 生成的页面既可以帮助产品用最低的成本还原页面原型,又可以帮助研发同学快速还原 UI。
同时我们看到视频中给予产品一定的 UI 配置能力,但是这远远不足,长远的规划上看,大模型在此步的应用只是为了快速生成,而后还是需要提供更加完备的可视化配置能力完成页面 UI 和交互配置(或者换句话说,大模型的应用是为了让产品和技术快速上手),并且这部分配置是可以沿用到全栈开发,我们希望能够做到 CQUD 的 UI 工作在 PRD 阶段搞定,全栈同学的主要工作是做接口配置和功能校准即可,当然这个是很理想的状态,但是我们希望全栈研发同学的单页面输出成本降低到 0.5 人日以下,同时不增加产品的成本。
智能答疑
前面提到的Wizard自有大模型,还承担着另外一个比较重要的角色,就是辅助全栈同学答疑,比如:
同时我们也会支持针对回答的内容进行正向和负向反馈,这些反馈也会用于丰富我们的训练池,让大模型准确率越来越高。
这部分能力目前使用率还不够高,根本问题还是准确率问题,关于下一步的规划,下面章节会提到。
六、大模型训练和应用
Wizard应用的基础大模型是更擅长做代码生成的DeepSeek Coder,我们使用的版本参数个数是 6.7b,但其实模型准确率如何,核心是训练的数据源怎么搞定,Wizard不像 Antd 或者 React 一样属于应用广泛的开源产品,社区有大量的代码、文章等让 ChatAIGC 进行训练,Wizard的训练数据需要我们自己整理,思路如下:
其实这里怎么去生成要看场景,主要思路是把大模型当做一个记忆力和理解能力超强的人去看待,你用哪些信息能够让它快速理解怎么使用一个组件,而后决定怎么去准备你的组件 QA 池。
综合流程整理如上
目前大模型在Wizard核心的应用有如下几种场景。
七、我对大模型应用研发提效的看法
八、功能架构图
Wizard的更多细节功能可以参考如下架构图。
功能架构图
1. Wizard 提供 3 种新建页面的方式,根本目的是为了尽可能降低页面 0 到 1 的成本,研发可以选择合适的方式或者组合使用。
a. 通过示例模板创建页面;
b. 通过 Mooncake 元数据创建页面;
c. Proto 工具实现根据 PRD 智能生成页面。
2. Mock 能力使得 Proto 生成的页面效果更加逼真,包括动态枚举,列表数据,表单提交等,既方便了产品丰富页面原型,又降低研发复用创建页面的成本。
3. Migrate 用于结合 AIGC 对于 ProTable、ProForm 等常用组件的配置转换成 Wizard 配置,用于进一步降低就页面迁移到Wizard页面的成本,提升全栈占比。
4. 基于天网和统一配置中心,完善了调试、发布、回滚、下线、菜单和权限管理等能力。
5. Wizard 根据文档和日常答疑并结合 AI 输出大量 QA 训练自有大模型,在整个全栈过程提供比较大的帮助。
a. 帮助产品画原型图,生成的页面可用于进一步配置;
b. 全栈答疑,降低认知成本;
c. 分析源码转换成Wizard页面,降低页面迁移成本;
d. 后期还会考虑通过需求描述做页面功能修改,进一步降低认知成本;
e. 通过训练大模型解决复杂的表达式生成问题,进一步降低联动配置成本。
九、问题和规划
需求提出流程
需求评估和交付流程