用 Next.js 实测 A2UI 智能体界面协议
作者在 Next.js 中安装 Google React 渲染器,使用 A2UI 1.0 消息测试交互,并分析协议结构、安全模型与当前局限。其核心机制是由智能体输出组件描述和数据,再由应用自己的组件库渲染。
AI 深度解读
Agent 能把部署方案解释清楚,却未必能把「确认部署」这件事做好。用户要核对环境、选择选项、填写备注,聊天窗口却让这些操作散落在多轮对话里。A2UI 想解决的就是这个问题:Agent 用 JSON 描述界面,应用用自己注册的组件把它画出来。
原文作者在 Next.js 16 中接入 Google 的 React renderer,验证了从界面生成到按钮回传的流程。结论是:这套协议适合审批、比较和表单,但协议稳定不等于 SDK 成熟,更不等于业务操作自动安全。
Agent 描述界面,前端还负责什么
A2UI 把界面拆成两份职责:Agent 决定展示哪些组件、绑定哪些数据,应用负责组件实现、视觉样式和交互执行。Agent 不发送 HTML 或可执行代码,也不能使用应用没有注册的组件。
这种分工适合三类任务:审批需要把操作对象和参数放在确认按钮旁边;比较需要把候选项组织成可选择的结构;表单需要一次展示字段并及时校验。这些界面的形状通常已知,变化的是数据,没必要每次让模型重新设计页面。
组件目录(catalog)就是双方约定的词汇表。基础目录包含 18 个组件,覆盖布局、展示和输入;生产应用可以定义自己的目录,约束组件名称及属性。A2UI 1.0 消息不携带主题,尝试设置主题会被判为无效,视觉控制权留在宿主应用。
作者给出的部署审批示例占 3,131 字节 JSON,对照的手写版本约有 118 行 JSX,尚未包含状态、校验和动作连接代码。不过,这不是严格的成本比较:JSON 背后仍需要 renderer、组件实现和业务处理,不能直接推导出开发量减少多少。
跨端复用也有条件。同一份描述能在 web、Android、iOS 或支持它的助手宿主中展示,前提是各端实现了对应目录。协议统一的是描述方式,各端组件仍然要有人维护。
四类消息,怎样把静态卡片变成交互界面
A2UI 用四类消息管理界面区域,也就是 surface:
createSurface创建区域,1.0 支持内联components和初始dataModel,一次消息即可送达完整界面,替代 0.9 的三消息流程。updateComponents添加或替换组件定义。updateDataModel按 JSON Pointer 路径替换数据。deleteSurface删除区域。
组件采用扁平列表,容器通过 id 引用子组件,其中必须有一个组件的 id 为 root。这样可以单独更新某个组件,也允许组件按不同顺序到达。结构与数据分离后,更新价格或输入值不必重发整张界面。
属性有三种来源:直接写值、通过 {"@path": "/user/name"} 读取数据,或通过 {"@call": "formatCurrency", "args": {...}} 调用目录函数。输入组件会把用户输入写回绑定路径,其他读取该路径的组件随之更新。它实际承担了一部分前端状态绑定工作。
基础目录还提供 formatString、formatNumber、formatCurrency、formatDate、pluralize 等格式化函数,以及供 @checks 使用的 required、email、regex、length、numeric 校验函数。原文称 regex 会先对模式做静态安全检查,但没有展开检查覆盖范围。
列表则用模板复用组件:返回 40 条搜索结果时,Agent 可以只描述一行,再绑定数组路径;${@index()} 提供当前项编号。这能减少重复描述,但原文没有给出 token 节省的实测数据。
按钮点击后,renderer 按点击时的数据解析 action context。1.0 还增加了 callAgentFunction 和 callRendererFunction,支持界面向 Agent 请求检查,以及 Agent 请求客户端能力。
消息可以经 AG-UI、A2A、MCP、SSE 或 WebSockets 传递,但必须有序、按独立 JSON 对象分帧,并支持元数据。流式体验也不是接通 SSE 就有了:Agent 要先发骨架,再补组件和数据。等完整 JSON 生成后才发送,首屏等待仍然很长。
Next.js 接入已经跑通,坑集中在哪
按原文,A2UI 1.0 于 2026 年 11 月 11 日稳定,但作者测试时 @a2ui/react 和 @a2ui/web_core 都是 0.15.0。安装命令是 npm install @a2ui/react @a2ui/web_core,作者建议随后锁定精确版本,因为 0.13、0.14、0.15 都调整过 v1.0 消息读取方式。
第一个坑是服务端加载。renderer 导入时会注册 custom elements,在预渲染阶段可能触发 ReferenceError: HTMLElement is not defined。作者用下面的客户端包装解决:
"use client";
import dynamic from "next/dynamic";
// The A2UI React renderer registers custom elements when it loads, so it
// cannot be imported on the server at all. Load the surface client-only.
export const AgentScreen = dynamic(
() => import("./AgentScreen").then((m) => m.AgentScreen),
{ ssr: false, loading: () => <p>Waiting for the agent...</p> },
);
关键是 { ssr: false },而且这层包装本身位于 client component 中。
接下来,从 @a2ui/web_core/v1_0 导入 MessageProcessor 和 basicCatalog,通过 processMessages(agentMessages) 喂入消息,再交给 A2uiSurface 渲染。作者特别指出,要显式声明 MessageProcessor<ReactCatalogComponent>;省略泛型会产生 A2uiSurface 无法接受的类型推断结果。
作者称示例通过了类型检查,并实际点击验证。界面展示了构建 dpl_8f3ka、提交 5037730,以及 214 个测试通过、0 个失败。选择 Preview、输入备注后,回调收到:
{
"name": "confirm_deploy",
"context": {
"environment": ["preview"],
"note": "Ship the A2UI deep dive"
}
}
这里要看 environment:即便 ChoicePicker 使用 mutuallyExclusive,实测仍返回数组。后端处理器若直接按字符串读取,就可能出错。
这次验证证明了固定消息的渲染、数据绑定和动作回传能够连通。它没有证明真实模型持续生成的消息都合法,也没有给出断线恢复、复杂表单或长期运行的测试结果。提供的原文还在限制章节中途截断,无法据此补全作者后续结论。
我的判断
A2UI 最有价值的地方,是把模型能生成的界面限制在可验证的组件契约里。
但「只能使用注册组件」约束的是表达能力,无法保证表达意图正确。合法卡片也可能把安全按钮文案绑定到危险动作。目录函数可以区分 renderer、Agent 或双方的调用权限,要求用户手势的 openUrl 可以设为 renderer-only;这些机制仍不能代替业务授权。
因此,作者提出的两条边界很合理:渲染前按目录 schema 校验,动作到达服务端后重新鉴权。sendDataModel 还可能把当前模型随动作传回,敏感字段是否进入 Agent,应当按表单数据流逐项决定。原文提到服务端绑定可以让展示值绕过模型,但没有给出接入实现。
选型也要分层看。AG-UI 管交互事件和传输,A2UI 管界面描述,两者可以配合。MCP Apps 通过 ui:// HTML 资源和沙箱 iframe 承载真实网页,布局更自由;A2UI 更依赖宿主组件,两者也能组合。
对单一产品、少量固定卡片,带版本和校验的自定义 JSON 仍然合理。跨团队、跨端需求增加后,标准协议才更可能抵消接入成本。作者提供了可运行案例,却没有维护成本、延迟或错误率的对照数据,这部分应当视作工程经验。
能拿来做什么
- 从一个部署审批卡片开始试验,把环境、构建信息和备注放在同一界面,验证用户能否明确理解确认对象,再决定是否扩大使用。
- 在 Next.js 中锁定 renderer 与 core 的精确版本,采用客户端动态加载,并记录真实 action payload,优先核对选择器等组件的返回类型。
- 在 Node 服务端分别校验界面消息和动作请求;对
confirm_deploy重新检查用户权限、目标环境及允许操作的构建,不能直接信任 context。 - 如果已有 SSE 或 AG-UI 通道,尝试先发送
createSurface骨架,再发送数据更新,分别观察首次可用界面和完整数据到达的时间,判断流式组织是否带来实际改善。