## 引言:从静态到自适应
想象一下,你走进一家餐厅,服务员不是递给你一本固定的菜单,而是问你:"今天想吃什么类型的菜?"然后根据你的回答,当场为你生成一张完全符合你口味和饮食偏好的菜单。这张菜单不是预先印好的,而是为你一个人、在这一刻、基于你的具体需求实时创造的。
这就是 Generative UI(生成式用户界面)的核心理念。
在传统的软件开发中,UI 是静态的。开发者预设好所有的按钮、表单、图表,用户只能在这些固定的选项中操作。就像一家餐厅只有一本固定的菜单,不管谁来、什么时候来、有什么特殊需求,看到的都是同样的东西。
但世界不是这样的。每个用户的需求都是独特的,每个场景都是动态变化的。
Generative UI 改变了这个范式。在这种新模式下,AI Agent 不再只是生成文字回复,它可以生成、选择或控制用户界面的部分或全部。Agent 可以发送 UI 状态、结构化的 UI 规范,甚至是完整的交互式 UI 块,前端实时渲染。界面从"开发者预定义的固定屏幕"变成了"随 Agent 工作和上下文变化而自适应的活界面"。
这不是科幻。CopilotKit 的 generative-ui 项目已经在 GitHub 上开源( https://github.com/CopilotKit/generative-ui ),他们的口号很直白:"Build apps that adapt to your users"(构建自适应用户的应用)。
但这里有一个关键问题:如果让 AI 完全自由地生成界面,会不会像给一个小孩一把油漆刷让他刷墙一样——虽然很有创造力,但结果可能难以预料?
答案是:控制与自由之间需要一个光谱。CopilotKit 将这个光谱划分为三种实践模式,每种模式对应不同的控制程度和应用场景。
让我带你一步步理解这三种模式,以及支撑它们的协议栈。这不是一个关于未来技术的遥想,而是正在发生的界面革命。
---
## 第一章:三种控制光谱——乐高、3D打印与自由雕塑
理解 Generative UI 的最佳方式,是把它想象成一个从"完全控制"到"完全自由"的光谱。在这个光谱上,CopilotKit 定义了三个主要位置,分别用三种制作物品的方式来比喻:**乐高积木**、**3D打印**和**自由雕塑**。
### 1.1 Controlled Generative UI:乐高积木模式
想象你在玩乐高。所有的积木块都是预先设计好的——红色的方块、蓝色的长条、绿色的平板,形状固定,颜色确定。你的创造力体现在**选择哪些积木**和**如何组合它们**,而不是发明新的积木形状。
这就是 **Controlled Generative UI(受控生成式 UI)** 的工作方式。
在这种模式下,开发者预先构建好所有的 UI 组件——天气卡片、股票行情图、审批流程表单。Agent 的工作不是"创造"组件,而是**选择**在此时此刻显示哪个组件,并传递它需要的数据。
举个例子。假设你正在开发一个天气助手应用。你预先设计了三个组件:
- `WeatherLoadingState`:显示"正在获取天气..."的加载动画
- `WeatherCard`:显示温度、湿度、风速的精美卡片
- `WeatherError`:显示错误信息和重试按钮
Agent 不需要知道这些组件长什么样、用什么颜色、字体多大。它只需要知道:当用户问"北京天气怎么样"时,调用 `get_weather` 工具,把"北京"作为参数传进去。剩下的——显示加载状态、展示结果、处理错误——都由前端根据组件定义自动处理。
代码长这样:
```typescript
useFrontendTool({
name: "get_weather",
description: "获取指定位置的天气信息",
parameters: z.object({
location: z.string().describe("要查询天气的城市或位置")
}),
handler: async ({ location }) => {
// 调用天气 API
return getWeatherData(location);
},
render: ({ status, args, result }) => {
// 根据状态渲染不同的预定义组件
if (status === "inProgress") {
return <WeatherLoadingState location={args?.location} />;
}
if (status === "complete" && result) {
const data = JSON.parse(result);
return (
<WeatherCard
location={data.location}
temperature={data.temperature}
conditions={data.conditions}
humidity={data.humidity}
/>
);
}
if (status === "error") {
return <WeatherError />;
}
},
});
```
这种模式的优势很明显:
- **完全控制**:开发者决定用户可以看到的所有界面元素
- **品牌一致性**:所有组件都遵循统一的设计系统
- **安全性高**:没有执行任意代码的风险
- **性能可预测**:预编译的组件,渲染速度有保障
代价是**自由度低**。Agent 只能在开发者预设的选项中选择,不能创造新的界面形式。如果用户问了一个你没想到的问题,Agent 可能只能回复纯文本,而不是一个精心设计的可视化组件。
这就像是乐高说明书上的模型——如果你只有这些积木,就只能拼出说明书上的东西。想拼一只长颈鹿但说明书上没有?抱歉,只能近似模拟。
**适用场景**:天气卡片、股票行情、审批流、任何需要严格控制品牌体验的场景。
### 1.2 Declarative Generative UI:3D打印模式
现在想象 3D 打印。你不是从固定的积木中选择,而是有一个设计文件(比如一个 .stl 文件),描述了一个物体的三维结构。3D 打印机读取这个文件,逐层构建出物体。你可以打印任何形状——只要设计文件描述得出来。
这就是 **Declarative Generative UI(声明式生成式 UI)** 的工作方式。
在这种模式下,Agent 不再只是选择预定义的组件,而是**生成一个结构化的 UI 描述**——用 JSON 或类似的格式,描述"这里应该有一个卡片,卡片里有一个标题、一段文字和一个按钮"。前端读取这个描述,用本地的组件库把它渲染出来。
关键的协议有两个:
**A2UI(Agent-to-User Interface)**:Google 提出的基于 JSONL(JSON Lines)的声明式 UI 规范。它定义了一套组件词汇表——Card、Text、Button、TextField、List 等等。Agent 用这些词汇来描述界面,但不涉及具体的样式和实现细节。
举个例子,A2UI 的描述可能是这样的:
```json
{"surfaceUpdate": {"surfaceId": "review-form", "components": [
{"id": "title", "component": {"Text": {
"usageHint": "h2",
"text": {"literalString": "请评价您的体验"}
}}},
{"id": "rating", "component": {"TextField": {
"label": "评分 (1-5)",
"inputType": "NUMBER"
}}},
{"id": "submit", "component": {"Button": {
"text": "提交评价",
"action": {"name": "submit_review"}
}}}
]}}
```
这段 JSON 没有说"用蓝色背景、圆角 8px、字体大小 16px"。它只是说:"这里有一个表单,包含一个标题、一个数字输入框和一个提交按钮。"具体长什么样,由前端决定——React 用 React 组件渲染,Flutter 用 Flutter widget 渲染,SwiftUI 用 SwiftUI 视图渲染。
**Open-JSON-UI**:OpenAI 内部声明式 UI 模式的标准化版本。原理类似,也是用 JSON 描述界面结构。
这种模式的优势:
- **平衡灵活**:Agent 可以创建动态界面,不需要为每个场景预定义组件
- **跨平台**:同一个描述可以在 Web、iOS、Android 上渲染出原生风格的界面
- **安全可控**:只是数据描述,没有代码执行风险
- **LLM 友好**:JSON 结构对语言模型来说很容易生成
代价是 **需要更多协调**。Agent 需要知道有哪些组件可用(组件目录),前端需要实现这些组件的渲染逻辑。如果 Agent 描述了一个前端不认识的组件类型,就会失败或降级。
**适用场景**:餐厅查找器(根据搜索结果动态生成卡片列表)、动态表单(根据对话上下文生成不同的输入字段)、数据可视化(根据数据类型选择图表类型)。
### 1.3 Open-ended Generative UI:自由雕塑模式
最后想象自由雕塑。你有一团黏土,可以捏成任何形状。没有预设的积木,没有设计文件的约束,只有你的想象力和双手的限制。
这就是 **Open-ended Generative UI(开放式生成式 UI)** 的工作方式。
在这种模式下,Agent(或 Agent 调用的服务)返回**完整的 UI 表面**——通常是 HTML,有时是一个 iframe 的 URL。前端基本上只是一个容器,负责把这个外部提供的界面显示出来。
这种模式的核心协议是 **MCP Apps(Model Context Protocol Apps)**。MCP 原本是一个让 AI 模型调用外部工具的协议(比如让 Claude 查询数据库、操作文件系统)。MCP Apps 扩展了这个协议,允许 MCP 服务器"运送"交互式 HTML UI。
流程是这样的:
1. Agent 决定调用一个工具(比如"创建 Excalidraw 图表")
2. 这个工具声明了一个 UI 资源(通过 `ui://` URI 方案)
3. 前端从服务器获取这个 UI 资源(一个 HTML 页面)
4. 前端在一个**沙箱化的 iframe** 中渲染这个 HTML
5. 用户可以与这个 iframe 中的界面交互(比如编辑图表)
6. iframe 通过 JSON-RPC 与宿主应用双向通信
代码示例:
```typescript
const agent = new BuiltInAgent({
model: "openai/gpt-5",
prompt: "你是一个有用的助手,可以帮用户创建图表。",
}).use(
new MCPAppsMiddleware({
mcpServers: [{
type: "http",
url: "https://mcp.excalidraw.com/mcp",
serverId: "excalidraw",
}],
}),
);
```
当 Agent 调用 `create_view` 工具时,Excalidraw MCP 服务器返回一个交互式的图表编辑器界面,用户可以直接在聊天窗口中编辑图表。
这种模式的优势:
- **极高自由度**:可以创建任何类型的界面,包括复杂的交互式模拟、3D 可视化、自定义控件
- **服务器端控制**:服务器可以完全控制 UI 的外观和行为
- **快速迭代**:不需要更新客户端,服务器端的 UI 可以随时更新
代价是**安全风险**和**性能考虑**:
- **安全性**:虽然 iframe 是沙箱化的,但运行外部 HTML 总是有风险的。CSP(内容安全策略)和权限控制至关重要。
- **性能**:iframe 加载需要时间,可能比本地组件慢
- **一致性**:外部 UI 可能与宿主应用的设计风格不一致
- **可移植性**:高度依赖 Web 技术,在非 Web 平台(如原生移动应用)上实现更复杂
**适用场景**:航班预订(复杂的日期选择、座位图)、交易模拟器(实时图表、下单界面)、Excalidraw 图表编辑、任何需要高度定制化界面且无法被预定义组件覆盖的场景。
### 1.4 三种模式的对比
| 维度 | Controlled (乐高) | Declarative (3D打印) | Open-ended (自由雕塑) |
|------|------------------|---------------------|----------------------|
| **控制方** | 开发者完全控制 | 共享控制 | 服务器/Agent 控制 |
| **自由度** | 低 | 中 | 高 |
| **协议** | AG-UI | A2UI, Open-JSON-UI | MCP Apps |
| **输出形式** | 预定义组件 | JSON 描述 | HTML / iframe |
| **安全性** | 高 | 高 | 中(需要沙箱) |
| **性能** | 最优 | 好 | 中(iframe 加载) |
| **适用场景** | 天气、股票、审批 | 动态表单、列表 | 复杂交互、可视化 |
### 1.5 第四种模式:Open Generative UI(补充说明)
CopilotKit 还提到了第四种模式,叫做 **Open Generative UI**,通过 `useComponent` hook 实现。
这种模式与 MCP Apps 类似,都是 Agent 生成 HTML,但有一个关键区别:**没有服务器往返**。在 MCP Apps 中,Agent 调用远程服务器的工具,服务器返回 UI。而在 Open Generative UI 中,LangGraph Agent 直接生成原始 HTML/SVG/Canvas 字符串,通过工具调用直接传递给前端。
这就像是即兴表演——演员(Agent)直接创造内容,不需要舞台工作人员(服务器)的帮助。适合算法可视化、数学绘图、简单的交互式模拟等场景。
---
## 第二章:协议栈解剖——神经系统、表现形式与事件流
理解了三种 Generative UI 模式后,我们需要深入底层,看看这些模式是如何实际运作的。这就像是了解汽车的发动机——你不需要成为机械师才能开车,但了解发动机会让你成为更好的司机。
### 2.1 协议生态系统全景
在 Generative UI 的世界里,有多个协议协同工作,每个协议负责不同的层次。想象一个交响乐团的结构:
| 协议 | 层级 | 作用 | 比喻 |
|------|------|------|------|
| **AG-UI** | 交互层 | Agent ↔ 用户 ↔ 应用的双向同步 | 神经系统 |
| **MCP** | 工具层 | 上下文和模型通信 | 手和工具 |
| **A2A** | 编排层 | Agent 间协调 | 乐团指挥 |
| **A2UI** | 表现层 | Agent 生成 UI 规范 | 乐谱 |
让我逐一解释。
### 2.2 AG-UI:神经系统
**AG-UI(Agent-User Interaction Protocol)** 是 CopilotKit 提出的开放协议,它是整个 Generative UI 生态的"神经系统"。
想象你的身体。神经系统不负责思考(那是大脑的工作),也不负责运动(那是肌肉的工作)。神经系统的职责是**在大脑和身体各部分之间传递信号**——感觉信号从皮肤传向大脑,运动指令从大脑传向肌肉。
AG-UI 做的就是类似的事情。它不关心 Agent 如何思考,也不关心 UI 如何渲染。它只关心一件事:**Agent 和前端之间的实时双向通信**。
AG-UI 定义了大约 16 种标准事件类型,涵盖:
- **消息事件**:Agent 说的话、用户的输入
- **工具调用事件**:工具开始执行、执行进度、执行完成
- **状态更新事件**:共享状态的快照(STATE_SNAPSHOT)、增量更新(STATE_DELTA,使用 JSON Patch 格式)
- **UI 表面事件**:渲染某个 UI、更新 UI 数据
这些事件通过标准化的方式传输——可以是 Server-Sent Events (SSE)、WebSockets、webhooks,甚至是 HTTP 轮询。AG-UI 的设计是**传输无关的**,只要能把事件从 A 点送到 B 点,用什么方式都可以。
一个典型的 AG-UI 事件流可能是这样的:
```json
// 1. 用户发送消息
{"type": "MESSAGE", "role": "user", "content": "北京天气怎么样?"}
// 2. Agent 决定调用天气工具
{"type": "TOOL_CALL_START", "toolName": "get_weather", "args": {"location": "北京"}}
// 3. 前端渲染加载状态(通过 Controlled GenUI)
{"type": "RENDER", "component": "WeatherLoadingState", "props": {"location": "北京"}}
// 4. 工具执行完成,返回结果
{"type": "TOOL_CALL_COMPLETE", "toolName": "get_weather", "result": {"temp": 22, "humidity": 45}}
// 5. 前端渲染结果卡片
{"type": "RENDER", "component": "WeatherCard", "props": {"temp": 22, "humidity": 45}}
```
AG-UI 的美妙之处在于它的 **统一性**。不管你是用 LangGraph、CrewAI、Pydantic AI 还是任何其他 Agent 框架,只要它能发出 AG-UI 兼容的事件,就能连接到任何 AG-UI 兼容的前端(比如 CopilotKit 的 React 组件)。
这就像是 USB 接口——不管是哪个品牌的键盘,只要接口匹配,插上就能用。
### 2.3 A2UI vs AG-UI:乐谱 vs 演奏
很多人容易混淆 A2UI 和 AG-UI,因为它们的名字很像。但它们的角色完全不同。
**A2UI 是乐谱**。它定义了"演奏什么音符"——用什么组件、怎么排列、显示什么数据。A2UI 的输出是 JSON 描述,比如:
```json
{"surfaceUpdate": {"components": [
{"id": "title", "component": {"Text": {"text": {"literalString": "Hello"}}}}
]}}
```
**AG-UI 是演奏过程**。它定义了"乐谱如何在音乐家和听众之间流动"——什么时候开始演奏、如何同步多个乐手、如何处理即兴发挥。AG-UI 的输出是事件流,比如:
```json
{"type": "RENDER", "surfaceId": "main", "a2uiPayload": {...}}
```
关键区别:
- **A2UI 定义"显示什么 UI"**(What)
- **AG-UI 定义"UI 如何在前后端流动"**(How)
它们可以独立工作,也可以协同工作。AG-UI 可以承载 A2UI 作为其事件的内容——AG-UI 负责传输,A2UI 负责描述要渲染的界面。
### 2.4 MCP:手和工具
**MCP(Model Context Protocol)** 是 Anthropic 提出的开放标准,最初目的是让 AI 模型能够调用外部工具和数据源。
想象一个工匠。工匠有手(模型)和工具箱(各种工具)。MCP 定义了手如何握持工具、如何传递指令、如何接收结果。它不关心工具内部是如何工作的(那是工具制造商的事),只关心**接口标准化**。
MCP 的核心概念:
- **Tools(工具)**:模型可以调用的功能(如"查询数据库"、"发送邮件")
- **Resources(资源)**:模型可以读取的数据(如文件内容、数据库记录)
- **Prompts(提示)**:预定义的提示模板
MCP Apps 扩展了这个协议,增加了 **UI Resources(UI 资源)**。现在工具不仅可以返回文本或数据,还可以返回一个指向交互式 UI 的引用(通过 `ui://` URI)。
### 2.5 A2A:乐团指挥
**A2A(Agent-to-Agent)** 是另一个新兴协议,专注于 **Agent 之间的协调**。
想象一个交响乐团。每个乐手(Agent)都是专家——小提琴手、大提琴手、钢琴家。但他们需要协调,否则就是噪音。乐团指挥(A2A)负责:
- 决定谁来演奏(哪个 Agent 处理当前任务)
- 协调节奏和时机(Agent A 完成后 Agent B 开始)
- 处理交接(Agent A 的输出作为 Agent B 的输入)
A2A 协议定义了 Agent 之间如何发现彼此、如何协商任务、如何传递状态和结果。在复杂的 Generative UI 场景中,可能有多个 Agent 协同工作——一个负责理解用户意图,一个负责查询数据,一个负责生成可视化。A2A 让它们能够无缝协作。
### 2.6 事件流的生命周期
让我们通过一个完整的例子,看看这些协议如何协同工作。
**场景**:用户说"帮我规划一次去东京的旅行",Agent 需要一个多步骤的界面来收集偏好(预算、日期、兴趣点)。
**步骤 1:用户输入**(AG-UI MESSAGE 事件)
```json
{"type": "MESSAGE", "role": "user", "content": "帮我规划一次去东京的旅行"}
```
**步骤 2:Agent 思考并决定启动旅行规划工作流**(可能是 A2A 协调多个 Agent)
**步骤 3:Agent 生成 UI 描述**(A2UI)
```json
{"surfaceUpdate": {"surfaceId": "travel-planner", "components": [
{"id": "title", "component": {"Text": {"text": {"literalString": "东京旅行规划"}}}},
{"id": "budget", "component": {"TextField": {"label": "预算(人民币)"}}},
{"id": "dates", "component": {"DateRangePicker": {"label": "旅行日期"}}},
{"id": "interests", "component": {"CheckboxGroup": {
"label": "兴趣",
"options": ["美食", "购物", "文化", "自然"]
}}},
{"id": "next", "component": {"Button": {"text": "下一步", "action": {"name": "plan_trip"}}}}
]}}
```
**步骤 4:通过 AG-UI 传输到前端**
```json
{"type": "RENDER", "surfaceId": "travel-planner", "a2uiPayload": {...}}
```
**步骤 5:用户填写表单并点击"下一步"**(AG-UI ACTION 事件)
```json
{"type": "ACTION", "surfaceId": "travel-planner", "action": "plan_trip", "data": {"budget": 10000, "dates": {...}, "interests": ["美食", "文化"]}}
```
**步骤 6:Agent 调用工具查询航班和酒店**(MCP 工具调用)
**步骤 7:Agent 生成结果界面**(可能是更复杂的 MCP Apps UI)
整个流程中:
- **AG-UI** 负责所有实时通信
- **A2UI** 负责描述表单界面
- **MCP** 负责调用外部工具(航班查询、酒店预订)
- **A2A**(如果使用)负责协调多个 Agent
这就是现代 Generative UI 的协议栈——不是单一协议解决所有问题,而是多个专业化协议分工协作。
---
## 第三章:实战案例——Excalidraw 与 OpenGenerativeUI
理论讲完了,让我们看看实际应用。CopilotKit 的 generative-ui 仓库包含两个精彩的实战案例,完美展示了不同模式的应用。
### 3.1 Excalidraw MCP Apps:开放式 Generative UI 的典范
**Excalidraw** 是一个流行的开源手绘风格图表工具。CopilotKit 团队与 Excalidraw 合作,创建了一个 MCP Apps 集成,让用户可以通过自然语言描述,在几秒钟内生成可编辑的图表。
**用户体验**:
1. 用户在聊天窗口中说:"画一个展示客户端-服务器架构的图"
2. Agent 调用 Excalidraw MCP 服务器的 `create_view` 工具
3. 工具返回一个交互式的 Excalidraw 编辑器界面,嵌入在聊天窗口中
4. 界面上已经有一个根据描述生成的架构图
5. 用户可以直接在界面中编辑图表、添加元素、调整布局
6. 一键点击,图表可以推送到 Excalidraw 主站保存
**技术实现**:
后端代码(Next.js API 路由):
```typescript
import { CopilotRuntime, ExperimentalEmptyAdapter, copilotRuntimeNextJSAppRouterEndpoint } from "@copilotkit/runtime";
import { BuiltInAgent } from "@copilotkit/runtime/v2";
import { MCPAppsMiddleware } from "@ag-ui/mcp-apps-middleware";
import { NextRequest } from "next/server";
// 创建 Agent,接入 Excalidraw MCP 服务器
const agent = new BuiltInAgent({
model: "openai/gpt-5",
prompt: `你是一个 AI 图表助手,由 Excalidraw 驱动。
用户描述任何东西,你都要生成对应的图表。
调用 create_view 工具,传入 Excalidraw 元素数组。`,
}).use(
new MCPAppsMiddleware({
mcpServers: [{
type: "http",
url: process.env.MCP_SERVER_URL ?? "http://localhost:3001/mcp",
serverId: "excalidraw",
}],
}),
);
const serviceAdapter = new ExperimentalEmptyAdapter();
const runtime = new CopilotRuntime({
agents: { default: agent },
});
export const POST = async (req: NextRequest) => {
const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
runtime,
serviceAdapter,
endpoint: "/api/copilotkit",
});
return handleRequest(req);
};
```
当 Agent 调用 `create_view` 时,它实际上传递的是这样一个 JSON 数组:
```json
[
{
"type": "rectangle",
"x": 100,
"y": 100,
"width": 150,
"height": 80,
"strokeColor": "#000000",
"backgroundColor": "transparent"
},
{
"type": "text",
"x": 125,
"y": 130,
"text": "客户端",
"fontSize": 20
},
{
"type": "arrow",
"x": 250,
"y": 140,
"points": [[0, 0], [100, 0]]
}
]
```
Excalidraw MCP 服务器接收这些元素,创建一个可编辑的图表视图,然后通过 MCP Apps 协议返回一个 URL。前端在一个沙箱化的 iframe 中加载这个 URL,用户看到的就是一个完整的 Excalidraw 编辑器。
**为什么这个案例重要**:
它展示了 Open-ended Generative UI 的强大之处。Agent 不需要知道如何绘制图形、如何处理用户交互、如何保存文件——这些复杂的工作都由 Excalidraw MCP 服务器处理。Agent 只需要"指挥":"我要一个这样的图表"。
这就像是请了一个专业的图表设计师。你告诉他你想要什么,他给你成品,你可以在他的工作室里现场修改,最后带走最终版本。你不需要自己学会设计软件。
### 3.2 OpenGenerativeUI:探索 Generative UI 的可能性边界
**OpenGenerativeUI** 是 CopilotKit 的另一个开源展示项目,它使用 `useComponent` hook 来实现开放式 Generative UI。
与 Excalidraw 案例不同,OpenGenerativeUI **不需要外部 MCP 服务器**。LangGraph Agent 直接生成 HTML/SVG/Canvas 代码,前端直接渲染。
**支持的内容类型**:
- **算法可视化**:排序算法、图遍历、动态规划的逐步演示
- **3D 动画**:Three.js、WebGL 驱动的交互式 3D 场景
- **交互式模拟**:物理模拟、生态系统模拟、经济模型
- **数学绘图**:函数图像、几何图形、统计图表
- **D3 力导向图**:网络关系可视化
**技术实现**:
```typescript
import { useComponent } from "@copilotkit/react-core/v2";
import { WidgetRenderer, WidgetRendererProps } from "@/components/generative-ui/widget-renderer";
// 注册一个名为 "widgetRenderer" 的组件
useComponent({
name: "widgetRenderer",
description: `在沙箱化 iframe 中渲染交互式 HTML/SVG 可视化。
用于算法可视化、图表、控件和模拟。`,
parameters: WidgetRendererProps,
render: WidgetRenderer,
});
```
当 Agent 需要展示一个可视化时,它直接生成 HTML 字符串:
```html
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
</head>
<body>
<div id="canvas"></div>
<script>
// Three.js 3D 场景代码
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
// ... 更多 3D 渲染代码
</script>
</body>
</html>
```
这个 HTML 字符串通过 AG-UI 协议传输到前端,`WidgetRenderer` 组件把它放进一个沙箱化的 iframe 中渲染。
**与 MCP Apps 的区别**:
| 维度 | MCP Apps (Excalidraw) | useComponent (OpenGenerativeUI) |
|------|----------------------|--------------------------------|
| **服务器往返** | 需要(调用远程 MCP 服务器) | 不需要(Agent 直接生成) |
| **延迟** | 较高(网络请求) | 较低(本地生成) |
| **能力范围** | 取决于 MCP 服务器的功能 | 取决于 Agent 生成代码的能力 |
| **安全性** | 依赖 iframe 沙箱 | 依赖 iframe 沙箱 |
| **适用场景** | 调用外部专业服务 | 快速原型、代码生成类任务 |
**为什么这个案例重要**:
它展示了 Generative UI 的极限可能性。如果 Agent 能够生成有效的 HTML/JavaScript 代码,那么理论上它可以创建任何类型的界面——从简单的图表到复杂的 3D 游戏。
当然,这也带来了挑战:
- **代码质量**:Agent 生成的代码可能有 bug
- **安全性**:需要严格的沙箱隔离
- **性能**:复杂的可视化可能消耗大量资源
但作为一种探索性工具,OpenGenerativeUI 展示了 Generative UI 的未来方向:Agent 不仅是信息的提供者,更是体验的创造者。
---
## 第四章:协议生态的未来——为什么没有唯一的"赢家"
在 Generative UI 的生态系统中,我们有多个协议:AG-UI、A2UI、MCP、A2A。一个自然的疑问是:会不会最终只有一个协议胜出?其他都被淘汰?
答案是:**不会。这些协议不是竞争者,而是协作者。**
### 4.1 协议分工的必然性
想象城市交通系统。我们有:
- **地铁**:大运量、固定路线、站点之间快速移动
- **公交**:灵活路线、覆盖面广、门到门服务
- **出租车/网约车**:个性化、随叫随到、点对点
- **自行车/步行**:短距离、灵活、健康
这些交通方式不是竞争关系,而是互补关系。你不会用地铁完成最后一公里的送货,也不会用出租车横跨整个城市上下班(除非你很富有)。
Generative UI 的协议生态也是如此。
### 4.2 每个协议的核心价值
**AG-UI:通用传输层**
AG-UI 的目标是成为"神经系统"——不管大脑(Agent)和肌肉(UI)如何变化,神经系统负责传递信号的工作是永恒的。
AG-UI 的价值在于:
- **框架无关**:任何 Agent 框架、任何前端框架都可以使用
- **实时双向**:支持流式响应、状态同步、人机协作
- **事件标准化**:统一的事件类型让调试和监控更容易
**A2UI:声明式 UI 标准**
A2UI 的目标是成为"乐谱标准"——就像五线谱让不同乐器的演奏者能够理解同一首曲子,A2UI 让不同平台的 UI 能够渲染同一个描述。
A2UI 的价值在于:
- **跨平台**:Web、iOS、Android、桌面,同一套描述
- **LLM 友好**:JSON 结构对语言模型来说很容易生成
- **安全声明式**:数据不是代码,没有执行风险
**MCP:工具生态系统**
MCP 的目标是成为"工具市场"——标准化让工具制造商(MCP 服务器开发者)和工具使用者(Agent 开发者)能够无缝对接。
MCP 的价值在于:
- **即插即用**:连接一个 MCP 服务器,立即获得新能力
- **安全隔离**:工具运行在独立的上下文中
- **生态丰富**:越来越多的服务提供 MCP 接口
**A2A:Agent 协作网络**
A2A 的目标是成为"Agent 社交协议"——让不同的 Agent 能够发现彼此、协商任务、协同工作。
A2A 的价值在于:
- **专业化**:每个 Agent 专注于自己的领域
- **组合性**:简单的 Agent 组合成复杂的系统
- **可扩展性**:新的 Agent 可以无缝加入网络
### 4.3 为什么不会有一个"赢家"
**技术原因**:
每个协议解决的问题本质不同。AG-UI 解决"如何通信",A2UI 解决"描述什么",MCP 解决"调用工具",A2A 解决"Agent 协作"。这就像问"螺丝刀和锤子哪个更好"——取决于你要做什么。
**社会原因**:
协议的背后是组织。AG-UI 由 CopilotKit 主导,A2UI 由 Google 主导,MCP 由 Anthropic 发起,A2A 是多个组织的协作。这些组织有不同的利益、不同的技术栈、不同的用户群体。强制统一既不现实,也不必要。
**历史原因**:
技术史告诉我们,开放标准往往比单一垄断更持久。TCP/IP 不是唯一的网络协议,但它是最通用的,所以其他协议(HTTP、FTP、WebSocket)都建立在它之上。Generative UI 的生态也在走向同样的方向——AG-UI 正在成为底层传输标准,而其他协议在其之上各显神通。
### 4.4 可组合性的重要性
未来的 Generative UI 应用,很可能是**多协议组合**的:
```
┌─────────────────────────────────────────────────────────┐
│ 用户界面层 │
│ (React / Vue / Flutter / SwiftUI) │
├─────────────────────────────────────────────────────────┤
│ AG-UI 传输层(事件流) │
├─────────────────────────────────────────────────────────┤
│ A2UI 渲染器 │ MCP Apps 容器 │ Controlled 组件 │
├─────────────────────────────────────────────────────────┤
│ Agent 运行时层 │
│ (LangGraph / CrewAI / Pydantic AI / AutoGen) │
├─────────────────────────────────────────────────────────┤
│ MCP 工具层(数据库、API、文件系统) │
├─────────────────────────────────────────────────────────┤
│ A2A 协调层(多 Agent 协作) │
└─────────────────────────────────────────────────────────┘
```
这种分层架构的好处是:
- **每一层可以独立演进**:升级 React 不需要改动 LangGraph
- **每一层可以替换**:不喜欢 LangGraph?换成 CrewAI,其他层不受影响
- **每一层可以组合**:根据需求选择不同的技术栈
### 4.5 开源生态的合作
令人鼓舞的是,这个生态的主要玩家正在走向合作而非对抗:
- **Oracle**:Open Agent Specification,定义可移植的 Agent 描述
- **Google**:A2UI 规范,声明式 UI 标准
- **CopilotKit**:AG-UI 协议 + 实现框架
- **Anthropic**:MCP 协议
- **OpenAI**:支持 MCP Apps
- **Microsoft**:Azure Agent Framework 支持 AG-UI
- **AWS**:AgentCore 提供 AG-UI 端点
这些合作的目标很明确:**即插即用可移植性**。开发者应该能够:
1. 用任何框架编写 Agent
2. 连接到任何支持 AG-UI 的前端
3. 使用任何 MCP 服务器提供的工具
4. 让 Agent 生成符合 A2UI 的界面
5. 在不同平台(Web、iOS、Android)上获得一致的体验
这不是一个公司的愿景,而是整个行业的方向。
---
## 第五章:开发者视角——选择、安全与性能
如果你是开发者,想要在自己的项目中使用 Generative UI,应该怎么做?这一章提供实用的指导。
### 5.1 如何选择合适的模式
选择 Generative UI 模式,就像选择交通工具——取决于你的目的地、预算和时间。
**选择 Controlled Generative UI(乐高模式)如果**:
- ✅ 你需要严格控制品牌体验
- ✅ 你的应用场景有限且明确(天气、股票、特定审批流)
- ✅ 你想要最高的性能和安全性
- ✅ 你的团队有前端开发能力,可以预构建组件
**选择 Declarative Generative UI(3D 打印模式)如果**:
- ✅ 你需要动态界面,但不想为每种可能预定义组件
- ✅ 你需要跨平台支持(Web + 移动端)
- ✅ 你想要平衡灵活性和安全性
- ✅ 你的 Agent 需要生成表单、列表、简单图表
**选择 Open-ended Generative UI(自由雕塑模式)如果**:
- ✅ 你需要高度定制化的交互体验
- ✅ 你在调用外部专业服务(如 Excalidraw、复杂数据可视化)
- ✅ 你可以接受 iframe 的性能和安全权衡
- ✅ 你的应用场景无法被预定义组件覆盖
**决策流程图**:
```
开始
│
├─ 是否需要严格控制 UI 外观?
│ ├─ 是 → Controlled GenUI
│ └─ 否
│ │
│ ├─ 是否需要调用外部专业服务?
│ ├─ 是 → MCP Apps (Open-ended)
│ └─ 否
│ │
│ ├─ 是否需要动态生成界面?
│ ├─ 是 → Declarative GenUI (A2UI)
│ └─ 否 → 传统 UI 即可
```
### 5.2 安全考虑
Generative UI 引入了新的安全挑战,尤其是当 Agent 生成的内容可能包含代码时。
**沙箱隔离(针对 MCP Apps 和 Open-ended GenUI)**:
所有外部 UI 内容都应该在**沙箱化的 iframe** 中运行。现代浏览器的 iframe sandbox 属性提供了强大的隔离:
```html
<iframe
sandbox="allow-scripts allow-same-origin"
src="..."
></iframe>
```
推荐的沙箱配置:
- `allow-scripts`:允许执行 JavaScript(否则界面无法交互)
- **不要** `allow-top-navigation`:防止 iframe 导航父页面到钓鱼网站
- **不要** `allow-forms`(除非必要):防止表单提交到恶意地址
- 使用 CSP(Content Security Policy)限制可加载的资源来源
**内容安全策略(CSP)示例**:
```http
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
connect-src 'self' https://api.yourservice.com;
```
**权限控制**:
MCP Apps 允许 UI 请求额外权限(如麦克风、摄像头)。这些权限应该:
- 明确显示给用户,获得用户同意
- 仅在必要时授予
- 可以随时撤销
**代码注入防护**:
对于 Agent 直接生成 HTML 的模式(Open Generative UI),需要防范 XSS 攻击:
- 使用 DOMPurify 等库清理 HTML
- 禁止 `eval()`、`new Function()` 等动态代码执行
- 对所有的用户输入进行转义
### 5.3 性能权衡
不同的 Generative UI 模式有不同的性能特征。
**渲染性能**:
| 模式 | 首次渲染 | 更新渲染 | 内存占用 |
|------|---------|---------|---------|
| Controlled | 极快(预编译组件) | 极快 | 低 |
| Declarative | 快(JSON 解析 + 组件映射) | 快 | 中 |
| Open-ended | 慢(iframe 加载) | 中(iframe 通信开销) | 高(独立上下文) |
**网络开销**:
- **Controlled**:无额外网络开销(组件已在客户端)
- **Declarative**:低(JSON 文本传输)
- **Open-ended**:高(HTML/JS/CSS 资源加载,可能需要多次往返)
**优化建议**:
1. **预加载**:对于 MCP Apps,可以在用户可能调用工具前就预加载 UI 资源
2. **懒加载**:对于不常用的组件,使用动态导入(dynamic import)
3. **缓存**:缓存 MCP 服务器的 UI 资源,避免重复下载
4. **降级策略**:如果 iframe 加载失败,提供纯文本或简化 UI 的降级方案
5. **流式渲染**:对于 A2UI,支持渐进式渲染,不要等到整个 JSON 完成再显示
### 5.4 开发工具与调试
调试 Generative UI 应用比普通应用更复杂,因为涉及前后端、多个协议的交互。
**推荐工具**:
1. **AG-UI DevTools**:查看事件流、检查状态更新
2. **MCP Inspector**:测试 MCP 服务器和工具调用
3. **浏览器开发者工具**:
- Network 面板:检查协议传输
- Performance 面板:分析渲染性能
- Console 面板:查看日志和错误
**调试技巧**:
- 在 AG-UI 事件中添加唯一的 `traceId`,跟踪一个用户请求的全链路
- 使用 `console.group()` 组织相关的日志输出
- 在 iframe 中使用 `postMessage` 进行跨窗口调试
---
## 结论:UI 的新纪元
让我们回到文章的开头。
传统的 UI 是静态的、预定义的、为"平均用户"设计的。就像一本固定的菜单,不管谁来、什么时候来、有什么特殊需求,看到的都是同样的选项。
Generative UI 改变了这个范式。界面不再是固定的,而是 **活的**——它随着用户的需求、Agent 的理解、上下文的变化而动态生成。
这不是关于技术的炫技,而是关于 **人机交互的本质转变**。
在旧范式中,用户必须学习软件的"语言"——菜单在哪里、按钮是什么意思、如何完成一个任务。软件是主人,用户是客人,必须遵循主人的规则。
在新范式中,软件学习用户的语言——用户用自己的话描述需求,Agent 生成适合当下需求的界面。用户是主人,软件是工具,随叫随到,按需变形。
这是从"**为所有用户设计一个界面**"到"**为每个用户、在每个时刻、生成专属界面**"的转变。
### 展望未来
Generative UI 还处于早期阶段。2026 年,我们看到了 AG-UI、A2UI、MCP Apps 等协议的初步标准化,看到了 CopilotKit、Google Opal、Claude 等产品的实践探索。
未来会怎样?我不知道确切的答案,但有一些方向是明确的:
**更智能的界面生成**:随着多模态模型的发展,Agent 不仅能生成结构化 UI,还能生成像素级精准的视觉设计。
**更深入的个性化**:界面不仅根据任务生成,还根据用户的偏好、习惯、能力水平动态调整。色盲用户看到高对比度配色,老年用户看到大字体,专家用户看到高级选项。
**更无缝的跨平台**:同一份 A2UI 描述,在手机上自动适配小屏幕,在 VR 设备上变成 3D 界面,在汽车中控屏上变成语音优先的交互。
**更强大的 Agent 协作**:通过 A2A 协议,多个专业 Agent 协同为用户服务。一个 Agent 负责理解需求,一个负责查询数据,一个负责生成可视化,一个负责优化性能——它们协同生成一个统一的界面。
### 最后的思考
费曼曾经说过:"如果你认为你理解了某样东西,试着把它解释给一个大一新生听。如果你做不到,说明你没有真正理解。"
Generative UI 的概念听起来很复杂——协议栈、事件流、声明式描述、沙箱隔离。但核心的 idea 其实很简单:
**让软件界面像人类服务一样,根据每个用户的独特需求动态调整。**
这不是终点,而是开始。我们站在 UI 新纪元的门槛上,前方是未知但令人兴奋的可能性。
正如 CopilotKit 的口号所说:"Build apps that adapt to your users."
让我们开始构建吧。
---
## 附录:参考资源
**官方资源**:
- CopilotKit generative-ui GitHub:https://github.com/CopilotKit/generative-ui
- AG-UI 文档:https://docs.ag-ui.com
- A2UI 规范:https://a2ui.org
- MCP Apps 规范:https://modelcontextprotocol.io/extensions/apps/overview
**相关协议**:
- Open Agent Specification (Oracle):https://blogs.oracle.com/ai-and-datascience/post/open-agent-specification
- MCP 协议:https://modelcontextprotocol.io
- A2A 协议:https://a2aprotocol.ai
**示例项目**:
- OpenGenerativeUI:https://github.com/CopilotKit/OpenGenerativeUI
- Excalidraw MCP 集成:https://github.com/CopilotKit/excalidraw-studio
- A2UI + Agent Spec 示例:https://github.com/CopilotKit/with-agent-spec
**深度阅读**:
- "The Developer's Guide to Generative UI in 2026" (CopilotKit Blog)
- "Introducing A2UI: An open project for agent-driven interfaces" (Google Developers Blog)
- "Reusable Agents Meet Generative UIs" (Oracle + Google + CopilotKit 联合发布)
---
#GenerativeUI #CopilotKit #AGUI #A2UI #MCP #AI前端 #小凯
登录后可参与表态
讨论回复
0 条回复还没有人回复,快来发表你的看法吧!