一句 Prompt 自动生成表单:我在低代码平台里是怎么接入生成式 AI 的

在过去的低代码平台中,用户通常通过拖拽组件完成页面搭建。但当生成式 AI 出现之后,我们逐渐意识到:对很多人来说,"拖拽"依然太重了,他们更希望的是------一句自然语言就能完成整个页面的构建。

比如:"帮我添加一个输入姓名的输入框和一个性别选择下拉框,选项为男、女。"

我是《低代码平台开发实践:基于 React》一书的作者,本文将结合我实际项目经验,介绍如何将生成式 AI 接入已有的低代码平台,并让 AI 理解组件规范、自动输出结构化的 Schema,最终在画布中渲染出页面。

本文内容分为三部分:

  1. 如何构建 Prompt 模板
  2. 如何调用 AI 模型,获取组件 Schema
  3. 如何将返回的 Schema 插入画布

背景:已有低代码平台的能力

本文基于一个已有的低代码平台,具备以下特性:

  1. 已实现基础组件体系;
  2. 用户可通过拖拽组件至画布;
  3. 组件支持属性编辑。

此前我在 《在 AI 编程的热潮下对低代码的思考》 一文中提到:AI 对低代码最直接的冲击,是"交互方式"的变革。这次我将以"自然语言生成表单页面"为例,展示具体的接入过程。

第一步:如何构建 Prompt 模板

为了让 AI 能够输出符合平台要求的 JSON Schema,首先要提供组件的 使用规范输出格式定义。也就是说,你得教它平台的规则。

在我的低代码平台中,每个组件都有对应的组件规格(ComponentSpec),定义如下:

php 复制代码
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:

css 复制代码
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:在组件面板中可编辑的字段以及默认值。生成规则为:
typescript 复制代码
const props: {[attr: string]: any} = {}
// componentSpecRaw 为组件规则
componentSpecRaw.props.forEach(prop => {
   props[prop.name] = prop.defaultValue
 })
  • extraProps:在组件面板中可编辑的字段以及默认值。生成规则为:
rust 复制代码
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 个关键点:

  1. AI 的角色

    你是一个低代码平台 AI 助手,能够根据用户的自然语言描述,生成符合平台组件规范的页面 Schema。你输出的结果应是一个 JSON 数组,代表组件树的根节点

  2. 输出格式要求

    直接返回 JSON schema,格式遵循规定,不要输出解释或注释

  3. Schema 字段要求

    每个组件需要输出一个完整的 JSON Schema 节点,包含以下字段:containerType、componentName、packageName、props、extraProps、isContainer、isFormControl、children。

  4. 可用组件列表

    你只能使用以下组件:Input(输入框)、Select(下拉框)、Row(行)、Column(列)

  5. 组件嵌套规则

    遵守以下组件嵌套规则:Input 和 Select 只能放在 Column 中;Column 只能放在 Row 中;Row 是页面的顶层布局容器;

  6. props 与 extraProps 的默认值与结构生成规则

  7. 各组件的 componentName、packageName 等元信息

Prompt 模板可由后端自动遍历组件规格生成。

第二步:如何调用 AI 模型生成 Schema

我使用 Node.js 配合 302.ai 的 API 来请求 AI 生成结果。核心代码如下:

php 复制代码
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 方法,它接受两个参数:

  1. schemas:AI 模型的返回值
  2. parentNode:schemas 的父节点
javascript 复制代码
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 实战交流群】,请关注我的公众号------前端知识小站,并联系我。

相关推荐
几何心凉10 分钟前
如何使用 React Hooks 替代类组件的生命周期方法?
前端·javascript·react.js
小堃学编程17 分钟前
前端学习(1)—— 使用HTML编写一个简单的个人简历展示页面
前端·javascript·html
hnlucky1 小时前
通俗易懂版知识点:Keepalived + LVS + Web + NFS 高可用集群到底是干什么的?
linux·前端·学习·github·web·可用性测试·lvs
懒羊羊我小弟1 小时前
使用 ECharts GL 实现交互式 3D 饼图:技术解析与实践
前端·vue.js·3d·前端框架·echarts
前端小巷子2 小时前
CSS3 遮罩
前端·css·面试·css3
运维@小兵2 小时前
vue访问后端接口,实现用户注册
前端·javascript·vue.js
雨汨2 小时前
web:InfiniteScroll 无限滚动
前端·javascript·vue.js
Samuel-Gyx2 小时前
前端 CSS 样式书写与选择器 基础知识
前端·css
天天打码3 小时前
Rspack:字节跳动自研 Web 构建工具-基于 Rust打造高性能前端工具链
开发语言·前端·javascript·rust·开源
字节高级特工3 小时前
【C++】”如虎添翼“:模板初阶
java·c语言·前端·javascript·c++·学习·算法