在过去的低代码平台中,用户通常通过拖拽组件完成页面搭建。但当生成式 AI 出现之后,我们逐渐意识到:对很多人来说,“拖拽”依然太重了,他们更希望的是——一句自然语言就能完成整个页面的构建。
比如:“帮我添加一个输入姓名的输入框和一个性别选择下拉框,选项为男、女。”
我是《低代码平台开发实践:基于 React》一书的作者,本文将结合我实际项目经验,介绍如何将生成式 AI 接入已有的低代码平台,并让 AI 理解组件规范、自动输出结构化的 Schema,最终在画布中渲染出页面。
本文内容分为三部分:
- 如何构建 Prompt 模板
- 如何调用 AI 模型,获取组件 Schema
- 如何将返回的 Schema 插入画布
背景:已有低代码平台的能力
本文基于一个已有的低代码平台,具备以下特性:
- 已实现基础组件体系;
- 用户可通过拖拽组件至画布;
- 组件支持属性编辑。
此前我在 《在 AI 编程的热潮下对低代码的思考》 一文中提到:AI 对低代码最直接的冲击,是“交互方式”的变革。这次我将以“自然语言生成表单页面”为例,展示具体的接入过程。
第一步:如何构建 Prompt 模板
为了让 AI 能够输出符合平台要求的 JSON Schema,首先要提供组件的 使用规范 和 输出格式定义。也就是说,你得教它平台的规则。
在我的低代码平台中,每个组件都有对应的组件规格(ComponentSpec),定义如下:
interface {
componentName: string;
packageName: string;
title: string;
iconUrl: string;
description: string;
docUrl?: string;
version: string;
props: PropRaw[];
// 描述该组件位于组件面板中哪个区域
group?: "base"|"layout"|"subjoin"|"template";
advanced?: {
// 组件的嵌套规则
nestingRule?: {
// 父级组件白名单
// 非容器组件必须放置在容器组件中
parentWhitelist?: string[];
// 子组件白名单。
// 空数组则说明其他组件不能放置在该组件中, undefined 则说明其他组件能放置在该组件中
childWhitelist?: string[];
};
supports?: {
// 是否能配置样式
styles?: boolean;
// 支持的事件列表,空数组意味着不支持任何事件
events?: string[]
},
component?: {
// 是否是容器
isContainer?: boolean;
// 容器类型
containerType?: 'Layout'|'Data'|'Page';
// 是否是表单组件
isFormControl?: boolean
},
},
// 嵌套的组件规格,通常只有模板才有这个字段
// 模板所嵌套的组件的嵌套规则不会被用到
// 注意:children 中的组件,必须在引擎中注册
children?: ComponentSpecRaw
}
你可以在这里查看 Input 组件的完整规格:
📎 unpkg.com/vitis-lowco…
基于组件规格,我们需要 AI 输出如下类型的 schema:
interface NodeSchema {
componentName: string;
packageName: string;
props: {[key: string]: any};
extraProps: ExtraProps;
isContainer: boolean;
isFormControl?: boolean;
containerType?: 'Layout'|'Data'|'Page';
children: NodeSchema[];
}
- componentName:组件名,比如 Input、Select 等,从组件规格中能得到该值。
- packageName:组件 npm 包名,比如 vitis-lowcode-input 等,从组件规格中能得到该值。
- isContainer:表明该组件是否是容器组件,从组件规格中能得到该值。
- isFormControl:表明该组件是否是表单控件,从组件规格中能得到该值。
- containerType:表明该组件属于哪一种容器组件,取值为 'Layout'|'Data'|'Page',从组件规格中能得到该值。
- children:子组件
- props:在组件面板中可编辑的字段以及默认值。生成规则为:
const props: {[attr: string]: any} = {}
// componentSpecRaw 为组件规则
componentSpecRaw.props.forEach(prop => {
props[prop.name] = prop.defaultValue
})
- extraProps:在组件面板中可编辑的字段以及默认值。生成规则为:
function initExtraProps(rawData: ComponentSpecRaw) {
// 将取值路径、name 和 id 放在 extraProps 中
const extraProps = {
id: {
type: 'JSRunFunction',
value: "node => node.id"
}
}
if (rawData.advanced?.component?.containerType !== 'Page') {
extraProps.pathToVal = ''
}
if (rawData.advanced?.component?.isFormControl) {
extraProps.name = ''
}
if (rawData.advanced?.component?.isContainer) {
extraProps.dataSource = {
type: 'DataSource',
value: {
url: '',
method: 'GET',
requestHandler: {
type: 'JSFunction',
value: 'function requestHandler(params){return params}'
},
responseHandler: {
type: 'JSFunction',
value: 'function responseHandler(response) { return response.data }'
}
}
}
}
if (!rawData.advanced?.component?.isContainer || rawData.advanced.component.containerType !== 'Page') {
extraProps.isHidden = {
type: 'JSFunction',
value: 'function isHidden(pageData, containerData, formData){ return false }'
}
}
if (rawData.advanced?.component?.isFormControl) {
extraProps.isDisabled = {
type: 'JSFunction',
value: 'function isDisabled(pageData, containerData, formData){ return false }'
}
extraProps.getValue = {
type: 'JSFunction',
value: ''
}
}
return extraProps
}
上述代码可在 github.com/react-low-c… 找到。
Prompt 模板包含哪些内容?
Prompt 模板是发送给 AI 的提示语,它包含以下 7 个关键点:
-
AI 的角色
你是一个低代码平台 AI 助手,能够根据用户的自然语言描述,生成符合平台组件规范的页面 Schema。你输出的结果应是一个 JSON 数组,代表组件树的根节点
-
输出格式要求
直接返回 JSON schema,格式遵循规定,不要输出解释或注释
-
Schema 字段要求
每个组件需要输出一个完整的 JSON Schema 节点,包含以下字段:containerType、componentName、packageName、props、extraProps、isContainer、isFormControl、children。
-
可用组件列表
你只能使用以下组件:Input(输入框)、Select(下拉框)、Row(行)、Column(列)
-
组件嵌套规则
遵守以下组件嵌套规则:Input 和 Select 只能放在 Column 中;Column 只能放在 Row 中;Row 是页面的顶层布局容器;
-
props 与 extraProps 的默认值与结构生成规则
-
各组件的 componentName、packageName 等元信息
Prompt 模板可由后端自动遍历组件规格生成。
第二步:如何调用 AI 模型生成 Schema
我使用 Node.js 配合 302.ai 的 API 来请求 AI 生成结果。核心代码如下:
import axios from "axios";
const instance = axios.create({
baseURL: 'https://api.302.ai',
headers: {
"Content-Type": 'application/json',
"Authorization": 'Bearer ' + 'YOUR-API-KEY'
}
});
export function fetchSchema(userPrompt: string) {
return instance.request({
url: '/v1/chat/completions',
method: 'POST',
data: {
model: 'gemini-1.5-pro',
messages: [
{ role: 'system', content: prompt },
{ role: 'user', content: userPrompt }
]
}
}).then(res => res.data.choices[0].message.content);
}
用户输入自然语言(如:“帮我生成一个页面,包含一个姓名输入框和性别选择下拉框”),最终会被传入 fetchSchema
方法,由 AI 返回符合组件规范的 JSON Schema。
第三步:如何将 Schema 插入画布
生成好的 schema 需要插入低代码平台的画布中。我们在核心类 DocumentModel
中新增 insertSchema
方法,它接受两个参数:
- schemas:AI 模型的返回值
- parentNode:schemas 的父节点
insertSchema(schemas: NodeSchema[], parentNode: Node<NodeSchema> = this.rootNode) {
const insert = (schemas: NodeSchema[], parentNode: Node<NodeSchema>) => {
schemas.forEach((schema, index) => {
const newNode = this.createNode(schema, parentNode);
parentNode.inertChildAtIndex(newNode, parentNode.childrenSize + index);
});
}
insert(schemas, parentNode)
// 重新渲染画布
this.project.renderer?.rerender();
}
这个方法支持插入复杂组件树,同时调用引擎的 rerender()
方法刷新视图。
结语
如果你正在开发自己的低代码平台,或对接入 AI 有更多探索,欢迎和我交流。我的图书《低代码平台开发实践:基于 React》全面介绍了低代码引擎、渲染器、代码生成等核心模块。想加入【低代码 + AI 实战交流群】,请关注我的公众号——前端知识小站,并联系我。