Agent-to-UI(A2UI)协议
A2UI 概览
Section titled “A2UI 概览”A2UI 是 A2A 的一个声明式 UI 协议扩展,允许智能体发出描述交互界面的结构化 JSON 消息。客户端接收这些消息后,会将其渲染为富 UI 组件,例如表单、卡片、列表、模态框等,而智能体无需了解客户端的渲染栈。
A2UI 建立在 A2A 扩展机制之上,使用 URI https://a2ui.org/a2a-extension/a2ui/v0.8 进行标识。
- 服务端扩展 扫描智能体输出中的 A2UI JSON 对象
- 有效消息会作为带有
application/json+a2uiMIME 类型的DataPart条目包装 - 客户端扩展 会把 A2UI 指令和组件目录附加到智能体的系统提示中
- 客户端会在会话轮次之间跟踪界面状态(活动界面和数据模型)
将 A2UIServerExtension 添加到 A2AServerConfig 中即可启用 A2UI 输出:
from crewai import Agentfrom crewai.a2a import A2AServerConfigfrom crewai.a2a.extensions.a2ui import A2UIServerExtension
agent = Agent( role="Dashboard Agent", goal="Present data through interactive UI surfaces", backstory="Expert at building clear, actionable dashboards", llm="gpt-4o", a2a=A2AServerConfig( url="https://your-server.com", server_extensions=[A2UIServerExtension()], ),)服务端扩展选项
Section titled “服务端扩展选项”catalog_ids list[str] | None default: None 服务端支持的组件目录标识符。设置后,只有这些目录会向客户端公开。
accept_inline_catalogs bool default: False 是否接受客户端传入的内联目录定义,除已命名目录外也允许。
将 A2UIClientExtension 添加到 A2AClientConfig 中即可启用 A2UI 渲染:
from crewai import Agentfrom crewai.a2a import A2AClientConfigfrom crewai.a2a.extensions.a2ui import A2UIClientExtension
agent = Agent( role="UI Coordinator", goal="Coordinate tasks and render agent responses as rich UI", backstory="Expert at presenting agent output in interactive formats", llm="gpt-4o", a2a=A2AClientConfig( endpoint="https://dashboard-agent.example.com/.well-known/agent-card.json", client_extensions=[A2UIClientExtension()], ),)客户端扩展选项
Section titled “客户端扩展选项”catalog_id str | None default: None 首选组件目录标识符。未设置时默认为 "standard (v0.8)"。
allowed_components list[str] | None default: None 限制智能体可使用的组件。为 None 时,标准目录中的全部 18 个组件都可用。
A2UI 定义了四种服务端到客户端的消息类型。每条消息都指向一个由 surfaceId 标识的界面。
初始化一个新界面,包含根组件和可选样式。
{ "beginRendering": { "surfaceId": "dashboard-1", "root": "main-column", "catalogId": "standard (v0.8)", "styles": { "primaryColor": "#EB6658" } }}在现有界面上发送或更新一个或多个组件。
{ "surfaceUpdate": { "surfaceId": "dashboard-1", "components": [ { "id": "main-column", "component": { "Column": { "children": { "explicitList": ["title", "content"] }, "alignment": "start" } } }, { "id": "title", "component": { "Text": { "text": { "literalString": "Dashboard" }, "usageHint": "h1" } } } ] }}更新绑定到界面的数据模型,从而启用动态内容。
{ "dataModelUpdate": { "surfaceId": "dashboard-1", "path": "/data/model", "contents": [ { "key": "userName", "valueString": "Alice" }, { "key": "score", "valueNumber": 42 } ] }}删除一个界面及其所有组件。
{ "deleteSurface": { "surfaceId": "dashboard-1" }}A2UI 内置 18 个标准组件,分为三类:
| 组件 | 说明 | 必填字段 |
|---|---|---|
| Text | 渲染文本,可带标题/正文提示 | text (StringBinding) |
| Image | 显示图片,可配置适配和尺寸 | url (StringBinding) |
| Icon | 从 47 个图标中渲染指定图标 | name (IconBinding) |
| Video | 嵌入视频播放器 | url (StringBinding) |
| AudioPlayer | 嵌入音频播放器,可选描述 | url (StringBinding) |
| 组件 | 说明 | 必填字段 |
|---|---|---|
| Row | 水平 flex 容器 | children (ChildrenDef) |
| Column | 垂直 flex 容器 | children (ChildrenDef) |
| List | 可滚动列表(垂直或水平) | children (ChildrenDef) |
| Card | 单子元素的抬升容器 | child (str) |
| Tabs | 选项卡容器 | tabItems (list of TabItem) |
| Divider | 视觉分隔线(水平或垂直) | — |
| Modal | 由入口点触发的覆盖层 | entryPointChild, contentChild (str) |
| 组件 | 说明 | 必填字段 |
|---|---|---|
| Button | 可点击按钮,触发操作 | child (str), action (Action) |
| CheckBox | 带标签的布尔开关 | label (StringBinding), value (BooleanBinding) |
| TextField | 支持类型和校验选项的文本输入框 | label (StringBinding) |
| DateTimeInput | 日期和/或时间选择器 | value (StringBinding) |
| MultipleChoice | 从选项列表中选择 | selections (ArrayBinding), options (list) |
| Slider | 数值范围滑块 | value (NumberBinding) |
组件通过绑定而不是原始字面量来引用值。这样当数据模型变化时,界面就能动态更新。
有两种方式可以绑定值:
- 字面量值 - 直接在组件定义中硬编码
- 路径引用 - 指向界面数据模型中的某个键
{ "surfaceUpdate": { "surfaceId": "profile-1", "components": [ { "id": "greeting", "component": { "Text": { "text": { "path": "/data/model/userName" }, "usageHint": "h2" } } }, { "id": "status", "component": { "Text": { "text": { "literalString": "Online" }, "usageHint": "caption" } } } ] }}在这个示例中,greeting 会从数据模型读取用户姓名(由 dataModelUpdate 更新),而 status 则使用硬编码字面量。
处理用户操作
Section titled “处理用户操作”像 Button 这样的交互组件会触发回传给服务端的 userAction 事件。每个操作都包含 name、来源 surfaceId 和 sourceComponentId,以及一个可选的包含键值对的 context。
{ "userAction": { "name": "submitForm", "surfaceId": "form-1", "sourceComponentId": "submit-btn", "timestamp": "2026-03-12T10:00:00Z", "context": { "selectedOption": "optionA" } }}操作上下文值也可以使用路径绑定,将当前数据模型值回传给服务端:
{ "Button": { "child": "confirm-label", "action": { "name": "confirm", "context": [ { "key": "currentScore", "value": { "path": "/data/model/score" } } ] } }}使用 validate_a2ui_message 验证服务端到客户端的消息,使用 validate_a2ui_event 验证客户端到服务端的事件:
from crewai.a2a.extensions.a2ui import validate_a2ui_messagefrom crewai.a2a.extensions.a2ui.validator import ( validate_a2ui_event, A2UIValidationError,)
# Validate a server messagetry: msg = validate_a2ui_message({"beginRendering": {"surfaceId": "s1", "root": "r1"}})except A2UIValidationError as exc: print(exc.errors)
# Validate a client eventtry: event = validate_a2ui_event({ "userAction": { "name": "click", "surfaceId": "s1", "sourceComponentId": "btn-1", "timestamp": "2026-03-12T10:00:00Z", } })except A2UIValidationError as exc: print(exc.errors)从简单开始
先使用一条 beginRendering 消息和一条 surfaceUpdate。等基础流程可用后,再加入数据绑定和交互。
用数据绑定承载动态内容
对于会变化的内容,优先使用路径绑定而不是字面量。通过 dataModelUpdate 推送新值,而不是重新发送完整组件树。
限制组件范围
在 A2UIClientExtension 上使用 allowed_components 选项限制智能体可输出的组件,从而减小提示词体积并保持输出可预测。
验证消息
使用 validate_a2ui_message 和 validate_a2ui_event 尽早捕获格式错误,尤其是在构建自定义集成时。
- A2A Agent Delegation — 配置智能体通过 A2A 协议进行远程委派
- A2A Protocol Documentation — 官方协议规范