前言

做 AI 应用的时候,代理返回的通常是文本。但实际场景里,光有文本不够——查天气想看卡片,查数据想看图表,填信息想看表单。

怎么让代理不只是返回文字,而是能控制 UI 的显示?这就是 Generative UI 要解决的问题。

本文基于 CopilotKit 团队的 generative-ui 仓库整理,介绍 Generative UI 的三种实现模式。


一、什么是 Generative UI

传统 UI 开发,界面是开发者写死的。用户点什么按钮,显示什么页面,都是提前定好的。

Generative UI 不一样:UI 的部分内容由 AI 代理在运行时决定

代理可以:

  • 选择显示哪个组件
  • 传入组件需要的数据
  • 甚至直接返回 UI 的结构描述

前端根据代理的输出,实时渲染界面。

这样做的好处是界面能根据对话内容动态变化,而不是固定不变的文本输出。


二、三种实现模式

CopilotKit 把 Generative UI 分成三类,按自由度从低到高排列。

2.1 Static Generative UI(静态生成式 UI)

这是最保守的方案。

思路:开发者预先写好一堆 UI 组件,代理只负责选择用哪个,传入什么数据。

特点

  • 组件的样式、布局、交互逻辑都是开发者控制
  • 代理只是触发器,决定什么时候显示什么组件
  • 安全性高,不会出现意料之外的 UI

使用的底层协议:AG-UI

代码示例

useFrontendTool({
  name: "get_weather",
  description: "获取天气信息",
  parameters: z.object({ 
    location: z.string() 
  }),
  handler: async ({ location }) => {
    return getMockWeather(location);
  },
  render: ({ status, args, result }) => {
    // 加载中显示 loading 状态
    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}
        />
      );
    }
    return null;
  },
});

代理调用 get_weather 这个工具的时候,前端会根据工具执行的状态,渲染对应的 React 组件。组件本身是开发者写好的,代理只是触发了它。

适用场景:需要严格控制 UI 风格的产品级应用。


2.2 Declarative Generative UI(声明式生成式 UI)

这个方案给代理更多自由度。

思路:代理返回一个 JSON 格式的 UI 描述,前端根据这个描述渲染界面。

代理能定义:这里放个卡片,卡片里有标题和内容,下面再放个按钮。前端有一套渲染器,能把这种描述变成真实的 UI。

两个主要规范

规范 来源 特点
A2UI Google JSONL 格式,支持流式传输
Open-JSON-UI OpenAI(开源版本) JSON 格式的 UI 描述

A2UI 的工作流程

代理输出三种消息:

  1. surfaceUpdate - 定义有哪些组件
  2. dataModelUpdate - 填充组件的数据
  3. beginRendering - 开始渲染
{"surfaceUpdate":{"surfaceId":"form-surface","components":[...]}}
{"dataModelUpdate":{"surfaceId":"form-surface","path":"/","contents":[...]}}
{"beginRendering":{"surfaceId":"form-surface","root":"form-column"}}

前端收到这些消息后,按照描述渲染出对应的表单、卡片、列表等组件。

前端集成方式

import { createA2UIMessageRenderer } from "@copilotkit/a2ui-renderer";

const A2UIRenderer = createA2UIMessageRenderer({ theme: a2uiTheme });

<CopilotKitProvider
  runtimeUrl="/api/copilotkit-a2ui"
  renderActivityMessages={[A2UIRenderer]}
>
  {children}
</CopilotKitProvider>

适用场景:需要代理能自主设计界面结构,但又不想完全放开的场景。


2.3 Open-ended Generative UI(开放式生成式 UI)

这是自由度最高的方案。

思路:代理直接返回完整的 UI 内容,可能是 HTML、iframe、或者任意可渲染的内容。前端只是个容器,负责展示。

使用的规范:MCP Apps

import { MCPAppsMiddleware } from "@ag-ui/mcp-apps-middleware";

const agent = new BuiltInAgent({
  model: "openai/gpt-4o",
  prompt: "You are a helpful assistant.",
}).use(
  new MCPAppsMiddleware({
    mcpServers: [
      { 
        type: "http", 
        url: "http://localhost:3108/mcp", 
        serverId: "my-server" 
      },
    ],
  }),
);

潜在问题

  • 安全风险:任意内容可能有 XSS 等问题
  • 样式不一致:代理生成的 UI 可能和应用风格不搭
  • 性能问题:渲染任意内容的开销不可控

适用场景:内部工具、实验性项目,或者对 UI 一致性要求不高的场景。


三、三种模式对比

模式 控制度 自由度 安全性 适用场景
Static 产品级应用
Declarative 需要灵活布局的场景
Open-ended 内部工具、实验项目

选择哪种方案,取决于你对 UI 控制的需求:

  • 想完全控制 UI 风格 → Static
  • 想让代理有一定自主权,但限制在可控范围 → Declarative
  • 想让代理完全自由发挥 → Open-ended

四、AG-UI 在其中的角色

上面三种模式,底层都依赖 AG-UI 协议。

AG-UI 提供的是代理和应用之间的通信能力:事件流、状态同步、工具调用等。Generative UI 的各种规范(A2UI、Open-JSON-UI、MCP Apps)都建立在 AG-UI 之上。

+------------------+     +----------------+     +---------------+
|   Static Gen UI  |     |  Declarative   |     |  Open-ended   |
| (预定义组件+数据) |     | (JSON UI 描述) |     | (完整 UI 内容)|
+--------+---------+     +-------+--------+     +-------+-------+
         |                       |                      |
         +-----------------------+----------------------+
                                 |
                                 v
                      +--------------------+
                      |       AG-UI        |
                      | (代理-用户交互协议) |
                      +--------------------+

可以这样理解:AG-UI 是通信管道,Generative UI 规范定义了管道里传输的内容格式。


五、总结

Generative UI 解决的问题是:让 AI 代理的输出不只是文本,而是能控制界面显示。

三种模式各有取舍:

  • Static:开发者控制 UI,代理只选择和填数据,最安全
  • Declarative:代理能定义 UI 结构,前端负责渲染,平衡了灵活性和可控性
  • Open-ended:代理直接输出 UI 内容,最灵活但风险最高

实际项目里,Static 模式用得最多,因为它能保证 UI 一致性和安全性。Declarative 和 Open-ended 更多用在需要高度灵活性的场景,或者原型验证阶段。


参考资料

  • Generative UI 示例仓库:https://github.com/CopilotKit/generative-ui
  • CopilotKit Generative UI 文档:https://docs.copilotkit.ai/generative-ui
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐