跳到主要内容

AG-UI — 架构与原理

30 秒导读: AG-UI 是一个开放的、基于事件的协议,专门解决「AI agent 后端怎么把它正在做的事,实时、标准地告诉用户界面」这件事。后端只管吐出一串约 16 种类型的事件(TEXT_MESSAGE_CONTENTTOOL_CALL_STARTSTATE_DELTA……),前端 SDK 把这串事件回放成「聊天消息 + 一份共享状态」。它和 MCP(给 agent 工具)、A2A(agent 之间通信)是互补的三块协议。

1. 这是什么(零基础也能懂)

一句话定义: AG-UI(Agent-User Interaction Protocol,「智能体—用户交互协议」)是一套事件格式 + 事件顺序规则 + 客户端 SDK,让任意 AI agent 后端能用统一的方式,把「我正在打字 / 我要调一个工具 / 我改了共享状态 / 我跑完了」这些过程流式地推给前端。

解决什么问题 / 给谁用。 假设你做了个 LangGraph / CrewAI / 自研的 agent,想把它接到一个聊天 UI 里。痛点是:

  • agent 后端的输出五花八门(OpenAI 风格、各家框架自有格式),前端要为每一种写一套解析。
  • 你想要「打字机效果」「工具调用进度」「实时改一个表单」,这些都需要增量(一个 token 一个 token、一个 JSON Patch 一个 Patch)地传,而不是等全部算完再返回。

AG-UI 把这层「中间语言」标准化:后端只要会吐 AG-UI 事件,任何 AG-UI 前端就能渲染它。给谁用——做 in-app agent 聊天 / copilot 的应用开发者,以及做 agent 框架集成的人(LangGraph、Mastra、Pydantic-AI 等都有官方集成)。

它能做什么(协议层面的功能):

  • 实时流式聊天(逐 token)。
  • 工具调用的全过程可视化(开始 / 参数流 / 结束 / 结果)。
  • 双向共享状态同步(后端改一个 JSON,前端跟着变;反之亦然)。
  • 「思考过程」(reasoning)流式展示,支持加密(零数据保留模式)。
  • Human-in-the-loop:agent 跑到一半「中断」,等人批准/编辑后再「恢复」。
  • 生成式 UI / 结构化消息、多模态输入(图/音/视频/文档)。

用起来什么样。 前端把一个 agent 包成对象,调 runAgent(),然后订阅事件:

// 示意,非源码:典型的前端调用方式
import { HttpAgent } from "@ag-ui/client";

const agent = new HttpAgent({ url: "https://my-backend/agui" });

agent.subscribe({
onTextMessageContentEvent: ({ textMessageBuffer }) => {
render(textMessageBuffer); // 打字机效果:每来一个 delta 就重画
},
onStateDeltaEvent: ({ state }) => {
syncForm(state); // 后端改了共享状态,前端表单跟着更新
},
});

await agent.runAgent({ tools, context }); // 跑一轮,Promise resolve 时这轮结束
// 跑完后 agent.messages / agent.state 就是最新的完整状态

后端那边吐的是一条 SSE 流,每行长这样(真实 wire 格式):

data: {"type":"RUN_STARTED","threadId":"t1","runId":"r1"}
data: {"type":"TEXT_MESSAGE_START","messageId":"m1","role":"assistant"}
data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"m1","delta":"Hel"}
data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"m1","delta":"lo"}
data: {"type":"TEXT_MESSAGE_END","messageId":"m1"}
data: {"type":"RUN_FINISHED","threadId":"t1","runId":"r1"}

一句话直觉/类比。 把 AG-UI 想成 agent 世界的「视频流协议 + 字幕轨」:后端不是一次性返回成片,而是边算边推帧(每个 delta 是一帧);前端是个播放器,按帧累积出完整画面(消息)和一份随时间变化的元数据(共享状态)。协议的核心价值,就是规定了「帧的类型」和「帧的合法播放顺序」。

2. 顶层全景(它大概怎么转)

AG-UI 的代码分两半:协议契约(纯类型/schema,各语言一份)和客户端运行时(把事件流回放成状态的引擎,目前 TypeScript 最完整)。

一次完整对话的数据流(以 HTTP+SSE 为例,从左到右):

后端 agent 前端 (@ag-ui/client)
┌──────────┐ SSE 事件流 ┌──────────────────────────────────────────────┐
│ 吐 AG-UI │ ─────────────▶ │ ① 解码 ② chunk展开 ③ verify ④ apply │
│ 事件 │ data:{...}\n\n │ SSE→JSON CHUNK→START/ 顺序 事件→消息/ │
└──────────┘ │ CONTENT 合法? 状态(reduce) │
└───────────────────────────────┬──────────────┘
│ ⑤ 写回 agent.messages
▼ / agent.state
订阅者回调 (渲染 UI)

主要部件一句话职责:

部件干什么在哪个包/文件
事件 schema(EventType + 各 *EventSchema)定义 ~16 种事件的字段与校验(Zod)packages/core/src/events.ts
数据模型(Message/Tool/RunAgentInput/Interrupt)定义消息、工具、运行输入、中断的形状packages/core/src/types.ts
能力声明(AgentCapabilities)agent 自报支持哪些特性(流式/工具/中断…)packages/core/src/capabilities.ts
AbstractAgent客户端运行时核心:runAgent() 串起整条流水线packages/client/src/agent/agent.ts
transformChunks把「chunk 事件」展开成标准的 start/content/endpackages/client/src/chunks/transform.ts
verifyEvents流的「语法检查」状态机:顺序非法就报错packages/client/src/verify/verify.ts
defaultApplyEventsreducer:把事件折叠进 messages/statepackages/client/src/apply/default.ts
HttpAgent + transport发 HTTP 请求、按 content-type 选 SSE/protobuf 解码packages/client/src/agent/http.tstransform/*

主线走一遍(高层,不进代码):

  1. 你调 agent.runAgent(params)AbstractAgent 把当前 messages/state/你传的 tools/context 打包成一个 RunAgentInput(agent.ts:380 prepareRunAgentInput)。
  2. 子类的 run(input) 真正去取事件流。HttpAgent 发一个 POST,拿回 SSE,解码成一串 BaseEvent
  3. 这串事件依次过 chunk 展开 → verify 顺序校验 → apply 折叠成状态(agent.ts:194pipe(...))。
  4. 每产生一次状态变化(消息变了 / 状态变了),就写回 agent.messages / agent.state,并通知所有订阅者(agent.ts:347 processApplyEvents)。
  5. 收到 RUN_FINISHED 且没有 outstanding 的消息/工具,流结束,runAgent() 的 Promise resolve,返回这轮新增的消息。

阅读地图(建议顺序)

  1. 01-event-protocol.md — 先搞懂「协议本体」:那 ~16 种事件、它们的字段、以及什么顺序才合法(生命周期)。这是理解后面一切的基础。
  2. 02-agent-loop.md — 客户端怎么把事件流跑起来:runAgent() 背后的 5 道 rxjs 工序、中间件、订阅者、中断/恢复。
  3. 03-verify-and-apply.md — 两个最值得读的算法:verifyEvents(流的状态机校验)和 defaultApplyEvents(事件→消息/状态的 reducer)。精华在这。
  4. 04-transport-cross-language.md — 底层:SSE 与 protobuf 解码、chunk 事件存在的理由、TS 和 Python 怎么共享同一份协议契约。

想快速判断相关性:只关心「协议有哪些事件、字段、顺序」→ 读 ch01;关心「前端怎么消费/集成」→ 读 ch02;关心「实现精华/算法」→ 读 ch03;关心「传输与多语言一致性」→ 读 ch04。