智能交互新范式:拒绝“黑盒”,带你用 MateChat 与 DSL 构建“高可靠”的 NL2UI 引擎

目录

前言

[一、 架构思考:为什么我们需要一个"中间商"?](#一、 架构思考:为什么我们需要一个“中间商”?)

[二、 核心实现:给 AI 立规矩,教它说"DSL"](#二、 核心实现:给 AI 立规矩,教它说“DSL”)

[三、 渲染引擎:把 JSON 变现为 DevUI 组件](#三、 渲染引擎:把 JSON 变现为 DevUI 组件)

[四、 价值闭环:不仅能看,还能"带走"](#四、 价值闭环:不仅能看,还能“带走”)

[五、 场景演示:MateChat 的"双面"能力](#五、 场景演示:MateChat 的“双面”能力)

[六、 总结与展望](#六、 总结与展望)

前言

大家在做后台系统开发时,有没有遇到过这样的场景?运营同事跑过来说:"我想要个简单的库存报表,这就这几个字段,能不能马上弄好?"

这时候,你看着手头堆积如山的需求,心里可能在想:要是能直接跟电脑说一句"给我个库存表",界面就能自己长出来该多好啊!

这就是 NL2UI (Natural Language to User Interface) 的终极梦想------用自然语言直接生成界面。但说实话,让 AI 直接写 Vue 代码稍微有点"吓人",代码质量不可控不说,改起来还费劲。

今天,我们换个思路。我们不追求一步到位的"全自动",而是基于 华为云 DevUI MateChat 组件,打造一个"受限但绝对可靠"的 UI 生成引擎。

我们会用一套自己定义的 JSON DSL(领域特定语言) 作为中间层,让 AI 做"填空题",而不是"作文题"。这样既利用了 AI 的理解能力,又保证了生成的界面是 100% 可用的。

为了方便大家验证,我把这个引擎的完整代码都开源了。大家可以去 GitCode 仓库 https://gitcode.com/kaminono/MateChatNL2UIEngine 看看源码,或者直接点这个 https://mate-chat-nl-2-ui-engine-components.vercel.app/ 在线体验一下"说话变界面"。

一、 架构思考:为什么我们需要一个"中间商"?

在动手写代码之前,我们得先定个基调。要实现 NL2UI,我们面临两个选择:是让 AI 直接吐出 Vue 代码,还是让它生成一个 JSON 数据?

我们坚定地选择了后者。直接生成代码就像是"开盲盒",你永远不知道 AI 会不会引入什么奇怪的依赖或者写出有安全漏洞的逻辑。

而生成 JSON DSL 就稳妥多了。我们把 DevUI 的组件------比如 d-card、d-form、d-chart------看作是乐高积木。我们只允许 AI 挑选这些积木来搭建页面。

我们可以把整个流程看作一个流水线:

  1. Input : 用户在 MateChat 输入自然语言。
  2. Reasoning : LLM 基于 System Prompt 进行意图识别,转化为标准 JSON。
  3. Parser : 前端引擎拦截消息,正则清洗数据,校验 JSON 合法性。
  4. Render : 递归组件读取 JSON,动态映射为 DevUI 组件。

这种"控制反转"的设计,是我们保证系统高可靠性的基石。

二、 核心实现:给 AI 立规矩,教它说"DSL"

搞定了架构,我们来看看核心代码是怎么实现的。这个引擎的"大脑"在 useNlParser.ts 文件里。

我们需要利用 Prompt Engineering(提示词工程),把我们的 DSL 语法"喂"给大模型。我们得明确告诉它:你只能用白名单里的组件,输出格式必须是标准的 JSON。

看看这段真实的代码,我们在 System Prompt 里做了非常严格的约束:

复制代码
// playground/src/nl2ui-engine/composables/useNlParser.tsconst systemPrompt = ``
`你是一个专业的前端 UI 构建专家。你的任务是将用户的自然语言需求转换为特定的 UI DSL (JSON 格式)。`

`### 🔴 严禁使用不存在的组件!只能使用以下白名单:`
`1. 布局: "d-row", "d-col"`
`2. 容器: "d-card" (必须包含 children), "d-form" (children 必须是 d-form-item)`
`3. 表单项: "d-form-item" (props: label), "d-input", "d-select", "d-button"`
`4. 图表: "simple-stat", "simple-chart"`

`### 输出格式规范 (JSON)`
`必须严格遵守以下 JSON 结构,不要包含 markdown 代码块标记:`
`{`
`  "page": { "title": "页面标题", "layout": "grid" | "default" },`
`  "components": [`
`    {`
`      "component": "d-card",`
`      "props": { "title": "卡片标题" },`
`      "children": [ ... ]`
`    }`
`  ]`
`}`
``;`
`

通过这种方式,无论用户怎么描述需求,AI 最终吐出来的都是我们能看懂、能渲染的标准数据。这就像是给 AI 戴上了"紧箍咒",让它的创造力在规则的轨道上运行。

在实际开发中,LLM 经常会在返回的 JSON 外面包裹 Markdown 标记(如 ```json ... ```)。如果不处理,JSON.parse 必挂。 我们在解析层做了一层"清洗":

复制代码
// 解析逻辑片段`
`const parseResponse = (content: string) => {`
`  // 1. 利用正则提取最外层的 {} 内容,去除废话和 markdown 符号`
`  const jsonMatch = content.match(/\{[\s\S]*\}/);`
`  if (!jsonMatch) return null;`
  
`  try {`
`    return JSON.parse(jsonMatch[0]);`
`  } catch (e) {`
`    console.error("JSON 解析失败,AI 生成了非法格式", e);`
`    // 这里甚至可以触发一个重试机制`
`    return null;`
`  }`
`}`
`

三、 渲染引擎:把 JSON 变现为 DevUI 组件

拿到了 JSON 数据,下一步就是把它变成真实的界面。我们在 DslRenderer.vue 里实现了一个递归渲染器。

这个组件的设计非常巧妙,它利用了 Vue 的 h() 函数和 defineAsyncComponent。我们建立了一个组件注册表,按需加载 DevUI 的组件。

这里有个关键点:对于 AI 可能产生的"幻觉"(比如生成了不存在的组件),我们做了兜底处理。

复制代码
// playground/src/nl2ui-engine/components/DslRenderer.vue// 1. 建立组件白名单映射const componentRegistry: Record<string, any> = {`
`  'd-card': defineAsyncComponent(() => import('vue-devui/card')),`
`  'd-form': defineAsyncComponent(() => import('vue-devui/form')),`
`  'd-input': defineAsyncComponent(() => import('vue-devui/input')),`
`  // ... 其他组件`
`};`

`// 2. 核心渲染函数const renderNode = (node: any): any => {`
`  // 兜底策略:如果 AI 生成了纯文本,直接渲染文本if (typeof node === 'string') return String(node);`

`  let Component = componentRegistry[node.component];`
  
`  // 错误处理:遇到未知组件,渲染一个红框提示,而不是让页面崩溃if (!Component) {`
`    return h('div', { style: 'border: 1px dashed red;' }, `[未知: ${node.component}]`);`
`  }`

`  // 递归渲染子节点const children = node.children?.map(renderNode);`
  
`  return h(Component, node.props, { default: () => children });`
`};`
`

这段代码保证了渲染器的健壮性。哪怕 AI 偶尔"发疯",我们的页面也不会白屏,开发者一眼就能看出是哪里出了问题。

递归渲染树 (The Recursive Magic)是引擎最精妙的地方。因为 UI 结构是树形的(Card 里有 Row,Row 里有 Col,Col 里有 Button),我们的渲染函数必须是递归的。

复制代码
const renderNode = (node: any): any => {`
`  if (typeof node === 'string') return node;`

`  const Component = componentRegistry[node.component];`
`  if (!Component) return h('div', { style: 'color:red' }, `[未知组件: ${node.component}]`);`

`  // 核心:处理 props 和 children`
`  // 1. 透传 AI 生成的属性 (如 label, placeholder)`
`  const props = { ...node.props };`

`  // 2. 递归构建子节点`
`  const children = node.children `
`    ? { default: () => node.children.map(renderNode) } // 插槽形式传递子节点`
`    : null;`

`  return h(Component, props, children);`
`};`
`

这段代码仅用十几行,就实现了理论上无限嵌套的 UI 构建能力。

四、 价值闭环:不仅能看,还能"带走"

如果只能在预览里看,那这个工具充其量只是个玩具。为了让它真正产生价值,我们必须实现"从对话到源码"的闭环。

试想一下,你让 AI 生成了一个复杂的表单,觉得效果不错。这时候,你肯定不想照着预览图再去手写一遍代码吧?

所以,我们开发了 useCodeGenerator.ts。它能把当前的 JSON DSL 逆向编译成标准的 Vue SFC(单文件组件)代码。

复制代码
// playground/src/nl2ui-engine/composables/useCodeGenerator.tsconst generateVueCode = (dsl: UiDsl) => {`
`  // 1. 逆向生成 Templateconst templateBody = dsl.components`
`    .map(node => generateTemplateNode(node, 2))`
`    .join('\n');`

`  // 2. 智能分析依赖,生成 Scriptconst imports = analyzeImports(dsl.components);`

`  // 3. 拼接成完整的 Vue 文件字符串return `<template>`
`  <div class="generated-page">`
`${templateBody}`
`  </div>`
`</template>`

`<script setup>`
`import { ${imports.join(', ')} } from 'vue-devui';`
`</script>`;`
`};`
`

在我们的 Demo 右侧,专门做了一个"查看源码"的 Tab。点击它,你就能复制这段生成的代码,直接粘贴到你的项目里。这才是真正的提效

五、 场景演示:MateChat 的"双面"能力

最后,我们看看这套系统在实际场景中的表现。我们设计了一个"左指令、右预览"的布局。

左边是大家熟悉的 MateChat 聊天窗口,它作为交互的入口。用户在这里输入自然语言,比如"帮我生成一个销售看板,要看总收入和活跃用户"。

MateChat 会显示"正在构建组件树...",几秒钟后,右边的预览区就会实时渲染出一个包含数据卡片和图表的 Dashboard。

如果你输入"创建一个用户注册表单,包含用户名和密码",右侧瞬间就会变成一个带有校验规则的 DevUI 表单。

这种"即问即答、即答即现"的体验,彻底改变了我们构建 UI 的方式。

六、 总结与展望

通过这个项目,我们验证了一个核心观点:受限的 DSL 反而是 AI 落地的最佳路径

我们没有追求让 AI 直接写出完美的代码,而是利用 MateChat 做交互,利用 DSL 做约束,利用 DevUI 做渲染。这套组合拳打下来,既保证了系统的稳定性,又发挥了 AI 的灵活性。

未来,这套架构还有很大的想象空间。比如,我们可以把 DSL 喂给后端,直接生成数据库模型;或者结合语音识别,实现"动动嘴做软件"的科幻场景。

希望这个开源项目能给大家带来一点启发,也欢迎大家来 GitCode 提 PR,我们一起把这个 NL2UI 引擎打磨得更强大!

附官方链接:

DevUI

MateChat - 轻松构建你的AI应用

相关推荐
橙子家2 小时前
浏览器缓存之【基础键值存储】:Local storage 和 Session storage
前端
星星在线4 小时前
MusicFree:一个「All in One」的个人音乐服务器,让听歌回归简单
前端·后端
IT_陈寒5 小时前
Redis的SETNX并发问题让我加了三天班
前端·人工智能·后端
demo007x5 小时前
Docling 文档转换以及技术架构分析
前端·后端·程序员
京东云开发者6 小时前
京东市民服务又“上新”!这次是黑龙江“龙易办”
前端
袋鱼不重7 小时前
我的神奇同事,AI 用多了居然写了个 Open In Codex
前端·后端·ai编程
大树887 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
施小赞7 小时前
普通 RAG vs GraphRAG 核心对比
人工智能·ai
Fireworks7 小时前
深入vue3源码解读 -- 1、响应式的基础概念
前端
程序员黑豆7 小时前
JDK 下载安装与配置详细教程
java·前端·ai编程