跳到主要内容

Generative-UI工程

核对日期:2026-08-26。

1. 定义与边界

Generative UI 是指 Agent 不只输出文字,还输出可渲染的界面单元:卡片、表格、图表、可编辑产物(Artifact / Canvas)。工程上它是「结构化结果 → 白名单组件」的渲染问题,不是「模型会写 HTML」。

本文件给出生产可用的模式选择、组件注册表和注入防护。流式传输见 流式协议与中断.md;工具状态见 流式Tool-Use与前端状态.md

明确不做:

  • 把模型输出的 HTML / JSX 字符串直接插入 DOM。
  • v-htmldangerouslySetInnerHTML、小程序 rich-text 渲染未消毒的模型内容。
  • 把 Claude Artifacts / ChatGPT Canvas 的产品形态当成可以无沙箱复制的能力。

2. 为什么重要

文字答案信息密度低,业务用户要的是订单卡、退款预览、图表。但不加白名单的 Generative UI 会把 XSS、供应链和越权展示一起打开:

  • 模型可以在「漂亮的卡片」里塞进钓鱼链接或伪系统提示。
  • HTML 字符串一旦进 DOM,审批卡片和聊天记录都变成攻击面。
  • 每个用户一套随机布局会破坏设计系统和无障碍。

所以生产默认应是:模型只填 JSON,组件是你写的。

3. 核心机制

UI part 是一种特殊工具结果,不是自由文本:

interface UiPart {
type: 'ui_part';
runId: string;
component: string;
props: Record<string, unknown>;
}

component 必须能在服务端注册表里查到。查不到就降级,不要动态 import(userString)

4. 架构模式

模式做法何时用何时不用
A. Markdown模型出 Markdown,客户端安全渲染解释、结论、引用要强交互控件
B. 结构化数据 + 模板工具 / UI part 出 JSON,固定组件填 props订单、天气、商品、审批摘要每次都要全新布局
C. 组件 DSL有限组件树 JSON(type + children)仪表盘拼装DSL 比业务还复杂
D. 沙箱 HTML / JSXiframe sandbox 或独立运行时代码演示、内部设计稿预览主聊天、主业务路径
E. 服务端组件流如 AI SDK RSC / streamUINext.js 且组件全在服务端Vue / 小程序 / 多端

对本仓库读者(Vue 3、小程序、H5):默认选 B。E 绑定 React Server Components,不能当跨端方案。D 只放在隔离窗口,且禁止同域 cookie。

Vercel AI SDK 可以把工具结果映射成 React 组件并流式到客户端。这是 B/E 的一种实现,不是「模型写 JSX 就安全」。

5. 工程实现

5.1 组件注册表

import type { Component, DefineComponent } from 'vue';

interface OrderCardProps {
orderId: string;
status: 'created' | 'paid' | 'shipped' | 'delivered' | 'cancelled';
amount: string;
}

type UiComponentName = 'order_card' | 'refund_preview' | 'citation_list';

const UI_REGISTRY: Record<UiComponentName, Component> = {
order_card: OrderCard,
refund_preview: RefundPreview,
citation_list: CitationList,
};

function isUiComponentName(value: string): value is UiComponentName {
return value in UI_REGISTRY;
}

function resolveUiPart(part: { component: string; props: unknown }): DefineComponent | Component {
if (!isUiComponentName(part.component)) {
return SafeUnknownPart;
}
return UI_REGISTRY[part.component];
}

props 必须再用运行时校验(Zod 等)收窄。不要 as OrderCardProps 直接传入。

5.2 Vue 3 渲染

<script setup lang="ts">
import { computed } from 'vue';

const props = defineProps<{
component: string;
rawProps: unknown;
}>();

const resolved = computed(() => {
if (!isUiComponentName(props.component)) {
return { comp: SafeUnknownPart, data: { label: props.component } };
}
const parsed = parseProps(props.component, props.rawProps);
if (parsed === undefined) {
return { comp: SafeUnknownPart, data: { label: 'invalid_props' } };
}
return { comp: UI_REGISTRY[props.component], data: parsed };
});
</script>

<template>
<component :is="resolved.comp" v-bind="resolved.data" />
</template>

SafeUnknownPart 只渲染纯文本「暂不支持该卡片」,不回显原始 JSON 里的 HTML。

5.3 产物面板(Artifact)

长代码、长文档、图表应离开气泡,进入右侧 / 下层产物面板:

  • 气泡保留任务叙事和工具过程。
  • 产物有独立版本、复制、下载、再编辑入口。
  • 再编辑走「选中片段 + 新指令」,不要把整份产物反复塞回上下文。上下文预算见 ../03-模型与推理能力/上下文窗口管理.md

小程序没有宽右侧栏时,用全屏页承载产物,而不是在消息列表里塞 WebView。

5.4 禁止项与例外

H5 / Web:禁止用 dangerouslySetInnerHTML 或 Vue v-html 渲染模型输出。若预览用户自己的 Markdown,使用消毒后的解析器,并关掉原始 HTML。

例外(必须同时满足):

  • 独立 sandbox iframe,sandbox 不含 allow-same-originallow-top-navigation
  • 内容来自内部只读预览,不带用户 Cookie。
  • 有 CSP 和体积上限。

小程序:不要把模型 HTML 丢进 rich-textrich-text 的节点白名单因端而异,且不是消毒方案。

6. 生产实践

实践说明
组件版本order_card@2 进 schema,旧 run 回放仍能渲染
空态工具成功但字段缺失时出空态,不编造数字
引用知识卡必须带可点击的文档 id,见 ../06-RAG与知识系统/引用与可追溯性.md
流式骨架ui_part 未就绪时用组件骨架,而不是闪烁整页
设计系统卡片复用业务组件,避免 Agent 专用一套视觉
降级文案未知 component 时保留可复制的纯文本摘要

7. 常见反模式

反模式表现后果修正
模型写 HTML把 HTML 当消息XSS、钓鱼模式 B
v-html 渲染回复图省事注入即 DOMMarkdown 安全子集或纯文本
动态组件名import(part.component)任意代码路径静态注册表
主路径上沙箱网页聊天里嵌未隔离 iframecookie / 跳转风险产物进隔离窗口
每问一新布局没有组件库不可测、不可访问先收口 5–10 个业务卡
把 RSC 示例搬到小程序复制 Next.js streamUI无法运行跨端只用 JSON + 本地组件

8. 评测方法

指标说明
Unknown Component Rate未知 component 占比,高说明 schema 失控
Prop Validation Fail Rateprops 未过校验
XSS Fixture Pass红队样本(javascript:、事件处理属性、伪系统横幅)均被纯文本化
Business Card Coverage高频任务是否有对应卡片
Artifact Isolation产物预览是否同域可读 cookie,目标为否

补一组 UI 红队样本到安全评测,见 ../10-Agent评测体系/安全评测.md

9. 安全与治理

  • UI part 的 props 与工具返回一样不可信。
  • 卡片里的链接只允许 https,并走业务域名白名单。
  • 禁止组件根据模型字段直接调用写工具;写操作仍走 HITL。
  • 产物下载要鉴权,run_id 不可枚举。
  • 日志里不要存完整 HTML 产物,存组件名、props 摘要、版本。

10. 权威资料