聊天框正在过时:Generative UI 与 A2UI/AG-UI 会怎样改变 AI 应用?
大多数 AI 应用都有一个相似界面:左边历史会话,右边聊天框,用户输入一句话,模型返回一大段 Markdown。
这种形态适合问答,却不适合完成复杂任务。
当用户让 Agent 安排会议时,比起回答"我找到了三个时间",更自然的交互应该是直接展示可选时间卡片;当用户分析销售数据时,Agent 应该返回图表和筛选器,而不只是用文字描述趋势。
这就是 Generative UI:Agent 根据当前任务动态选择和组织界面。
Google 在 2026 年发布 A2UI v0.9,用统一声明描述 Agent 的 UI 意图;AWS 近期也展示了通过 AG-UI 在 Agent 与前端之间同步事件、状态和人工输入的生产架构。Google:A2UI v0.9、AWS:Generative UI with AG-UI
这类协议的价值不是让模型随意生成网页代码,而是让 Agent 在安全边界内"说 UI"。
一、为什么不能让模型直接返回 HTML?
最直接的 Generative UI 方案,是让模型生成 HTML 和 JavaScript,然后放进页面。
这会立刻带来问题:
- XSS 和任意脚本执行;
- 样式与产品设计系统不一致;
- 无障碍和多端适配困难;
- 组件行为无法测试;
- Agent 可能构造钓鱼界面;
- 每次生成的 UI 结构都不同。
更稳妥的方式是:前端掌握组件实现,Agent 只能从已注册的组件目录中选择,并填写经过 Schema 校验的属性。
text
Agent 不生成:<script>...</script>
Agent 生成:
component = "sales_chart"
props = { period: "30d", group_by: "region" }
前端收到声明后,使用自己的 SalesChart 组件渲染。
二、A2UI 与 AG-UI 分别解决什么问题?
两者经常一起出现,但关注点不同。
A2UI:描述"界面是什么"
A2UI 是一种声明式 UI 格式。Agent 表达组件树、数据和交互意图,客户端使用自己的组件库渲染。
AG-UI:传输"Agent 正在做什么"
AG-UI 更关注 Agent 与前端之间的事件流,例如:
- Run 开始和结束;
- 文本增量输出;
- 工具调用;
- 状态同步;
- UI 组件更新;
- 等待用户输入。
可以把两者理解为:
text
AG-UI = Agent 与前端之间的通信管道
A2UI = 管道中可传递的一种声明式 UI 内容
Google 的 A2UI 文档也将它定位为跨框架、跨设备的 UI 意图标准,而不是某个特定前端框架的替代品。Google:Introducing A2UI
三、一个受控的 Generative UI 架构
核心边界有三条:
- Agent 只能使用组件目录中存在的组件;
- 属性必须通过严格 Schema 校验;
- 真正的业务写操作仍然走后端工具和权限检查。
界面按钮不能直接等于业务权限。即使 Agent 渲染了"退款"按钮,点击后仍要经过退款服务的身份、金额和状态校验。
四、Go 后端如何表达 UI 事件?
可以先定义一个简单、与前端框架无关的事件结构:
go
type UIEvent struct {
RunID string `json:"run_id"`
Sequence int64 `json:"sequence"`
Type string `json:"type"`
Component string `json:"component,omitempty"`
Props json.RawMessage `json:"props,omitempty"`
}
type SalesChartProps struct {
Title string `json:"title"`
Labels []string `json:"labels"`
Values []int64 `json:"values"`
}
Agent 决定展示销售图表后,后端不直接接受任意 JSON,而是先转换并验证:
go
func NewSalesChartEvent(
runID string,
sequence int64,
props SalesChartProps,
) (UIEvent, error) {
if len(props.Labels) == 0 || len(props.Labels) != len(props.Values) {
return UIEvent{}, ErrInvalidChartData
}
payload, err := json.Marshal(props)
if err != nil {
return UIEvent{}, err
}
return UIEvent{
RunID: runID,
Sequence: sequence,
Type: "component.upsert",
Component: "sales_chart",
Props: payload,
}, nil
}
事件通过 SSE 或 WebSocket 发送。Sequence 用于断线后补发,component.upsert 表示同一个组件可以随着 Agent 获得新数据而增量更新。
五、Generative UI 还需要解决状态一致性
假设 Agent 展示一个旅行计划表,用户手动修改酒店,Agent 随后又更新行程。如果双方都覆盖整个对象,很容易丢失用户修改。
因此需要明确:
- 哪些状态由 Agent 拥有;
- 哪些状态由用户拥有;
- 哪些字段允许共同修改;
- 冲突时采用版本号、Patch 还是人工确认。
推荐每次更新携带版本:
text
component_id: itinerary_1
base_version: 7
next_version: 8
patch: replace /days/2/hotel
如果前端已经处于版本 9,就拒绝旧 Patch,并让 Agent重新读取当前状态。
六、什么场景值得使用 Generative UI?
适合:
- 数据分析与图表;
- 表单填写;
- 商品或方案比较;
- 日程、旅行和工作流编排;
- Human-in-the-loop 审批;
- Agent 任务进度与产物展示。
不一定适合:
- 简单问答;
- UI 高度固定的核心交易页面;
- 需要像素级稳定和严格合规的流程;
- 组件目录尚未建立的早期 Demo。
Generative UI 的目标不是让所有页面都由 AI 生成,而是在任务不确定时动态组合有限、可信的交互能力。
结语
聊天框不会消失,但它很可能从最终界面退回到一种输入方式。
未来的 AI 应用会根据任务在文本、表格、图表、表单和审批组件之间切换。Agent 负责决定当前需要什么交互,前端负责使用可信组件把它呈现出来。
真正可生产的 Generative UI,不是"模型生成任意代码",而是:
Agent 表达 UI 意图,协议传递事件和状态,客户端掌握渲染与安全边界。