1. 从一句需求到可运行应用,这条链路到底卡在哪
“帮我做一个客户跟进记录的小工具,能录入、能按状态筛选、能导出表格就行。”——这句话如果扔给传统开发流程,接下来就是需求评审、原型设计、接口定义、前端页面、后端服务、数据库建表、联调测试,一套走完少说两三天。但如果扔给一套调教好的“开源AI+低代码”组合,从输入这句话到拿到一个能点、能填、能导出的页面,可能只需要几分钟。
这就是我最近花了不少时间折腾的方向:用开源大模型做意图理解与代码生成,用低代码引擎做页面渲染与数据绑定,中间用一层结构化的“应用描述协议”把两者串起来。标题里说的“一句话生成业务应用”,核心并不是AI直接吐出一个完整的前后端工程,而是AI把自然语言翻译成低代码平台能识别的结构化配置,再由低代码引擎把这套配置渲染成真正可交互的应用。
关键词其实就三个:意图解析、Schema生成、动态渲染。摘要描述可以概括为:通过开源AI模型将自然语言业务需求转化为低代码平台的应用Schema,实现从一句话到可运行业务应用的快速落地,并附带源码层面的拆解分析。
这篇文章适合谁看?如果你是对AI应用开发感兴趣的前端或全栈工程师,想搞清楚“自然语言生成应用”这件事在工程上到底怎么落地;如果你是低代码平台的开发者,想知道怎么把大模型接进现有的渲染体系;或者你只是一个喜欢折腾开源项目的技术爱好者,想自己搭一套玩玩——那这篇内容应该能给你不少可直接参考的东西。
我下面会从整体架构讲起,然后逐层拆解意图解析、Schema协议设计、动态渲染引擎、源码关键路径,最后聊几个我实际踩过的坑和优化思路。全程尽量说人话,代码和配置都会给到能跑的程度。
2. 整体架构:AI负责“翻译”,低代码负责“执行”
2.1 为什么不是AI直接生成完整代码
很多人第一反应是:既然大模型能写代码,为什么不直接让它生成一个React项目或者Vue页面?我一开始也是这么想的,试过之后发现几个很现实的问题。
第一,生成完整工程的token消耗极大,一个稍微像样的业务页面,组件、样式、状态管理、接口调用全算上,轻松几千行,大模型输出又慢又容易在中途“跑偏”。第二,生成结果的稳定性差,同样的需求换个说法,出来的代码结构可能完全不同,后续想改都没法统一处理。第三,业务人员根本改不动,生成一堆代码文件,非技术人员看一眼就放弃了,这跟低代码“让业务人员也能改”的初衷是矛盾的。
所以更合理的思路是:让AI只负责它最擅长的事——理解自然语言意图,输出结构化描述;剩下的渲染、交互、数据绑定,交给成熟的低代码引擎。这样AI的输出被约束在一个有限的Schema空间里,稳定性大幅提升,而且生成的配置业务人员也能在可视化编辑器里继续调整。
2.2 三层架构的职责划分
我把整套系统拆成三层,职责非常清晰:
| 层级 | 核心职责 | 关键技术 | 输出物 |
|---|---|---|---|
| 意图解析层 | 理解自然语言,提取实体、动作、约束 | 开源大模型 + Prompt工程 + 输出校验 | 结构化需求JSON |
| Schema生成层 | 将需求映射为低代码平台的应用描述 | Schema协议 + 模板匹配 + 字段推断 | 应用Schema |
| 渲染执行层 | 根据Schema渲染页面、绑定数据、处理交互 | 低代码引擎 + 组件库 + 数据源适配 | 可运行应用 |
这三层之间是松耦合的。意图解析层不关心低代码平台长什么样,它只负责把“人话”变成“机器能懂的意图”。Schema生成层是桥梁,它知道低代码平台支持哪些组件、哪些属性,负责把意图“翻译”成平台能吃的配置。渲染执行层则完全复用低代码平台原有的能力,AI的介入对它来说是透明的。
这种分层的好处是:换一个大模型,只需要改意图解析层;换一个低代码平台,只需要改Schema生成层的映射规则;渲染层几乎不用动。我在实际项目中就是先跑通了一层,再逐步替换和优化,迭代起来很舒服。
2.3 数据流转的完整链路
用户输入一句话之后,数据在系统里的流转大致是这样的:
- 用户输入自然语言需求,比如“做一个员工请假申请页面,包含姓名、部门、请假类型、起止时间、事由,提交后进入待审批状态”。
- 意图解析层调用大模型,输出一个结构化的需求描述,包含页面类型(表单页)、字段列表(每个字段的名称、类型、是否必填)、动作(提交)、状态流转(待审批)。
- Schema生成层拿到这个需求描述,对照低代码平台的组件能力,把“姓名”映射为文本输入框,“部门”映射为下拉选择(数据源来自组织架构),“起止时间”映射为日期范围选择器,“事由”映射为多行文本,提交按钮绑定一个数据写入动作。
- 渲染执行层拿到完整的Schema,调用低代码引擎的渲染器,生成页面。用户看到的就是一个可以直接填写和提交的表单。
- 如果用户觉得哪里不对,可以在可视化编辑器里直接拖拽调整,调整后的Schema可以保存下来,作为后续类似需求的模板。
整条链路里,最需要精心设计的是Schema协议,它决定了AI能表达什么、低代码平台能渲染什么,以及两者之间的映射是否顺畅。下一节我会重点拆解这个协议的设计思路。
3. 意图解析层:让开源模型稳定输出结构化结果
3.1 模型选型:为什么我最终选了7B级别的开源模型
一开始我试过用更大的模型,效果确实好,但成本和延迟都上去了。后来发现,意图解析这个任务其实并不需要模型有太强的推理能力,它更像是一个信息抽取+分类的任务:从一句话里找出字段、动作、约束条件。这种任务,一个经过良好指令微调的7B级别开源模型完全能胜任。
我对比过几个常见的开源模型系列,最后选了一个在中文指令跟随上表现比较稳的7B模型。选它的理由有三个:一是中文理解够用,业务需求基本都是中文描述,模型的中文语感很重要;二是输出格式可控,通过Few-shot示例和格式约束,它能比较稳定地输出JSON;三是本地部署成本低,一张消费级显卡就能跑起来,对于个人开发者和小团队来说门槛低很多。
当然,如果你的场景更复杂,比如需要理解多轮对话、处理跨页面的业务流程,那可能需要更大的模型或者更强的推理能力。但对于“一句话生成单页应用”这个场景,7B级别足够了。
3.2 Prompt设计:把“人话”翻译成“机器意图”的关键
Prompt设计是意图解析层的核心。我踩过的最大坑就是:一开始直接把用户输入扔给模型,让它“输出一个JSON描述这个页面”,结果模型输出的字段名五花八门,有时候用中文,有时候用英文,有时候还自己发明一些不存在的属性。
后来我改成了一套结构化Prompt模板,包含四个部分:
- 角色设定:明确告诉模型它是一个“业务应用需求解析器”,只负责提取信息,不负责生成代码。
- 输出格式约束:给出一个严格的JSON Schema,规定每个字段的名称、类型、可选值范围。
- Few-shot示例:给两到三个完整的输入输出示例,覆盖表单页、列表页、详情页等常见类型。
- 边界说明:告诉模型遇到不确定的信息时怎么处理,比如“如果用户没有指定字段类型,默认按文本处理”。
这套Prompt模板调好之后,模型输出的稳定性有了质的提升。下面是一个简化版的Prompt示例:
你是一个业务应用需求解析器。请将用户的自然语言需求解析为以下JSON格式: { "pageType": "form | list | detail", "title": "页面标题", "fields": [ { "name": "字段名", "label": "字段显示名", "type": "text | number | date | select | textarea | daterange", "required": true | false, "options": ["选项1", "选项2"] } ], "actions": [ { "type": "submit | export | search", "label": "按钮文字" } ] } 用户需求:做一个员工请假申请页面,包含姓名、部门、请假类型、起止时间、事由,提交后进入待审批状态。 输出:实测下来,只要Few-shot示例给得够清楚,模型基本能稳定输出符合格式的JSON。偶尔有格式错误,加一层JSON解析校验和重试机制就能兜住。
3.3 输出校验:别让模型的“自由发挥”毁掉整条链路
模型输出不可控是常态,所以输出校验层是必须的。我的做法是:
- JSON语法校验:先尝试解析,解析失败就触发重试,最多重试两次。
- Schema结构校验:检查必填字段是否存在,字段类型是否在允许范围内。
- 业务规则校验:比如“select类型的字段必须有options”,“daterange类型的字段不能有options”等。
- 兜底策略:如果重试后仍然失败,就降级到一个默认的空白表单Schema,至少保证用户能看到一个可用的页面,而不是直接报错。
这套校验机制看起来简单,但实际跑起来能挡掉80%以上的异常情况。剩下的20%,大部分是因为用户输入实在太模糊,比如“帮我做个东西”,这种就只能引导用户补充信息了。
3.4 多轮澄清:当一句话说不清楚的时候
有些需求一句话确实说不清楚。比如用户说“做一个管理页面”,管理什么?有哪些字段?要什么操作?这时候就需要多轮澄清。
我的做法是:在意图解析层加一个“置信度评估”环节。如果模型对某些关键信息(比如字段列表、页面类型)的置信度低于阈值,就生成一个澄清问题返回给用户,比如“您说的管理页面,具体需要管理哪些信息呢?比如姓名、日期、金额等”。
用户补充之后,把补充信息和原始需求拼在一起,再次送入模型解析。这样经过一到两轮澄清,基本就能拿到足够完整的需求描述。这个机制在实际使用中非常有用,尤其是面对非技术用户的时候。
4. Schema协议设计:AI和低代码引擎之间的“合同”
4.1 为什么需要一个中间协议
如果没有中间协议,AI直接输出低代码平台的配置,会有什么问题?最大的问题是耦合太紧。低代码平台的配置格式一旦调整,Prompt就得跟着改,模型也得重新调。而且不同低代码平台的配置格式差异很大,换一个平台就得重写一套Prompt,完全没法复用。
中间协议的作用就是解耦。AI只负责输出符合协议的“意图描述”,Schema生成层负责把协议映射到具体平台的配置。这样AI侧和平台侧可以独立演进,只要协议不变,两边怎么改都不影响对方。
这个思路其实在很多领域都有应用,比如编译器里的中间表示(IR),数据库里的SQL,都是类似的解耦思想。我们这里做的就是一个“业务应用描述”的领域特定中间表示。
4.2 协议的核心字段设计
我设计的协议核心字段包括以下几类:
页面级字段:
pageType:页面类型,枚举值包括form(表单页)、list(列表页)、detail(详情页)、dashboard(看板页)。title:页面标题。layout:布局方式,比如single-column、two-column、grid。
字段级字段:
name:字段唯一标识,用于数据绑定。label:字段显示名称。type:字段类型,枚举值包括text、number、date、select、textarea、daterange、upload等。required:是否必填。options:选项列表,仅select类型使用。defaultValue:默认值。placeholder:占位提示文字。
动作级字段:
type:动作类型,比如submit、export、search、reset。label:按钮文字。target:动作目标,比如提交到哪个数据表,导出哪些字段。
数据级字段:
dataSource:数据源标识,可以是静态数据、API接口、或者数据库表。fields:数据字段映射。
这套协议不算复杂,但覆盖了大部分常见业务页面的需求。关键是它足够中立,不绑定任何具体的低代码平台,也不绑定任何具体的AI模型。
4.3 从协议到平台配置的映射策略
Schema生成层的核心工作就是映射。我采用的是“模板+规则”的方式:
- 对于常见的页面类型(表单页、列表页),预置一套模板,把协议字段填充进去就能生成平台配置。
- 对于字段类型,建立一张映射表,比如协议里的
text映射到平台的Input组件,select映射到Select组件,daterange映射到DatePicker.RangePicker。 - 对于数据源,根据协议里的
dataSource标识,调用对应的数据适配器,生成平台能识别的数据配置。
映射过程中还需要处理一些平台特有的约束。比如某个低代码平台的Select组件要求options必须是{label, value}的数组,而协议里可能只给了字符串数组,这时候就需要做一层转换。这些转换规则都放在映射层,AI侧完全不用关心。
4.4 协议的可扩展性考虑
业务需求是千变万化的,协议不可能一开始就设计得面面俱到。所以我在设计时留了几个扩展点:
- 自定义字段类型:通过
customType字段,允许协议表达平台特有的组件类型。 - 扩展属性:通过
extra字段,允许携带平台特有的配置项,AI侧不解析,直接透传。 - 版本号:协议本身带版本号,后续升级时可以兼容旧版本。
这些扩展点在实际使用中很有用。比如后来我想支持“富文本编辑器”这种平台特有的组件,只需要在协议里加一个customType: "rich-text",然后在映射层加一条规则就行,完全不用动AI侧的Prompt。
5. 动态渲染引擎:让Schema变成能点能填的页面
5.1 渲染引擎的核心工作流程
渲染引擎拿到Schema之后,大致经过以下几个步骤:
- Schema解析:把JSON格式的Schema解析成内部的数据结构,做一轮校验和补全。
- 组件映射:根据字段类型,找到对应的UI组件。
- 布局计算:根据layout配置,计算每个组件的位置和尺寸。
- 数据绑定:把字段的name和内部的数据模型绑定起来,实现双向绑定。
- 事件绑定:把动作配置和事件处理器绑定起来,比如submit按钮点击后触发数据提交。
- 渲染输出:生成最终的页面DOM。
这个过程听起来不复杂,但实际实现时有很多细节要处理。比如字段之间的联动(选了“请假类型”之后,“事由”的必填状态可能变化),比如数据校验(必填字段没填时提交按钮要禁用),比如异步数据源加载(下拉选项从接口获取时的loading状态)。
5.2 组件映射表的维护
组件映射表是渲染引擎的核心配置。我维护的映射表大致长这样:
| 协议字段类型 | 平台组件 | 默认属性 | 备注 |
|---|---|---|---|
| text | Input | maxLength: 200 | 单行文本 |
| textarea | Input.TextArea | rows: 4 | 多行文本 |
| number | InputNumber | precision: 0 | 数字输入 |
| date | DatePicker | format: YYYY-MM-DD | 日期选择 |
| daterange | DatePicker.RangePicker | - | 日期范围 |
| select | Select | showSearch: true | 下拉选择 |
| upload | Upload | maxCount: 5 | 文件上传 |
这张表看起来简单,但维护起来需要细心。比如不同平台对maxLength的默认值要求不一样,有的平台默认不限制,有的平台默认100,这些细节都需要在映射时处理好,否则生成的页面体验会不一致。
5.3 数据绑定的实现细节
数据绑定是渲染引擎里最容易出问题的部分。我的做法是:为每个字段生成一个数据路径,比如formData.name、formData.department,然后用平台自带的状态管理机制做双向绑定。
对于复杂的嵌套结构,比如“起止时间”这种一个字段对应两个值的,我会在数据模型里把它拆成startTime和endTime两个字段,但在UI上用一个RangePicker来展示。提交时再把两个字段合并成一个时间范围对象。
数据校验也是绑定层要处理的。每个字段的required、type、options都会转换成对应的校验规则。提交时先跑一遍校验,有错误就在对应字段下方显示提示信息。
5.4 动作处理的扩展机制
动作处理我设计了一个处理器注册机制。每种动作类型对应一个处理器函数,比如submit处理器负责收集表单数据、校验、调用数据写入接口;export处理器负责把列表数据转成CSV或Excel下载。
这种机制的好处是可扩展。如果后续需要支持新的动作类型,比如“发送通知”、“触发工作流”,只需要注册一个新的处理器就行,不用改渲染引擎的核心逻辑。
处理器函数接收两个参数:当前的Schema配置和运行时上下文(包含表单数据、用户信息等)。返回值可以是一个Promise,渲染引擎会根据Promise的状态显示loading或提示信息。
6. 源码关键路径拆解:从入口到渲染完成
6.1 项目目录结构与模块划分
整个项目的目录结构大致如下:
ai-lowcode/ ├── packages/ │ ├── intent-parser/ # 意图解析层 │ │ ├── prompt/ # Prompt模板 │ │ ├── validator/ # 输出校验 │ │ └── index.ts # 入口 │ ├── schema-generator/ # Schema生成层 │ │ ├── templates/ # 页面模板 │ │ ├── mappers/ # 字段映射 │ │ └── index.ts │ ├── renderer/ # 渲染执行层 │ │ ├── components/ # 组件映射 │ │ ├── bindings/ # 数据绑定 │ │ ├── actions/ # 动作处理 │ │ └── index.ts │ └── shared/ # 共享类型和工具 │ ├── types.ts │ └── utils.ts ├── examples/ # 示例应用 └── package.json这种monorepo的组织方式让各层的边界很清晰,依赖关系也一目了然。intent-parser只依赖shared,schema-generator依赖shared和intent-parser的输出类型,renderer依赖shared和schema-generator的输出类型。没有循环依赖,每一层都可以独立测试。
6.2 意图解析的核心函数
意图解析的入口函数大致长这样:
async function parseIntent(userInput: string): Promise<AppIntent> { const prompt = buildPrompt(userInput); let lastError: Error | null = null; for (let attempt = 0; attempt < MAX_RETRIES; attempt++) { try { const rawOutput = await callLLM(prompt); const parsed = JSON.parse(rawOutput); const validated = validateIntent(parsed); return validated; } catch (err) { lastError = err; // 在重试时,把错误信息拼进Prompt,让模型自我修正 prompt = buildRetryPrompt(userInput, err.message); } } // 重试失败,返回兜底Schema return getFallbackIntent(); }这里有几个细节值得说。第一,重试时把错误信息拼进Prompt,让模型知道上次哪里错了,这样第二次成功的概率会高很多。第二,兜底策略保证即使模型完全失败,用户也能看到一个可用的空白页面,而不是一个报错弹窗。第三,最大重试次数设为2,再多就意义不大了,反而增加延迟。
6.3 Schema生成的关键映射逻辑
Schema生成的核心是一个映射函数:
function generateSchema(intent: AppIntent): AppSchema { const template = selectTemplate(intent.pageType); const fields = intent.fields.map(field => mapField(field)); const actions = intent.actions.map(action => mapAction(action)); return { ...template, title: intent.title, fields, actions, dataSource: inferDataSource(intent), }; } function mapField(field: IntentField): SchemaField { const mapper = fieldMappers[field.type] || fieldMappers.text; return { name: field.name, label: field.label, component: mapper.component, props: { ...mapper.defaultProps, ...(field.options ? { options: field.options } : {}), ...(field.required ? { required: true } : {}), }, }; }这段代码的关键在于映射表的可配置性。fieldMappers是一个对象,每个字段类型对应一个映射器。如果某个平台需要特殊的映射规则,只需要覆盖对应的映射器就行,不用改核心逻辑。
6.4 渲染引擎的启动流程
渲染引擎的启动流程大致如下:
function renderApp(schema: AppSchema, container: HTMLElement) { // 1. 初始化数据模型 const dataModel = initDataModel(schema.fields); // 2. 创建渲染上下文 const context = { schema, dataModel, actions: registerActions(schema.actions), }; // 3. 递归渲染组件树 const vnode = renderFields(schema.fields, context); // 4. 挂载到容器 mount(vnode, container); // 5. 绑定全局事件 bindGlobalEvents(context); }这里最复杂的是renderFields函数,它需要处理字段的嵌套、布局、联动等逻辑。我的实现里用了一个递归的渲染器,每个字段根据其类型找到对应的组件渲染函数,渲染函数返回虚拟节点,最后统一挂载。
6.5 一个完整的端到端示例
假设用户输入:“做一个客户信息登记表,包含客户名称、联系人、电话、邮箱、客户等级(普通、VIP、SVIP),提交后保存。”
经过意图解析,得到:
{ "pageType": "form", "title": "客户信息登记表", "fields": [ { "name": "customerName", "label": "客户名称", "type": "text", "required": true }, { "name": "contact", "label": "联系人", "type": "text", "required": true }, { "name": "phone", "label": "电话", "type": "text", "required": true }, { "name": "email", "label": "邮箱", "type": "text", "required": false }, { "name": "level", "label": "客户等级", "type": "select", "options": ["普通", "VIP", "SVIP"], "required": true } ], "actions": [ { "type": "submit", "label": "提交" } ] }经过Schema生成,得到平台配置:
{ "component": "Form", "props": { "layout": "vertical" }, "children": [ { "component": "Input", "props": { "label": "客户名称", "required": true }, "binding": "customerName" }, { "component": "Input", "props": { "label": "联系人", "required": true }, "binding": "contact" }, { "component": "Input", "props": { "label": "电话", "required": true }, "binding": "phone" }, { "component": "Input", "props": { "label": "邮箱" }, "binding": "email" }, { "component": "Select", "props": { "label": "客户等级", "options": ["普通", "VIP", "SVIP"], "required": true }, "binding": "level" }, { "component": "Button", "props": { "type": "primary", "label": "提交" }, "action": "submit" } ] }渲染引擎拿到这个配置,就能生成一个完整的表单页面。用户填写后点击提交,数据通过submit处理器写入后端。
7. 实测中踩过的坑与优化思路
7.1 模型输出不稳定:从“抽奖”到“可控”
最开始用大模型做意图解析,最大的感受就是“抽奖”。同样的输入,有时候输出完美,有时候缺字段,有时候字段类型搞错。后来我做了三件事,把稳定性从“看运气”提升到了“基本可控”。
第一,收紧输出空间。把Prompt里的JSON Schema写得非常具体,每个字段的类型、可选值、是否必填都明确列出,不给模型自由发挥的空间。第二,增加Few-shot示例。示例覆盖了各种边界情况,比如“用户没提字段类型怎么办”、“用户提了不支持的字段类型怎么办”。第三,加校验和重试。前面说过的校验层,配合重试机制,基本能兜住大部分异常。
优化之后,一次解析成功率从大概60%提升到了90%以上。剩下的10%,大部分是用户输入实在太模糊,需要多轮澄清。
7.2 字段类型推断错误:日期和数字最容易翻车
字段类型推断是错误高发区。最常见的是把“日期”识别成“文本”,把“数量”识别成“文本”。原因是用户描述里往往不会明确说“这是一个日期字段”,而是说“填写入职时间”,模型需要从“时间”这个词推断出日期类型。
我的优化方法是:在Prompt里加一条规则——“如果字段名或描述中包含‘时间’、‘日期’、‘年月日’等词,优先推断为date类型;如果包含‘数量’、‘金额’、‘人数’等词,优先推断为number类型”。这条规则加上之后,类型推断的准确率明显提升。
另外,对于“起止时间”这种范围类型的字段,我在Prompt里也加了专门的说明,让模型输出daterange而不是两个独立的date字段。这样渲染时就能直接用一个RangePicker组件,体验更好。
7.3 复杂布局的Schema表达:从单列到多列
一开始我的协议只支持单列布局,所有字段从上到下排列。但实际业务中,很多表单需要两列甚至三列布局,比如“姓名”和“电话”并排,“起止时间”占一整行。
为了支持这种需求,我在协议里加了layout字段和colSpan属性。layout可以是single-column、two-column、grid,colSpan表示字段占几列。渲染引擎根据这些配置计算每个字段的实际宽度。
这个改动看起来简单,但实际实现时需要考虑响应式。在小屏幕上,两列布局要自动变成单列,否则字段会挤在一起。我在渲染引擎里加了一个断点判断,屏幕宽度小于768px时强制单列布局。
7.4 数据源接入的坑:静态数据和API的差异
数据源接入是另一个容易出问题的地方。协议里我设计了dataSource字段,可以指定静态数据、API接口、或者数据库表。但实际接入时发现,不同数据源的加载时机和错误处理差异很大。
静态数据是同步的,渲染时直接就能用。API接口是异步的,需要处理loading状态和请求失败的情况。数据库表通常需要先查元数据,再查数据,链路更长。
我的处理方式是:在渲染引擎里为每种数据源类型实现一个适配器,适配器负责数据的加载、缓存、错误处理。渲染组件只关心数据是否就绪,不关心数据从哪来。这样新增一种数据源类型时,只需要加一个适配器,不用改渲染逻辑。
7.5 性能优化:Schema缓存和增量渲染
当页面字段比较多的时候,每次重新渲染整个表单会有明显的卡顿。我做了两个优化:
第一,Schema缓存。对于相同的用户输入,缓存解析和生成的Schema,下次直接复用,省去模型调用和映射的时间。缓存key用输入文本的哈希值,简单有效。
第二,增量渲染。当只有个别字段变化时,不重新渲染整个页面,只更新变化的字段。这需要渲染引擎支持细粒度的更新,实现起来稍微复杂一些,但对大表单的体验提升很明显。
这两个优化加上之后,从输入到页面呈现的时间从最初的十几秒降到了三到五秒,基本做到了“说完就能看到”。
8. 这套方案还能怎么扩展
8.1 从单页应用到多页应用
目前这套方案主要解决的是单页应用的生成。但实际业务中,很多需求是多个页面组成的,比如“列表页点击进入详情页,详情页可以编辑”。要支持这种场景,需要在协议里增加页面间跳转和数据传递的描述。
我的思路是:在协议里加一个pages数组,每个页面有自己的Schema,页面之间通过navigate动作连接。navigate动作可以携带参数,比如把列表页选中行的ID传到详情页。渲染引擎需要支持路由管理,根据当前页面和参数渲染对应的Schema。
这个扩展已经在规划中,核心难点在于页面间的数据流管理,以及如何让AI理解“列表页”和“详情页”之间的关系。
8.2 接入更多低代码平台
目前我只对接了一个低代码平台,但协议本身是平台无关的。要接入新的平台,只需要写一套新的映射规则。我计划把映射规则做成插件化的,每个平台一个插件包,通过配置切换。
插件化的好处是社区可以贡献不同平台的适配器,比如有人用A平台,有人用B平台,各自写各自的适配器,互不影响。协议本身保持稳定,生态就能慢慢长出来。
8.3 结合可视化编辑器做二次调整
AI生成的Schema不可能100%符合用户预期,所以可视化编辑是必要的补充。用户可以在生成的页面上直接拖拽调整字段位置、修改字段属性、增删字段。调整后的Schema可以保存为模板,下次类似需求直接复用。
这块我目前只做了基础的字段属性编辑,拖拽布局还在开发中。我的体会是,可视化编辑器和AI生成不是替代关系,而是互补关系。AI负责从0到1快速生成初稿,可视化编辑器负责从1到1.1的精调。两者结合,才能真正让非技术用户也能做出符合自己需求的应用。
8.4 模板市场的可能性
当Schema积累到一定数量之后,可以形成一个模板市场。用户可以把调好的Schema发布出来,其他人可以直接复用。比如“员工入职登记表”、“客户跟进记录”、“报销申请单”这些常见场景,都可以有现成的模板。
模板市场的好处是降低使用门槛。新用户不需要从一句话开始,可以直接选一个接近的模板,改改字段就能用。AI的作用则变成“根据用户需求推荐合适的模板”,而不是每次都从零生成。
这个方向我觉得挺有意思,但需要一定的用户量才能跑起来。目前我先把基础能力做扎实,等有了一定用户基础再考虑。
9. 一些个人体会
折腾这套东西最大的感受是:AI和低代码的结合点,不在于让AI写代码,而在于让AI做“翻译”。把自然语言翻译成结构化配置,这个任务AI做得很好,而且输出可控。剩下的渲染、交互、数据绑定,交给成熟的低代码引擎,稳定可靠。
另一个体会是:协议设计比模型选型更重要。我花在Schema协议设计上的时间,远比调Prompt和选模型的时间多。但事实证明这个投入是值得的,协议设计好了,后面换模型、换平台都很轻松。
最后,别追求一步到位。我一开始想做一个“什么都能生成”的系统,结果什么都做不好。后来缩小范围,先只做“表单页生成”,把这一件事做透,再逐步扩展到列表页、详情页。这种渐进式的做法,比一开始就追求大而全要靠谱得多。
如果你也在做类似的事情,我的建议是:先从一个小场景切入,把链路跑通,再慢慢加功能。不要一上来就想着支持所有页面类型、所有组件、所有数据源,那样很容易陷入细节泥潭。先让一个最简单的表单能跑起来,你就已经成功了一半。