从 0 搭一个 RAG Agent:工具调用 + 检索闭环
普通 RAG 是「一问一答」,RAG Agent 是「会自己决定要不要查、查什么、查完怎么用」。这篇讲我搭一个带检索闭环的 Agent 的完整思路,以及几个让循环不跑飞的关键设计。
一、RAG Agent 和普通 RAG 的区别
普通 RAG:用户问 → 检索 → 拼 prompt → 答。检索是固定前置的。
RAG Agent:用户问 → 模型判断要不要检索 → 决定检索词 → 检索 → 看结果决定要不要再查/调别的工具 → 综合作答。检索是模型决策的一部分。
价值场景:多跳问题(答案要拼多个来源)、需要实时数据(接搜索/数据库工具)、问题模糊(先拆解再查)。
二、架构四步循环
1 | 规划(Plan) → 行动(Act: 检索/调工具) → 观察(Obs) → 反思(Reflect) → 回到规划 |
- 规划:让模型先把问题拆成子问题;
- 行动:调用检索工具或外部 API;
- 观察:把结果回填上下文;
- 反思:判断信息够不够,不够继续,够了就总结。
三、把检索包成一个工具
关键设计:检索不再是管道,而是一个可被模型调用的函数。这样模型能决定「现在该查吗、查什么」。
1 | def retrieve(query: str, top_k: int = 5) -> str: |
检索实现本身复用 手搓 RAG 那套(混合检索 + 重排),Agent 只负责「何时调」。
四、让循环收敛的三个开关
这是最容易翻车的地方——模型一直查、一直不答,烧钱又超时。我的三道闸:
- 最大步数:硬上限(如 5 步),到点强制总结;
- 反思判定:要求模型每步输出「信息是否充足」的显式判断;
- 工具失败兜底:检索超时/空结果时,给模型一个「查不到」的信号,让它换路径或坦诚告知,而不是死循环硬调。
五、上下文管理
多轮检索后上下文会爆。我的做法:
- 工具结果只保留「被引用到的」段落,其余丢弃;
- 长对话用摘要压缩历史;
- 优先保「用户意图 + 当前任务」,再保检索内容。
六、踩坑实录
- 检索结果污染:查回来的脏数据让模型偏离,解决是加引用约束 + 重排提纯;
- 循环不收敛:没设步数上限时,弱模型会反复查同一个词,靠上面三道闸解决;
- 工具描述写太泛:模型不知道何时调,描述要写清「什么时候该用、传什么」。
小结
RAG Agent 的本质是把「检索」从管道升级为「可调度的能力」。只要把检索包成工具、给循环加收敛闸、管好上下文,你就能搭出一个可控的多跳问答系统——这比单纯说「我用了 Agent」有说服力得多。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 河岳日星的博客!

