尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从零构建Agentic RAG:基于智能体决策循环的下一代检索增强生成系统

从零构建Agentic RAG:基于智能体决策循环的下一代检索增强生成系统 1. 先搞清楚 Agentic RAG 到底解决了什么问题如果你正在处理基于大模型的文档问答、知识库检索并且对传统 RAG 的“检索-生成”流程中那些死板、不灵活、容易出错的地方感到头疼那 Agentic RAG 就是你接下来最值得花时间研究的方案。它不是一个新工具而是一种新的实现思路和架构模式。简单来说传统 RAG 就像一个严格执行固定指令的实习生你问一个问题它去向量库搜一下然后把搜到的内容塞给大模型让模型生成答案。这个过程很直接但问题也很明显如果一次检索没找到关键信息或者检索到的信息有冲突、不完整这个“实习生”就没办法了只能硬着头皮给出一个可能不准确的答案。Agentic RAG 则把这个“实习生”升级成了一个有思考、会判断、能主动执行多步骤任务的“智能体”。它不再是一次性检索完事而是让大模型本身或一个轻量级规划器来主导整个过程先理解问题再决定需要检索哪些信息、分几步检索、如何验证和整合检索到的信息最后才生成答案。这个过程是动态的、可迭代的更接近人类处理复杂问题的思考方式。所以这篇文章的核心不是介绍另一个 RAG 框架而是带你理解这种“智能体化”的 RAG 实现原理并手把手从零搭建一个可运行的示例。最关键的价值在于它能显著提升复杂、多跳问题的回答准确性并让整个系统在面对模糊查询或信息不全时具备更强的鲁棒性。如果你已经用过 LangChain、LlamaIndex 这类框架的基础 RAG并且遇到了效果瓶颈那么 Agentic RAG 就是你下一步的优化方向。2. 理解 Agentic RAG 的核心原理从“流水线”到“决策循环”在动手写代码之前必须把原理吃透否则你调参和排错都会没有方向。Agentic RAG 的核心思想是引入一个“规划-执行-观察”的循环。2.1 与传统 RAG 的对比我们先看一个典型的多跳问题“苹果公司 CEO 蒂姆·库克在大学期间主修的专业是什么”传统 RAG 流程将问题“苹果公司 CEO 蒂姆·库克在大学期间主修的专业是什么”进行向量化。在向量数据库中搜索最相似的文本块。可能搜到“蒂姆·库克是苹果公司的 CEO。” 和 “工业工程是一个涉及复杂系统优化的专业。”将这两个不连续的片段连同问题一起交给大模型。大模型基于这些碎片信息“猜”答案很可能出错或回答“不知道”。Agentic RAG 流程规划大模型作为智能体的大脑分析问题制定计划。计划可能是“要回答这个问题我需要知道a) 蒂姆·库克在哪所大学就读b) 他在该大学主修的专业。”执行-观察第一轮智能体执行第一个子任务“检索‘蒂姆·库克 大学’相关信息”。执行后观察到结果“蒂姆·库克毕业于奥本大学并获得杜克大学富卡商学院的 MBA。”规划第二轮智能体根据新信息更新计划“现在我知道他在奥本大学读的本科。接下来需要检索‘蒂姆·库克 奥本大学 专业’。”执行-观察第二轮执行第二个子任务检索到“蒂姆·库克在奥本大学主修工业工程。”生成智能体整合所有观察到的信息生成最终答案“蒂姆·库克在奥本大学主修工业工程。”这个对比清晰地展示了 Agentic RAG 的“主动性”。它把检索动作从固定的一次变成了由大模型推理驱动的多次、有目的的迭代过程。2.2 核心组件拆解一个典型的 Agentic RAG 系统包含以下几个关键部分理解它们的关系比记住名字更重要智能体/规划器这是系统的大脑。通常由一个大语言模型担任。它的核心职责是任务分解将用户的复杂问题拆解成一系列可执行的子问题。工具调用决定在每一步调用哪个工具如检索工具、计算器、代码解释器。状态管理记住之前的步骤、观察到的结果并据此规划下一步。答案合成在所有必要信息收集完毕后综合生成最终答案。工具集这是系统的手和脚。最核心的工具就是检索工具。它封装了访问向量数据库、知识图谱或任何外部知识源的逻辑。其他工具可能包括计算器、网页搜索API需合规使用、代码执行器等。智能体通过调用这些工具来与外界交互。工作记忆/上下文管理器这是系统的笔记本。它负责维护对话历史、智能体之前的思考过程Chain-of-Thought、以及从工具执行中观察到的结果。这部分信息会作为上下文在每一轮循环中反馈给智能体帮助它做出下一步决策。执行引擎这是系统的调度中心。它负责协调整个循环调用智能体进行规划调用工具执行动作将结果存入工作记忆并判断循环是否应该终止例如当智能体决定“现在可以回答最终问题了”或达到最大迭代次数时。关键原理整个系统运行在一个ReAct (Reasoning Acting)或类似框架的循环中。智能体输出的不是最终答案而是一个包含“思考”和“行动”的指令。执行引擎解析这个指令调用对应工具再把工具返回的结果作为“观察”塞回给智能体开启下一轮循环。直到智能体输出一个最终答案为止。3. 从零搭建环境准备与最小可行性系统理论讲完了我们开始动手。我建议的路径是先搭建一个能跑通最小流程的系统验证核心循环然后再去考虑优化和扩展。不要一开始就追求功能完整。3.1 环境与依赖准备我们使用 Python 作为实现语言。以下是你需要准备的核心库我通常会创建一个新的虚拟环境来管理它们# 创建并激活虚拟环境 (可选但强烈推荐) python -m venv agentic_rag_env source agentic_rag_env/bin/activate # Linux/macOS # agentic_rag_env\Scripts\activate # Windows # 安装核心依赖 pip install openai # 用于调用大模型API如GPT-4/GPT-3.5 pip install chromadb # 轻量级向量数据库用于本地存储和检索文档 pip install sentence-transformers # 用于生成文本向量的嵌入模型 pip install langchain # 提供了丰富的Agent和Tool抽象能极大简化开发。我们用它来搭建主体框架。 pip install tiktoken # 用于计算Token管理上下文长度 pip install pypdf # 用于解析PDF文档如果你用PDF作为知识源版本说明以上库的版本尽量选择较新的稳定版。如果遇到兼容性问题优先考虑调整langchain和openai的版本。这是搭建过程中第一个容易踩坑的地方。3.2 构建知识库向量数据库Agentic RAG 依然需要知识库作为检索来源。我们先构建一个最简单的本地向量数据库。# prepare_knowledge_base.py import os from langchain_community.document_loaders import TextLoader, PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import Chroma def create_vector_store(data_dir, persist_directory./chroma_db): 从指定目录加载文档切分生成向量并存储到Chroma中。 documents [] # 1. 加载文档 for filename in os.listdir(data_dir): filepath os.path.join(data_dir, filename) if filename.endswith(.txt): loader TextLoader(filepath, encodingutf-8) elif filename.endswith(.pdf): loader PyPDFLoader(filepath) else: continue # 跳过不支持的文件 loaded_docs loader.load() documents.extend(loaded_docs) print(f已加载: {filename}) if not documents: print(未找到支持的文档文件。) return None # 2. 分割文本 # 这里的分块策略直接影响检索效果。Agentic RAG 允许动态多步检索所以单块可以稍大。 text_splitter RecursiveCharacterTextSplitter( chunk_size500, # 每个块约500字符 chunk_overlap50, # 块间重叠50字符保持上下文 separators[\n\n, \n, 。, , , , , , ] ) splits text_splitter.split_documents(documents) print(f文档共分割为 {len(splits)} 个文本块。) # 3. 创建向量存储 # 使用本地嵌入模型避免网络调用。all-MiniLM-L6-v2 是一个效果和速度平衡的选择。 embeddings HuggingFaceEmbeddings(model_nameall-MiniLM-L6-v2) vectordb Chroma.from_documents( documentssplits, embeddingembeddings, persist_directorypersist_directory ) vectordb.persist() # 持久化到磁盘 print(f向量数据库已创建并保存至: {persist_directory}) return vectordb if __name__ __main__: # 假设你的文档放在 ./data 目录下 vectordb create_vector_store(./data)关键点文档加载确保你的文档.txt,.pdf放在正确的data_dir路径下。文本分割chunk_size和chunk_overlap需要根据你的文档类型调整。对于 Agentic RAG由于智能体会进行多轮精炼检索初始块可以稍大以减少总块数。嵌入模型使用sentence-transformers的本地模型避免初期调试时受网络和API限制。all-MiniLM-L6-v2是一个不错的起点。持久化persist_directory指定了向量数据库的存储位置。第一次运行后会生成该目录后续可以直接加载无需重复处理文档。运行这个脚本确保你的知识库成功构建没有报错。这是后续所有工作的基础。4. 实现 Agentic RAG 核心循环现在进入最核心的部分实现智能体循环。我们将利用 LangChain 提供的AgentExecutor和Tool抽象这比完全从零写要高效和稳定得多。4.1 定义核心工具检索工具首先我们需要把上一步创建的向量数据库包装成一个智能体可以调用的“工具”。# agentic_rag_core.py from langchain.agents import Tool, AgentExecutor from langchain.agents import create_react_agent from langchain.prompts import PromptTemplate from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import Chroma from langchain_openai import ChatOpenAI import os # 1. 加载之前创建的向量数据库 def load_retriever(persist_directory./chroma_db): embeddings HuggingFaceEmbeddings(model_nameall-MiniLM-L6-v2) vectordb Chroma( persist_directorypersist_directory, embedding_functionembeddings ) # 将向量数据库转换为检索器设置相似度检索的返回数量 retriever vectordb.as_retriever(search_kwargs{k: 3}) # 每次检索返回3个最相关的块 return retriever # 2. 定义检索函数 def knowledge_base_search(query: str) - str: 智能体调用的检索工具函数。 输入查询字符串 输出检索到的相关文本拼接成一个字符串。 # 这里是关键我们在这个函数内部调用 retriever # 注意retriever 需要在函数外部定义或通过其他方式传入。 # 为了简单我们在这里直接调用全局的 retriever。 # 更工程化的做法是使用类或闭包来封装状态。 docs retriever.get_relevant_documents(query) content \n\n.join([doc.page_content for doc in docs]) # 如果什么都没找到返回一个明确的提示这能帮助智能体调整策略。 if not content.strip(): return 在知识库中未找到与查询直接相关的内容。 return f根据知识库检索到以下相关信息\n{content} # 初始化检索器全局变量供工具函数使用 retriever load_retriever() # 3. 将函数包装成 LangChain Tool 对象 tools [ Tool( nameKnowledgeBaseSearch, funcknowledge_base_search, description在本地知识库中搜索与问题相关的信息。当你需要查找具体事实、数据、概念或细节来回答问题或验证信息时就使用这个工具。 输入应该是一个清晰、具体的搜索查询语句。 ), # 未来可以在这里添加更多工具例如 Calculator, WebSearch 等 ] print(工具列表已定义。)工具定义的精髓description字段至关重要它是给智能体大模型看的“说明书”。清晰、具体的描述能极大提升智能体调用工具的准确率。这里我们明确告诉它当你需要找事实、数据、细节时就用这个工具。4.2 构建智能体与执行引擎接下来我们创建智能体并将工具赋予它。# 接上段代码 (agentic_rag_core.py) # 4. 初始化大语言模型 # 注意你需要设置你的 OpenAI API Key。出于安全考虑不要硬编码在代码中。 os.environ[OPENAI_API_KEY] 你的-OpenAI-API-Key # 请替换为你的Key或通过环境变量设置 llm ChatOpenAI(modelgpt-3.5-turbo, temperature0) # 使用 GPT-3.5温度设为0保证稳定性 # 5. 创建 ReAct 智能体 # LangChain 提供了预设的 ReAct 提示词模板我们基于它创建智能体。 react_prompt PromptTemplate.from_template( Answer the following questions as best you can. You have access to the following tools: {tools} Use the following format: Question: the input question you must answer Thought: you should always think about what to do Action: the action to take, should be one of [{tool_names}] Action Input: the input to the action Observation: the result of the action ... (this Thought/Action/Action Input/Observation can repeat N times) Thought: I now know the final answer Final Answer: the final answer to the original input question Begin! Question: {input} Thought:{agent_scratchpad} ) agent create_react_agent(llm, tools, react_prompt) # 6. 创建执行器 agent_executor AgentExecutor( agentagent, toolstools, verboseTrue, # 设为 True可以看到智能体完整的思考过程便于调试 handle_parsing_errorsTrue, # 处理智能体输出格式解析错误 max_iterations5, # 限制最大迭代次数防止死循环 early_stopping_methodgenerate # 当智能体输出 Final Answer 时停止 ) print(智能体执行器已创建。)关键参数解析verboseTrue这是调试阶段最重要的设置。打开后控制台会打印出智能体完整的“Thought/Action/Observation”循环你能清晰地看到它是如何思考、如何决策的。max_iterations5安全阀。防止智能体陷入无限循环。对于大多数问题3-5轮迭代足够。handle_parsing_errorsTrue智能体的输出有时可能不符合严格的格式要求这个设置能让执行器更鲁棒。temperature0对于任务规划和工具调用这类需要确定性的任务将温度设为0可以减少随机性使行为更可预测。4.3 运行你的第一个 Agentic RAG 查询现在让我们用一个小测试来验证整个流程。# 接上段代码 (agentic_rag_core.py) if __name__ __main__: # 测试查询 test_question 蒂姆·库克在大学主修什么专业 print(f用户问题: {test_question}) print(- * 50) try: result agent_executor.invoke({input: test_question}) print(\n *50) print(最终答案: , result[output]) except Exception as e: print(f执行过程中出现错误: {e})运行并观察确保你的./data目录下有一些包含“蒂姆·库克”和“工业工程”相关信息的文档可以是简单的.txt文件。运行python agentic_rag_core.py。观察控制台输出。你应该能看到类似以下的日志格式可能因版本略有不同用户问题: 蒂姆·库克在大学主修什么专业 -------------------------------------------------- Entering new AgentExecutor chain... Thought: 用户想知道蒂姆·库克在大学主修的专业。我需要先找到蒂姆·库克的教育背景信息。 Action: KnowledgeBaseSearch Action Input: 蒂姆·库克 大学 教育背景 Observation: 根据知识库检索到以下相关信息 文档1: 蒂姆·库克Tim Cook是苹果公司的首席执行官。他于1982年毕业于奥本大学Auburn University获得工业工程学士学位。 文档2: 此后他于1988年在杜克大学福库商学院Duke Universitys Fuqua School of Business获得了工商管理硕士学位MBA。 Thought: 我找到了相关信息。蒂姆·库克在奥本大学获得了工业工程学士学位。这应该就是他主修的专业。 Final Answer: 蒂姆·库克在奥本大学主修工业工程专业并获得了工业工程学士学位。 Finished chain. 最终答案: 蒂姆·库克在奥本大学主修工业工程专业并获得了工业工程学士学位。恭喜你已经成功运行了一个最简单的 Agentic RAG 系统。可以看到智能体主动生成了搜索查询“蒂姆·库克 大学 教育背景”检索工具返回了包含关键信息的文档智能体据此得出了最终答案。这个过程是动态规划的而不是一次性检索。5. 进阶优化与生产环境考量一个能跑通的 Demo 只是开始。要让 Agentic RAG 真正可靠、高效你需要关注以下几个进阶方向。5.1 提升检索质量与工具设计多路检索与重排序不要只依赖单一的向量相似度检索。可以设计多个工具KeywordSearchTool: 基于关键词如 BM25检索对专有名词更敏感。VectorSearchTool: 基于语义向量检索。HybridSearchTool: 结合两者并对结果进行重排序使用像 Cohere Rerank 或 BGE Reranker 这样的交叉编码器模型。 智能体可以根据问题类型决定调用哪个工具或者调用多个工具后自己整合信息。工具描述的优化工具的description需要不断打磨。你可以通过记录智能体“错误调用工具”或“调用工具时输入不佳”的案例来反推如何修改描述使其更精准地指导智能体。例如如果智能体总用很长的问题去检索你可以在描述里加上“输入应为简洁的关键词组合”。检索结果的精炼在knowledge_base_search函数中可以对检索到的文档进行后处理比如提取最相关的句子、去重、总结再把更精炼的结果返回给智能体减少其上下文负担。5.2 优化智能体规划与控制定制提示词LangChain 默认的 ReAct 提示词是个通用模板。你可以根据你的领域定制提示词。例如在提示词开头加入“你是一个严谨的研究助手。在回答关于公司、人物、产品的问题时必须严格依据知识库中的信息。如果知识库中没有明确信息就说不知道不要编造。” 这能显著提升答案的准确性和可控性。设置系统角色在使用 OpenAI API 时可以通过system_message参数为模型设定一个更稳固的角色这比在用户提示词中强调更有效。实现记忆管理对于多轮对话需要将历史对话包括智能体的思考过程有效地管理起来避免超出模型的上下文窗口。LangChain 提供了多种Memory组件如ConversationBufferWindowMemory可以集成到AgentExecutor中。5.3 工程化与稳定性错误处理与重试在AgentExecutor外层包裹错误处理逻辑。网络超时、API 限额、工具调用失败等情况都需要有降级或重试机制。超时与迭代限制max_iterations必须设置。同时可以考虑设置总超时时间防止单个查询耗时过长。日志与监控记录每一次智能体的思考、行动和观察。这对于分析失败案例、优化系统至关重要。可以将verbose的输出重定向到日志文件。流式输出对于耗时较长的查询可以考虑支持流式输出先给用户返回部分信息或思考过程提升体验。成本控制Agentic RAG 由于涉及多轮 LLM 调用Token 消耗会比传统 RAG 高。需要监控每次查询的 Token 使用量并设置预算上限。5.4 常见问题排查清单当你搭建的系统不工作时按照以下顺序排查知识库问题现象智能体调用了检索工具但 Observation 总是“未找到相关内容”。排查首先直接测试你的检索器retriever.get_relevant_documents(“你的问题”)看是否能返回相关文档。如果不能问题出在文档加载、文本分割或嵌入模型上。检查原始文档内容、分割后的块是否合理、向量数据库是否成功持久化和加载。工具调用问题现象智能体的 Action 不是调用KnowledgeBaseSearch或者 Action Input 不合理。排查检查工具的description是否足够清晰。打开verboseTrue看智能体的 Thought 是否显示它理解了问题并决定搜索。可能是提示词或系统角色设置不当。LLM API 问题现象程序在agent_executor.invoke处卡住或报错。排查检查OPENAI_API_KEY是否正确设置网络是否通畅API 额度是否充足。尝试先用一个简单的llm.invoke(“你好”)测试 LLM 连接是否正常。无限循环问题现象智能体不停地在 Thought 和 Action 之间循环无法输出 Final Answer。排查首先检查max_iterations是否设置。然后分析verbose日志是不是检索工具始终无法提供关键信息导致智能体陷入“寻找-失败-再寻找”的循环可能需要优化检索策略或者在提示词中明确告诉智能体“如果经过X轮检索仍无法确定答案可以基于已有信息给出最合理的推断或直接说明信息不足”。6. 总结从 Demo 到实用系统的关键跃迁搭建出第一个能运行的 Agentic RAG Demo 只是理解了概念。要把它变成一个真正解决实际问题的系统你需要完成从“玩具”到“工具”的跃迁。这个过程中最该投入精力的不是追求更复杂的智能体架构而是夯实基础。首先把数据管道做扎实。Agentic RAG 再智能检索到的垃圾信息它也变不成黄金。花时间优化你的文档解析、文本清洗、分块策略和嵌入模型。一个高质量、结构清晰的向量库能解决后续 50% 的问题。其次像训练实习生一样“训练”你的智能体。通过精心设计的工具描述description和系统提示词system_message明确告诉它你的领域规则、回答风格和底线。收集一批典型的失败案例分析是工具调用不对、检索查询不佳还是合成逻辑有误然后有针对性地调整提示词。最后建立完善的观测和反馈闭环。在生产环境中记录每一个用户问题、智能体的完整思考链、调用的工具、检索到的内容以及最终答案。这不仅能帮你快速定位问题更是迭代优化系统最宝贵的材料。你可以定期用这些日志数据去评估效果发现模式持续改进。Agentic RAG 代表了 RAG 系统从静态管道走向动态认知的一个方向。它通过赋予系统“思考后再行动”的能力在处理复杂性、应对模糊性方面展现出了巨大潜力。虽然它会带来更高的复杂性和计算成本但对于那些答案质量至关重要、且问题模式多变的场景这种投入往往是值得的。现在你已经有了从零开始的代码和清晰的路径下一步就是将它应用到你的具体数据和问题上开始真正的迭代之旅。
返回列表