三大Agent-UI协议

AI产品形态的进化

维度 Chatbot Agent
运行时间 短(毫秒级) 长(秒/分钟级)
输出类型 纯文本 文本 + 结构化数据 + UI 控制
状态管理 无状态 复杂状态机
交互模式 单轮 Q&A 多轮工具调用 + 人机协作

Agent 越来越聪明,但交互若停留在"聊天框时代",则复杂任务效率低、体验差、用户流失

三种协议的定位

维度 AG-UI A2UI MCP-UI
解决的问题和效果 Agent 的输出并不是一次性完成时的用户体验问题
思考中…
调用搜索…
分析结果…
生成答案…
虽告诉前端发生了什么,但仍未解决答案仅用文字应答不够好的体验问题
周一 30℃
周二 29℃…
不如直接返回天气卡片
已经用 MCP 了,顺便给工具加上 UI
核心理念 事件驱动的状态同步
不关心 UI 长什么样,只负责把 Agent 的状态变化广播出去。让前端实时感知 Agent 的每一步思考(状态变化)
声明式UI组件规范
Agent 描述UI意图(UI结构描述),客户端负责渲染
MCP工具的UI扩展
UI控制权 前端主导 后端主导(结构) 后端主导(内容代码)
安全模型 前端自定义 白名单标准组件 iframe沙箱
来源 CopilotKit
docs.ag-ui.com
Google
a2ui.org
社区
MCP-UI
核心机制 事件类型系统(20+种)
- 生命周期事件
- 消息流事件
- 工具调用事件
- 状态同步事件
声明式的邻接表组件描述
- 不采用传统UI的嵌套结构
- 标准组件库涵盖常见UI需求
不发明协议,三种渲染模式
- 内联 HTML (Raw HTML)
- 外部页面 (External URL)
- 远程 DOM (Remote DOM)
kanban
  AG-UI
    Frontend:React+RxJs
    EventRouter:20+事件类型
    AgentServer:Python/TS
    LLM
  A2UI
    LLM 生成 JSON 组件
    白名单组件目录:安全
    平台渲染器适配
    原生优先:web/ios/android/flutter
  MCP-UI
    MCP Server: Tool Result
    HTML/URI-List/Remote-Dom
    IFrame/Web Worker
    Host App:Actions

补充解读

  1. AG-UIA2UI 名字看起来像,但做的事差别很大,可以说有互补性。复杂场景下,三种协议可同时使用,也并不冲突

    1. AG-UI:做总线,负责事件路由和状态同步

    2. A2UI:负责复杂的、跨平台的声明式 UI

    3. MCP-UI:负责工具级别的快速 UI 扩展

    AG-UI 可以作为 A2UI 的传输层,MCP-UI 可以通过中间件集成到 AG-UI

    个人观点 A2UI 会吞并/挤占AG-UI的生存空间,没办法,AG-UI属于 Think Small,画地为牢,价值范畴没有迭代飞轮设计,太无想象力

  2. MCP-UI 定义了五种 UI 交互类型:

    1. tool:触发工具调用(如"预订"按钮)
    2. prompt:发送新的用户消息
    3. link:打开外部链接
    4. intent:触发应用内意图
    5. notify:显示通知消息
  3. A2UI的概念很有趣:不是你先把整个UI手写好,再让Agent塞内容进去;而是让Agent根据既定的UI Widget清单,自己决定组成丰富又可互动的界面。除了Web,App和Desktop端也能干

    1. A2UI拥有Flutter、Web Components和Angular的客户端库,以后可能会扩展到其它库

    2. A2UI将UI结构和UI实现分离,Agent发送组件树及其关联数据模型的描述(基于JSONL格式,利于渐进式渲染),客户端负责将这些抽象描述映射到原生小部件。

    3. 数据模型与组件结构分离:组件可以通过 path 绑定数据,更新数据无需重新发送组件结构,多个组件可绑定同一数据路径,LLM 也可以分步生成结构和数据

       flowchart LR
        subgraph Input["📥 输入"]
            JSONL["JSONL Stream"]
        end
        
        subgraph Process["⚙️ 处理流程"]
            JSONL --> Parse["解析消息"]
            Parse --> Build["构建组件树"]
            Build --> Bind["数据绑定"]
            Bind --> Render["原生渲染"]
        end
        
        subgraph Output["📱 输出"]
            Render --> UI["原生 UI 组件"]
        end
        
        subgraph DataFlow["📊 数据流"]
            DM[("DataModel")] -.->|"/path/to/data"| Bind
        end

实现A2UI是不是需要模型内置支持?

不需要。它不要求模型进行任何特殊的、底层的硬编码。这意味着它本身不生成可执行代码,只是模型输出的一种格式。现有的LLM通过简单的“即时工程(Prompt Engineering)”就能满足A2UI的生成要求,其核心和生成JSON格式的数据没有本质区别。

如何让现有模型“学会”A2UI?

  1. 核心方法:轻量级即时工程(Prompt Engineering)

    通过巧妙设计提示词(Prompt),可以为模型注入关于A2UI组件库、JSON Schema的范例和模式等上下文信息,以此“教会”模型输出特定格式的A2UI JSON。这是最主流、最经济的方式。

  2. 备选方案:使用函数调用(Function Calling)

    预先定义一个“生成UI”的工具函数,模型在需要生成界面时,无需自己编写JSON,只需通过Function Calling调用这个函数并传递参数即可

Demo项目

MadLongTom/demo-agent-ui-protocols 验证有效。在windows上直接运行,run.sh需要改成powershell脚本。适配dsv4 ag-ui demo也需要小幅修改。都不难,结合AI编程工具可轻松解决

AG-UI实现

stateDiagram-v2
    [*] --> Idle: 初始化
    Idle --> Running: RUN_STARTED
    state Running {
        [*] --> Streaming
        Streaming --> ToolCalling: TOOL_CALL_START
        ToolCalling --> Streaming: TOOL_CALL_RESULT
        Streaming --> Streaming: TEXT_MESSAGE_CONTENT
    }
    Running --> Idle: RUN_FINISHED
    Running --> Error: RUN_ERROR
    Error --> Idle: 重试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[SSE Stream]
event: RUN_STARTED
data: {"runId":"run_001","threadId":"thread_001"}

event: TEXT_MESSAGE_START
data: {"messageId":"msg_001","role":"assistant"}

event: TEXT_MESSAGE_CONTENT
data: {"messageId":"msg_001","delta":"好的,我来帮您"}

event: TEXT_MESSAGE_CONTENT
data: {"messageId":"msg_001","delta":"搜索北京的川菜馆..."}

event: TOOL_CALL_START
data: {"toolCallId":"call_001","toolCallName":"search_restaurants"}

event: TOOL_CALL_ARGS
data: {"toolCallId":"call_001","delta":"{\"cuisine\":\"川菜\",\"location\":\"北京\"}"}

event: TOOL_CALL_RESULT
data: {"toolCallId":"call_001","result":"[{\"name\":\"川办餐厅\",...}]"}

event: TEXT_MESSAGE_CONTENT
data: {"messageId":"msg_001","delta":"为您找到以下餐厅:"}

event: RUN_FINISHED
data: {"runId":"run_001"}

A2UI实现

1
{"createSurface":{"surfaceId":"results","catalogId":"standard"}}
1
2
3
4
5
{"updateComponents":{"surfaceId":"results","components":[
{"id":"root","component":{"Column":{"children":{"explicitList":["header","list"]}}}},
{"id":"header","component":{"Text":{"text":{"literalString":"推荐餐厅"},"usageHint":"h1"}}},
{"id":"list","component":{"List":{"children":{"template":{"dataBinding":"/restaurants","componentId":"card-template"}}}}}
]}}
1
2
3
4
5

{"updateDataModel":{"surfaceId":"results","path":"/restaurants","value":[
{"name":"川办餐厅","rating":4.8,"price":"$$","image":"..."},
{"name":"眉州东坡","rating":4.5,"price":"$$$","image":"..."}
]}}

MCP-UI实现

Request

1
{ "messages": [{ "role": "user", "content": "帮我找一家北京的川菜馆" }] }

Response

1
2
3
4
5
6
7
8
9
10
11
12
{
"role": "assistant",
"content": "好的,这是搜索结果:",
"ui_resource": {
"type": "resource",
"resource": {
"uri": "ui://restaurant/list",
"mimeType": "text/html",
"text": "<div class='restaurant-list'>..."
}
}
}

展示效果可直接看原博主的文章[1]

参考