普通 RAG 是「一问一答」,RAG Agent 是「会自己决定要不要查、查什么、查完怎么用」。这篇讲我搭一个带检索闭环的 Agent 的完整思路,以及几个让循环不跑飞的关键设计。

一、RAG Agent 和普通 RAG 的区别

普通 RAG:用户问 → 检索 → 拼 prompt → 答。检索是固定前置的。

RAG Agent:用户问 → 模型判断要不要检索 → 决定检索词 → 检索 → 看结果决定要不要再查/调别的工具 → 综合作答。检索是模型决策的一部分。

价值场景:多跳问题(答案要拼多个来源)、需要实时数据(接搜索/数据库工具)、问题模糊(先拆解再查)。

二、架构四步循环

1
规划(Plan) → 行动(Act: 检索/调工具) → 观察(Obs) → 反思(Reflect) → 回到规划
  • 规划:让模型先把问题拆成子问题;
  • 行动:调用检索工具或外部 API;
  • 观察:把结果回填上下文;
  • 反思:判断信息够不够,不够继续,够了就总结。

三、把检索包成一个工具

关键设计:检索不再是管道,而是一个可被模型调用的函数。这样模型能决定「现在该查吗、查什么」。

1
2
3
4
5
6
7
8
9
def retrieve(query: str, top_k: int = 5) -> str:
hits = vector_db.search(embed(query), top_k)
return "\n".join(h.text for h in hits)

TOOLS = [{
"name": "retrieve",
"description": "当用户问题需要外部知识时调用,传入检索词,返回相关文档片段",
"parameters": {...}
}]

检索实现本身复用 手搓 RAG 那套(混合检索 + 重排),Agent 只负责「何时调」。

四、让循环收敛的三个开关

这是最容易翻车的地方——模型一直查、一直不答,烧钱又超时。我的三道闸:

  1. 最大步数:硬上限(如 5 步),到点强制总结;
  2. 反思判定:要求模型每步输出「信息是否充足」的显式判断;
  3. 工具失败兜底:检索超时/空结果时,给模型一个「查不到」的信号,让它换路径或坦诚告知,而不是死循环硬调。

五、上下文管理

多轮检索后上下文会爆。我的做法:

  • 工具结果只保留「被引用到的」段落,其余丢弃;
  • 长对话用摘要压缩历史;
  • 优先保「用户意图 + 当前任务」,再保检索内容。

六、踩坑实录

  • 检索结果污染:查回来的脏数据让模型偏离,解决是加引用约束 + 重排提纯;
  • 循环不收敛:没设步数上限时,弱模型会反复查同一个词,靠上面三道闸解决;
  • 工具描述写太泛:模型不知道何时调,描述要写清「什么时候该用、传什么」。

小结

RAG Agent 的本质是把「检索」从管道升级为「可调度的能力」。只要把检索包成工具、给循环加收敛闸、管好上下文,你就能搭出一个可控的多跳问答系统——这比单纯说「我用了 Agent」有说服力得多。