【agent 开发】RAG 的瓶颈不只是检索:从 Verbal-R3 看 Agent 如何用好知识
🔥 个人主页:铁皮哥(欢迎关注)
📌 作者简介:28届校招生,后端开发/Agent 方向在学
📚 学习内容:Java、Python、计算机视觉、大语言模型、Agent开发
📝 专栏内容:从零开始的Claude Code零代码生活(持续更新中)
✨不只背八股,更想搞懂为什么这样设计
前言
做 RAG 系统时,很多人都会遇到一个很反直觉的问题:检索器明明已经把相关文档找出来了,最后生成的答案却还是不对。
一开始,我们很容易把问题归因到检索本身。是不是 embedding 模型不够好?是不是 chunk size 切得不合理?是不是 topK 太小?是不是应该再加一层 reranker?这些方向当然都有价值,但它们默认了一个前提:
只要相关文档被召回,后面的
LLM就能稳定地读懂它、筛选它,并把它转化成正确答案。
问题恰恰出在这里。
传统 RAG 的流程,本质上是把检索结果作为原始材料塞进上下文,然后交给模型自己处理。可检索结果通常不是一篇结构完整、逻辑清晰的文章,而是若干个被切分出来的文档片段。它们可能包含答案,也可能包含背景信息、相似信息、过时信息,甚至是和问题只有表面相关的噪声。
于是,RAG 的失败就不一定发生在“找不到资料”这一刻,也可能发生在“资料已经找到了,但模型没有真正用好它”这一刻。
这个问题在普通问答里已经会影响答案质量,而在 Agent 场景中会更加明显。因为检索结果不只是生成答案的依据,还可能影响下一步搜索、工具调用和计划更新。
所以,这篇文章想讨论的不是如何继续堆检索能力,而是另一个更容易被忽略的问题:检索出来的知识,能不能被模型稳定理解和使用?
一、传统 RAG 的痛点:检索结果不是答案,只是原材料
1.1 RAG 的默认假设
传统 RAG 的流程并不复杂。
用户提出问题后,系统先把问题转成向量,再从知识库中检索出相关文档片段,最后把这些片段和用户问题一起拼进 Prompt,交给 LLM 生成答案。
用户问题
↓
检索相关文档
↓
拼接上下文
↓
LLM 生成答案
它背后其实有一个很强的默认假设:只要检索器把相关内容找出来,LLM 就能自动完成剩下的工作。
也就是说,模型需要自己判断哪些内容真正有用,哪些内容只是背景信息;它还要从多个片段中抽取证据,建立逻辑关系,最后组织成一个可靠答案。
在简单问题里,这个假设通常问题不大。比如用户问一个明确事实,而某个文档片段里刚好直接包含答案,模型只需要把答案提取出来即可。
但一旦问题稍微复杂一点,这个默认假设就会变得不稳定。
因为检索出来的内容,并不等于已经整理好的答案。
1.2 检索结果往往是混杂的
真实系统里的检索结果,通常不是一段干净、完整、专门为当前问题准备好的解释,而是一组从原始文档中切出来的 chunk。
这些 chunk 可能来自不同页面、不同段落,甚至不同时间的材料。它们之间未必有清晰的上下文关系,也未必都在回答同一个问题。
有些片段确实包含关键证据,有些片段只是提到了相似关键词,还有些片段看起来相关,但其实只能提供背景,无法直接支撑最终答案。
这就会带来一个问题:LLM 面对的不是“答案”,而是一堆还没有被加工过的材料。
如果把传统 RAG 的检索结果直接塞进上下文,本质上相当于把阅读、筛选、判断、归纳这些工作全部交给了模型。模型不仅要回答问题,还要先在检索结果里完成一次隐性的资料整理。
而这个整理过程,恰恰是最容易出错的地方。
比如,模型可能抓住了一个表面相关但并不关键的片段,也可能忽略了真正有用的证据;它可能把多个文档里的信息错误拼接在一起,也可能被噪声内容带偏,生成一个看似合理但依据并不充分的答案。
所以,传统 RAG 的问题并不总是“没有检索到”。
更常见的情况是:相关内容已经在上下文里了,但它没有被模型稳定地理解和使用。
1.3 原材料需要被加工成推理材料
原材料只告诉模型:这里有一些可能相关的信息。但推理材料还需要进一步说明:哪些内容能作为证据,哪些内容只是背景,哪些片段之间存在逻辑关系,当前信息是否足以支持一个结论。
如果这些判断全部压到最终生成阶段,LLM 就要同时完成阅读、筛选、推理和表达。任务叠在一起之后,任何一个环节出错,都会影响最终答案。
这也是 Verbal-R3 值得关注的原因:它试图在检索结果进入推理之前,先把文档片段解释成更容易被模型使用的材料。
二、Verbal-R3 的思路:在检索和推理之间加一座桥
2.1 不是让模型硬读原始文档
Verbal-R3 的改进思路就很清晰:不要让模型在一堆文档片段里硬读,而是在文档进入推理链条之前,先对它做一次解释。
重新思考一个问题:
检索结果到底应该以什么形式交给模型?
传统做法通常是把 topK 文档片段直接拼进上下文。模型看到的是原始文本,它需要自己判断每个片段和问题的关系,也要自己区分证据和噪声。
Verbal-R3 关注的正是这个中间环节。
它认为,检索结果不应该只是原始文本,还应该带上一层面向推理的说明。也就是说,在 Retriever 找到候选文档之后,系统需要进一步告诉 Generator:这些文档为什么相关,相关在哪里,能不能支撑当前问题。
这一步,就是 Verbal-R3 里最核心的 Verbal Annotation。
2.2 Verbal Annotation:把文档片段解释成推理材料
Verbal Annotation 不是简单摘要,也不是把文档换一种说法复述一遍。
它更像是对检索结果做一次面向问题的阅读批注:这段材料和用户问题有什么关系,哪一部分是有效证据,哪一部分只是背景信息,当前文档是否真的能帮助回答问题。
比如,传统 RAG 可能只会把几段文档交给模型:
Doc1: ...
Doc2: ...
Doc3: ...
而加入 Verbal Annotation 之后,模型拿到的就不只是文档本身,还包括对文档价值的解释:
Doc1 中的某个信息可以直接支持结论 A。
Doc2 虽然提到了相关实体,但没有给出问题需要的关键事实。
Doc3 只提供背景信息,不能单独作为答案依据。
这类说明的价值在于,它提前完成了一部分“资料整理”的工作。
原本这些判断都要由 Generator 在最终回答时临时完成。现在,Verbal-R3 把这一步前置到了检索结果进入推理链条之前,让模型面对的不再是一堆未经加工的文本,而是已经标注过逻辑关系的材料。
2.3 Verbal Reranker:从排序器变成解释器
在 Verbal-R3 中,承担这项工作的模块叫 Verbal Reranker。
它仍然有 reranker 的功能,会判断候选文档和当前问题的相关程度,并给出相关性分数。但它和普通 reranker 最大的区别在于:它不只输出排序结果,还会同时生成 Verbal Annotation。
普通 reranker 更像是在回答:
哪几个文档更相关?
而 Verbal Reranker 进一步回答:
它们为什么相关?
哪些信息能作为证据?
哪些内容虽然出现了关键词,但不能真正回答问题?
这让 reranker 的角色发生了变化。
它不再只是检索系统末尾的一个排序组件,而是变成了连接检索和推理的解释器。它把 Retriever 找到的候选材料,转化成 Generator 更容易使用的上下文。
整个流程可以简化成这样:
Generator 生成搜索问题
↓
Retriever 检索候选文档
↓
Verbal Reranker 评分并生成解释
↓
Generator 基于解释继续推理或输出答案
这个设计最值得注意的地方在于,它没有否定检索的重要性,也没有简单地把问题归结为模型不够大。
它真正强调的是:在 RAG 系统中,检索和生成之间不应该只有“拼接上下文”这一步,还应该有一层面向推理的解释过程。
这也是 Verbal-R3 给我们的核心启发:当检索结果进入模型上下文之前,最好先被整理成模型能够理解、判断和利用的形式。
三、回到 Agent 开发:RAG 不该只返回文档,而要返回可行动上下文
3.1 Agent 需要的不是材料,而是判断依据
如果只是普通问答,RAG 的作用通常比较直接:检索几段材料,然后让模型基于材料回答用户问题。
但在 Agent 系统里,RAG 的位置会更关键。
因为 Agent 不只是生成一段回答,它还要决定下一步怎么做。它可能要继续搜索,也可能要调用工具;可能要更新计划,也可能要判断当前信息是否足够给出结论。
这时,如果 RAG 只返回几段原始文档,Agent 拿到的其实还是一堆材料。它仍然需要自己判断:这几段内容到底有没有用,哪些信息能支撑当前任务,哪些信息只是噪声,下一步是否还需要继续查。
这就会让整个推理过程变得不稳定。
更适合 Agent 的 RAG,不应该只返回:
Doc1
Doc2
Doc3
而应该返回:
Doc1 能支持什么结论;
Doc2 只是背景信息,不能直接回答问题;
Doc3 缺少关键条件,还需要继续检索;
当前信息是否足够进入下一步。
这样一来,RAG 就不只是一个文档召回模块,而是变成了 Agent 的上下文解释模块。
它提供的不只是“我找到了什么”,而是“这些内容能不能帮助你继续做决策”。
3.2 一个具体例子:让 Agent 分析线上接口报错
假设我们正在做一个运维辅助 Agent,用户的问题是:
订单创建接口最近频繁返回 500,帮我分析可能原因。
传统 RAG 可能会从知识库里检索出几段材料:
Doc1:订单创建接口依赖库存服务,库存服务超时会导致订单创建失败。
Doc2:支付回调接口在高峰期可能出现重复通知,需要做幂等处理。
Doc3:最近一次发布修改了订单创建接口的参数校验逻辑,如果 userId 为空会抛出异常。
Doc4:订单查询接口支持根据 orderId 查询订单详情。
如果这些内容被原样塞给 Agent,模型当然也可能答对。但它需要自己完成一次判断:哪些文档和“订单创建接口 500”真正相关,哪些只是看起来都和订单系统有关,哪些信息应该优先分析。
如果上下文再长一点,或者文档里夹杂了更多相似接口、历史故障、无关配置,模型就很容易被带偏。它可能把注意力放到支付回调上,也可能误以为订单查询接口也和当前问题有关。
借鉴 Verbal-R3 的思路,我们可以在 RAG 和 Agent 推理之间加一层解释,让系统返回的不是原始 topK,而是类似这样的上下文判断:
Doc1 和问题相关。它说明订单创建接口依赖库存服务,如果库存服务超时,可能导致接口返回 500。这个方向可以作为排查线索之一。
Doc2 相关性较弱。它讨论的是支付回调接口的幂等问题,虽然属于订单链路的一部分,但不能直接解释“订单创建接口返回 500”。
Doc3 高度相关。它提到最近发布修改了订单创建接口的参数校验逻辑,并且 userId 为空会抛出异常。由于用户问题强调“最近频繁返回 500”,这个变更很可能是优先排查对象。
Doc4 基本无关。它描述的是订单查询接口,不是订单创建接口,不能支撑当前故障分析。
这时,Agent 再继续推理就会稳定很多。
它不会只看到几段松散文档,而是能明确知道:当前最值得优先排查的是最近发布的参数校验变更,其次才是库存服务超时。支付回调和订单查询虽然也属于订单系统,但不能直接解释当前问题。
于是它的下一步行动也会更清晰:
优先查看最近一次发布记录;
检查订单创建接口的异常日志;
确认 500 是否集中出现在 userId 为空的请求;
如果不是,再排查库存服务超时。
3.3 从工程实现上,可以先加一个轻量解释层
对大多数业务系统来说,我们不一定要完整复现 Verbal-R3 的训练和蒸馏流程。
更现实的做法,是先在现有 RAG 管线里加一个轻量的解释层。它可以是一个单独的 LLM 调用,也可以是一个小模型,甚至可以先用固定 Prompt 实现。
例如,原来的流程可能是:
用户问题
↓
Retriever 检索 topK 文档
↓
拼接文档
↓
Agent 推理
改造后可以变成:
用户问题
↓
Retriever 检索 topK 文档
↓
Context Explainer 分析文档和问题的关系
↓
Agent 基于解释后的上下文继续推理
这个 Context Explainer 最重要的任务,是把每个文档和当前问题之间的关系说明白。
比如可以让它输出:
这个文档是否能回答当前问题;
它支持什么结论;
关键证据是哪一句;
哪些内容只是背景或噪声;
当前信息是否足够,是否需要继续检索。
Agent 的稳定性,不只取决于它本身会不会推理,也取决于它拿到的上下文是否足够清晰。如果输入本身就是混杂的、松散的、缺少判断依据的,那么后面的规划和行动就很容易走偏。
所以,Verbal-R3 对工程开发最直接的启发是:不要只把 RAG 当成“检索模块”,而要把它看成 Agent 推理链条里的上下文加工环节。
当 RAG 返回的不只是文档,而是文档和任务之间的关系,Agent 才更容易知道下一步该做什么。
写在文后
期待您的一键三连!如果有什么问题或建议欢迎在评论区交流!
更多推荐



所有评论(0)