1. 传统RAG的极限在哪里1.1 一次标准RAG的过程长什么样先别急着上概念我直接说一个场景你做了一个基于大模型的内部知识库问答助手把几十篇产品文档切成小块做了向量化处理然后用户问“退货流程是什么”系统就把问题转成向量去向量数据库里做相似度检索把最像的几个文本块取出来再拼到Prompt里交给大模型生成答案。这套流程就是最经典的Retrieval-Augmented Generation也就是常说的RAG。它的核心假设很简单大模型的参数记忆有限但这不影响它“临时抱佛脚”你先把参考资料塞进上下文它就能对着材料回答问题。我最初搭这种系统时最大的感受是管道很顺但脑子很死。所谓“脑子很死”指的是RAG只有固定的三步Embedding - TopK检索 - 生成。无论用户问什么问题都走同一条管道差不过一个阈值就返回结果。这也是RAG被形容成“搜索引擎套了一个壳”的原因。很多小白卡在这里为什么同样的RAG工程别人做出来效果不错自己做出来回答离谱其实不是因为代码水平差而是没有意识到RAG的瓶颈不是生成而是检索和决策层面的僵化。检索结果的好坏直接决定了最终答案的上限而检索环节恰恰是整个链路里最不会“思考”的一部分。1.2 处理复杂查询时固定流程为什么失效传统RAG最常见的翻车场景是“混合条件查询”和多跳问题。举个例子有一个用户问“哪个酒店在市中心附近有宠物友好的餐厅而且我们小组预算人均不能超过150”如果你拿这个问题直接去做相似度检索会遇到三重麻烦语义被稀释问题里同时包含酒店位置、宠物友好、人均预算三个约束条件向量化之后它们被揉成一团匹配出来的文本块可能只对上了“酒店”和“市中心”另外两个条件直接被忽略。TopK两难K设小了漏掉关键信息K设大了噪声片段全挤进来。我见过不少人把TopK从4调到8再调到16效果依然不稳定就是因为问题本身可以被拆成多个独立维度而单次向量检索只适合“一段话找一段话”。时间或其他结构化条件没法满足“最近一个月”“预算不超过150”“南部区域”这类条件向量检索根本不懂它只能算语义的相似却无法做数值过滤和逻辑判断。还有更典型的多跳问题比如“张三在上家公司主导的支付系统被哪篇技术博客评价过”这种问题有两个隐含的实体跳转先要查出张三在哪家公司然后再拿这家公司去匹配技术博客。传统RAG在第一步就已经把相关性最高但方向不对的内容拉回来了后续生成自然不会对。用句大白话说传统RAG像一个把所有快递都送到一个配送站再分发的大仓库它适合批量处理、高效分发但遇到“多个收件人之间还有依赖关系”的包裹就手足无措了。业界把这类问题叫“单跳检索失效”现在大家已经形成共识问题复杂到一定程度后需要让模型自己决定“下一步干嘛”而不是靠工程师写死流程。2. Agentic RAG的核心思路2.1 从“检索后生成”到“边想边检”的转变Agentic RAG这个名词这两年高频出现但理解起来并不难。它没有把RAG推翻而是把“一次性检索”换成了“Agent驱动的多步迭代检索”。如果把传统RAG比作一个只能按菜单做菜的厨师那么Agentic RAG就是一个会自己看冰箱、查菜谱、边炒边尝、发现缺调料还会自己出门买的厨师。这个转变的关键在于系统由一个大模型作为“大脑中枢”它有权限调用不同的工具比如向量数据库、SQL查询器、Web搜索、文档解析器并且在每一步执行完之后把工具返回的结果拿回来“看”一眼再决定下一步是直接生成最终答案还是继续调用新工具补查信息。换句话说检索不再是一次性动作而是被放进了Agent循环里。很多没有动手做过的人会以为这不就是在代码里写个while循环让模型反复调用检索函数吗说出来你可能不信思路确实就这么简单但难点在于什么情况下该继续检索什么情况下该停止工具调用的参数怎么填多轮结果之间怎么合并去重这些细节不处理好系统就会陷入无休止的循环或者口吐一堆拼凑出来的错误答案。2.2 组建Agentic RAG的四类核心部件一个可用的Agentic RAG系统通常由下面几个部件组成搞懂它们你就能自己画出架构图。规划器规划器的职责是“拆解任务”。当用户提出一个复杂问题时规划器会先判断这是单一问题还是需要拆成多个子问题每个子问题应该交给哪个工具在实现上规划器通常是让大模型直接输出一个结构化意图比如JSON格式的“行动计划”然后在代码里解析并执行。我做项目时固定使用一段Prompt让模型从四个角度规划是否需要检索、检索哪个知识源、当前信息是否足够、是否需要继续。初期你可以不写复杂的规划逻辑让模型只决定“要检索”和“不要检索”其实已经能规避很多胡言乱语了。路由与工具路由是Agentic RAG的“交通枢纽”。用户问题进来之后先经过路由判断不同意图的查询被分发到不同的知识源。举一个实际例子我的项目里有两个知识库一个是产品手册另一个是FAQ会话记录。如果直接混合检索两者互相干扰做了路由之后先判断问题是“询问操作细节”还是“询问已知问题”再决定只查其中一个库准确率明显上升。工具层也不止是向量检索。一个成熟的Agent可以调用SQL查询器查订单数据、调用API查实时天气、调用文档解析器处理PDF表格。这里的核心价值是不同类型的知识交给最合适的工具而不是一律转成向量。记忆Agent循环会产生大量中间结果记忆模块负责在这些结果之间保留上下文。它分为短期记忆和长期记忆短期记忆保存当前任务中已检索过的内容避免重复查询长期记忆可以用一个向量库保存用户偏好或历史修正记录让系统越用越顺。最常见的坑是把所有中间步骤一股脑全部拼进下一轮Prompt结果上下文膨胀模型反而把关键信息淹没在大量检索片段里。所以记忆模块不只要“记住”还要“会忘”。反思反思机制是Agentic RAG与传统RAG最大的区别之一。模型在生成最终答案之前会先自检“这些检索到的片段与问题相关吗有没有自相矛盾的信息有没有关键条件没覆盖到”如果发现不足系统会触发新一轮检索或改写问题再查。这里我强烈建议反思不要只依赖大模型自己的感觉最好配合打分规则。比如如果答案里缺少数字实体但问题中包含时间或数值就触发重查如果两个片段对同一事实说法矛盾则追加一次对比检索。规则和模型结合比单纯让模型“自己判断一下”稳得多。3. 落地时最常见的几种Agentic RAG模式3.1 路由模式一条Query选择最合适的知识源路由模式是成本最低、见效最快的Agentic RAG改造。它不改变现有RAG的检索和生成只是在入口加一个“分流器”用户查询先进模型模型输出一个路由决策然后根据决策把查询发往不同知识源。我在一个具体的模拟项目里实现过两个知识源拆分一个放标准化产品文档另一个放客服工单记录。最初所有查询都同时走两个库返回结果经常是文档和工单拼接在一起前后风格完全不像。后来加了路由Prompt只让模型输出两个词product_docs或support_tickets实测之后回答的连贯性好了很多。这个小改动背后的原理是不同知识源有各自的语域和覆盖范围混合召回看似“什么都有”实际是“什么都不精”。路由让每个知识源只处理它真正擅长的问题噪声自然减少。3.2 查询改写模式把模糊的问题变成明确的条件用户问题往往高度口语化、充满隐含表达。查询改写的目标不是简单地“换一个说法”而是扩展和澄清查询背后的多个可能方向。举个例子用户问“这家餐馆怎么样”这句话直接去检索结果一定很飘。查询改写之后可能会变成三个子查询“这家餐馆的菜品口碑”“这家餐馆的价格是否合理”“这家餐馆的环境和排队情况”。三个子查询分别去检索再把结果汇总最后生成的答案就能覆盖多个维度。这个模式早期有一个很流行的叫法叫“多查询生成”有些人也把它和“假设性文档嵌入”混着用先用一个小模型生成一个假想的答案文档再用这个假想的文档去和库里的文档做匹配。在实际项目里多查询更常用因为它实现简单只需要在代码里拆分列表然后循环执行现有检索函数就行。查询改写有一条核心经验改写方向不能凭空想象要让模型基于已有的知识切片去提候选问题。没有依据的改写就像猜谜最后检索出来的内容通常也不在线。3.3 子问题分解与递归检索真正解决多跳问题如果你遇到的最棘手Case都是“要先搞清A才能拿到B最终要回答C”这种多跳问题那就要上子问题分解模式。我几年前处理过一个典型多跳查询“甲公司的售后退款政策里对超过30天提交的申请是怎么处理的”要回答这个问题需要先定位“甲公司售后政策”这个文档片段再在其中找“退款申请时限”相关条款。放在传统RAG里一次检索经常只命中“售后退款政策”却没有抓到“30天时限”因为这两个信息在同一个文档的不同段落相似度计算时被排名一起压制了。子问题分解的做法是先让Agent拆出两个子问题——“甲公司的售后政策文本是什么”和“其中超过30天申请的处理规则是什么”然后先检索第一个子问题拿到段落A再把段落A和第二个子问题的组合检索最终才生成结论。这里的递归检索质量远高于一次性大TopK因为它每一步都带着上一步的已知信息继续收敛。递归检索有一个必须注意的控制点子问题的数量不宜超过3个。我实测拆出4个子问题时最后拼接生成的内容会发散因为每个子问题都消耗一段上下文而大模型在长上下文里对信息的利用率并不均衡。3.4 RAG-Fusion与重新排序让多路结果合并出新答案RAG-Fusion的思路是对同一个问题同时执行多个不同检索策略然后把各路结果合并后再让模型生成。比如可以同时走向量检索、关键词检索、SQL轻量过滤三条路径分别拿到排名列表再做融合重排。最常见也最简单的融合算法是倒数排名融合。它不依赖得分绝对值而是根据每份列表的排名位置给分。原因很简单不同检索方式的分数尺度不一样向量相似度是0到1的小数关键词命中的权重则可能很大直接用原始分数相加会不公平。按排名算分则绕开了尺度问题所有检索结果都在同一标准下竞争。融合之后的结果并不完美由于多路来源的文本可能存在语义重复或互相矛盾合并完还需要一道重排让有价值的片段排在前面然后才进入生成阶段。这一步是锦上添花但对稳定性的提升非常直观。4. 从零搭建一个最小可用的Agentic RAG系统4.1 技术选型与总体架构有些朋友一听到“Agent”就以为要上多复杂的框架其实最小系统完全可以用纯代码实现核心就是一个函数调用循环。总体架构是这样一个LLM入口负责意图判断两个工具函数分别负责向量检索和SQL查询一个循环控制器负责处理“继续”或“停止”信号。选型上LLM用常规的对话模型就行向量库用常见解决方案都差不多SQL操作也不需要额外引入重型框架。我的建议是前期不要引入复杂的Agent框架或者可视化的编排平台先在代码里把“选工具、调用工具、把结果加回上下文、再次判断”这个循环写清楚你会更明白底层发生了什么。框架虽然好用但会把你和核心逻辑隔开出问题反而不好排查。4.2 核心代码结构与关键实现下面给一个极简的Agentic RAG实现省略了工程上的异常处理只保留最重要的循环逻辑。这里我用两个工具做演示search_docs负责向量库检索query_db负责查结构化订单数据。from typing import Callable, Dict, List import json class MinimalAgent: def __init__(self, llm, tools: Dict[str, Callable]): self.llm llm self.tools tools def run(self, user_query: str, max_steps: int 5): messages [{role: user, content: user_query}] for step in range(max_steps): response self.llm.chat( messagesmessages, tools[ { name: search_docs, description: 在知识库中检索文档片段, parameters: { type: object, properties: { query: {type: string, description: 检索关键词或句子} }, required: [query] } }, { name: query_db, description: 查询结构化订单数据只支持精确条件过滤, parameters: { type: object, properties: { customer_id: {type: string, description: 用户编号}, }, required: [customer_id] } } ] ) tool_calls response.get(tool_calls) if not tool_calls: return response[content] messages.append(response) for call in tool_calls: fn_name call[function][name] args json.loads(call[function][arguments]) result self.tools[fn_name](**args) messages.append({ role: tool, tool_call_id: call[id], content: json.dumps(result, ensure_asciiFalse) }) raise RuntimeError(超过最大步数没有生成最终答案)这段代码的逻辑很直白把用户问题放进消息列表。让模型输出一个动作。如果模型没有调用工具说明它认为已经有足够信息直接输出答案。如果模型调了工具就执行工具把结果作为一条tool消息加回对话中让模型在下一轮参考工具结果继续决策。这里的“妙”之处在于工具返回的结果不是替代用户问题而是作为“新的上下文证据”加入对话。模型会看到原问题、自己的判断、工具返回的内容然后综合决定下一步。这种模式也被称为“ReAct循环”理解它对后面学习更复杂框架非常有帮助。在实际项目中search_docs的返回结果通常长这样一个包含文本片段、来源文件名、页码、相似度分数的列表。你可以在工具函数内部就做好截断和格式化避免把大段JSON全塞回给模型。我的经验是每个片段最多留300字来源信息用一行摘要否则工具返回消息会变得很臃肿模型反而不容易抓到重点。4.3 数据准备与评测指标Agentic RAG对数据质量的要求比传统RAG更高。因为Agent会主动调用多个工具如果知识库本身切块质量差、埋点信息缺失再聪明的规划器也没法检索到想要的答案。做数据准备时我习惯按“最小语义单元”来切块而不是按固定字数切。举例来说一段合同文本可能包含甲方的义务、乙方的义务、违约责任三个不同的语义块每块300字左右比一个固定512字符的窗口合理得多。对于结构化数据则要保证表字段名和业务含义在Prompt里能对得上否则LLM填工具参数时会出现“猜字段名”的情况。评测方面我给自己定过一套简单实用的指标推荐给新手回答覆盖率标准答案中涉及的关键信息点有多少出现在模型回答里权重大概占60%。引用准确率回答里标出的引用来源是否真的包含对应信息这个指标在知识库场景特别重要因为AI制造幻觉的第一高发区就是引用。路由准确率针对有知识源路由的场景统计实际路由目标和预期是否一致这个能帮你判断入口意图识别是否可靠。评测集不用太大三类问题各准备30条就够了。但有一个关键动作跑出错误Case之后把失败记录保存成回归集每次改动路由或改写逻辑都重跑一遍。没有回归集的Agent项目改一个参数就会引入一堆不可预期的新错误。5. 实际项目里踩过的坑与避坑经验5.1 上下文失控把整个检索历史全塞给模型我最早做Agentic RAG时踩的最大坑是每次工具返回后把上一轮的完整检索结果、工具输出、解析日志全部拼进新的Prompt。结果跑到第三步模型已经忘记最初的问题是什么了开始围绕工具输出自我发散生成的内容像在复述文档而不是回答问题。后来我把上下文改成“只保留最近两轮摘要”第一轮工具输出会做信息压缩第二轮再检索时Prompt里只放一个问题、一个工具结果的精简摘要、一个待生成指令。系统效果稳定很多Token消耗也明显下降。这条经验可以总结成一句话Agent循环不是堆料越多越好而是上下文越精炼越好。工程上要专门写一个摘要函数对工具返回结果做二次压缩而不是让原始输出直接进入下一轮。5.2 工具参数“可空但关键”路由到空知识源疯狂重试还有一个印象深刻的问题某一次上线后日志里出现大量“调用query_db返回空结果Agent继续重新生成查询条件”的现象。原因是模型在填参数时没有校验字段一定是非空导致它经常拿一个空的ID去查数据库。查回来是空表Agent就换个参数再查陷入了无意义的循环。排查方法是给工具函数加上“空结果就明确报错”的约束并且在Prompt里强调“如果参数缺少必要字段不要自行猜测先向用户确认。”这听起来很基础但没有这个约束模型会产生大量无效工作对实时接口尤其伤。5.3 评测的“自我感觉良好”陷阱还有一次我在内部测试集上的回答准确率做到了85%感觉很满意但一到线上就发现用户反馈很差。调查后发现测试集的80多条问题里有超过一半的句子结构和表达套路非常接近模型的回答质量是被这种重复性数据“抬上去”的。后来我专门做了“表达扰动测试”把同一道问题的表达换五六种说法比如把“张三在哪家公司”改成“你知道张三的雇主是谁吗”。结果准确率直接掉了将近20个点。这说明模型很多时候是在“熟悉问题句式”而不是“真的理解了需求”。评测一定不能只看答案内容更要看“问题表达变化后系统是否还能稳定回答”。表达扰动方法是成本最低、最容易发现Agent系统脆弱点的测试手段。5.4 成本与延迟控制Agentic RAG每多一轮工具调用就多一次LLM调用Token开销成倍增长。一个需要3轮检索的复杂问题往往要花掉5到8次模型交互而传统RAG只需要一次。控制成本有几个实操技巧设置硬性最大步数5轮还出不了结果的直接降级成普通RAG回答别让系统无限跑。轻量意图路由用小的分类模型只有真正需要复杂推理的问题才进入完整Agent循环。缓存高频问题的检索结果。同一个查询内容如果向量结果一致性很高就可以直接复用上一轮的工具输出不用再调一次模型来判断。我见过有人把Agentic RAG的Effectiveness和Cost混为一谈觉得“能搜就多搜几次”导致单个请求成本比普通RAG高好几倍最后项目被叫停。先把成本兜住再谈效果才是落地的思路。6. 给新手的几条上手建议不要一上来就追求“全知全能的Agent”。我的建议是先动手写一个最简单的循环一个工具、一个模型、一个判断分支能回答一个问题就行。在这个基础上再逐步加入路由、改写、多路召回。每加一个模块就回归一次之前踩过的Case守住质量的底线。我曾经用整整两周时间只做了一个“查询改写子问题分解”的Agent流程没有加任何复杂框架。那两周的收获比后来两周研究新架构更多因为我真正理解了每一步里数据是怎么流动的、模型为什么要这样决策以及哪些环节可以交给代码规则去控制。还有一个经验想多说一句Agentic RAG本质上是工程问题不是算法问题。很多效果不佳的Case根源不在模型不够聪明而在路由规则混乱、工具接口模糊、检索片段质量参差。把工程的坑填好复杂度已经解决大半。最后分享一个小技巧在每个工具函数内部记录一条结构化日志内容包括调用参数、返回数量、耗时、是否空结果。这个日志平时看着不起眼但当线上反馈出现问题时它能帮你快速定位是路由跳错、检索不到、还是生成发散。我靠着这套日志在模拟项目和线上项目里排查问题的时间减少了一半以上。