AI Agent 明明在工作,用户为什么只看到“转圈”?有谷大脑用 AG-UI 重做了问答链路
用 AI 知识库提问时,有一种体验特别劝退。
用户问:
我们合同里的赔偿责任上限是多少?有哪些例外?
系统没有报错,也没有卡死。
但接下来的十几秒里,页面只有一个 loading 图标。用户看不到系统是否正在检索知识库、有没有查到合同原文、是否又调用了联网搜索,也不知道答案还要等多久。
十几秒后,一整段文字突然出现。
答案也许是对的,但用户仍然会怀疑:
- 它真的查了我的知识库吗?
- 中间调用了哪些工具?
- 结论来自哪份文档、哪一页?
- 如果等太久,我能不能取消?
这不是模型能力的问题,而是 Agent 和前端之间的通信方式出了问题。
传统接口习惯“发出请求,等待完整 JSON”。但一个真正的 Agent,可能要经历多轮推理、知识库检索、联网搜索、引用整理和状态更新。把这些过程全部藏在一个 loading 后面,用户看到的就只剩下漫长的沉默。
这也是我们在做企业级 AI 知识库「有谷大脑」时,为什么没有把问答功能做成普通的 HTTP 请求,而是使用 AG-UI 协议连接前端和 Deep Agent。
一、普通 HTTP 为什么接不住 Agent 问答?
普通问答接口通常是这样的:
前端发送问题
↓
后端生成完整答案
↓
返回一份 JSON
↓
前端一次性渲染
这种模式适合“输入确定、输出确定”的接口,比如查询一条订单、读取一个用户资料。
但 Agent 的执行过程并不确定。
面对同一个问题,它可能:
- 先检索企业知识库;
- 发现资料不足,再调用联网搜索;
- 根据检索结果继续组织答案;
- 整理引用,让用户能够回到原文核对;
- 中途失败、重试,或者被用户取消。
前端不仅要拿到最终答案,还要实时知道:当前 Run 是否开始、文本生成到了哪里、调用了哪个工具、工具返回了什么、引用如何变化,以及任务最终成功还是失败。
如果仍然只返回一个最终 JSON,这些过程状态就全部丢了。
所以 Agent 问答真正需要的,不只是“接口能返回答案”,而是:
Agent 每发生一件事,前端都能收到对应事件,并立即更新界面。
二、AG-UI 到底解决了什么?
AG-UI(Agent-User Interaction Protocol)可以理解为 Agent 与用户界面之间的事件协议。
有谷大脑的问答后端由 LangGraph + deepagents 构建,前端通过 SSE 订阅 AG-UI 事件流。后端不是等全部工作结束后才交付一个大 JSON,而是一边执行,一边把文本、工具调用、状态和引用推给前端。
图 1:有谷大脑 AG-UI 问答链路。前端通过 SSE 接收 Deep Agent 推送的文本、工具调用、状态与引用事件。
如果没有这一层协议抽象,每换一个 Agent 框架、每增加一种前端,都要重新处理 SSE 格式、工具事件和状态合并,很容易变成 M × N 的对接困境。
AG-UI 把这些交互统一成标准事件。前端不必理解 Agent 内部每一步是怎么实现的,只要按事件类型更新界面。
三、用户看到的“正在回答”,背后其实有四类事件
1. 文本不是最后一次性出现,而是逐段流出来
一次回答可以依次产生:
TEXT_MESSAGE_START
↓
TEXT_MESSAGE_CONTENT { delta }
↓
TEXT_MESSAGE_CONTENT { delta }
↓
TEXT_MESSAGE_END
前端每收到一个 delta 就追加一段文字,打字机效果自然形成。
这不仅让等待显得更短,也让用户确认系统已经开始工作。对 Agent 产品来说,“及时反馈”本身就是体验的一部分。
2. 工具调用不再藏在黑盒里
有谷大脑的 Agent 可能调用:
query_knowledge_base:检索企业知识库;search_web:知识库信息不足时补充联网资料。
工具执行过程中,前端会接收到类似下面的事件:
TOOL_CALL_START
TOOL_CALL_ARGS
TOOL_CALL_RESULT
于是界面可以展示:
正在检索知识库……
已找到 5 条相关内容
正在整理引用……
用户看到的就不再是一个沉默的 loading,而是一条可理解的执行过程。
更重要的是,工具返回的 chunk、来源和 metadata 都可以保持结构化,不需要先塞进一大段 Markdown,再让前端反向猜测哪些文字属于引用。
3. Run 的开始、结束和错误都有明确边界
Agent 问答不是一次普通的“请求—响应”,而是一次 Run。
RUN_STARTED
RUN_FINISHED
RUN_ERROR
收到 RUN_STARTED 后,前端可以切换输入状态;收到 RUN_FINISHED 后恢复;出现 RUN_ERROR 时,则展示明确的错误信息并允许重试。
重新生成答案、编辑上一条用户消息再运行,也可以围绕同一个 Run 边界与 LangGraph checkpoint 对齐。
如果没有统一的生命周期事件,前端很容易遇到这些问题:按钮提前恢复、上一轮回答还没结束就开始下一轮、报错后页面永远停在 loading。
4. 引用不是模型写出来的角标,而是结构化状态
企业知识库问答里,答案后面出现一个 [1] 并不难。
难的是用户点击 [1] 后,真的能打开对应文件,并定位到具体页码或原文片段。
如果只让模型在 Markdown 中写“根据第 3 章”,前端并不知道它对应哪个文档、哪个 chunk、哪一页。
因此,有谷大脑会通过 STATE_SNAPSHOT、STATE_DELTA 等状态事件,同步 citation 列表及来源信息。引用可以携带 chunk id、文档路径、页码等字段,前端再把回答中的角标与阅读器关联起来。
这样,“回答附带出处”才不是视觉装饰,而是一条真正可点击、可追溯、可核验的证据链。
四、AG-UI、MCP、A2A 有什么区别?
这三个协议经常被放在一起讨论,但解决的并不是同一个问题。
| 协议 | 连接谁 | 主要解决什么 |
|---|---|---|
| AG-UI | Agent ↔ 用户界面 | 流式文本、工具过程、运行状态、引用同步 |
| MCP | Agent ↔ 工具与数据 | 让 Agent 调用知识库、搜索及其他外部能力 |
| A2A | Agent ↔ Agent | 多 Agent 之间的通信与协作 |
有谷大脑网页端问答使用 AG-UI;开放平台通过 MCP 把知识库能力接入 Cursor 等 AI 客户端。
一个负责“Agent 怎么把过程展示给人”,一个负责“Agent 怎么连接工具和数据”。两者边界不同,并不是谁替代谁。
五、有谷大脑的完整问答链路是怎样的?
后端 Agent 在 API 进程启动时构建,并使用 Postgres checkpointer 保存对话执行状态。
当用户发起提问时,大致会经过:
用户提问
↓
前端 AG-UI Client 连接 /ag-run
↓
Deep Agent 开始 Run
↓
优先调用 query_knowledge_base
↓
知识库资料不足时调用 search_web
↓
通过 SSE 持续推送文本、工具、引用与状态事件
↓
前端分别更新聊天气泡、工具卡片、引用侧栏和全局状态
同一个 thread_id 的请求会在进程内串行,避免多个 Run 同时写入 checkpoint,造成对话分支竞争。
实时状态和历史记录则各自承担不同职责:
- LangGraph checkpoint 保存执行中的对话状态;
- PostgreSQL 的
chat_message保存用户可见的历史消息; - 两者通过
thread_id关联。
用户重新生成答案或编辑历史问题时,需要先把 checkpoint 对齐到正确分支,再更新持久化消息。否则就可能出现“界面显示的是新回答,后端上下文却还停在旧分支”的错乱。
六、真正落地时,最容易踩的四个坑
坑 1:同一会话允许多个 Run 同时写状态
用户快速点击两次发送,如果两个 Run 同时修改同一个 checkpoint,很容易产生竞争。
更稳妥的做法是:同一 thread_id 串行执行,或者先取消上一轮,再开始下一轮。产品界面也应明确提示“上一条仍在生成中”。
坑 2:checkpoint 和历史消息只更新了一边
重新生成或编辑消息后,如果只改了数据库记录,没有同步 checkpoint,下一轮 Agent 仍可能沿着旧上下文继续回答。
协议层负责实时事件,持久化层负责历史记录,但两者必须在同一条对话分支上保持一致。
坑 3:错误只结束连接,不告诉前端发生了什么
SSE 连接断开,不等于用户知道发生了什么。
RUN_ERROR 应携带可处理的 message 和 code。前端收到后要停止 loading、展示原因,并提供重试入口,而不是留下一个永远转圈的页面。
坑 4:引用被当成普通文本
模型生成一个 [3] 很容易,但如果没有与真实来源建立结构化关联,用户仍然无法核对结论。
对企业知识库来说,引用至少应该尽可能关联:文档、chunk、页码或章节路径。否则“有引用”和“可验证”之间,还隔着很远。
七、怎么判断 Agent 问答链路是否真的做好了?
比起只测试“最后能不能答对”,下面几种操作更容易暴露协议层问题。
长答案测试
问一个需要综合多份资料的问题。
看首个 token 是否及时出现,而不是等待十几秒后整段弹出。
工具调用测试
问一个必须查知识库的问题。
看界面能否展示正在检索、命中结果和工具结束状态,而不是只有统一的 loading。
引用测试
点击答案中的 [1]、[2]。
看它能否打开真实来源,并定位到对应原文,而不是只显示一个无法追溯的脚注。
连续点击测试
在上一轮还没结束时再次发送。
看系统是否正确排队或取消,是否会把两轮 token 混进同一个回答。
失败恢复测试
让工具超时或主动中断连接。
看界面能否结束 loading、说明错误并允许重试。
这些场景,比“请求最终返回了 200”更能说明一个 Agent 应用是否真正可用。
八、最后:Agent 问答最怕的,不只是答得慢,而是让用户不知道它在做什么
Agent 的价值,在于它能够检索资料、调用工具、组织答案并给出依据。
但如果这些过程都被藏在一个 loading 后面,用户看到的仍然只是一个偶尔很慢、偶尔报错、无法解释答案来源的聊天框。
所以在有谷大脑里,我们用 AG-UI 把 Deep Agent 的执行过程转成前端可消费的事件:
token 实时流出,工具调用看得见,运行状态能同步,答案引用可以点回原文。
不过,AG-UI 解决的是“问”的体验。企业资料入库时怎么解析、怎么切块,决定的则是 Agent 能不能检索到完整且可靠的内容。
有谷大脑也在针对不同企业文档设计对应的解析方式:
- Excel 表格怎样切块,才不会把表头和数据拆散?
- PDF 复杂版面怎样识别,才不会打乱阅读顺序?
- 代码怎样按 AST 切分,才不会把函数从中间截断?
- 合同怎样保留条款、定义和交叉引用?
入库结构正确,检索才能命中好 chunk;检索结果可靠,AG-UI 才能把依据实时、清楚地呈现给用户。
如果你也在做企业知识库、RAG、Agent 应用或企业 AI 落地,可以关注后续更新。
想体验这套“过程可见、引用可点”的知识库问答,可以访问:
Agent 问答不应该是“发请求,等 JSON”。真正可用的 Agent,需要让每一步都被用户看见。
更多推荐


所有评论(0)