大家周一早。
最近在做 AI 项目的前端同学,一定遇到过这个痛点:老板说:“用户查天气的时候,AI 别干巴巴地说‘25度’,给我渲染一个带有动画的、能切换城市的天气卡片组件!”
以前我们怎么做?
- 强迫大模型输出特定格式的 JSON(比如
{ "type": "weather", "temp": 25 })。
- 前端写一堆
if-else去解析这个 JSON。
- 如果 JSON 没截断或者格式错了,前端直接白屏报错。
这种写法极其脆弱。而在 2026 年,借助于 React Server Components (RSC) 和 Vercel AI SDK,我们有了一种极其优雅的降维打击方案:Generative UI(生成式 UI)。
今天,我们来看看到底什么是“让 AI 直接写 UI”。
01. 概念颠覆:什么是 Generative UI?
传统聊天:
- 用户:北京天气如何?
- AI:String->“北京今天晴,25度。”-> 前端渲染。
生成式 UI:
- 用户:北京天气如何?
- AI:Component->-> 前端直接渲染出可交互的组件。
这意味着,聊天框不再是一个文本流,而是一个无限延伸的动态网页。用户可以直接在聊天记录里点击按钮、拖拽图表、甚至完成支付。
02. 核心原理:大模型凭什么懂组件?
大模型当然不懂 React,它只懂文字。
生成式 UI 的底层依然是 Function Calling(函数调用)。只不过我们把“工具执行的结果”换成了“一个 UI 组件”。
Vercel AI SDK 内部逻辑:
- 告诉 AI:“你有一个叫
showWeather的工具,参数是city”。
- AI 决定调用
showWeather({ city: 'Beijing' })。
- 关键点来了:在服务端的工具执行完毕后,服务端不返回 JSON,而是直接返回一个序列化后的 React Server Component。
- 前端收到组件,直接挂载渲染。
03. 实战:手写一个生成式天气卡片
我们使用 2026 年最主流的 Next.js App Router + Vercel AI SDK 来演示。
第一步:准备你的 UI 组件 (前端)
这是一个普通的 React 组件,带点 Tailwind 样式。
// components/WeatherCard.tsx
export function WeatherCard({ city, temp, condition }) {
return (
<div className="p-4 border rounded-xl bg-blue-50 shadow-sm w-64">
<h2 className="text-xl font-bold">{city}</h2>
<div className="flex justify-between items-center mt-2">
<span className="text-3xl">{temp}°C</span>
<span>{condition === ‘Sunny’ ? ‘☀️’ : ‘🌧️’}</span>
</div>
<button className="mt-4 text-sm text-blue-500">查看未来一周预报</button>
</div>
);
}
第二步:服务端定义 streamUI (全栈魔法)
当用户提问时,我们在服务端处理逻辑,并直接将组件 Stream 给前端。
// actions.ts (Server Action)
‘use server’;
import { streamUI } from ‘ai’;
import { openai } from ‘@ai-sdk/openai’;
import { z } from ‘zod’;
import { WeatherCard } from ‘./components/WeatherCard’;
export async function askAI(question: string) {
// 🌟 神奇的 streamUI 函数
const result = await streamUI({
model: openai(‘gpt-4o’),
prompt: question,
text: ({ content }) => <div>{content}</div>, // 普通文本对话
tools: {
// 告诉 AI 这个工具用来展示天气
showWeather: {
description: ‘获取指定城市的天气并展示 UI 卡片’,
parameters: z.object({
city: z.string(),
}),
// 🌟 核心:generate 函数直接返回一个 React 组件!
generate: async function ({ city }) {
// 模拟查数据库/API 获取真实数据
const temp = Math.floor(Math.random() * 30);
const condition = temp > 15 ? ‘Sunny’ : ‘Rainy’;
// 直接返回刚才写的 UI 组件
return <WeatherCard city={city} temp={temp} condition={condition} />;
},
},
},
});
return result.value; // 返回流式 UI
}
第三步:前端页面展示
// page.tsx (Client Component)
‘use client’;
import { useState } from ‘react’;
import { askAI } from ‘./actions’;
export default function Chat() {
const [messages, setMessages] = useState<React.ReactNode[]>([]);
const [input, setInput] = useState(‘’);
return (
<div>
{/* 渲染消息列表 (里面可能是文本,也可能是 WeatherCard 组件!) */}
{messages.map((msg, i) => <div key={i}>{msg}</div>)}
<form onSubmit={async (e) => {
e.preventDefault();
// 1. 把用户的问题 push 到列表 (这里用文本展示)
setMessages(m => [...m, <div>{input}</div>]);
// 2. 调用后端 Action
const responseUI = await askAI(input);
// 3. 把后端返回的 UI 组件直接 push 进列表!
setMessages(m => [...m, responseUI]);
}}>
<input value={input} onChange={e => setInput(e.target.value)} />
<button>发送</button>
</form>
</div>
);
}
体验一下:
用户输入:“北京天气怎么样?”
屏幕上不会出现生硬的 Markdown 文本,而是直接凭空“刷”出了一个带有按钮、有温度、排版精美的卡片组件!
04. Vue 开发者怎么办?
细心的同学发现了,这套 streamUI 是严重依赖 React Server Components (RSC) 生态的。
那 Vue/Nuxt 开发者怎么玩 Generative UI?
目前社区的解法是:动态组件映射。让 AI 返回标准化的 JSON 结构(如{ component: “WeatherCard”, props: {...} }),前端维护一个组件注册表(Component Registry),利用 Vue 的 <component :is=“…”> 进行动态渲染。虽然没有 React 原生支持那么丝滑,但也能实现同样的用户体验。
结语
从“纯文本”到“结构化 JSON”,再到“可交互 UI”。Generative UI 彻底打破了我们对 Chatbot 的刻板印象。它让 AI 助理真正变成了一个个按需生成的“微型 App”。对于想深入探索更多前沿技术实现和案例的朋友,可以到云栈社区 的开发者论坛交流讨论。