/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}
一、一个容易被忽略的问题:Agent 做完了,用户却不知道它做了什么
开发 Agent 应用时,我们通常把大量精力放在后端:
- 如何让 LLM 规划任务?
- 如何调用工具、执行任务?
- 如何管理上下文和记忆?
- 如何实现多 Agent 协作?
- 如何处理任务中断和异常恢复?
但当 Agent 真正接入业务系统时,还有一个问题绕不开:
前端如何感知 Agent 的执行过程?
假设我们开发了一个企业级 AI 研究助手。
用户输入:
帮我调研国内 RAG 技术的发展情况,整理主要厂商、技术路线和市场趋势,最后生成一份报告。
后端 Agent 可能需要经历:
如果前端只收到最终结果,用户看到的就是一个不断转圈的等待界面。
问题在于,Agent 并不是一个简单的 API 请求。它是一个持续运行、可能调用多个工具、甚至需要人工介入的执行过程。
传统的请求-响应模式,很难完整表达这种交互关系。
LangChain 在 DeepAgents 文档中介绍了一个解决方案:AG-UI(Agent User Interaction Protocol)。
它的核心思路是:
把 Agent 的执行过程转换为标准化事件流,让前端能够实时接收消息、工具调用、状态变化和任务生命周期事件。
这也是本文重点讨论的内容。
二、AG-UI 到底是什么?
AG-UI 全称 Agent User Interaction Protocol,是一个开放、轻量级、基于事件的协议。
它主要解决的问题是:
如何建立 Agent 后端与用户界面之间的标准化通信机制。
可以把它理解为 Agent 与前端之间的一层交互协议。
传统模式:
用户
|
v
前端发送 HTTP 请求
|
v
Agent 后端执行任务
|
v
返回最终结果
|
v
前端展示结果
AG-UI 模式:
用户
|
v
前端应用
|
v
AG-UI 协议
|
v
Agent Runtime
|
+—- LLM 推理
|
+—- 工具调用
|
+—- 子 Agent
|
+—- 状态更新
|
v
持续产生事件
|
v
AG-UI
|
v
前端实时更新界面
两者最大的区别:
传统模式关注最终响应,AG-UI 关注整个执行过程。
例如 Agent 正在调用搜索工具,前端可以立即收到工具调用事件;Agent 更新了任务计划,前端也可以同步更新任务列表。
用户不必等到 Agent 完成全部工作,才能知道发生了什么。
三、不要把 AG-UI、MCP、A2A 和 ACP 混为一谈
LangChain 的 DeepAgents 文档将几种协议的职责划分得比较清楚。
| AG-UI | Agent 如何与用户交互 | Agent ↔ 前端 |
| MCP | Agent 如何使用外部工具 | Agent ↔ 工具服务 |
| A2A | Agent 如何与其他 Agent 协作 | Agent ↔ Agent |
| ACP | Agent 如何连接代码编辑器等开发环境 | Agent ↔ IDE |
这里需要特别注意:
MCP 不是 AG-UI 的替代品。
MCP 解决的是工具接入问题,例如 Agent 调用搜索服务、数据库或第三方 API。
AG-UI 解决的是前端交互问题,例如前端如何知道 Agent 正在调用哪个工具、任务执行到哪一步、是否需要用户确认。
两者处于不同的技术层次。
如果把一个完整的 Agent 应用拆开来看:
- MCP 负责工具连接。
- A2A 负责 Agent 之间的协作。
- AG-UI 负责 Agent 与用户界面的交互。
- ACP 负责 Agent 与开发工具环境的交互。
它们可以组合使用,而不是相互竞争。
四、AG-UI 最重要的技术机制:事件流
这是理解 AG-UI 的重点。
AG-UI 并不是简单地把 Agent 的输出文字通过 SSE 推送给前端。
它定义了多种具有明确语义的事件类型。
根据 LangChain 文档,主要事件可以分成以下几类。
1. 任务生命周期事件
RUN_STARTED
RUN_FINISHED
RUN_ERROR
表示一次 Agent 执行任务的开始、结束和异常。
例如:
RUN_STARTED
|
| Agent 开始执行
|
RUN_FINISHED
前端可以利用这些事件管理:
- 加载状态
- 执行状态
- 任务完成提示
- 异常处理
注意,任务完成不一定意味着用户已经拿到了最终答案。某些场景下,Agent 可能是因为等待人工输入而暂停。
2. 节点执行事件
STEP_STARTED
STEP_FINISHED
这两个事件用于表示图执行过程中的节点进度。
如果使用 LangGraph 构建 Agent,可以利用这些事件让前端感知执行进度。
例如:
STEP_STARTED: research
STEP_FINISHED: research
STEP_STARTED: analysis
STEP_FINISHED: analysis
STEP_STARTED: report
STEP_FINISHED: report
前端可以把它展示为:
研究资料 已完成
分析材料 已完成
生成报告 执行中
这样用户就不再面对一个无法解释的等待过程。
3. 消息事件
TEXT_MESSAGE_START
TEXT_MESSAGE_CONTENT
TEXT_MESSAGE_END
这组事件负责处理 Agent 的文本输出。
其中:
- START:消息开始。
- CONTENT:文本内容增量。
- END:消息结束。
例如:
TEXT_MESSAGE_START
TEXT_MESSAGE_CONTENT
delta = "根据本次调研"
TEXT_MESSAGE_CONTENT
delta = "发现以下几个趋势"
TEXT_MESSAGE_END
前端可以逐步拼接 delta,实现类似 ChatGPT 的流式输出。
一个值得注意的细节:
消息事件和任务生命周期事件不是一回事。
一次 Agent Run 可能产生多条消息,也可能包含多次工具调用。
因此,前端不能简单地认为一次 Run 就对应一条完整消息。
4. 工具调用事件
TOOL_CALL_START
TOOL_CALL_ARGS
TOOL_CALL_END
TOOL_CALL_RESULT
这组事件对于 Agent 产品非常重要。
以搜索 Agent 为例:
TOOL_CALL_START
tool = web_search
TOOL_CALL_ARGS
query = "国内 RAG 技术发展"
TOOL_CALL_END
TOOL_CALL_RESULT
result = 搜索结果
前端就可以展示:
正在调用搜索工具
搜索关键词:国内 RAG 技术发展
搜索完成,正在整理结果
这里有个工程上的细节:
工具调用的参数、调用结束和工具返回结果是不同的事件,不应该全部当作普通文本处理。
如果前端想展示工具执行卡片,就需要根据工具调用 ID 和事件类型进行关联。
5. 状态同步事件
STATE_SNAPSHOT
STATE_DELTA
这是我认为 AG-UI 很值得关注的一组设计。
为什么?
因为复杂 Agent 不只是产生文本,还会持续修改内部状态。
例如:
{
"todos": [
{
"task": "收集行业资料",
"status": "completed"
},
{
"task": "分析技术路线",
"status": "in_progress"
},
{
"task": "生成报告",
"status": "pending"
}
]
}
如果每次状态变化都发送完整数据,会产生不必要的数据传输。
AG-UI 提供了两种机制:
STATE_SNAPSHOT:完整状态快照。
前端先获取一个完整的状态基线。
STATE_DELTA:增量状态更新。
后续只发送发生变化的部分。
文档明确指出,STATE_DELTA 使用 JSON Patch(RFC 6902)表达增量变化。
例如:
原始状态:
{
"research": "pending",
"analysis": "pending"
}
发生变化后:
[
{
"op": "replace",
"path": "/research",
"value": "completed"
}
]
前端只需要应用这次变更,不必重新接收整个状态对象。
对于多步骤 Agent、任务列表、子 Agent 状态展示,这种机制很实用。
6. 对话历史事件
MESSAGES_SNAPSHOT
用于同步当前对话消息的完整快照。
它与实时消息增量事件承担不同职责:
- MESSAGES_SNAPSHOT:同步已有消息。
- TEXT_MESSAGE_CONTENT:接收正在生成的文本。
实际开发中,需要同时考虑历史消息初始化和新消息实时更新。
五、DeepAgents 如何接入 AG-UI?
下面结合官方文档,走一遍基本接入过程。
这里有一个容易忽略的地方:
DeepAgents 使用 Python 构建 Agent,但官方快速接入示例中的 @ag-ui/langgraph 适配器是 TypeScript 包。
也就是说,Python Agent 和 AG-UI 适配器并不一定要写在同一个语言环境里。
整体结构:
Python
|
v
DeepAgents
|
v
LangGraph Graph
|
v
LangGraph Server
|
v
@ag-ui/langgraph
|
v
AG-UI Client
|
v
前端界面
第一步:安装 Python 依赖
pip install deepagents "langgraph-cli[inmem]"
如果使用 uv:
uv add deepagents "langgraph-cli[inmem]"
第二步:创建 DeepAgent
新建 agent.py:
from deepagents import create_deep_agent
agent = create_deep_agent(
model="anthropic:claude-sonnet-4-6",
system_prompt="You are an expert researcher.",
)
这里使用 create_deep_agent 创建一个 DeepAgent。
它本质上是一个 LangGraph Graph,可以通过 LangGraph Server 对外提供服务。
实际项目中,可以继续配置工具、子 Agent、Middleware、Backend 等能力。
第三步:配置 LangGraph Server
项目根目录创建 langgraph.json:
{
"dependencies": ["."],
"graphs": {
"deep_agent": "./agent.py:agent"
},
"env": ".env"
}
其中:
- dependencies:项目依赖。
- graphs:注册需要对外提供服务的 Graph。
- env:环境变量文件。
启动服务:
langgraph dev
默认开发服务地址:
http://localhost:2024
至此,Python DeepAgent 已经通过 LangGraph Server 暴露出来。
第四步:接入 AG-UI 适配器
在 TypeScript 项目中安装对应依赖:
npm install @ag-ui/langgraph
创建 Agent:
import { LangGraphAgent } from "@ag-ui/langgraph";
const agent = new LangGraphAgent({
graphId: "deep_agent",
deploymentUrl: "http://localhost:2024",
});
这里有两个关键参数:
graphId
必须与 langgraph.json 中注册的 Graph 名称一致。
deploymentUrl
指向 LangGraph Server 的地址。
这样就建立了 TypeScript AG-UI 适配器与 Python DeepAgent 之间的连接。
第五步:监听 Agent 事件
下面是一个简化的事件监听示例:
import { LangGraphAgent } from "@ag-ui/langgraph";
const agent = new LangGraphAgent({
graphId: "deep_agent",
deploymentUrl: "http://localhost:2024",
initialMessages: [
{
id: "1",
role: "user",
content: "Research the AG-UI protocol."
}
]
});
await agent.runAgent(
{},
{
onTextMessageContentEvent({ event }) {
process.stdout.write(event.delta);
},
onToolCallStartEvent({ event }) {
console.log(
"[tool]",
event.toolCallName
);
},
onStateSnapshotEvent({ event }) {
console.log(
"[state]",
event.snapshot
);
},
onRunFinishedEvent() {
console.log("[done]");
}
}
);
这段代码主要演示四件事:
这里的 onTextMessageContentEvent 可以实现文本实时输出。
onToolCallStartEvent 可以让前端知道 Agent 正在调用什么工具。
onStateSnapshotEvent 则可以接收 Agent 的完整状态。
实际业务开发中,可以把这些事件进一步映射到 Vue 或 React 的响应式状态中。
六、比事件监听更重要的是:Human-in-the-loop
如果 Agent 只能执行任务、推送消息,那么它仍然只是一个能够实时展示过程的应用。
但企业级 Agent 往往需要人参与决策。
例如:
- 发送邮件之前需要用户确认。
- 执行数据库修改之前需要审批。
- 生成合同之前需要人工检查。
- 执行高风险操作之前需要授权。
这就涉及 Human-in-the-loop(人在回路)。
DeepAgents 支持通过 interrupt 暂停执行,等待人工输入,然后恢复任务。
AG-UI 负责把这个交互过程传递到前端。
官方文档提供了一个值得注意的配置:
const agent = new LangGraphAgent({
graphId: "deep_agent",
deploymentUrl: "http://localhost:2024",
emitInterruptOutcome: true,
});
开启 emitInterruptOutcome 后,可以在 RUN_FINISHED 事件中获取结构化的中断结果:
outcome.type === "interrupt"
这里要注意:
Agent 暂停等待人工确认时,Run 可能已经结束当前这次执行,但不代表整个业务任务已经完成。
接下来需要向 Agent 发送 resume 信息。
例如:
const input = {
threadId: "t1",
runId: "r2",
messages: [],
resume: [
{
interruptId: "int-abc",
status: "resolved",
payload: {
approved: true
}
}
]
};
这里几个字段需要理解:
- threadId:对应的对话线程。
- runId:对应的执行记录。
- interruptId:需要处理的中断标识。
- status:中断处理状态。
- payload:人工提交的处理结果。
这意味着前端不再只是一个聊天窗口,还可以成为 Agent 的任务审批入口。
例如:
Agent:已经生成客户报价单。
系统:等待人工确认。
用户:
[确认发送]
[修改报价]
[取消任务]
Agent:收到确认,继续执行。
这才是企业 Agent 真正需要的交互能力。
七、LangGraphAgent 提供了哪些 API?
官方文档还介绍了 LangGraphAgent 的几个主要方法。
| runAgent() | 执行 Agent,并通过回调接收事件 |
| subscribe() | 注册持续订阅者,跨多次 Run 接收事件 |
| abortRun() | 取消当前正在执行的任务 |
| addMessage() | 添加一条消息 |
| setMessages() | 设置消息列表 |
其中值得关注的是 subscribe() 和 abortRun()。
subscribe()
普通 runAgent 的 subscriber 主要针对当前这次执行。
而 subscribe() 可以注册持续监听器,在多次 Run 中持续接收事件。
适合需要统一管理事件的应用。
abortRun()
允许主动取消正在执行的任务。
例如用户发现任务方向不对,可以点击停止按钮。
前端调用 abortRun(),而不是简单地关闭聊天窗口。
这两个 API 对构建真正可用的 Agent 工作台很有价值。
八、从工程角度看,AG-UI 带来了什么?
读完官方文档,我认为 AG-UI 的价值主要体现在三个方面。
1. 降低 Agent 后端与前端的耦合
以前开发 Agent 前端,往往需要针对后端的执行逻辑设计一套专用接口。
比如:
POST /agent/run
GET /agent/status
GET /agent/result
POST /agent/approve
POST /agent/cancel
不同 Agent 框架、不同执行引擎,接口设计可能完全不同。
AG-UI 通过标准化事件,减少了前端对具体 Agent 框架内部实现的依赖。
前端主要关注:
- 事件类型
- 事件数据
- 状态变化
- 消息更新
后端则负责把自己的执行过程转换成符合协议的事件。
2. Agent 从聊天机器人走向任务工作台
一个普通聊天机器人,前端主要需要展示消息。
一个复杂 Agent,前端需要展示的东西明显更多:
| 实时消息 | TEXT_MESSAGE_CONTENT |
| 工具执行过程 | TOOL_CALL_* |
| 任务进度 | STEP_* |
| 任务状态 | RUN_* |
| Todo 列表 | STATE_SNAPSHOT / STATE_DELTA |
| 对话历史 | MESSAGES_SNAPSHOT |
| 人工审批 | Interrupt / Resume |
| 停止任务 | abortRun() |
这也解释了为什么复杂 Agent 不能只靠一个 Chat UI。
用户真正需要的是一个能够观察、控制和管理任务的交互界面。
3. 为多 Agent 应用提供统一的前端交互基础
假设一个企业 Agent 系统有:
- 需求分析 Agent
- 数据检索 Agent
- 文档处理 Agent
- 报告生成 Agent
这些 Agent 可能由不同的节点和工具组成。
如果每个 Agent 都采用自己的前端通信格式,后续维护会越来越复杂。
通过统一事件协议,可以让前端以相对一致的方式处理不同 Agent 的执行信息。
当然,这并不意味着 AG-UI 自动解决了多 Agent 调度、任务依赖和状态一致性问题。
这些仍然需要 Agent Runtime 和业务系统负责。
九、实际项目中需要注意的几个问题
协议解决了通信标准化问题,但不代表接入之后就能直接满足生产要求。
我认为有几个地方值得提前设计。
1. 不要把所有事件都直接展示给用户
后端执行事件和用户需要看到的信息不是一回事。
例如工具参数可能包含内部数据、访问令牌或敏感业务信息。
应该设计事件过滤和展示映射层。
另外,文档中存在 REASONING 相关事件类型,也不意味着应该把模型的私有推理过程直接展示给用户。产品界面更适合展示经过整理的执行状态、操作摘要和可验证结果。
2. 正确处理状态快照和增量更新
STATE_SNAPSHOT 和 STATE_DELTA 需要配合使用。
前端需要维护一份本地状态,并按照 JSON Patch 规则应用增量更新。
如果事件丢失、顺序错乱或者客户端重新连接,就需要考虑状态恢复机制。
不能简单地把每个 STATE_DELTA 当作一份完整状态。
3. 做好任务与事件关联
生产环境中通常会有多个用户、多个会话、多个任务同时执行。
需要正确管理 threadId、runId、消息 ID、工具调用 ID 和 interruptId。
否则很容易出现任务状态串线、消息显示错误等问题。
4. 把人工审批作为业务流程,而不是前端按钮
人工审批不只是前端弹出一个确认框。
后端必须保证中断状态可以持久化、审批结果可以追踪、恢复执行时能够关联原来的任务。
涉及付款、发送、删除、数据修改等操作时,还应该增加服务端权限校验和幂等控制。
5. 生产环境还要考虑鉴权和部署
官方示例使用本地 LangGraph 开发服务,适合快速验证。
真正上线时,还需要处理:
- 用户身份认证
- Graph 访问权限
- 服务端与前端的跨域配置
- 多用户并发
- 长时间任务管理
- 断线重连
- 事件持久化
- 异常恢复
这些不是 AG-UI 自动替你完成的。
十、我的理解:Agent 工程正在从模型调用转向运行时与交互系统
以前我们开发 AI 应用,关注点主要是:
Prompt
|
v
LLM
|
v
Response
后来引入 RAG、Function Calling 和 Agent:
用户
|
v
Agent
|
+—- LLM
|
+—- Tools
|
+—- RAG
|
v
执行结果
现在,随着 DeepAgents、LangGraph 等框架逐渐支持更复杂的任务执行,Agent 的工程形态也在发生变化。
一个完整的 Agent 应用,需要考虑:
用户交互层
|
v
AG-UI
|
v
Agent Runtime
|
+—- 任务规划
|
+—- 上下文管理
|
+—- 工具执行
|
+—- 子 Agent
|
+—- 状态管理
|
+—- Human-in-the-loop
|
v
业务系统与外部服务
从这个角度看,AG-UI 的意义并不只是让聊天界面支持流式输出。
它让 Agent 的执行过程拥有了标准化的表达方式。
对于未来的 Agent 产品,前端可能不再只是一个输入框和消息列表,而是一个真正的任务交互界面。
用户可以看到 Agent 在做什么,可以检查执行结果,可以修改计划,可以中断任务,也可以在关键节点参与决策。
Agent 不应该只是回答问题,还应该能够执行任务;而一个成熟的 Agent 产品,也不能只展示答案,还应该让用户能够理解、监督和控制执行过程。



