AG-UI 事件驱动协议:Agent 与 UI 的通信标准
AG-UI 事件驱动协议
学习目标
理解 AG-UI 协议如何定义 Agent 与 UI 之间的标准化通信:
- 事件生命周期的五个阶段
- SSE 流式传输的编码与解码
- 工具调用如何通过协议在前端和后端之间协同
- 传输模式的自动协商策略
前置知识
本章涉及事件驱动架构和流式传输的通用原理,建议先阅读:
下文假设你已理解上述概念,直接聚焦 CopilotKit 的 AG-UI 协议实现。
项目实践
AG-UI 协议是什么
AG-UI 是一个独立的事件协议标准(由 CopilotKit 团队维护,包名 @ag-ui/core),定义了 Agent 和 UI 之间的结构化通信方式。CopilotKit 的前端和运行时层都通过 AG-UI 事件进行交互。
协议的核心设计理念:Agent 的输出不是一次性的”回复”,而是一系列结构化事件的流。
事件生命周期
每个 Agent 运行遵循固定的事件生命周期:
AG-UI 协议通过 EventType 枚举定义所有事件类型:
| 事件类型 | 生命周期位置 | 说明 |
|---|---|---|
RUN_STARTED | 运行开始 | 携带 threadId、runId |
STEP_STARTED | 步骤开始 | 一个 step 对应一次 LLM 调用 |
TEXT_MESSAGE_START | 文本消息 | 消息开始,标记 role 和 messageId |
TEXT_MESSAGE_CONTENT | 文本消息 | 流式内容增量(delta 字段) |
TEXT_MESSAGE_END | 文本消息 | 消息结束 |
TOOL_CALL_START | 工具调用 | 携带 toolCallId、toolCallName |
TOOL_CALL_ARGS | 工具调用 | 流式参数增量 |
TOOL_CALL_END | 工具调用 | 参数完整 |
TOOL_CALL_RESULT | 工具调用 | 工具执行结果 |
STEP_FINISHED | 步骤结束 | |
RUN_FINISHED | 运行结束 | |
RUN_ERROR | 异常 | 协议级错误事件 |
CUSTOM | 自定义 | 框架自定义事件(如 HITL 中断) |
SSE 传输编码
事件通过 Server-Sent Events(SSE)流式传输到前端。编码规则:
事件格式(伪代码): event: {EventType} id: {eventId} data: {JSON 序列化的事件载荷}
(空行分隔)前端通过 EventSource 或自定义 HTTP 请求(带 Accept: text/event-stream)接收流。
传输模式协商
CopilotKit 支持三种传输模式,由客户端的 runtimeTransport 配置控制:
| 模式 | 说明 | 端点格式 |
|---|---|---|
rest(默认) | 每个操作独立 REST 端点 | /agent/{id}/run、/agent/{id}/connect、/agent/{id}/stop |
single | 所有操作统一 POST 到单端点 | /api/copilotkit,body 中 method 字段区分 |
auto | 自动检测:先试 REST,失败降级到 single | 首次请求 /info 探测 |
自动协商流程(源码验证):
- 客户端发送
GET /info(REST 模式) - 若 2xx 成功 → 使用
rest模式 - 若失败 → 发送
POST {url}+body: { method: "info" }(single 模式) - 若 single 也成功 → 使用
single模式 - 若两者都失败 → 抛出异常
工具调用的前后端协同
AG-UI 协议的关键设计:工具可以在前端执行,也可以在后端执行。
流程:
- Agent 在推理过程中生成
TOOL_CALL_START+TOOL_CALL_ARGS事件 - 前端
CopilotKitCore收到事件,在工具注册表中查找toolCallName - 若找到前端 handler → 在浏览器执行,结果通过 HTTP 回传给 Agent
- 若未找到 → 事件继续传输到后端,后端查找并执行
前端工具执行的结果传递:
前端执行工具 → 结果回传 → Agent 继续推理 → 生成下一个事件这种设计让 Agent 能安全地操作 DOM、触发用户交互、读取浏览器状态——这些是后端无法完成的操作。
问题与规避
| 问题 | 规避策略 |
|---|---|
| SSE 连接在 CDN/代理后被缓冲 | 服务器响应头设置 X-Accel-Buffering: no 和 Cache-Control: no-cache |
| 并发 Agent 运行导致事件交错 | 前端使用 detachActiveRun() 确保每次只有一个活跃 run;isRunning 状态通过生命周期事件桥接 |
| Agent 工具执行超时 | 前端设置合理的 AbortController 超时;ZodError 和 AbortError 在 Observable 管道中被静默处理(EMPTY) |
| 传输模式协商失败 | auto 模式的降级逻辑只在首次运行触发,之后缓存结果;若运行时切换模式需要重新协商 |
RUN_ERROR 事件未重置前端状态 | 前端 onRunErrorEvent 回调负责重置 isRunning;与 onRunFailed(本地异常)分开处理 |
设计取舍
为什么用 AG-UI 协议而非直接调用 LLM API?
优势:
- 标准化事件层屏蔽不同 Agent 框架的差异(LangGraph、CrewAI、OpenAI 等)
- 事件驱动的细粒度 UI 更新比”等待完整回复”用户体验更好
- 前端工具执行模型让 Agent 能操作浏览器
代价:
- 增加了一层抽象,调试时需要理解事件流而非直接的请求/响应
- 前端需要维护事件解析和状态管理逻辑
为什么提供三种传输模式?
REST 模式(默认):
- 优势:符合 RESTful 设计,每个操作有明确语义,便于 HTTP 中间件处理
- 代价:多个端点增加服务器配置复杂度
Single 模式:
- 优势:一个端点处理所有操作,简化部署(尤其 Serverless 环境)
- 代价:请求 body 需要
method字段包装,非标准 REST
Auto 模式:
- 优势:开发者无需关心传输细节
- 代价:首次请求有额外的协商延迟
参考来源
@ag-ui/core包源码(EventType 枚举定义)@ag-ui/client包(HttpAgent、事件流转换)- CopilotKit 架构文档