用 A2UI 与 CopilotKit 构建智能体界面
介绍如何通过 CopilotKit 的 A2UI SDK 原生渲染智能体界面,覆盖 LangChain、AWS Strands、Microsoft 智能体和 Google ADK。
AI 深度解读
Agent 能推荐航班,却只能把航空公司、起降时间、经停次数塞进一大段文字,用户还得继续追问、跳转页面。A2UI 想补上这层交互:让 Agent 描述界面,由应用把它画成可操作的卡片、表单和按钮。
Google Developers Blog 这篇文章给出的组合是 A2UI + CopilotKit,前者描述界面,后者连接 Agent 与应用。值得看的不只是如何生成一张卡片,而是组件权限、数据校验、事件回传怎样串起来,让对话承载多步业务流程。
Agent 负责组合,前端仍然掌握组件
生成式 UI(Generative UI)并不只有一种实现方式。
控制较强的一端,是前端提前写好组件,Agent 只选择组件并填数据。品牌和交互容易统一,但个性化空间有限。开放的一端,是让模型生成 HTML、JavaScript 或 Dart,放进沙箱 iframe 执行。覆盖面更广,也要面对设计风格偏移和模型代码执行的问题。
原文说 A2UI 可以参与不同程度的生成,重点介绍的是声明式模式:应用提供组件目录,Agent 用结构化数据描述如何组合组件。
模型决定展示什么,应用决定允许什么、怎样渲染。
这里的组件目录不只是名称列表,还包含组件用途、属性结构以及对应的渲染实现。模型可以使用目录里的能力;目录外凭空生成的组件,会在显示前被检查出来。
A2UI 的消息是通过 ID 相互引用的小型 JSON 消息组成的扁平列表,适合逐步生成、逐步渲染。它不绑定特定传输方式,也不绑定单一前端框架。原文列出了 React、Lit、Angular、SwiftUI、Jetpack Compose 和 Flutter,以及社区提供的 Vue、终端渲染器。
不过,可移植的是界面描述,不代表一份 React 组件代码就能直接跑到 Flutter。各端仍需要相应的渲染实现。
Basic Catalog 提供了 18 个通用基础组件,覆盖布局、展示、输入和容器。生产应用通常还要定义自己的组件,例如地图:Agent 提供坐标,已有地图组件负责绘制路线。前端积累的业务组件因此仍然有用,只是多了一个由 Agent 驱动的入口。
A2UI、AG-UI 和 CopilotKit 各管哪一段
这三个名字容易混在一起,可以沿着一次交互拆开看。
A2UI 表达界面意图;AG-UI(Agent-User Interaction Protocol)把相关操作作为类型化事件传到客户端,并承载用户操作的回传;CopilotKit 提供接入 Agent 的工具、运行时和聊天界面,使用渲染器把结果显示出来。
原文列出的兼容后端包括 Google ADK、LangChain、Mastra 和 AWS Strands。它以 Google ADK 为例,通过 ag_ui_adk 包里的 ADKAgent 包装 LlmAgent,再用 add_adk_fastapi_endpoint 暴露 FastAPI 端点,路径为 /。
示例使用的模型字符串是 gemini-3.8-flash,指令要求调用 generate_a2ui。这是原文给出的配置,文章没有提供模型可用范围或实测数据。
应用侧再把 Agent 端点接入 /api/copilotkit。真正决定能画什么的配置,在客户端组件目录里。下面这段代码把业务组件 FlightCard 的用途、属性和 React 实现绑定起来:
import { CopilotKit, CopilotChat } from '@copilotkit/react-core/v2';
import {
createCatalog,
type CatalogDefinitions,
type CatalogRenderers,
} from '@copilotkit/a2ui-renderer';
import { z } from 'zod';
const catalogDefinitions = {
FlightCard: {
description: 'A flight result with airline, times, and stops.',
props: z.object({
airline: z.string(),
departs: z.string(),
arrives: z.string(),
stops: z.number(),
}),
},
} satisfies CatalogDefinitions;
const catalogRenderers = {
FlightCard: MyFlightCard, // 你的 React 组件
} satisfies CatalogRenderers<typeof catalogDefinitions>;
const catalog = createCatalog(catalogDefinitions, catalogRenderers, {
catalogId: 'flight-catalog',
includeBasicCatalog: true,
});
<CopilotKit runtimeUrl="/api/copilotkit" a2ui={{ catalog }}>
<CopilotChat />
</CopilotKit>
要留意 description 和 props:前者会进入 Agent 的提示词,帮助模型选对组件;后者通过 Zod 校验返回的属性。MyFlightCard 仍然需要开发者自己实现。
目录既可以预先配置在服务端,也可以随请求从客户端动态发送。原文解释,ADK 示例采用动态配置,所以没有手动定义 generate_a2ui。
如果不需要 CopilotKit 提供的整套界面,也可以用 @ag-ui/client 直接消费事件流,再交给 @a2ui/react 或其他兼容渲染器。这条路径提供了更多接入自由,也把相应的集成工作交回团队。
多步交互靠数据绑定,不靠每次都问模型
生成一张结果卡片并不难,难的是用户修改条件之后,界面与 Agent 能继续保持一致。
原文的做法是把输入绑定到数据路径,再让按钮声明需要回传哪些路径。下面的片段展示了舱位选择和确认动作:
{ "id": "cabin", "component": "ChoicePicker", "variant": "mutuallyExclusive",
"options": [ { "label": "Economy", "value": "eco" },
{ "label": "Business", "value": "biz" } ],
"value": { "path": "/booking/cabin" } },
{ "id": "confirm", "component": "Button", "child": "confirm_label",
"action": { "event": { "name": "confirmBooking",
"context": { "cabin": { "path": "/booking/cabin" },
"email": { "path": "/booking/email" } } } } }
关键在 value.path 和 action.event.context:选择器修改 /booking/cabin,按钮触发 confirmBooking 时,再读取舱位和邮箱。它是消息片段,不能当作完整 JSON 文档直接使用。
客户端可以先执行表单校验,例如在邮箱格式错误时就地提示,不必调用模型。按钮触发后,绑定值才发给 Agent;Agent 使用同一个 surfaceId 重新输出界面,让搜索、结果、乘客信息和确认停留在同一交互表面。
原文还列出了生成链路的配套机制:专用子 Agent 接收目录和协议规则,减少它们对主 Agent 上下文的占用;无效输出携带具体错误返回修正;重试耗尽后,用短消息结束该界面,让对话继续。
这些机制解释了它打算如何处理失败,但文章没有给出修复成功率、重试次数、延迟或调用成本。
我的判断
我认同它把生成能力接到已有组件体系上的方向。对已经有设计系统的团队,声明式目录比直接执行模型生成的 UI 代码更容易建立边界,也便于复用业务组件和前端校验。
但「可信组件」不等于「可信业务操作」。属性符合 Zod 结构,只能证明数据形状符合要求;航班是否仍有余票、价格是否有效、当前用户能否确认订单,仍要由业务后端检查。原文没有展开这一层,不能把渲染校验理解成完整的业务安全保障。
它也没有证明生成式 UI 一定比固定页面好用。文章提供了代码、架构说明和可运行演示入口,没有用户完成率、错误率或与传统表单的对照数据。因此,多步流程更顺畅、上下文更节省等收益,目前应视为方案主张,不能直接当成团队落地后的结果。
固定且高频的流程,用普通表单或现成组件加工具调用,可能更省维护成本。A2UI 更值得验证的地方,是请求差异较大、展示组合经常变化,同时又能用有限组件表达的任务。
它减少的是逐个编写展示组合的工作,组件设计、状态约定和业务验证仍要有人负责。
能拿来做什么
- 从已有 React 业务组件中挑一个边界清楚的结果卡片,补齐用途描述和 Zod 属性约束,先验证 Agent 是否能稳定选对组件、填对数据,再扩大目录。
- 对多步表单先定义数据路径、按钮事件和服务端校验。把即时格式校验留在客户端,在需要 Agent 推进流程时提交状态,避免每次输入都触发生成。
- 根据现有架构选择接入层。需要完整聊天界面和运行时可以评估 CopilotKit;已经有交互容器的团队,可以研究
@ag-ui/client配合兼容渲染器的方式。 - 用原文命令
npx copilotkit@latest create --name my-app --framework a2ui创建验证项目,重点检查无效属性、未知组件、生成中断和重试耗尽时的表现。文章未固定依赖版本,接入正式项目之前应锁定验证过的版本。