我为LowCodeEngine低代码写了个AI插件,降低低代码上手难度。

上一篇分享了一个不用写代码也能使用低代码的插件,但是还是有些上手难度,比如要理解事件、方法等概念。现在AI大模型可以帮助我们处理很多事情,那AI和低代码结合能碰撞出什么样的火花呢?下面和大家分享一下。

项目体验地址:dbfu.github.io/easy-builde…

项目仓库地址:github.com/dbfu/easy-b…

功能还比较简陋,后面慢慢完善,大家可以先体验一下AI助手功能。

目前插件是和低代码项目放在一起,后面我会给单独拆出来,让想用这个插件的朋友也能在自己项目里快速使用。目前物料使用的是官方的基于antd的物料,我只改了几个组件,让组件对外暴露了方法和值。

前言

上一篇分享了一个不用写代码也能使用低代码的插件,但是还是有一些上手难度,比如要理解事件、方法等概念。现在AI大模型可以帮助我们处理很多事情,那AI和低代码结合能碰撞出什么样的火花呢,下面和大家分享一下我的实践。

建议先看一下上篇文章

我为LowCodeEngine低代码引擎写了个插件,让不懂代码的产品人员也能自己开发页面了。

思考

前面使用插件后配置流程虽然简化了很多,但是还不是不够简单,如果能借助AI分析用户的输入,自动实现前面所有的动作就好了。

下面来分析一下点击按钮,打开弹框这个动作,其实只需要给它转换为下面这样的数据结构,我们就能解析并对接我们前面开发的事件流插件。

json
复制代码
{ "componentName": "Button", "event": "onClick", "action": { "type": "ComponentMethod", "componentName": "Modal", "method": "open" } }

componentName: 触发事件的组件

event: 事件

action: 执行的动作

可能有人会有疑问,这里只知道组件名称,不知道是哪个组件,怎么给组件绑定事件呢,我这里的处理是,如果画布中只出现一个当前类型的组件时,就取这个组件,如果画布中没有当前类型的组件,自动生成一个插入到画布中,如果画布中有多个当前类型的组件,会让用户选择一个。

也就是说当用户输入了点击按钮,打开弹框,会自动在画布中添加一个按钮和弹框,并且给按钮的点击事件绑定打开弹框的动作。

demo演示

点击按钮,打开弹框

我为LowCodeEngine低代码写了个AI插件,降低低代码上手难度。

点击按钮,打开弹框,一秒后关闭。

我为LowCodeEngine低代码写了个AI插件,降低低代码上手难度。

实现原理

分析用户输入

靠我们自己写代码去解析用户输入,基本不可能,因为用户可以随便输入,没有固定的格式,这时候我们需要借助 AI 大模型,帮我们分析用户的输入,然后转换为上面的数据结构。

分析用户输入这里我验证了两套方案,一个是langchain+ zod,还有一个是微软出的typechat,他们都可以实现把用户输入以json格式输出。

初始化后端框架

因为要对外暴露接口,所以要用到后端框架,我这里后端框架采用midway

创建一个midway项目

sh
复制代码
npm init midway@latest -y

推荐选择koa-v3模版,项目名称自己输入。

langchain + zod

在service里封装一个方法,调用 langchain 库,根据 zod 定义的模型,解析用户输入,最终返回和模型一致的数据结构。用的大模型是gpt-3.5-turbo,因为某些网站可以免费获取到3.5的密钥。

ts
复制代码
async formatInputToEventFlows(input: string) { const parser = StructuredOutputParser.fromZodSchema(schema); const chain = RunnableSequence.from([ PromptTemplate.fromTemplate( fs .readFileSync(this.koaApp.getAppDir() + '/template.txt', 'utf-8') .toString() ), new OpenAI({ temperature: 0.5, modelName: 'gpt-3.5-turbo', configuration: { // openapi 代理地址 baseURL: process.env.OPENAI_ENDPOINT, // api key apiKey: process.env.OPENAI_API_KEY, }, }), parser, ]); const response = await chain.invoke({ input, format_instructions: parser.getFormatInstructions(), }); return response; }

看一下 schema 怎么定义的

ts
复制代码
import { z } from "zod"; export const showMessageAction = z .object({ name: z.literal("showMessage"), type: z.enum(["success", "error"]), content: z.string().describe("消息内容"), }) .describe("显示消息"); export const openPageAction = z .object({ name: z.literal("openPage"), url: z.string().describe("打开页面的url"), }) .describe("打开页面"); export const componentAction = z .object({ name: z.literal("ComponentMethod"), component: z.union([ z.literal("Button").describe("按钮组件"), z.literal("Modal").describe("弹窗组件"), z.literal("Input").describe("输入框组件"), ]), method: z.string().describe("组件方法"), }) .describe("调用组件方法"); export const schema = z.object({ componentName: z.string().nullish().describe("触发事件的组件,可以为空"), eventName: z.union([ z.literal("success").describe("成功事件"), z.literal("error").describe("失败事件"), z.string().describe(`当前组件对应的事件。 点击(onClick) 确认按钮点击事件(onOK), 取消按钮点击事件(onCancel),弹出事件(onShow) 获取焦点事件(onFocus), 失去焦点事件(onBlur)`), ]), action: z .union([showMessageAction, openPageAction, componentAction]) .describe("执行的动作"), children: z.lazy(() => schema.nullish().describe("后续事件,没有后续可以为空") ), });

大模型之所以能根据用户输入解析到对应的字段上,完全靠 describe 方法里的字段描述。

看一下Prompt定义,format_instructions和input是变量,发送给openai的时候,会被替换成zod的模型描述和用户输入。

txt
复制代码
你是一个低代码平台 希望你能根据用户输入,分析用户的行为。 {format_instructions} {input} 要求children字段数据格式和JSON Schema保持一致

typechat

使用typechat解析用户输入

ts
复制代码
async formatInputToEventFlows(input: string) { const model = createLanguageModel({ OPENAI_MODEL: 'gpt-3.5-turbo', OPENAI_API_KEY: process.env.OPENAI_API_KEY, OPENAI_ENDPOINT: process.env.OPENAI_ENDPOINT, }); const schema = fs.readFileSync( path.join(this.koaApp.getAppDir(), 'src/service/schema.ts'), 'utf8' ); const translator = createJsonTranslator<EventFlow>( model, schema, 'EventFlow' ); const response = await translator.translate(input); if (response.success) { return response.data; } else { throw new Error('error'); } }

看一下schema定义,typechat支持使用ts定义,在字段上加注释就行了。

ts
复制代码
export type SuccessEvent = 'success'; export type ErrorEvent = 'error'; export type ButtonEvent = 'onClick'; export type ModalEvent = 'onOk' | 'onCancel' | 'onShow'; export type EventFlow = { // 当前触发的事件的组件,可以为空 componentName?: 'Button' | 'Modal' | null; // 事件 event: SuccessEvent | ErrorEvent | ButtonEvent | ModalEvent; // 动作 action: { onSuccess?: EventFlow['action']; onError?: EventFlow['action']; } & ( | { name: 'ComponentMethod'; component: 'Modal'; method: 'open' | 'close'; } | { name: 'showMessage'; type: 'success' | 'error'; content: string; } | { name: 'openPage'; url: string; } | { // 执行定时器 name: 'setTimeout'; // 毫秒 timer: number; // 多少毫秒后执行的动作 onSuccess?: EventFlow['action']; } ); };

我开始使用的是langchain+zod方案,这种方式定义模型的方式比较复杂并且返回值还不稳定,后面把方案换成了typechat,定义模型也比较简单,不需要太多的注释,并且还准确率也高,返回的结果比较稳定。

接口测试

输入:点击按钮,打开弹框

输出:

json
复制代码
{ "componentName": "Button", "event": "onClick", "action": { "name": "ComponentMethod", "component": "Modal", "method": "open" } }

我为LowCodeEngine低代码写了个AI插件,降低低代码上手难度。

输入:点击按钮,打开弹框。成功后显示提示,提示内容为 hello。显示成功后关闭弹框。

输出:

json
复制代码
{ "componentName": "Button", "event": "onClick", "action": { "name": "ComponentMethod", "component": "Modal", "method": "open", "onSuccess": { "name": "showMessage", "type": "success", "content": "hello", "onSuccess": { "name": "ComponentMethod", "component": "Modal", "method": "close" } } } }

我为LowCodeEngine低代码写了个AI插件,降低低代码上手难度。

输入:点击按钮,打开弹框。一秒后,关闭弹框。

输出:

json
复制代码
{ "componentName": "Button", "event": "onClick", "action": { "name": "ComponentMethod", "component": "Modal", "method": "open", "onSuccess": { "name": "setTimeout", "timer": 1000, "onSuccess": { "name": "ComponentMethod", "component": "Modal", "method": "close" } } } }

我为LowCodeEngine低代码写了个AI插件,降低低代码上手难度。

使用typechat方案测试了很多遍,输出还是挺稳定的。

前端解析

前端采用对话的方式,用户输入完需求后,向后端发送请求,从后端拿到通过大模型格式化后的数据结构,在前端代码中再去解析数据结构,生成组件和绑定事件。

对话框实现

我为LowCodeEngine低代码写了个AI插件,降低低代码上手难度。

对话内容分为用户和AI,消息类型定义

ts
复制代码
// 用户消息类型 interface UserMessage { id: string, role: 'user', content: string, status: 'success', } // AI消息类型 export interface AIMessage { id: string, role: 'ai', content: AIContent, status: 'loading' | 'success' | 'error' } export interface AIContent { componentName?: string, event: string, action: { onSuccess?: AIContent['action'], onError?: AIContent['action'], } & { name: string, [k: string]: any, }, }

布局使用的是flex布局

我为LowCodeEngine低代码写了个AI插件,降低低代码上手难度。

我为LowCodeEngine低代码写了个AI插件,降低低代码上手难度。

关于输入框回车事件有个需要注意的地方,中文输入法输入英文单词按回车键,也会触发回车事件,这种情况可以用keyCode来判断,英文下的回车keyCode是13,中文输入法下的keyCode是229,可以用这个判断。

解析后端返回的结构

先解析触发事件的组件。如果当前组件类型画布中没有,那么自动给用户生成一个。如果有多个,需要用户选择一个。如果只有一个,就选择当前这个。

我为LowCodeEngine低代码写了个AI插件,降低低代码上手难度。

再解析事件。判断组件是否有当前事件,如果没有就终止

我为LowCodeEngine低代码写了个AI插件,降低低代码上手难度。

解析动作。根据不同的动作生成事件流数据结构

我为LowCodeEngine低代码写了个AI插件,降低低代码上手难度。

解析后续事件。如果 有onSuccess或onError不为空,表示还有后续事件,递归解析。

我为LowCodeEngine低代码写了个AI插件,降低低代码上手难度。

解析完成后,生成的事件流。

点击按钮,打开弹框为例,看一下自动生成事件流数据结构。

json
复制代码
{ "onClick": { "type": "flow", "value": { "id": "root", "label": "开始", "type": "start", "children": [ { "type": "action", "id": "deadd458-1357-4d64-9810-6169458cdb51", "label": "组件方法", "key": "action", "config": { "type": "ComponentMethod", "config": { "componentId": "db1a11a4-47a7-4c88-b86c-025aca7fe168", "method": "open" } } } ] } } }

最后调用node的setPropValue方法,把生成的事件流绑定到当前组件的事件属性上。

未来规划

上面通过AI大模型实现了用户输入需求,自动生成组件和组件事件绑定。虽然例子比较简单,但是我觉得这一块还是有很大的前景的,我也在慢慢完善。

关于这一块我的规划是:

  • 继续完善组件事件绑定动作这一方式,支持更复杂的用户输入,比如自动生成条件,和组件属性绑定变量。

    举个例子,当用户输入点击按钮,检验输入框的内容是否为邮箱格式,如果是邮箱格式,发送邮件。如果不是提示错误消息,消息内容为邮箱格式不正确,这里解析用户输入的内容,需要加上条件,当然还有更复杂的场景,我慢慢完善吧。

  • 还有一个方向,用户直接输入功能需求,使用大模型直接生成低代码的schema,这样的方式对比上面更简单,比如输入我要使用阿里的LowCodeEngine低代码平台开发员工管理系统,帮我生成一个员工管理页面低代码Schema。,AI会自动生成一个增删改查的Schema,拿过来简单解析一下就能对接到低代码平台了。

json
复制代码
{ "title": "员工管理系统", "components": [ { "type": "Table", "name": "employeeTable", "dataSource": { "type": "API", "api": "/api/employees" }, "columns": [ { "title": "姓名", "field": "name", "type": "Text" }, { "title": "职位", "field": "position", "type": "Text" }, { "title": "部门", "field": "department", "type": "Text" }, { "title": "电子邮件", "field": "email", "type": "Email" }, { "title": "电话号码", "field": "phone", "type": "Text" } ], "actions": [ { "type": "Button", "title": "新增", "action": { "type": "Dialog", "title": "新增员工", "form": { "type": "Form", "fields": [ { "name": "name", "label": "姓名", "type": "TextInput" }, { "name": "position", "label": "职位", "type": "TextInput" }, { "name": "department", "label": "部门", "type": "TextInput" }, { "name": "email", "label": "电子邮件", "type": "EmailInput" }, { "name": "phone", "label": "电话号码", "type": "TextInput" } ], "submit": { "type": "API", "api": "/api/employees/create" } } } }, { "type": "Button", "title": "编辑", "action": { "type": "Dialog", "title": "编辑员工信息", "dataSource": { "type": "API", "api": "/api/employees/{id}" }, "form": { "type": "Form", "fields": [ { "name": "name", "label": "姓名", "type": "TextInput" }, { "name": "position", "label": "职位", "type": "TextInput" }, { "name": "department", "label": "部门", "type": "TextInput" }, { "name": "email", "label": "电子邮件", "type": "EmailInput" }, { "name": "phone", "label": "电话号码", "type": "TextInput" } ], "submit": { "type": "API", "api": "/api/employees/update" } } } } ] } ] }
  • 还有一个方向,如果用户想开发一个系统,直接输入我想开发XXX系统,利用AI大模型给出表模型,然后我们解析模型之间的关系自动生成接口和页面,这样就可以轻松完成一个系统。比如用户输入我要开发一套员工管理系统,帮我生成一个员工管理系统的所有表结构,以json格式输出。
json
复制代码
{ "tables": [ { "name": "Employee", "fields": [ { "name": "id", "type": "int", "primaryKey": true, "autoIncrement": true }, { "name": "name", "type": "varchar", "length": 255 }, { "name": "email", "type": "varchar", "length": 255, "unique": true }, { "name": "phone", "type": "varchar", "length": 255, "nullable": true }, { "name": "positionId", "type": "int", "reference": { "table": "Position", "field": "id" } } ] }, { "name": "Department", "fields": [ { "name": "id", "type": "int", "primaryKey": true, "autoIncrement": true }, { "name": "name", "type": "varchar", "length": 255 } ] }, { "name": "EmployeeDepartment", "fields": [ { "name": "employeeId", "type": "int", "reference": { "table": "Employee", "field": "id" } }, { "name": "departmentId", "type": "int", "reference": { "table": "Department", "field": "id" } } ], "primaryKey": [ "employeeId", "departmentId" ] }, { "name": "Position", "fields": [ { "name": "id", "type": "int", "primaryKey": true, "autoIncrement": true }, { "name": "title", "type": "varchar", "length": 255 } ] } ] }

上面是AI回复的结果,我们拿到这个json数据自动生成接口和页面也不是不行。

这几个就是我未来的规划,打算慢慢完善他们。

最后

项目体验地址:dbfu.github.io/easy-builde…

项目仓库地址:github.com/dbfu/easy-b…

功能目前还比较简陋,后面会慢慢完善,比如添加更多的物料、对接后端接口、让AI支持更复杂的用户输入等。

目前插件是和低代码项目放在一起,后面我会给单独拆出来,让想用这个插件的朋友也能在自己项目里快速使用。

本文正在参加阿里低代码引擎征文活动

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

给TA打赏
共{{data.count}}人
人已打赏
人工智能

xAI 发布 Grok-1:最大的开源LLM

2024-5-31 17:49:05

人工智能

Ollama + Openwebui 本地部署大型模型与交互式可视化聊天

2024-5-31 21:36:42

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索