CodingTour
什么是 Generative UI

背景

Generative UI = 用 AI 把 “一句话需求” 实时翻译成可交互的界面,并可随用随改。

比如:

  • 说 “我要看北京天气” → 立刻出现天气卡片 — 这是根据自然语言即时渲染组件,动态界面生成
  • “再加一个素食选项”→ 立即出现勾选框 — 需求变了,界面秒级长出新输入框
  • 股票价刷新 → 走势图实时重绘 — 数据变,界面自动跟着变

在 Agent 产品中相当有用,想象一个股票助理 Agent,从看 “特斯拉股价” → 实时股价卡片 + 折线图 + 买卖按钮,可直接在 Agent 界面中下单。

这背后就引出了 Generative UI 的话题,实现上有些差异,可以大致分为工具驱动型 Generative UI 和 模型驱动型 Generative UI。

工具驱动型 Generative UI

典型代表:Amazon BedrockVercel AI SDK

LLM 不直接写代码,而是像「调度员」一样,判断要干啥 → 然后选工具 → 把工具返回的数据塞进现成的 UI 组件里:

从实现上看,Agent 系统的工具区分出了后端工具和前端工具,前端工具是一套严格定义的组件库,和后端工具一起作为 Agent 的供应链,只要前端有一个天气渲染组件,Agent 后端具备获取天气的能力,那么 Agent 就能在返回天气数据时把天气渲染组件的名称 or id 一起给到前端,前端通过组件名称 or id 实例化天气组件,然后用一个标准化的 result 填充进去即可。

前端工具 = 带 Schema 的函数:

export const weatherTool = createTool({
  description: 'Display the weather for a location',
  parameters: z.object({ location: z.string() }),
  execute: async ({ location }) => /* … */,
});

执行函数、React/Vue 组件渲染:

{message.toolInvocations?.map(inv => {
  if (inv.toolName === 'displayWeather' && inv.state === 'result') {
    return <Weather key={inv.toolCallId} {...inv.result} />;
  }
})}

从运行时上看,只有 “组合” 与 “数据填充”,不会 “现场造轮子”,扩展前端工具集就能扩展 UI 的表现层,加一个 stockTool → 渲染 <Stock/>,再加一个 calendarTool → 渲染 <Calendar/>,所有组件都能随对话即时出现。

工具驱动型 Generative UI 很像传统的 MVVM 架构,可以把它看成 MVVM 的 AI 时代升级版,差异主要体现在 “谁来写 ViewModel” 和 “谁来触发命令” 这两点上:

骨架没变(M-V-VM 三层仍在),VM 层被 LLM 动态拼装,从而让用户用自然语言就能 “零代码” 驱动界面。

模型驱动型 Generative UI

Generative and Malleable User Interfaces with Generative and Evolving Task-Driven Data Model

相比工具驱动型 Generative UI,模型驱动型 Generative UI 就属于 “现场造轮子” 了,它的理念是:与其使用固定的前端组件/代码,不如用 AI 先把 “任务” 抽象成可演化的数据模型,再由这个数据模型驱动 UI 界面的动态生成与随时修改:

  1. 模型先把需求转化成任务数据模型,明确有哪些实体、属性、关系、约束,例如 “我要办晚宴” → 会出现 Guest、Dish、ShoppingList 这些实体,以及 “忌口影响菜品” 这类依赖
  2. 模型再驱动 UI 生成,有了数据模型,系统按既定规则把实体映射成界面,Guest → 联系人卡片列表,Dish → 可勾选菜单,ShoppingList → 地图+清单
  3. 模型可随用户继续演化,用户随时用自然语言修改实体或数据(如 “再加两位素食朋友”),模型立即更新,界面跟着实时变化,无需重写代码

不得不说,思路和传统软件开发里的 Domain-Driven Design 是一样的,先基于需求理解建立领域模型(蓝图),再利用领域模型驱动 UI:

论文中的示例 — “给我一周膳食计划”,然后搞出了右边的 UI:

注:SVAL = 单值,ARRY = 数组,DICT = 键值,PNTR = 引用其他实体

先拆出实体模型:

实体属性示例(部分)
WeeklyMealPlanstart_date, end_date, daily_meal_plans(ARRAY)
DailyMealPlandate, recipes(ARRAY)
Recipename, calories, ingredients(ARRY), cuisine_type
Ingredientname, store(PNTR)
Storename, address

建立 Dependency Graph:

  • 校验型,如 Checkout 日期必须 ≥ Check-in
  • 更新型,如 Ingredient 数量变 → Recipe 总热量自动重算

从领域模型到 UI,第一步,先标注,每个属性被打上 <function, render, editable> 标签:

  • start_date → <display, time, true> → 渲染成可编辑日历
  • guest_list → <display, expanded, true> → 直接展开联系人卡片

第二步,UI 控件映射:

标签组合对应 UI 控件
<display, number, true>数字输入框
<display, location, true>地图选点
<display, summary, true>折叠按钮 + 弹窗详情

pipeline 如下:

一个是做任务理解、领域建模的 LLM,一个是基于前者画 UI 的 LLM,再搭配一个前端的 UI 控件库。

两种方式对比

维度AI-SDK 做法(工具驱动)论文做法(模型驱动)
新增字段/实体必须事先把 tool 规范(函数 + React 组件)写好;运行时 LLM 只能 “调用” 工具,不能 “发明” 工具LLM 先把需求抽象成可演化的数据模型(Schema),再按规则自动映射出 UI;字段可随时增删
UI 组件来源开发者预先写死 <Weather/><Stock/> 等,LLM 仅决定 “用哪一个”系统根据数据类型和规则自动生成或组合组件,无需提前枚举
扩展成本每新增一个实体(如 “忌口”)和展现形式时,要加或者更新 Tool复用或更新可映射的 UI 控件库
适用场景垂直类、确定型任务(天气、股票、航班查询)开放式任务(办晚宴、做研究、旅行规划)

两种方式都没办法完全由 LLM 在运行时造轮子,但模型驱动的扩展性稍微更好一些:

  • 工具驱动型 UI 控件,是业务组件粒度;模型驱动型 UI 控件,是更细粒度的「渲染规则」
  • 前端有大量可直接复用的组件库,比如国内的 Ant Design,不用人为写组件和造控件级别的轮子

所以只要「任务理解到位建模 ok懂 UI 设计」,LLM 按「数据类型 + 展示意图」自动拼装出 React/Vue 组件即可。

但问题是,要做好「任务理解到位建模 ok懂 UI 设计」这三件事也并不容易,特别是 UI 设计不仅是能看、能用,还得满足公司设计团队的要求,所以也不简单。