上一篇文章我们聊了传统 RAG 从朴素检索到混合检索的演进过程,结尾留了个扣子:如果检索本身需要“思考”怎么办?这篇文章就来填这个坑,也就是 Agentic RAG 的第二篇。之所以单开一篇,是因为 Agentic RAG 不是简单的“加一个 AI 搜索框”,它把 RAG 从“查字典”变成了“做调研”,整个系统的交互逻辑、技术架构、失败模式都会发生变化。
这篇文章我会用一个真实做过的模拟项目 X(某公司内部知识库 Agent)作为主线,从架构选型、核心节点设计、常见失败模式三个角度拆解,最后附上成本控制和可观测性的实践经验,希望对你正在做的或者准备做的 Agentic RAG 系统有所参考。
1. Agentic RAG 与传统 RAG 的核心差异
1.1 你原来的 RAG 其实是个“单线程”系统
传统 RAG 的路径非常简单:用户提问,系统把问题和文档库里的文本块做相似度匹配,取 top-k 个结果拼进上下文,LLM 基于这些内容生成答案。整个过程是“一次检索、一次生成”,没有中间环节,也没有反馈回路。
这个设计在文档结构规整、问题类型单一的場景下没有问题。比如“某个产品的退货政策是什么”,这类问题用关键词和向量相似度就能命中。但一旦问题变成“帮我对比 A 方案和 B 方案在成本、周期、风险三个维度上的差异”,传统 RAG 就有点吃力了。因为系统不知道这个问题其实包含三个子问题,也不清楚先查哪个、查完怎么组织答案。它只会把所有包含“A 方案”和“B 方案”的文本块塞进上下文,让 LLM 自己去挑——结果往往就是答非所问或者漏掉关键信息。
我从实际使用中遇到最多的一个场景是:用户问“我们上个月的项目复盘文档里,客户投诉主要集中在哪几个方面?”。传统 RAG 会先把“项目复盘”相关的文本块捞出来,但这些文本块可能分布在十几个文档里,每个文档只讲了一部分,LLM 很难从碎片里归纳出结构化的结论。这时候你才会意识到,RAG 缺的不是检索质量,而是“检索策略”。
1.2 Agentic RAG 把“策略”还给了模型
Agentic RAG 的核心变化,是把“检索什么”“什么时候检索”“检索结果要不要采信”这些决策权交给 LLM 自己,而不是通过固定的代码逻辑写死。系统不再是一条直线,而是一个循环:模型的每一步输出都可能触发新的检索、调用外部工具,或者决定直接回答。
用一句话概括:传统 RAG 是“我给你找一堆材料,你自己看”,Agentic RAG 是“我一边看材料一边判断,发现不够就继续找,直到能回答为止”。
这个转变听起来很简单,但实现难度不小。因为一旦模型的输出会触发检索动作,你就不能再用一个“检索 → 生成”的简单函数来封装系统。你需要一个能承载状态、能调用工具、能把多轮检索结果合并的运行时。这也是为什么现在大家讨论 Agentic RAG 时,言必称 ReAct、Plan-and-Execute、Graph 这些概念的原因。
1.3 适合 Agentic RAG 的场景清单
不是所有场景都需要上 Agentic RAG。我建议你在动手前先做个判断,如果下面几条里有两条以上中了,那可以考虑:
- 问题经常是复合型,包含多个实体条件或多个子问题(比如跨文档对比、因果分析、多条件筛选)
- 单次检索的结果老是不够用,需要连续查多轮才能拼出完整答案
- 答案的质量要求高,禁不起幻觉,需要检索结果作为事实依据
- 文档类型杂、结构不统一,同一个问题可能散落在不同格式的文件里
- 你受够了每次都要人工调整 top-k、chunk_size 这些参数来迁就不同问题
反过来,如果你的场景是标准 FAQ、产品说明书查询、内部政策问答这类“单跳问题为主”的简单任务,传统 RAG 加上一个好的混合检索就已经够用了,上 Agentic 纯属增加延迟和成本。
2. 架构设计与工具选型
2.1 两种主流编排模式的取舍
实现 Agentic RAG 的第一件事,是确定 agent 的编排模式。当前主流有两种,我把它们的适用场景和坑都列一下。
ReAct 模式(Reason + Act)是最常见的。模型每走一步,先输出推理过程和下一步动作,如果动作是检索,检索结果再作为下一轮的上下文输入。整个流程是“思考-行动-观察”的循环,适合交互式问答、需要多轮探查的问题。
我实际用下来,ReAct 最大的优点是灵活,模型想查什么就查什么,不需要预先定义固定的流程。缺点是慢,因为每一步推理都要调用一次 LLM,如果问题简单,纯属浪费 token;如果问题复杂,模型可能会反复尝试同一步操作,陷入“检索 - 觉得不够 - 再检索”的死循环。
Plan-and-Execute 模式则是先让模型生成一个完整的执行计划,然后按照计划依次执行检索和生成步骤,最后汇总结果。这种方式适合结构清晰、阶段明确的任务,比如“先查背景资料,再查具体数据,最后对比两个方案”。优点是可以并行执行独立的检索任务,整体延迟比 ReAct 低;缺点是计划可能执行到一半发现方向错了,前面的工作全部白费。
我在模拟项目 X 里用的是 ReAct 为主、Plan-and-Execute 为辅的混合方案。具体来说,简单问题走 ReAct 快速回答,复杂问题先生成计划再逐步执行。这个方案也比较推荐给刚上手的团队,能兼顾灵活性和稳定性。
2.2 Agent 框架的选型心得
市面上 agent 框架不少,LangChain、LlamaIndex、Haystack 都提供了 Agentic RAG 的封装。我自己踩了一圈坑之后的结论是:不要一上来就选最抽象、最高级的 API,除非你已经非常熟悉底层逻辑。
如果你只是想快速验证想法,LangChain 的 AgentExecutor 和 create_react_agent 是最快上手的路径,文档多、示例多,遇到问题能搜到很多现成的答案。LlamaIndex 的 Agent 体系和 RAG 的结合更紧密,如果你是 LlamaIndex 的重度用户,直接用它的 AgentRunner 会让数据链路更顺畅。我们团队在模拟项目 X 里用的是 LangChain,但说实话不是因为它最好,是因为我们最熟悉、问题最好排查。
有一点特别提醒:框架封装得越高级,出问题的时候越难定位。有时候 Agent 行为不对,你根本分不清是 prompt 写得不对、函数调用参数格式有问题,还是检索结果本身太差。所以我在项目里一直提倡“先裸写一个最小可用版本,确认核心链路能跑通,再套框架”,避免一开始就陷入框架的抽象层里。
2.3 检索工具集的设计原则
Agentic RAG 比传统 RAG 多了一个“工具”的概念。在传统 RAG 里,检索是一个内部步骤;在 Agentic RAG 里,检索是 agent 可以调用的一批外部工具。工具设计得好不好,直接决定 agent 的上限。
我的经验是,工具不要贪多,但一定要职责清晰。模拟项目 X 里,我只给 agent 暴露了三个检索工具:
第一个是精确关键词检索,走 BM25 或全文索引,适合查代码、编号、精确名称这类不能被语义改写“糊弄”掉的实体内容。第二个是语义向量检索,走 embedding 相似度,适合查“意思相近但用词不同”的内容。第三个是结构化数据查询,走数据库/API,适合查“某个部门的人数”“某个项目的预算”这类有明确数值答案的问题。
这三个工具分开设计,agent 才会在合适的场景选合适的工具。如果你用一个“万能检索工具”把所有方法包在里面,agent 就失去了决策空间,退化成传统 RAG。
还有一点值得注意:工具的描述信息(description)要写清楚“这个工具适合查什么、不适合查什么”,因为模型是靠描述来决定调用策略的。我在项目里吃过亏,把向量检索工具的描述写成“通用知识查询”,结果 agent 问什么都先去芝麻大点的事都先搜一遍向量库,慢且不准。
3. 核心节点设计与参数调优
3.1 路由决策:先判“要不要检索”
Agentic RAG 里最重要的一步不是检索,而是“路由决策”——也就是让 agent 先判断:这个问题需不需要查文档?直接回答行不行?
这一个判断能省掉大量无效检索。我见过不少人把所有问题都丢给检索流程,结果用户只是问“你好,你会什么”,系统也去检索一遍文档库,纯属浪费。优化之后,agent 先判断是闲聊还是知识查询,闲聊就直接回答,知识查询才进入检索环节。
路由决策的实现方式有两种。简单方案是给 agent 加一个工具叫“no_retrieval”,它不需要调用任何检索,直接输出答案。复杂方案是自己写一个分类器,但说实话没必要,因为 agent 本身就能做语义分类,只要在 system prompt 里把规则写清楚就行。我用的 prompt 片段是:
你的任务分为两类:闲聊寒暄直接回复,知识查询需要调用检索工具。如果用户问题中提到了具体产品、流程、政策、项目名称或数据指标,一律视为知识查询。
这个看似简单的一句话,把 agent 的行为约束得非常好。你只要注意别把“判断”的成本算漏了,这个分类本身也是要消耗 token 的。
3.2 查询改写:让“问题”更可检索
用户的原始提问往往不适合直接拿去检索,因为口语化、指代不明、缺少上下文是常态。比如用户说“那个上线失败的方案后来怎么调整的?”,这句话拿去向量检索,匹配效果很差,因为“那个”“后来”这些词没有明确的语义指向。
所以 Agentic RAG 在检索前通常要加一个“查询改写”节点。改写的目标有三个:去掉指代词、补全缺失主体、把复合问题拆成单问题。
我的做法是自己实现一个 rewrite 函数,把用户问题和对话历史一起喂给 LLM,让它生成 3 到 5 个适合检索的独立问题。注意,不是生成 1 个,而是多个。因为一个复杂问题往往需要多个角度的检索结果,多生成几个候选查询,可以显著提高召回率。
举个例子,用户问“对比一下 A 方案和 B 方案在云资源成本上的差异”,改写后可能生成三个查询:“A 方案云资源成本构成”“B 方案云资源成本构成”“A 与 B 云资源成本对比分析”。这三条查询分别检索,结果拼在一起作为最终上下文。
查询改写这个环节容易被忽略,但它对 Agentic RAG 的效果提升非常明显。我实测下来,在同样的语料库上,加了改写的系统比不写改写的系统在答案完整度上有质的差别。
3.3 检索结果的合并与去重
Agentic RAG 跑完多个检索调用之后,经常会拿到大量文本块,这些文本块之间可能信息重叠,甚至互相矛盾。如果不做合并和去重,上下文既浪费 token,又容易让模型混淆。
合并阶段我用的策略是“按来源 + 语义相似度”双重去重。首先按文档来源分组,同一个文档里高度相似的句子只保留一块;然后跨文档比较,如果两块文本的向量余弦相似度超过 0.9,也视为重复,保留更完整的那块。
上下文长度控制也很关键。我一般把用于最终生成的上下文压缩到 2000 token 以内,按“相关度排序 + 截断”来处理。不是说检索出来的所有块都要填进上下文,优先保证前面 1000 token 是最高质量的答案信息源。
这里有个小技巧:别只是简单截断,而是在截断前问一次模型“哪些内容与答案最相关”。让模型先提炼出关键信息,再用提炼后的紧凑摘要作为最终答案的事实依据。这个思路有人叫“压缩重排”,它比直接塞原文要贵一点,但答案质量高出一大截。
4. 实操过程:从搭建到调优
接下来进入到文章最实操的部分。我以模拟项目 X——某公司内部知识库问答 Agent——为例,完整梳理了从环境搭建到调优的整条路径,你可以拿着这套流程直接复现。
4.1 环境准备与依赖安装
我假设你已经有一个可以运行的 Python 环境,版本 3.10 以上。这部分我不展开讲 Python 安装,直接给出依赖清单和安装命令。
pip install langchain langchain-openai faiss-cpu chromadb pip install "unstructured[pdf,docx]" markdown pip install "arize-ai[agent]" # 可观测性组件,后面讲我在项目里采用的组合是:LangChain 作为 agent 框架,OpenAI 作为 LLM,ChromaDB 加 FAISS 做向量存储,Unstructured 负责解析 PDF 和 Word。
一个非常容易踩的坑是:LangChain 的版本升级频率很高,API 经常变。如果你照网上教程写代码,最好把版本号锁住,否则同一个示例代码在两个月后可能直接报错跑不起来。我的建议是装完立刻执行 pip freeze > requirements.txt,把版本锁定记录下来。
4.2 文档入库与索引构建
入库这一步看似简单,其实对最终效果影响巨大。文档解析、切分、向量化的每一步都决定了下游检索的上限。
第一个要处理的问题是格式解构。Unstructured 解析 PDF 时,如果文档是扫描件,必须先用 OCR;如果是 Word,要留意表格内容的提取。我遇到过很多次的坑:表格被解析成纯文本后,行和列的关系全部丢失,检索出的内容让人看不懂在说什么。后来我改用“表格优先存入结构化字段,文本块只保留上下文描述”的方案,情况才好转。
第二个问题是 chunk 切分。我的经验是 chunk_size 设在 512 到 1024 token 之间,overlap 设为 128 左右。但实际切分不能只看 token 数,一定要按语义边界切。最理想的切分边界是“段落级别”,也就是从“案例二:退款纠纷”到“案例三:发票问题”这种有明显标志的位置断开。我建议不要用固定长度硬切,至少要有标题/段落感知的切分逻辑,否则跨段语义断裂会严重影响检索质量。
第三个问题是向量化模型选型。我用的 embedding 模型是 text-embedding-3-large,支持 3072 维向量。实际项目中我对向量维度的感受是:不是越高越好,因为高维度向量在 FAISS 里的内存占用和检索延迟都会上升,而且在中小规模的文档库里,256 维和 3072 维的效果差距并不明显。你完全可以先用开源 embedding 模型跑通,再逐步升级。
4.3 Agent 核心配置与关键代码
环境跑通之后,重点就是 agent 本身的配置。我把模拟项目 X 里最核心的一个部分拆出来看,也就是 agent 怎么组织检索行为。
我用的核心配置是 LangChain 里的 create_react_agent,绑定一批工具,然后通过一个循环运行。示例代码如下,你可以直接跑通看看效果:
from langchain_openai import ChatOpenAI from langchain.agents import create_react_agent, AgentExecutor from langchain import hub # 1. 定义检索工具 semantic_search = create_semantic_tool(collection, embedding_model) keyword_search = create_keyword_tool(index_path) sql_query = create_sql_tool(database_uri) tools = [semantic_search, keyword_search, sql_query] # 2. 加载 prompt(从 hub 拉一个 ReAct 模板,再自定义调整) prompt = hub.pull("hwchase17/react") # 3. 实例化 agent llm = ChatOpenAI(model="gpt-4o", temperature=0) agent = create_react_agent(llm, tools, prompt) agent_executor = AgentExecutor( agent=agent, tools=tools, verbose=True, max_iterations=5, handle_parsing_errors=True, ) # 4. 运行查询 response = agent_executor.invoke({ "input": "对比 A 方案与 B 方案在云资源成本上的差异,并给出推荐" }) print(response["output"])代码里几个参数值得单独说明:
max_iterations 是 agent 的最大循环轮数,我设成 5。意思是 agent 最多进行五轮“思考-行动-观察”,如果五轮还没得出答案,就放弃并返回当前已获得的信息。这样设置是为了防止死循环。如果你发现 agent 经常走到迭代上限,先别急着调大数值,而是去检查工具描述是否清晰、检索质量是否达标。
handle_parsing_errors 设置为 True 也很关键。agent 在推理时偶尔会输出不规范的 JSON,导致解析失败,开启这个参数后系统会把这个错误传回给模型,让它自行修正。不加这个参数,一个小格式错误就能让整个流程崩溃。
temperature 要设置为 0。因为这是一项知识查询任务,不是创意生成,不需要引入随机性。一旦 temperature 不为 0,同样的查询可能给出不同答案,这在企业场景里是不可接受的。
4.4 从 60 分到 80 分的调优心得
到了这一步,你的 Agentic RAG 应该能跑起来了,但答案质量可能只有 60 分。接下来我和大家分享几个从 60 分到 80 分的常用调优心得。
第一个心得是工具描述词的“反向调优”。我发现 agent 选择工具的行为很大程度取决于工具 description 里的措辞。语义检索工具描述里如果写“适合查找含义相似的信息”,模型就会更多调用它;如果你希望模型优先用精确检索,就把那个工具的描述改得更“主动”一些。有一次我把工具描述里的“推荐使用”三个字删掉后,调用频次立刻下降了三成。这说明了描述对 agent 行为的引导力非常强。
第二个心得是检索结果的数量不宜太多。传统 RAG 里 top-k 一般设 4-6 个,但 Agent 场景里如果每个工具都返回一堆块,上下文很快就会撑满。我建议每个工具的 top-k 控制在 3 个以内,并且启用重排(rerank),把最相关的放到最前面。我用的重排模型是 Cohere Rerank,效果比直接用向量的相似度排序好很多。
第三个心得是优先修“检索盲区”,而不是修“生成幻觉”。很多人一旦发现答案不对,第一反应是换大模型、写更长的 prompt。但我排查了多个案例后发现,多数答案质量问题的根源是检索阶段没找到关键文档,而不是生成阶段没表达好。遇到质量问题时,先去看看 agent 的中间日志,确认它到底检索到了什么,再决定是优化检索还是优化生成。
第四个心得是对话历史的管理。Agentic RAG 如果要做多轮对话,不能把所有历史都塞进上下文。我用的是一个简单的滑动窗口:保留最近三轮对话,再加上当前问题的改写结果。这样既保留了指代关系,又不会让上下文失控。更复杂的摘要式记忆我试过,但实际收益不大,反而增加了系统复杂度。
5. 常见问题与排查技巧实录
Agentic RAG 的可观测性比传统 RAG 要差,因为流程长了,任何一步出问题都会导致最终答案异常。这一节我把实操中反复踩过的坑整理成速查表,并给出具体的定位思路。
| 症状 | 可能原因 | 排查建议 |
|---|---|---|
| Agent 反复调用同一个检索工具无法停止 | 查询改写不彻底导致每次拿到的结果都一样 | 检查 rewrite 是否产生了新查询,如果没变化就是改写 prompt 强度不够 |
| 答案与检索到的文档无关 | 上下文被截断或压缩环节丢掉了关键信息 | 打开 verbose 日志,确认最终送进 LLM 的上下文是否包含关键段落 |
| 检索质量高但答案没有引用证据 | 是 prompt 里没有强调“必须标注来源” | 在 system prompt 里加一句“每个结论必须标注来源文档编号” |
| Agent 频繁触发迭代上限 | 工具调用失败或检索结果太差,模型无法收敛 | 先检查工具返回的格式是否正确,再检查检索内容是否相关 |
| 多轮对话后 Agent 行为漂移 | 历史对话太长,降低了指令优先级 | 缩短历史窗口,或者在每轮对话重新注入 system 指令 |
| 答案生成很慢 | 检索调用过多 | 减少 max_iterations,或者对简单问题直接走传统 RAG 快路径 |
排查思路上,我的核心建议是:不要只看最终答案,一定要完整 dump agent 的思考过程。LangChain 在 verbose=True 时会打印每一步的 action、observation,这是你在定位问题时最重要的线索。
我遇到过一次很有趣的案例:某个深夜,用户问的问题很简单,agent 却走上了一条完全错误的调用链,最后调了六七次工具也没答出来。我看日志发现,原因是用户的输入里有一个错别字,导致改写出来的查询和向量库里的所有内容相似度都极低。这个问题如果只盯着最终答案,根本不知道怎么定位。后来我加了“查询置信度预警”信号,当检索结果的最高相似度低于 0.6 时,自动告诉用户“我可能没找到相关资料,你可以换个说法吗”,效果好了很多。
6. 成本控制与可观测性建设
6.1 成本模型:Agent 为什么烧钱
Agentic RAG 比传统 RAG 昂贵的根本原因是:检索过程本身也是 LLM 推理,每一步“思考”都要花钱。传统 RAG 一个请求的成本大头就是最终生成那一次调用,Agent 模式下,查询改写、路由判断、每轮推理、工具输出解析都是成本项。
我按一个中等规模的内部知识库来看跑一组数字:假设用户每天 1 万次查询,每次查询平均 5 轮推理,每轮 token 消耗 400 个输入 + 100 个输出,以我们使用的模型价格计算,每天的 token 成本大概在整体约 450 元人民币上下。而传统 RAG 同样请求量下,成本可能只有它的六分之一。
那么这个成本花得值不值呢?我的看法是,如果 Agentic RAG 把“答对率”从 70% 提升到 90%,对企业来说,省下的客服人力成本和试错成本远远超过 token 费用。但前提是你要精细控制,不然成本会失控。
我主要的控制手段有三个:第一,分级路由,先判断问题是简单还是复杂,简单问题直接走传统 RAG,不进 Agent 循环;第二,限制检索工具单次返回块数和重排的候选数量;第三,给每个任务的迭代次数设上限,不至于无限制烧钱。
6.2 Agent 的可观测性是排查问题的救命稻草
传统 RAG 出问题,看输入输出就能大致猜出哪里不对,Agentic RAG 不行。如果你没有完整的 trace 链路,出了问题就像在黑盒里找针。我在模拟项目 X 里使用的是 Arize Phoenix 来捕获 agent 的每一步思考过程,包括 prompt、推理链、工具调用结果、耗时。
核心的拦截点有三个:第一,每条 LLM 调用的输入输出;第二,每个工具函数的入参和出参;第三,整个 trace 的树状结构,能清楚看到 agent 从哪个节点开始走偏。
可观测性建设越早越好。我见过很多团队原型阶段不重视 trace,demo 跑得欢,一上生产遇到问题就抓瞎。我强烈建议你在写第一行 agent 代码时就接上 trace SDK,后面会省掉很多不必要的痛苦。
6.3 评测集的价值:做一个记录“标准答案”的回归测试数据库
最后想分享的是评测集。没有评测集的 Agentic RAG 优化,本质上是在“盲调”。你调了一个参数,感觉效果好了一点,但不知道是不是心理作用;过了几天参数漂移,你又得重新来回调。评测集的意义在于:把“感觉”变成“可验证的分数”。
我在模拟项目 X 里做了五十条覆盖典型困难场景的评测用例,每条包含查询、标准答案、检索必须命中的文档 ID。每次调优之后都跑一遍这五十条,记录通过率。通过率从 60% 提到 80%,每一分变化背后的原因都一目了然。
建评测集时有一个细节值得注意:不要只记录“正确/不正确”,还要给答案分三个等级:完全正确、部分正确(缺关键信息)、完全错误。部分正确占了大多数,这类情况往往反映的不是单个环节的问题,而是“检索到了但没用上”或“检索没找全”的系统性缺陷。把部分正确的用例单独拿出来做根因分析,你会发现优化空间远比想象的大。
评测集从第一天就建最好。哪怕一开始只有十条用例,也比没有强。因为评测集不只是用来验证优化效果,更是帮你理解系统行为的一面镜子。你可以从里面快速总结出当前系统的短板在哪里。
我这里把整个项目的架构总结成一张核心逻辑链,希望对你组织自己的 Agent 有帮助:
传统 RAG(一步到位)→ 加查询改写(让问题更可检索)→ 加路由节点(让简单问题走快路径,复杂问题走 Agent)→ 加工具集(让 Agent 有选择空间)→ 加迭代循环(让 Agent 能自我纠错)→ 加评测与 trace(让一切变得可维护)
这条链路走完之后,你会发现 Agentic RAG 并没有想象中那么神秘,它就是“把 RAG 从函数变成了系统”,所有的复杂度都是为了让系统在更多情况下有应对策略。
如果你想在预算有限的情况下先做一个最小验证,别急着把整套架构搭完。先实现一个 ReAct + 一个检索工具 + 一个查询改写,把这三个环节跑通,拿一组真实问题测一测效果。绝大多数项目在这个最小配置下已经能解决七成以上的复杂检索问题,后面的成本优化、工具细化、评测体系,都是在 80 分以上时再逐步补课的。希望你读完这篇文章之后能少走一些弯路,这也是我自己实操中最想早点知道的那些事。