引言
当你向 ChatGPT 提问,你得到的是文本——有时有格式,有时没有,但本质上是渲染在聊天气泡中的一串字符。这种人机交互模式已经开始显得过时。
Generative UI 是一种新范式:AI 系统不只返回文本,而是返回渲染好的界面组件。让它展示销售数据,它会生成一个交互式图表。让它帮你预订航班,它会内联渲染一个预订表单。让它总结合同,它会生成一张带可展开条款的结构化卡片。
这一转变之所以重要,是因为文本是最低公分母媒介。用文字描述一个数据集,永远不如对该数据进行精心可视化来得有效。GenUI 通过赋予 AI 访问现代 UI 工具包完整词汇的能力,弥合了 AI 所知与其有效表达之间的差距。
本指南涵盖 Generative UI 的技术工作原理、当今主流实现框架、真实使用场景、客观权衡分析,以及你第一次实现的实用起点。
Generative UI 的工作原理
Generative UI 背后的机制是一条四阶段流水线。理解每个阶段对于在生产环境中调试和扩展 GenUI 系统至关重要。
1. LLM 生成结构化输出
不是提示 LLM 生成自由文本,而是提示它生成结构化数据——通过函数调用/工具调用,或指示模型输出 JSON。这个输出描述的是渲染什么,而非原始内容本身。
例如,模型不是返回「本季度营收为 120 万美元」,而是返回类似这样的内容:
{
"component": "RevenueChart",
"props": {
"period": "Q1 2026",
"value": 1200000,
"change": 0.14,
"chartType": "bar"
}
}2. 组件注册表将输出映射到 UI
客户端或服务端的组件注册表将组件名称映射到实际的 React、Vue 或 Svelte 组件。当模型输出 "component": "RevenueChart" 时,注册表将其解析为真实的 RevenueChart 组件,传入 props 并渲染。
注册表是关键的安全与质量边界。你决定哪些组件对模型可用——它只能渲染你明确注册的内容。这与让 LLM 生成任意 HTML 有根本区别,后者既危险又不可预测。
3. 流式传输渐进式推送组件
最优秀的 GenUI 实现会在生成过程中流式推送组件数据。不必等待 LLM 完成整个响应,组件数据一旦就绪就会立即推送到客户端。这为用户提供了渐进式展示体验,即使对于复杂的多组件响应也感觉流畅。
React 的流式模型(通过 Server Components 和 Suspense)特别适合这种模式。Vercel AI SDK 的 streamUI 原语正是基于此构建的。SSE(Server-Sent Events)是一种更简单的替代方案,适用于任何框架。
4. UI 渲染,用户交互
渲染完成后,组件的行为与其他任何 UI 组件完全相同——它们可以持有内部状态、调用 API、分发事件,并触发进一步的 LLM 调用。这使得多轮「智能体式」UI 成为可能,AI 与用户通过一系列渲染界面进行迭代协作。
主流框架
GenUI 生态系统已围绕少数几个成熟框架形成共识。以下是截至 2026 年初主要选项的客观比较。
| 框架 | 语言 | 核心特性 | 许可证 | Stars |
|---|---|---|---|---|
| Vercel AI SDK | TypeScript / React | streamUI + React Server Components | Apache 2.0 | 13k+ |
| CopilotKit | TypeScript / React | Headless hooks, in-app copilot | MIT | 17k+ |
| Thesys | TypeScript (any framework) | Framework-agnostic component protocol | Apache 2.0 | 2k+ |
| Custom SSE | Any | Full control, no dependencies | N/A | N/A |
Vercel AI SDK
Vercel AI SDK 是 React/Next.js 团队中采用最广泛的 GenUI 框架。其 streamUI 函数允许你定义一个工具映射,每个工具在同一位置指定加载骨架、最终组件和 LLM 工具定义。框架自动处理流式传输、hydration 和 Suspense 边界。
该 SDK 倾向于 React Server Components,这使其对 Next.js 应用极为强大,但在此之外的场景中人体工程学稍差。它通过统一接口支持所有主流 LLM 提供商。
CopilotKit
CopilotKit 采取不同思路,专注于在现有应用中嵌入「copilot」,而非从零构建以聊天为中心的界面。它提供无头 React hooks(useCopilotAction、useCopilotReadable),让 AI 读取你的应用状态并触发操作——包括在操作响应中渲染 UI 组件。
CopilotKit 特别适合内部工具和仪表板场景,适用于希望在不重建现有界面的情况下添加 AI 辅助功能。
Thesys(原 LivebenchAI)
Thesys 是 GenUI 领域的新兴入场者,提供框架无关的方案。它不依赖 React 的流式原语,而是使用自己的跨框架组件协议。这使其成为 Vue、Svelte 或框架无关环境的实用选择。权衡在于其生态系统和社区相比 Vercel SDK 更小。
自定义 SSE 实现
对于有特定需求的团队——特定框架、现有流式基础设施或严格的延迟预算——使用 Server-Sent Events 和手写组件注册表的自定义实现是合理的选择。核心模式直接明了:服务端通过 SSE 输出 JSON token,客户端将其解析为组件描述符,注册表将其解析为真实组件。
自定义实现提供最大控制权,但需要你构建和维护框架开箱即提供的流式基础设施、错误恢复和类型安全。
使用场景
Generative UI 在最优 UI 呈现方式因用户输入或意图而显著不同的场景中价值最大。以下是最清晰的生产使用场景。
数据可视化与分析
用户询问「展示本月转化漏斗的变化」。传统聊天机器人返回一张 markdown 表格。GenUI 系统返回一个带下钻功能的交互式漏斗图——因为模型可以判断这份数据最适合用漏斗图呈现,并渲染正确的组件。
这是 GenUI ROI 最高的场景。对于分析数据,文本与精心选择的可视化之间的差距是巨大的。
富响应的对话式界面
当 AI 能够用预订表单、产品卡片、确认对话框和交互式问卷来响应,而非大段说明文字时,客户支持、引导流程和产品探索都会发生根本性改变。响应完成率提升,因为所需操作是直接呈现的,而非描述的。
表单生成与多步工作流
无需预先构建所有可能的表单排列,GenUI 系统可以根据用户表达的需求生成适当的表单。保险录入根据不同险种生成不同字段。费用报销根据员工角色和费用类别调整其结构。原本需要数周设计的表单可以按需生成。
内容创作工具
当 AI 的输出应在上下文中展示(而非在独立聊天界面中)时,写作助手、代码生成工具和设计系统都能从 GenUI 中受益。文档编辑器中 AI 内联插入格式化的引用块。代码助手内联渲染 diff 视图。设计工具在代码旁生成组件预览。
内部工具与管理面板
内部工具或许是最被低估的场景。工程师和运营团队经常需要临时界面来查询数据、执行操作或审查记录。GenUI 系统可以在查询时生成适当的 UI,而无需手动构建这些界面——结果是表格时显示表格,需要操作时显示表单,结果是单一实体时显示状态卡片。
优势
减少数据密集型 UI 的开发时间
GenUI 系统生成的每一个数据可视化或自定义表单,都是工程师无需手动构建的。对于 UI 变化性高的产品——需要数十种图表类型的仪表板、因用户群体而异的表单——这代表前端工作量的显著减少。
按用户上下文个性化界面
传统 UI 是为平均用户设计的。GenUI 允许界面本身适应每个用户正在做的事情和已知的内容。有经验的用户获得密集的数据表格;新用户获得带说明标注的引导式卡片。AI 从上下文推断适当的细节层次。
超越纯文本的丰富 AI 响应
文本是有损的。用文字描述图表,永远比不上直接展示图表。GenUI 弥合了 AI 所理解的与其能表达的之间的语义差距。当 AI 知道数据的结构,它可以为其选择最合适的视觉编码。
复杂性的渐进式呈现
通过让 AI 在正确时机呈现正确的细节层次,复杂系统可以变得平易近人。组件可以从摘要折叠状态开始,按需展开。下钻导航可以根据用户关注点动态生成。这在静态 UI 中很难实现,除非构建显式状态机;GenUI 使其自然涌现。
挑战
GenUI 确实强大,但它带来了真实的工程权衡,在承诺这一模式之前值得充分理解。
测试复杂度
测试传统 UI 有成熟方法:用给定 props 渲染组件,断言输出。测试 GenUI 更难,因为渲染输出取决于 LLM 输出,而后者是非确定性的。你需要一套覆盖组件注册表(每个注册组件的单元测试)、LLM 集成(使用模拟响应的快照测试)以及端到端流程(将 LLM 视为黑盒、使用受控输入的集成测试)的测试策略。
流式传输的性能开销
流式组件在首次有效绘制时引入延迟:用户在 LLM 开始输出第一个组件描述符之前看不到任何内容。这通常优于等待完整页面加载,但需要谨慎处理加载状态和骨架屏,以避免令人不安的「空白闪现」体验。首 token 时间因 LLM 提供商和模型大小而差异显著。
无障碍性挑战
动态生成的 UI 带来真实的无障碍性挑战。流式更新期间的焦点管理、生成组件上有意义的 ARIA 标签,以及跨动态插入元素的键盘导航,都需要刻意的工程投入。除非你在设计组件注册表时就将无障碍性纳入考量,否则自动生成的组件不会内置良好的无障碍设计。
确定性与可重现性
LLM 是概率性的。相同的用户查询在不同请求中可能产生不同的组件选择。这通常是可取的——意味着 AI 能适应措辞的细微差异——但它使调试更难,当「正常运行」的流程突然呈现不同效果时,可能令用户困惑。将 temperature 设为 0 并使用精确的工具定义可以减少但不能消除这种不确定性。
快速入门
构建 GenUI 原型最快的路径是将 Vercel AI SDK 与 Next.js 结合使用。以下是演示核心模式的最小化示例。
安装依赖
npm install ai @ai-sdk/openai zod用 streamUI 定义服务端 action
// app/actions.tsx
'use server'
import { streamUI } from 'ai/rsc'
import { openai } from '@ai-sdk/openai'
import { z } from 'zod'
import { WeatherCard } from '@/components/WeatherCard'
import { StockChart } from '@/components/StockChart'
export async function chat(userMessage: string) {
const result = await streamUI({
model: openai('gpt-4o'),
messages: [{ role: 'user', content: userMessage }],
text: ({ content }) => <p>{content}</p>,
tools: {
showWeather: {
description: 'Show current weather for a location',
parameters: z.object({
location: z.string(),
unit: z.enum(['celsius', 'fahrenheit']).default('celsius'),
}),
generate: async ({ location, unit }) => {
// Fetch real data here
const data = await fetchWeather(location, unit)
return <WeatherCard {...data} />
},
},
showStockChart: {
description: 'Show a stock price chart',
parameters: z.object({
ticker: z.string(),
period: z.enum(['1d', '1w', '1m', '3m', '1y']),
}),
generate: async ({ ticker, period }) => {
const data = await fetchStockData(ticker, period)
return <StockChart ticker={ticker} data={data} />
},
},
},
})
return result.value
}在 React 组件中流式输出响应
// app/chat/page.tsx
'use client'
import { useState } from 'react'
import { readStreamableValue } from 'ai/rsc'
import { chat } from '../actions'
export default function ChatPage() {
const [messages, setMessages] = useState<React.ReactNode[]>([])
const [input, setInput] = useState('')
async function handleSubmit(e: React.FormEvent) {
e.preventDefault()
const userMessage = input
setInput('')
const response = await chat(userMessage)
setMessages(prev => [...prev, response])
}
return (
<div>
<div className="messages">
{messages.map((msg, i) => (
<div key={i}>{msg}</div>
))}
</div>
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={e => setInput(e.target.value)}
placeholder="Ask anything..."
/>
<button type="submit">Send</button>
</form>
</div>
)
}这个模式的关键洞察在于 tools 对象。每个条目定义了 LLM 工具(名称、描述、参数 schema)以及该工具被调用时渲染的 React 组件。模型根据用户输入决定调用哪个工具;你的代码决定渲染什么。
对于 Vue/Nuxt 环境,同样的模式可以通过 SSE 和手动组件注册表实现。如果你不想从头构建,Thesys 框架为此提供了更高层次的抽象。