ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Agentic RAG实战:从传统检索到带决策的智能问答系统

Agentic RAG实战:从传统检索到带决策的智能问答系统 简介面向AI应用开发者与RAG技术研究者的Agentic RAG可运行源码包围绕传统RAG单一知识源与一次性检索的短板演示如何将AI代理接入检索增强生成管道实现多源动态选择与多轮自主检索。包内共3个文件以index.html可视化说明页面、inscode在线运行环境配置、gitignore工程辅助文件为主压缩包仅8KB轻量精炼适合直接下载后对照学习。内容覆盖Agentic RAG的核心概念、单代理与多代理架构对比、函数调用语言模型与DSPy/LangChain等代理框架的实施路径并给出可直接运行的示例源码既能作为理解原理的配套材料也可作为启动二次开发的脚手架。目前已有139人学习适合正在探索企业级智能信息检索方案的技术人员快速上手同时资源也客观点出了技术门槛与维护成本等落地挑战。 说实话我第一次看到“Agentic RAG”这个词时第一反应是“又是一个新包装概念”但当我真的把一套可运行源码搭起来、跑通一个多轮检索的问答场景后我转变了看法——这玩意确实不是包装它把RAG从“检索一段就回答”的单程流程升级成了“边查边想、缺了再查”的决策循环。这篇文章不打算做名词科普而是以我手头这个可运行的极简项目为主线带你拆一遍Agentic RAG的完整实现状态机怎么设计、检索器如何和LLM协同、几个关键决策点怎么落地以及我在实测中踩过的坑。适合两类人一类是已经搭过基本RAG、想让系统能处理复杂问题的开发者另一类是刚接触Agent/RAG、想通过可运行代码理解概念的同学。1. Agentic RAG到底是什么从“检索抄写员”到“带决策的助理”1.1 传统RAG的三大硬伤RAG现在用的是文档切片-向量化-检索-拼接-生成的套路。这套东西对简单问答够用但一旦问题变复杂三个毛病就会被放大。第一出错是“一次性”的。文档切片如果切得不好检索到的内容质量差生成阶段再怎么调prompt也没用。传统RAG没有“发现自己检索错了”的机制第一次检索命中垃圾片段答案基本就废了。第二只能处理单跳问题。比如“这个项目的负责人是谁”一次检索能搞定。但“项目A的负责人现在负责哪个项目”需要先查到负责人再查另一个项目的关联信息传统RAG做不了这种链式查询。第三对上下文拼接不敏感。多篇文档拼到一块后如果存在矛盾或重叠信息模型只能硬着头皮答既无法判断证据是否冲突也无法主动找更多材料佐证。我习惯用一个类比传统RAG像一个检索抄写员你问它什么它飞快去文档库复印几页纸拿回来念给你。复印到关键页就答得好复印到无关页就胡说。而Agentic RAG更像是你雇了一个会查资料的助理它拿到问题后会先判断“这需要去档案库吗”然后拆成几个小任务去翻翻完如果发现还缺信息会再回头继续查直到它觉得自己手里的材料足够支撑一个答案。1.2 Agentic RAG的核心把“检索”变成可调用的工具放到技术层面这个“助理”其实来自ReAct模式的延伸Thought思考- Action行动- Observation观察结果循环反复。Agentic RAG把“检索”当作一个可以被反复调用的工具而不是流程里的一次性步骤。每次检索后系统都会问自己两个问题现有上下文能回答用户吗如果不能缺口是什么然后针对缺口重新生成新的检索请求再走一轮。这看起来只是多了一个循环实际改变了RAG的根本性质——从“流水线”变成“闭环”。闭环的价值在于系统有了纠错和自我规划的能力这是传统RAG加再多样本命中率也换不来的。所以判断一个系统是不是真Agentic看它有没有“决策环”就够了。这个标准直接影响后面的架构选择和代码设计。接下来我会以这个极简项目的完整实现为主线把决策环的每个环节拆开来看。2. 系统架构与关键设计动手前先想清楚这四件事2.1 识别真Agentic有没有决策环现在市面上的RAG项目越来越多喜欢挂“Agentic”的牌子但不少只是套了个多查询检索本质还是单向流程。我判断一个系统是不是真Agentic只问三个问题系统能不能在缺少信息时自己决定再查一次 检索策略是不是固定的 回答前有没有对证据充分性的独立评估如果答案都是肯定的才是Agentic RAG。如果只有“多查询检索”而没有任何评估、反馈、再规划的环节那它充其量叫“增强版RAG”。这个判断标准在选型时候特别重要——很多人没想清楚就上LangGraph结果只是把原来的流水线画成了图并没有引入真正的决策。2.2 技术选型LangChain、LangGraph还是手写循环我在这个项目里做了一个偏“保守”的选择文档切块和向量化用了现成方案但Agent决策循环没有上LangGraph而是手写了一个极简状态机。选型对比如下方案优点缺点LangGraph节点化架构清晰、有图可视化、适合复杂多智能体抽象层多调试要绕好几层版本升级还容易破坏APILangChain Agent框架配置方便内置工具调用决策逻辑被框架包住遇到问题不好定位手写循环所有逻辑摊在眼前易于调试和理解依赖少复杂场景需要自己管理状态最终选择手写理由是我要做的核心动作只有三个——规划、检索、评估。三个动作写成函数就能跑不需要引入一整套图执行引擎。而且对读者来说看50行手写代码比看300行框架代码更容易理解Agentic RAG的本质。如果你要做的场景是复杂多智能体协作、多个工具交叉调用再考虑LangGraph这类方案也不迟。另外要注意的是embedding和LLM的选择。我用了OpenAI的text-embedding-3-small和gpt-4o-mini这个组合在性价比上相当能打。如果你没有OpenAI的key把这两处替换成你本地部署的Embedding和对话模型逻辑完全一样。3. 核心流程拆解一轮回答背后的三次决策3.1 第一次决策这个问题需要检索吗很多RAG一上来就无脑检索用户问“你好”也去扫描一遍文档库浪费token还影响回答质量。Agentic RAG的第一步是让LLM判断当前问题是否需要检索。不需要的情况包括闲聊、对已有上下文的追问等。这个判断我用一个很轻的prompt实现要求模型输出JSON包含need_search字段和sub_questions列表。如果判断不需要检索就直接进入回答阶段。这里有个小技巧不检索时不要简单返回空结果而是让模型生成一段自然答复比如用户问“你好”系统可以正常打招呼而不是答非所问地搬出文档内容很影响体验。3.2 第二次决策检索结果够不够回答这是和传统RAG差异最大的一步。传统RAG拿到top-k就直接生成答案而Agentic RAG会先把检索到的片段交给另一个评估环节让它判断“这些材料到底够不够回答用户”。这里的评估prompt需要明确要求模型列出“还缺什么”不要只给一个yes/no否则后一轮没法定位问题。实测下来这个评估环节能挡住不少瞎答。比如用户问“第三季度营收下降的原因”第一次检索到的片段可能只有下降数据没有原因分析模型的评估结果就会是“数据有了但缺少对下降原因的解释”。这个缺口的描述会直接作为下一轮检索的输入。3.3 第三次决策怎么补检和生成拿到缺口后不是简单拿原问题再搜一遍而是把缺口描述转换成一个新的检索问题。这一步相当于在原有问题上“追加了一个追问视角”。比如原始问题是“营收下降原因”缺口是“缺少华东区促销活动效果数据”那新检索问题就会是“华东区促销活动效果如何”之类的表述。循环会持续到两种情况为止评估认为材料充分或达到最大轮数限制。最后一次生成时我会把全部收集到的证据按轮次和来源编号整理好让回答要求带上引用标记。这一步有附带好处用户可以追溯答案来自哪一段原文回答的置信度和可信度都会明显提升。4. 可运行源码实现极简版本也能跑出效果4.1 环境准备这个项目只需要两个核心依赖openai调用LLM和Embedding版本1.0和numpy做余弦相似度计算。Python 3.10就行。安装命令pip install openai numpy然后设置环境变量OPENAI_API_KEY。我实际测试时用的Python 3.11openai版本是1.30左右numpy用的2.0兼容性没有遇到问题。如果你要把检索换成自己的文档库建议把Embedding部分接FAISS或Chroma我这里为了演示简洁直接用向量点积做相似度几段演示文档规模下完全够用。4.2 核心代码检索器与决策循环先建索引和检索函数import json import numpy as np from openai import OpenAI client OpenAI() # 记得配置 OPENAI_API_KEY def build_index(docs, chunk_size500): chunks [] for doc in docs: chunks.extend([doc[i:ichunk_size] for i in range(0, len(doc), chunk_size)]) emb client.embeddings.create(modeltext-embedding-3-small, inputchunks) embs np.array([e.embedding for e in emb.data]) return chunks, embs def retrieve(query, chunks, embs, top_k3): emb client.embeddings.create(modeltext-embedding-3-small, input[query]) qv np.array(emb.data[0].embedding) scores (embs qv) / (np.linalg.norm(embs, axis1) * np.linalg.norm(qv) 1e-9) ids np.argsort(scores)[::-1][:top_k] return [(chunks[i], float(scores[i])) for i in ids] def call_llm(system, user): resp client.chat.completions.create( modelgpt-4o-mini, temperature0, messages[{role: system, content: system}, {role: user, content: user}], ) return resp.choices[0].message.content def format_evidence(evidence): return \n.join(f[来源{i}] {e[chunk]} for i, e in enumerate(evidence))然后是核心的决策循环def agentic_rag(question, chunks, embs, max_steps3): evidence [] query question history [] for step in range(max_steps): plan json.loads(call_llm( 你是检索规划器。判断当前问题是否需要检索若需要拆成最多3个子问题。 只输出JSON{\need_search\: true/false, \sub_questions\: []}, f当前问题{query}\n已收集证据片段{format_evidence(evidence)} )) if not plan[need_search]: break for sq in plan[sub_questions]: for chunk, score in retrieve(sq, chunks, embs): evidence.append({chunk: chunk, score: score, query: sq}) verdict json.loads(call_llm( 你是质检员。判断已收集的片段是否足以回答原始问题。 只输出JSON{\enough\: true/false, \reason\: \还缺少什么具体一点\}, f原始问题{question}\n已收集片段{format_evidence(evidence)} )) if verdict[enough]: break query call_llm( 把质检员的缺口描述转换成一个新的检索问题不要任何解释只输出问题。, verdict[reason] ) history.append(query) answer call_llm( 你是答疑助手。只根据提供的片段回答如果片段不足以作答就明说。 引用格式[来源N]。, f原始问题{question}\n片段{format_evidence(evidence)} ) return answer这段代码就是整个系统的核心。三个决策点分别对应上面的规划、评估、补查。主要注意一点JSON解析。模型偶尔会输出多余内容我建议在解析前做一次字符串清洗比如用正则截取第一个{到最后一个}之间的内容否则会直接抛异常整个流程就断了。4.3 跑一个真实例子多轮检索是怎么发生的我用一组销售月度分析文档测了这个流程。文档里有华东区、华北区的营收数据和促销记录。用户的问题是“华东区上季度营收下滑原因是什么和促销活动有没有关系”第一次循环规划环节先拆出两个子问题“华东区上季度营收数据是多少”“华东区上季度的促销活动有哪些”检索后进入评估模型认为“已经拿到下滑数据和促销活动列表但缺少促销活动效果和下滑的关联性证据”。于是第二轮生成的新检索问题变成了“华东区促销活动对营收的影响”再次检索后证据链就完整了系统最终给出的回答不仅涵盖了下滑原因还标注了来源。这个例子看起来简单但如果你用传统RAG跑第一步很可能只检索到营收数据直接答成“下滑了X%”根本不会往促销活动方向延伸。差别不在检索器而是系统有没有“自我审视”的能力。5. 实测记录跑通后我发现的问题与被忽略的坑5.1 问题一上下文污染导致检索漂移刚开始我把前一轮的“缺口描述”和“已收集证据”全部拼进新的检索问题里发现效果反而变差。比如原始问题是华东区销售前一轮证据里有华北区的数据模型在生成新问题时会被这些干扰带偏检索出来的东西开始偏离主题。后来我把规则改成新检索问题只基于缺口描述生成不掺入完整历史往来记录。结果稳定很多。这也解释了为什么很多Agent项目越跑越偏——不是模型不行是上下文的信噪比在每一轮都在降低检索漂移就是在大量劣质历史上下文中累积出来的。5.2 问题二循环失控与Token浪费在评估prompt写得不严格的时候模型很爱说“不够、还需要更多”然后系统就不停地检索。我在一个长文档场景里遇到过连续6轮检索实际上第3轮就已经覆盖了答案。这个问题的解法有两个一是max_steps设硬上限我默认3轮二是在评估prompt里加强制指令只有当事实确实无法组织答案时才允许返回enoughfalse。另外每轮检索的top_k不要设太大。3-4个片段足够太大只会增加token成本而且重要信息会被淹没在无关片段里。这个值不能照搬要根据你的文档粒度来调文档切片越碎top_k可以稍微大一点否则信息覆盖不够。5.3 问题三结果不可复现Agentic RAG带LLM就有随机性更别说中间还要多次解析JSON。为了结果可复现我把所有LLM调用的temperature设成0同时对embedding结果做了缓存。实测同样的文档和问题跑两遍的输出基本一致。还有一个小细节把依赖版本固定住。openai这个库版本升级特别快一次升级后我遇到过返回对象结构变化代码直接崩了。建议在项目里加一个requirements.txt锁定版本比如openai1.30.0、numpy2.0.0这样别人clone下来也能直接跑出和你一样的结果。6. 个人经验让Agentic RAG稳定的几条土办法最后聊几句实操体会。这套系统跑通之后我又拿它试了内部知识库的问答整体稳定性比传统RAG高了一个档次但也别指望它万能。如果问题本身在文档里根本不存在答案再多的检索轮次也救不回来所以我的第一个经验是在系统入口加一层范围校验告诉用户它在什么范围内回答问题省得用户问超纲问题系统在那里空转。第二个经验是调试Agentic RAG时把每一轮的规划和评估输出打出来看。我会在代码里加一个debug参数把plan、verdict、query都print出来。很多你以为的“模型理解问题”实际看一眼日志才发现是prompt写得有歧义。日志在Agent系统里不只是排错工具更是观察系统行为的窗口这个习惯帮我排掉了至少一半的诡异问题。第三个经验别一上来就上重框架。先用这种手写的极简循环把逻辑跑通理解每个决策点再根据业务复杂度决定要不要上LangGraph或更完整的Agent框架。我见过太多人一上来就引入一套复杂的图编排最后卡在框架版本适配问题上反而离核心业务越来越远。这套代码改一改就能落在自己的文档场景里。如果你已经在跑传统RAG不妨把评估和补查这两个环节先加上你会明显感觉到同样的文档库回答的质量和可追溯性都会上一个台阶这也是我觉得Agentic RAG最值得投入的地方。本文还有配套的精品资源点击获取
返回列表