上一篇文章我们聊了传统 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 作为 LLMChromaDB 加 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(modelgpt-4o, temperature0) agent create_react_agent(llm, tools, prompt) agent_executor AgentExecutor( agentagent, toolstools, verboseTrue, max_iterations5, handle_parsing_errorsTrue, ) # 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 在 verboseTrue 时会打印每一步的 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 从哪个节点开始走偏。可观测性建设越早越好。我见过很多团队原型阶段不重视 tracedemo 跑得欢一上生产遇到问题就抓瞎。我强烈建议你在写第一行 agent 代码时就接上 trace SDK后面会省掉很多不必要的痛苦。6.3 评测集的价值做一个记录“标准答案”的回归测试数据库最后想分享的是评测集。没有评测集的 Agentic RAG 优化本质上是在“盲调”。你调了一个参数感觉效果好了一点但不知道是不是心理作用过了几天参数漂移你又得重新来回调。评测集的意义在于把“感觉”变成“可验证的分数”。我在模拟项目 X 里做了五十条覆盖典型困难场景的评测用例每条包含查询、标准答案、检索必须命中的文档 ID。每次调优之后都跑一遍这五十条记录通过率。通过率从 60% 提到 80%每一分变化背后的原因都一目了然。建评测集时有一个细节值得注意不要只记录“正确/不正确”还要给答案分三个等级完全正确、部分正确缺关键信息、完全错误。部分正确占了大多数这类情况往往反映的不是单个环节的问题而是“检索到了但没用上”或“检索没找全”的系统性缺陷。把部分正确的用例单独拿出来做根因分析你会发现优化空间远比想象的大。评测集从第一天就建最好。哪怕一开始只有十条用例也比没有强。因为评测集不只是用来验证优化效果更是帮你理解系统行为的一面镜子。你可以从里面快速总结出当前系统的短板在哪里。我这里把整个项目的架构总结成一张核心逻辑链希望对你组织自己的 Agent 有帮助传统 RAG一步到位→ 加查询改写让问题更可检索→ 加路由节点让简单问题走快路径复杂问题走 Agent→ 加工具集让 Agent 有选择空间→ 加迭代循环让 Agent 能自我纠错→ 加评测与 trace让一切变得可维护这条链路走完之后你会发现 Agentic RAG 并没有想象中那么神秘它就是“把 RAG 从函数变成了系统”所有的复杂度都是为了让系统在更多情况下有应对策略。如果你想在预算有限的情况下先做一个最小验证别急着把整套架构搭完。先实现一个 ReAct 一个检索工具 一个查询改写把这三个环节跑通拿一组真实问题测一测效果。绝大多数项目在这个最小配置下已经能解决七成以上的复杂检索问题后面的成本优化、工具细化、评测体系都是在 80 分以上时再逐步补课的。希望你读完这篇文章之后能少走一些弯路这也是我自己实操中最想早点知道的那些事。