1. 为什么说传统RAG的瓶颈卡在“检索时机”而不是“检索精度”
过去几年做RAG(检索增强生成)的朋友应该都有同感:一开始觉得只要有向量库、有个大模型,把文档切一切、embedding打一下,效果就出来了。可一旦把问题从演示Demo换成真实业务,就开始露馅。很多团队的第一反应是换更好的embedding模型、调top-k、上重排,忙活半天,最后还是发现模型答非所问。
我自己的体会是,传统RAG的问题往往不是“没检索到对的文档”,而是整个流程的设计本身就带着几个看不见的坑。这里说的“传统RAG”,指的是最经典的那种固定流水线:用户提问后,不管三七二十一先做检索,把Top-K文档拼进Prompt,再让LLM做答案生成。整个过程像一条传送带,查询进去,答案出来,中间没有任何反馈和校正的机会。
1.1 传统RAG流水线的三个隐藏断裂点
第一个断裂点,是检索时机错位。用户随口一句闲聊或者一个常识问题,系统也会老老实实去做一轮向量检索。比如有人问“你们的产品支持哪些语言”,这个问题的答案明明就在产品文档首页,但有些文档可能没有把这么基础的信息写得语义贴近用户问法,向量检索反而把一些边角料文档捞上来,答得稀碎。检索本身不便宜,检索错了还会把模型带偏。
第二个断裂点,是单轮检索不可迭代。用户的真实需求往往不是一句话能问完的。很多情况下,用户对问题本身的描述很模糊,比如“这个东西到底靠不靠谱”,模型需要先搞清楚“这个东西”指什么,再判断用户关心的“靠谱”是稳定性、安全性还是成本。传统RAG没有“先澄清再检索”的能力,一次检索失败就是最终失败,没有第二次机会。
第三个断裂点,是多跳问题拆解失败。业务场景里大量问题是跨实体的复合问题,比如“某项目上线后,用户反馈集中在哪些模块,对应的负责人是谁”。要回答这个问题,至少要检索项目文档、用户反馈记录、组织人员表三类来源,然后做信息拼接。传统RAG会在一次检索里试图用向量相似度把这几个子问题全“撞”出来,结果往往是每个子问题都只沾了一点边,拼出来的答案既不敢肯定,也不敢否定。
为了更直观,我把两种模式放在一起对比一下:
| 维度 | 传统RAG | Agentic RAG |
|---|---|---|
| 检索触发方式 | 固定流水线,提问即检索 | 模型根据需求自主决策,必要时才检索 |
| 检索轮次 | 单轮,不可迭代 | 多轮,可动态调整检索策略 |
| 信息缺失处理 | 直接硬答或答错 | 识别缺口后继续检索或澄清 |
| 工具扩展 | 一个向量库走到底 | 可接入全文检索、SQL查询、API等 |
| 失败恢复 | 无恢复机制 | 支持自我纠错、重写查询词 |
1.2 Agentic RAG的核心变化:把检索策略决策权交给模型
Agentic RAG(智能体检索增强生成)本质上做的事情,是把“检索策略”从人为设计的固定规则,变成了模型在推理过程中自主决策的一环。检索不再是流水线上一个必经过的站,而是Agent工具箱里的一个行动,用不用、什么时候用、用哪把工具,由模型根据当前任务状态动态决定。
这个转变听起来只是把决策权交出去,但实际工程上的影响非常深远。一方面,模型的推理能力决定了它能像一个人一样思考“我还缺什么信息”;另一方面,工具的调用能力决定了它能把“想法”变成实际动作,比如发起查询、读取数据库、翻看某一段文档。
所以我的结论是:做Agentic RAG,最先要转变的不是技术栈,而是心智模型。你不再问“这个检索器好不好”,而要问“这个Agent在什么条件下会选对检索器、会在什么时候停下来”。这决定了后续所有模块的设计思路。
2. 核心闭环拆解:推理、行动、观察如何驱动多轮检索
Agentic RAG最核心的机制,是把LLM当作一个能“思考—行动—观察”的闭环控制器。现在业界常见的模式有两种,一种是ReAct,另一种是Plan-and-Execute。我先把这两种模式都拆开讲,因为它们解决的场景稍微不一样。
2.1 ReAct循环在RAG中的落地形态
ReAct的思路非常质朴:让模型交替输出“思考”和“行动”,然后把行动的结果“观察”反馈给模型,直到模型认为信息足够了,才输出最终答案。落到RAG场景里,它的循环一般是这样的:
- 思考:用户这个问题需要哪些信息?我现在手里有什么?还缺什么?
- 行动:调用检索引擎,输入查询词,或者调用其他工具。
- 观察:看到检索回来的片段,判断这些信息能否回答用户问题。
- 再次思考:信息够了吗?不够的话,换什么关键词继续查?
我举个具体例子。用户问:“我们平台最近三个月的用户流失率有什么变化,主要原因是哪些?”
一个可用的循环可能是这样的:
第一轮,Agent先思考:要回答流失率变化,需要拿到三个月的数据指标;要回答原因,需要翻看用户反馈和调研记录。这两类信息来源不同,先查运营周报里的数据。于是行动1是“全文检索:运营周报 流失率 月度数据”,观察结果是找到了三个月的数据表,但是周报里没有原因分析。
第二轮,Agent继续思考:数据有了,原因还没找到,用户反馈属于另一个库,换工具。于是行动2是“向量检索:用户反馈 投诉 流失原因 最近90天”,观察结果是一堆用户留言片段。
第三轮,Agent看到信息基本齐全,再做个简单交叉验证:把数据曲线和用户留言中的关键事件对应起来,最终生成一个有数据、有原因的答案。
这个例子里最值钱的地方,不是每一步用了什么工具,而是模型每轮都在做“信息缺口判断”。它知道数据查完不代表任务结束,原因还没着落,就继续查。这种能力是普通RAG永远不会有的。
2.2 计划先行:把复杂问题拆成检索子任务
ReAct是“边走边看”,适合大多数中度复杂场景。但有一类问题,如果让Agent一路随机应变,容易绕圈子。这类问题的特点是要点很多、相互依赖、需要系统性地把答案拼起来。比如“梳理某业务线从立项、开发、上线到运营各阶段的关键风险点”,这种问题天然适合先计划、后执行。
Plan-and-Execute模式在RAG里的做法是:第一步,让Agent生成一个检索计划,比如:
- 检索项目立项文档,获取目标与范围。
- 检索技术方案与评审记录,找出开发阶段风险。
- 检索上线报告与事故记录,掌握上线后问题。
- 汇总各阶段风险清单,输出结构化结论。
计划生成后,Agent按计划逐项执行,每项产出摘要并存入中间状态。全部完成后,再进入汇总生成阶段。
我见过很多团队在Plan-and-Execute上踩坑,核心问题是计划生成后不再动态调整。计划赶不上变化,检索中可能发现某个阶段根本没有任何文档,或者立项文档里直接写了后期风险,这时候计划要允许追加、删减、调序。所以更保险的做法是“计划控制 + 每步校验”:大方向按计划走,但每执行完一个子任务,让模型简单判断一下计划是否仍然成立。
2.3 关键设计:什么时机触发检索、何时终止
Agentic RAG如果设计不好,最常见的毛病是“乱检索”和“停不下来”。“乱检索”是每轮回答都调用一堆工具,哪怕问题简单到常识就能回答;“停不下来”是模型总觉得信息不够,查完一轮又一轮,最后成本和延迟双双爆炸。
我给一个比较实际的策略:让模型在每轮循环后输出一个“信息充分度评分”,分数范围0到1,低于阈值就继续检索,高于阈值就生成最终答案。同时设置硬性条件:最多执行4到6轮工具调用,超过后强制进入生成阶段。这个设计既给了模型主动性,又给了系统兜底。
再补一个细节:终止条件里一定要包含“信息缺失时的处置方式”。模型如果查了3轮都没找到答案,不能硬编,要让它在最终回复里明确说出“目前检索结果无法覆盖某某方面,建议补充某某资料”。让系统学会认怂,比让它强行作答安全得多。
3. 检索层升级:向量库之外,Agent还需要会调用哪些工具
很多人以为Agentic RAG就是把原来RAG的检索步骤外围包一个Agent循环,检索内部还是老一套。但实际要做好的话,检索层本身也得升级。因为单靠向量检索,根本撑不起Agent的一次次决策。
3.1 向量检索的局限:不是所有问题都能靠语义相似解决
我在做知识库问答的时候发现,向量检索对三类问题非常不友好。第一类是精确匹配类问题,比如“某某设备的型号是FW-301还是FW-310”,用户的说法和文档里的写法差一个字,语义上相近,但语义向量不一定能拉出正确文档;第二类是数值条件类问题,比如“找出所有成本超过预算20%的项目”,这类查询本质是结构化过滤,不是语义相似;第三类是时间敏感类问题,文档里不断更新最新版本,Embedding模型训练时根本没看过这些新内容,向量检索拿它没办法。
所以Agent工具集不能只有向量检索这一个维度。一个合格的工具箱,至少要有三类能力。
3.2 工具注册表:让Agent知道有什么可用、什么时候用哪个
Agent要调用工具,前提是它知道工具有什么、能干什么。工程上这靠“工具注册表”实现。每个工具在大模型接口里暴露成一个带有明确描述的Schema,模型根据任务描述和工具描述做匹配选择。
工具描述写得是否清楚,直接影响选择准确率。我这里给一个参考结构,不是具体代码,就是Schema的思路:
{ "tool_name": "semantic_search", "description": "基于语义向量的相似度检索,适合查找含义相近但表述不同的内容,如用户观点、主题描述。", "parameters": { "query": "字符串,检索关键词,建议提炼核心名词", "top_k": "整数,返回结果数量,默认5", "doc_filter": "可选字符串,按文档来源或类型过滤" } }再给一个精确检索工具:
{ "tool_name": "exact_search", "description": "基于关键词与短语的全文检索,适合查找精确型号、人名、产品名、代码片段。", "parameters": { "query": "字符串,必须使用原文中的精确词汇", "top_k": "整数,返回结果数量,默认5" } }工具不在多,而在于边界清晰。我见过一个项目里塞了十来个工具,结果模型选工具的正确率反而不到一半。后来精简到4个,准确率明显上升。工具描述如果含糊,比如只说“搜索文档”,模型根本分不清该选哪个。
3.3 结构化查询与外部API:让Agent具备“查档”能力
除了文档检索,Agentic RAG最能发挥价值的地方,是接通企业内部的各类结构化数据源。比如客户管理系统的数据库、工单平台、订单系统,这些数据不可能全部灌进向量库,它们适合用SQL查询或API调用。
这里有一个技巧:让Agent生成查询参数比让Agent直接生成SQL更稳。直接生成SQL风险高,权限控制也麻烦。折中的方案是做一个查询适配层,把常见查询预定义成带参数的函数,Agent只需要填参数。比如“查询某时间段某产品的销量”,就是一个函数,Agent知道传入时间范围和产品ID就行。这样既保留了Agent的自主性,又把风险锁死在白名单查询中。
3.4 查询词重写与结果驯化:检索前多一步,效果差很多
用户原始问法往往口语化,直接拿去检索效果不好。Agent在行动之前,应该先做查询词重写,把“最近大家的反馈咋样”重写成“用户反馈 近一个月 汇总”。这一步看似简单,但对召回率影响极大。
检索结果回来后,也不能全量塞给模型,需要做截断和去重。我的习惯是:每个工具的返回结果只保留摘要级别的内容,控制在每段150字以内,再按相关度排序。这样既节省Token,也让模型在“观察”环节更容易聚焦关键信息。
4. 多轮对话与记忆管理:Agentic RAG的状态到底该存什么
把Agent循环搭起来之后,很快会遇到一个新问题:多轮对话下,Agent怎么记得自己已经查过什么?如果一个用户连续追问好几轮,Agent每次都把全部历史重新推理一遍,不仅浪费,而且容易混乱。
4.1 会话记忆与检索过程的分离策略
我建议把记忆拆成两个层面:一个叫会话记忆,记录用户和Agent的对话往来;另一个叫检索过程状态,记录本次任务中已经查过哪些查询词、看过哪些文档、还缺哪些信息。
这两个层面必须分开存储、分开更新。会话记忆负责维持对话的连续感,检索状态负责让Agent不至于重复劳动。很多实现翻车,就是因为把检索过程也写进会话记忆里,结果下一轮用户提到另一个问题时,Agent把上一轮检索过的片段全当作当前任务的背景,反而干扰判断。
4.2 事实零散记录:把“已确认信息”单独存起来
我实践下来最有用的一个设计,是让Agent在每轮“观察”结束后,把已经确认的事实抽取成条目,存进一个叫“事实卡”的结构里。比如:
- 事实卡1:2025年第一季度流失率为8.2%
- 事实卡2:季度末客服系统升级引发投诉上升
- 待补信息:影响流失的具体产品模块
最终生成答案时,Agent不是重新去翻所有历史,而是直接基于事实卡组合答案。这个设计的另一个好处是,Agent可以明确知道待补信息还剩什么,下一步该查什么一目了然。
4.3 长上下文窗口不等于记忆,别把Token当数据库用
现在很多模型上下文窗口动辄很大,有的团队就干脆把全部对话历史加检索片段全塞进去。这个思路短期能跑通,长期隐患很大。一是Token成本随对话轮数线性上涨,二是当上下文中无关内容太多时,模型注意力会被稀释,反而越答越差。
我比较推荐的是“滑动窗口 + 摘要 + 事实卡”的组合。会话历史保留最近2-3轮原文,更早的对话由模型每次总结成一段摘要;事实卡作为当前任务的“工作记忆”;检索片段用完即弃,只保留关键结论。这三种信息各司其职,才不会让状态越滚越乱。
| 记忆类型 | 存储内容 | 更新策略 |
|---|---|---|
| 会话窗口 | 最近2-3轮对话原文 | 每轮对话后追加,超出长度后滚动 |
| 对话摘要 | 更早对话的高层要点 | 每轮或每几轮重新生成一次 |
| 事实卡 | 当前任务已确认的信息 | 每次“观察”后抽取更新 |
| 检索过程状态 | 查询词、已看文档、缺口列表 | 每次工具调用后同步更新 |
5. 从零搭建一个Agentic RAG系统:模块划分与控制流设计
理论部分讲完,下面进入动手环节。我分享一个自己项目里验证过的最小可用方案,帮大家走通一个基础闭环。整体逻辑不复杂,但模块分界要想清楚。
5.1 模块划分与数据流
一个可用的Agentic RAG系统,至少要包含以下模块:
- 核心循环控制器:负责驱动思考、行动、观察的循环,决定何时终止。
- 工具注册表:把所有检索工具的描述、Schema、执行函数集中管理。
- 记忆管理器:维护会话窗口、摘要、事实卡和检索过程状态。
- 上下文组装器:把当前查询、事实卡、最近的观察结果组装成新的Prompt。
- 评估与兜底:负责信息充分度评分、步数熔断和异常处理。
数据流是这样的:用户query进入核心循环控制器,控制器调用模型做推理,模型可能输出一个工具调用意图,控制器从工具注册表里找到对应工具并执行,返回值被格式化成“观察”结果,写进记忆管理器,然后再回到模型做下一步推理。直到模型输出的是最终答案,而不是工具调用,循环结束。
5.2 最小可用版本的调度流程
下面是一个简化版的Python风格伪代码,目的是把控制流讲清楚。实际生产环境里,模型接口、工具执行、记忆存储都可以替换成具体实现。
class AgenticRAG: def __init__(self, tools, model_client, max_steps=5): self.tools = tools self.model_client = model_client self.max_steps = max_steps self.memory = MemoryManager() def run(self, user_query): self.memory.add_user_message(user_query) step = 0 while step < self.max_steps: step += 1 # 1. 组装当前Prompt,包含事实卡、最近观察、待补缺口 prompt = self.memory.build_prompt() # 2. 让模型输出下一步动作,可能是工具调用或最终答案 response = self.model_client.respond(prompt) # 3. 如果模型给的是最终答案,且充分度评分达标,则结束 if response.is_final_answer: if response.confidence_score >= 0.6: return response.answer else: # 充分度不够但仍结束,需在答案中说明信息缺口 return response.answer + " [注意:部分信息检索不充分]" # 4. 如果模型要求调用工具,执行并记录观察结果 if response.tool_call is not None: tool_result = self.tools.execute(response.tool_call) self.memory.add_observation(response.tool_call, tool_result) # 5. 如果模型输出无法解析,强制让模型重新思考 else: self.memory.add_system_message("输出格式不符合要求,请重新给出动作。") # 超过最大步数,强制生成答案并备注 return self.model_client.force_answer(self.memory.build_prompt())这个流程里最关键的两个点是:记忆管理器里的“事实卡”是跨轮共享的,而不是只保留最后一轮;最大步数不是包治百病,但要配合“充分度评分”一起用,否则可能出现步数没用完但已经无信息可查的情况。
5.3 落地选型建议:模型、接口与检索库怎么配
模型选型上,优先选支持结构化工具调用的大模型。因为Agent场景里,模型需要稳定输出“工具名+参数”的结构,解析越简单,出错越少。如果用的模型不支持内建工具调用,也可以通过Prompt约定输出JSON格式来实现,但这时候一定要加格式校验和重试逻辑。
向量库选型要看数据量和QPS。数据量百万级以下,单机方案足够;千万级以上再考虑分布式。重点是向量索引的参数要按实际检索延迟来调,别只看召回率好看,几十毫秒级的差距对Agent场景影响很大。
embedding模型的选择也需要重新考虑。传统RAG场景只要语义相似度够好就行,但Agent场景里检索结果往往要喂给模型做进一步推理,所以embedding对关键词的敏感度也要关注。我个人的习惯是,主检索用向量,但工具集里永远预留着全文检索,用于兜底。
6. 落地一个月后我遇到的那些坑:从幻觉轰炸到死循环
纸上谈兵说得再多,真到跑起来才见真章。我把实际项目中踩过的高频坑整理出来,每个坑都对应一个解决思路,希望能帮大家少走弯路。
6.1 检索结果互相矛盾,Agent反而被带进“幻觉轰炸”
当Agent学会多轮检索后,一个很隐蔽的新问题出现了:不同来源的文档内容可能互相矛盾。比如一份文档说“平均响应时间小于200毫秒”,另一份测试报告却写着“高峰期平均响应时间达400毫秒”。Agent如果不加判断,容易在答案里同时引用两个冲突的数据,让用户一头雾水。
我的处理方式是增加一个“证据汇总与冲突判定”的环节,放在生成最终答案之前。具体做法是:让模型把所有事实卡里相关的信息列出来,标注各自来源,如果存在冲突,先按时间优先级排序,再按来源类型给权重。实在无法判定谁对时,把差异如实写进答案,让用户自己判断。绝不能为了显得诚实而把所有信息都端上去,反而变成没有观点。
6.2 搜索词重复:模型翻来覆去查同一句话
还有一个让我一度很崩溃的问题:Agent会在循环里反复检索同一个关键词,看起来忙得很,实际毫无进展。原因分析下来,是模型在“观察”环节没有得到足够强的反馈信号,于是默认重复当前动作是最稳妥的选择。
我用的对策有两层。第一层是去重机制:记忆管理器里记录最近N轮的查询词,如果模型下一轮要用的查询词和之前完全一样,系统会拦截工具调用,并提示模型“该关键词已检索过,请基于已有结果判断是否存在信息缺口,或换一个角度调整查询词”。第二层是成本熔断:每个任务设置最大步数和最大Token消耗,超过就直接进入答案生成,避免一轮对话花掉几十次检索费用。
6.3 旧版RAG的评测指标完全失效,怎么重新打分
用传统RAG的指标来评估Agentic RAG,会发现明显不匹配。比如“答案相关度”和“忠实度”只能评估最终答案,但Agent系统最需要关注的是过程质量:工具选对了没有?检索了几轮才停?中间有没有无效重复?
我在项目里建立了一套新的观测指标体系,核心包括:
| 指标 | 定义 | 我们的目标值 |
|---|---|---|
| 工具调用准确率 | 模型选择的工具与理想工具一致的比例 | 高于80% |
| 无效检索率 | 检索结果与最终答案完全无关的次数占比 | 低于10% |
| 平均循环步数 | 每次任务完成问答所需工具调用轮数 | 3到4轮 |
| 信息充分度评分 | 最终答案生成前模型对自身信息覆盖的评分均值 | 高于0.75 |
| 引用完整率 | 答案中涉及的事实有对应检索来源的比例 | 高于90% |
这套指标在开发阶段帮了大忙。只要工具调用准确率下滑,我先去检查工具描述是否清晰;只要无效检索率上升,我就去追查用户query和文档分布的匹配度。过程指标好了,最终答案质量通常是水涨船高,不用再玄学调参。
6.4 部署与性能:异步检索、缓存与预检索
Agentic RAG因为多轮交互,延迟比传统RAG高是必然的。想缩短用户等待时间,最有效的做法不是压缩模型大小,而是把能并行的事情并行掉。比如Agent判断需要同时查文档库和工单系统时,两步可以并发执行;计划生成阶段如果拆出多个子任务,也可以对照子任务先整体检索再逐项生成。
另外,常用查询的缓存很值得做。用户问题高度重复时,直接把上次Agent跑完的结果缓存起来,不需要重新走循环。再深一步,如果业务场景里有已知的高频主题,可以提前做“预检索”,预先跑好一轮检索并把结果挂到关键词下,真正提问时直接引用,能省掉好几步决策。
我在实际部署中还习惯给Agent循环做超时控制。每个工具调用设置独立的超时时间,比如5秒,超过就直接返回空结果并提示模型“该检索超时,可以继续下一步或调整查询”。这个兜底机制放在生产环境里非常管用,否则某个下游接口卡住,整个Agent循环也跟着挂起。
最后再分享一点个人体会。Agentic RAG这套东西,真正难的地方不在Agent这个词本身。把Agent框架搭起来只花了两三天,后面花了一个多月,全在调工具边界、设终止条件、做记忆去重这些“笨功夫”。但恰恰是这些笨功夫,决定了系统是真实可用,还是演示完就扔到一边。如果你正打算上手,我建议别急着堆功能,先把一套最小的检索闭环跑稳,再逐步加工具、加记忆。一步一步来,这个方向确实是值得深耕的。