行业资讯
📅 2026/9/6 10:39:56
从RAG到Agent:Agentic RAG架构演进与工程实践指南
1. 先把事情说清楚RAG和Agent到底是什么关系最近很多人在聊AI Agent聊RAG但说实话大部分讨论都停留在概念层面——PPT里画个流程图说“我们用RAG解决了幻觉问题”再画个框说“我们加了Agent实现自动规划”真到落地的时候一堆问题全冒出来了。我先用一句大白话把两者的关系说清楚RAG是给大模型装上一双可以随时翻书的手Agent是给大模型装上一个会做决定的大脑。这两者不是二选一而是层层递进的关系。RAGRetrieval-Augmented Generation检索增强生成解决的核心问题是大模型的知识截止日期和领域深度都不够。你让它写一篇关于你公司内部流程的文档它根本没见过你公司的流程瞎编是必然的。RAG的做法是先检索再生成——把用户的问题拿去知识库里找相关内容找到之后塞进上下文让模型“看着资料回答问题”。这样一来答案有了依据幻觉问题大幅缓解你的知识库也能随时更新不用动不动就重新训练模型。Agent解决的问题则更进一步当任务本身不是一个“问一句答一句”的简单问答而是需要多步推理、调用外部工具、动态决策的时候RAG单点检索就不够用了。比如你问“帮我分析一下今年Q3的销售数据找出下滑最严重的区域并生成一封提醒邮件”这里面涉及数据查询、数据分析、邮件撰写三个步骤每一步都需要不同的工具和不同的资料。Agent就是那个把这些步骤串起来、决定每步该干什么的角色。所以如果你只是想做一个“文档问答机器人”RAG就够了如果你要做的是一个“能自动干活的数字员工”你需要的是AgentRAG。现在行业里常说的Agentic RAG本质就是把检索能力包装成Agent可以调用的工具或者让Agent来决定什么时候检索、检索什么、检索几轮。这篇文章适合正在做RAG落地、准备往Agent方向进阶的开发者。我会把自己踩过的坑、验证过的方案、以及目前比较靠谱的工程实践都摊开来讲不吹概念只讲能跑的东西。2. 理解RAG的核心链路从文档到可用知识库RAG说起来简单但真正落地的时候你会发现80%的问题都出在“知识库构建”这一步而不是模型调用上。很多人第一次搭RAG拿几个PDF丢进去用现成框架跑通demo觉得也就那样。但一旦进入真实业务场景——几千份合同、几万条工单、上百个内部系统文档——问题就全冒出来了。2.1 RAG向量化流程拆解别小看“切文档”这一步RAG的完整链路是文档加载 - 文本清洗 - 分块Chunking - 向量化Embedding - 存储 - 检索 - 重排 - 生成。链路不算长但每一环都有坑。先说文档加载。很多人用pdfminer或PyPDF直接抽文本结果发现PDF里是扫描件抽出来全是乱码。这种情况需要先做OCR。我自己的经验是如果文档里有表格、图片、复杂排版直接用文本抽取会丢失大量语义信息。现在比较靠谱的方案是用版面分析模型先把文档结构解析出来区分标题、正文、表格、页眉页脚然后按结构去抽取。开源的LayoutLMv3、PaddleOCR的版面分析能力都可以用虽然比不上商用产品但对中小规模场景足够了。然后是文本清洗。这一步看起来不起眼但直接影响向量化质量。我见过最典型的坑是把页眉页脚、日期、页码这些噪音也向量化进去导致检索的时候匹配到一堆无关片段。清洗环节至少要做三件事去掉页眉页脚和页码、合并断行、统一编码格式。如果文档是HTML或Markdown还要去掉标签只留文本。另外注意不要过度清洗。有些人把所有标点都删了结果句子语义变得支离破碎检索效果反而变差。清洗的目的是去掉噪音不是破坏语义。关键的挑战在于分块。分块策略直接决定检索质量。我用过很多种分块方式简单总结固定长度分块比如每512个字符切一块实现最简单但边界容易切断语义。比如一个句子的后半截到了下一块检索的时候某块里只有半个意思模型拿到这半个信息回答自然不完整。段落级分块按段落切分语义相对完整但段落长短不一有的段落过短导致信息不足有的段落又超长。语义分块Semantic Chunking用向量相似度来判断哪里该断开。这种方法效果最好但计算成本高适合对质量要求高的场景。递归字符分块给定一个目标块大小按照分隔符优先级递归切分。这是LangChain里常用的一种方式兼顾了效率和效果。我自己在实践中的经验是分块大小不要拍脑袋定要根据你的文档类型和下游模型的上下文窗口来决定。比如你用的是GPT-4级别的大窗口模型块可以适当大一点如果用的是小模型块就小一点。常见的经验值是512到1024个token之间。但具体多少一定要拿你自己的文档去测试——拿一批有代表性的问题分别用不同的块大小跑一遍看检索命中率才能找到最优值。2.2 向量化模型与向量数据库选型分完块之后就是向量化。这一步的核心是选Embedding模型。选模型有两个关键指标维度不是越多越好检索效果才是王道以及中英文混合场景下要选多语言模型。我自己对比过几个主流选择OpenAI的text-embedding-3-small1536维、text-embedding-3-large3072维以及开源的BGE系列中文场景很强、M3E系列、以及智源的bge-m3。如果数据主要是中文BGE和M3E的效果往往不比OpenAI差而且本地部署没有数据泄露风险。这里有一个“为什么”层面的道理要讲透Embedding模型的质量决定了你的知识库“懂不懂”语义。它把一段文本映射到一个高维向量空间语义相近的文本向量距离也近。如果模型本身训练语料不够广或者对垂直领域术语理解不够再好的检索算法也救不回来。举个例子医疗领域有大量专业术语“CKD”和“慢性肾脏病”是同一个东西如果Embedding模型没学过这些术语它就无法把这两个词映射到相近的位置检索召回就会失败。向量数据库的选型也要讲策略。市面上的选择很多Milvus、Qdrant、Chroma、Pinecone还有传统数据库PostgreSQL的pgvector扩展。如果你只是做demoChroma和FAISS就够了本地跑起来最快如果是生产环境且数据量在千万级以下pgvector是个不错的选择省得再维护一套独立的向量库如果数据量过千万且并发高上Milvus或Qdrant是更稳的选择。这里有个工程上的小建议不要把向量检索当作唯一召回方式。实际业务中混合检索Hybrid Search即向量检索关键词检索的效果往往远好于单一向量检索。原因在于向量检索擅长处理语义相似但表述不同的情况关键词检索擅长精确匹配专有名词、编号、型号。比如用户搜“A100显卡的功耗”关键词检索能精确命中“A100”而向量检索可能把它和“A800”“H100”混淆。业界比较成熟的方案是先用向量检索召回Top 50再用BM25关键词检索召回Top 50合并去重后交给重排模型。2.3 重排Rerank为什么不能省很多人做RAG做到检索就完了直接把Top 5结果塞给大模型。这样做的结果是模型输出的答案经常“文不对题”——检索回来的片段里确实有相关信息但正确的信息排在第8位、第10位没被选进上下文。这就是为什么重排这一步不能省。召回阶段追求的是“尽量多地把可能相关的片段捞出来”精度可以低一点所以召回100条也不嫌多但模型上下文窗口有限你只能塞进去有限的片段。重排模型Reranker的作用是从这100条里选出和问题最相关的Top 5或Top 10。重排模型和Embedding模型不同。Embedding模型是把问题和文档分别编码成向量然后算相似度重排模型是把问题和文档拼接在一起用交叉编码器Cross-Encoder直接计算相关性分数。因为重排模型“同时看到了问题和文档的全部内容”所以它能捕捉到更细腻的语义匹配信息精度远高于向量相似度。代价是速度慢、计算成本高这也是它只能用来重排候选集、不能用来做全库检索的原因。推荐的组合方式是召回阶段用Embedding模型双塔架构速度快适合海量候选精排阶段用Cross-Encoder重排模型精度高适合小规模精排。开源的重排模型有BGE-Reranker系列、Cohere Rerank实测下来都能有效提升RAG效果。3. 从RAG到AgentAgentic RAG的架构演进如果你把RAG跑通了下一个问题一定是能不能让整个系统更智能一些用户的问题不再只是“XX是什么”而是“帮我对比一下这几个方案的差异”、“基于这些资料写一份报告”。这些问题没法靠一次检索解决需要多次检索、多次推理、甚至调用其他工具。这时候就需要引入Agent。3.1 Agent框架选型自己写还是用现成的Agent框架现在已经很成熟了选型时主要看你的场景和团队的技术栈。我用过的几个主流选择LangChain/LangGraph生态最全文档多踩坑的人也多社区能找到各种案例。LangGraph比LangChain更偏底层适合编排复杂的图结构流程。AutoGen微软出品适合多Agent协作场景多个Agent之间可以互相对话讨论。Spring AI如果你所在团队是Java技术栈这是个好选择。它提供了类似LangChain的抽象能力但用的是Java生态。自己写如果你的场景不复杂自己写一个Agent也不难。核心就是循环调用大模型每次把工具调用的结果反馈给模型让模型决定下一步做什么。这就是最朴素的ReAct模式。我在工程实践中的建议是如果你的流程相对固定比如固定先检索再总结用LangGraph或直接手写一个状态机就够了不要上太重的框架。复杂框架带来的学习成本和维护成本在早期会让项目进度拖慢一倍。反过来如果你的业务场景有很多不确定性Agent的行为需要非常灵活那可以考虑LangGraph尤其是它有持久化、人工介入、分支并行这些能力更适合生产级应用。3.2 Agent里面如何调用RAG工具Agentic RAG和传统RAG的关键区别在于谁来决定检索。传统RAG是“问题进来就检索”不管问题需不需要外部知识。Agentic RAG是“Agent先判断这个问题是否需要检索、需要检索什么、检索几轮”。这就带来一个核心问题如何让Agent具备“是否检索”的决策能力常见做法是Function Calling。你把RAG检索功能封装成一个工具函数比如knowledge_base_search(query: str) - list[str]然后在给模型的消息里声明这个工具的存在。模型的输出如果是“需要调用工具”就会返回一个结构化指令格式大概是{name: knowledge_base_search, arguments: {query: ...}}。你的代码检测到这样就执行函数把结果作为新的消息再喂给模型模型继续推理直到最终给出答案。有一点值得注意工具的描述信息非常关键。比如knowledge_base_search这个函数你给模型的描述如果只是“搜索知识库”模型不一定理解什么时候该用它。但如果你写“当你需要查询企业内部制度、产品文档、历史工单信息时使用此工具”模型就能更准确地进行工具调用的决策。这一步我是在实际测试中发现的差异——描述从一句话改成一段话之后工具调用的准确率大概提升了十几个百分点。除了RAG检索Agent里还可以挂很多其他工具计算器、SQL查询、API调用、代码解释器等等。这就是Agent的威力所在——它不局限于“从资料里找答案”而是能“调用各种工具完成一个完整任务”。3.3 理解Skill和Agent之间的边界热词里有一个搜索量很高的问题“skill和agent的区别”。这个问题恰好是很多人刚接触Agent开发时会产生的疑惑。用最朴素的话来解释Skill是“能力”Agent是“拥有能力的实体”。如果你在一个Agent开发平台里工作Skill通常意味着某个特定领域的一组能力封装——比如“生成合同文本”是一个Skill“做数据分析”是另一个Skill而Agent则是把这些Skill组合起来加上大模型的决策逻辑为一个完整的业务目标服务的系统。类比一下Skill就像工具箱里的扳手、螺丝刀、电钻Agent是那个会判断“现在该用扳手还是电钻”的工人。没有工具工人效率低没有人调度工具就只是躺在工具箱里的死物。在设计Agent系统时我的建议是先梳理业务需要哪些Skill再考虑Agent的编排逻辑。一个常见的错误是上来就画各种复杂的Agent流程图结果连基础能力还没封装好。好的做法是先把工具能力一个个做扎实做成标准的函数或API然后再设计Agent的决策逻辑去编排这些工具。4. 实操环节搭建一个可用的Agentic RAG系统概念讲再多不如上手跑一遍。这一节我把自己搭建一个Agentic RAG系统的完整过程拆开来讲。技术栈以Python为主中间会穿插Java场景的说明。4.1 基础技术栈搭建我这次用的是LangGraph OpenAI兼容接口 BGE-M3 Embedding Qdrant BGE-Reranker。如果你有特殊原因用不了OpenAI用开源的Qwen或DeepSeek模型也完全可以Agent和RAG的核心逻辑是一样的。先安装依赖pip install langgraph langchain langchain-openai qdrant-client fastembed sentence-transformers bge-reranker注意fastembed和sentence-transformers是两个不同的向量化库fastembed更轻量适合跑BGE系列如果要用其他模型可以选sentence-transformers这里有兼容性取舍按需选择。然后加载Embedding模型和向量数据库from sentence_transformers import SentenceTransformer from qdrant_client import QdrantClient embedding_model SentenceTransformer(BAAI/bge-m3) client QdrantClient(path./local_qdrant) # 本地持久化模式 # 将文档向量写入集合 docs [...] # 已经分块清洗好的文档列表 vectors embedding_model.encode(docs) client.upload_points( collection_namemy_knowledge, points[ {id: i, vector: vec, payload: {text: doc}} for i, (vec, doc) in enumerate(zip(vectors, docs)) ], )这一步没什么黑科技但有一个容易踩坑的地方bge-m3的向量维度是1024如果你之前用的是其他模型比如OpenAI的1536维同一个集合里只能用一种维度。所以向量库的集合设计要提前想好不同模型的向量不能混存。4.2 用代码实现Agentic RAG一次完整流程下载包里加了一条记录标注了日期、文档内容和来源链接方便后续审计。接下来看Agent的编排逻辑。首先定义RAG检索工具def rag_search(query: str, top_k: int 5) - list[str]: 当你需要查询公司内部制度、产品文档、历史工单、FAQ等资料时使用此工具。 query_vec embedding_model.encode(query) hits client.search( collection_namemy_knowledge, query_vectorquery_vec, limittop_k * 10 # 先召回50条 ) # 重排 reranker CrossEncoder(BAAI/bge-reranker-v2-m3) pairs [(query, hit.payload[text]) for hit in hits] scores reranker.predict(pairs) top_indices sorted(range(len(scores)), keylambda i: scores[i], reverseTrue)[:top_k] return [hits[i].payload[text] for i in top_indices]然后是Agent的核心循环from langgraph.graph import StateGraph class AgentState(TypedDict): messages: list final_answer: str def agent_node(state): # 使用大模型判断是否需要调用工具 response llm_with_tools.invoke(state[messages]) if response.tool_calls: for call in response.tool_calls: if call[name] rag_search: results rag_search(call[arguments][query]) state[messages].append({role: tool, content: results}) else: state[final_answer] response.content return state # 构建图 graph StateGraph(AgentState) graph.add_node(agent, agent_node) graph.add_edge(agent, agent) # 循环直到最终回答这段代码看起来简单但背后有个关键点值得多说几句Agent的循环机制。大模型每一次输出要么是工具调用指令要么是最终回答。你的代码要判断如果输出了工具调用指令就把工具执行结果作为新的消息追加到对话里再次调用模型直到模型不再调用工具、给出最终答案。这个循环需要有上限控制比如最多5轮否则遇到复杂任务可能无限循环下去。4.3 Java技术栈怎么落地Spring AI实践如果你的团队是Java技术栈用Python搭一套RAG服务可能不太现实。这时候Spring AI是个不错的选择。Spring AI最近在Java社区讨论度很高它提供了ChatClient、EmbeddingModel、VectorStore等抽象用法和Spring Boot的风格很统一。我在Java项目里的落地思路是用Spring AI的EmbeddingModel接口对接BGE-M3或OpenAI的Embedding接口用PgVectorStore作为向量存储如果团队已经有PostgreSQL实例省得再引入其他中间件用ChatClient对接大模型传入系统提示词和检索结果一段典型的Spring AI代码长这样Service public class RagService { private final ChatClient chatClient; private final VectorStore vectorStore; public String answer(String question) { // 检索相关片段 ListDocument docs vectorStore.similaritySearch( SearchRequest.builder() .query(question) .topK(5) .build() ); String context docs.stream() .map(Document::getContent) .collect(Collectors.joining(\n---\n)); // 生成回答 return chatClient.prompt() .system(你是一个知识库助手。请仅基于以下资料回答问题如果资料中没有相关信息请明确告知用户。资料\n context) .user(question) .call() .content(); } }Java生态的好处是部署方便和现有系统集成容易。缺点是很多新的Agent能力比如复杂的LangGraph编排在Spring AI里还不够成熟如果需要复杂的Agent流程还是需要自己实现状态机或引入其他编排框架。5. RAG效果测评怎么量化指标并看懂指标热词里有两个高频问题“rag测评怎么做”和“rag知识库指标有哪些如何理解各指标”。这是非常现实的痛点——RAG系统上线前你怎么证明它效果好上线之后怎么持续监控它的效果没有量化手段优化就是拍脑袋。5.1 核心指标拆解召回、答案质量、任务成功率业界目前比较成熟的RAG评测框架是RAGASRAG Assessment它把RAG系统的质量拆成几个维度每个维度都可以用大模型自动评分上下文相关性Context Relevance检索回来的片段和问题相关吗如果检索结果和问题完全不相关那就说明检索阶段有问题可能是分块策略不对、Embedding模型不合适或查询改写不到位。忠实度Faithfulness最终答案的每个关键信息点都能在检索到的上下文里找到依据吗如果答案里有上下文没有的信息那说明模型在“自由发挥”幻觉问题依然存在。答案相关性Answer Relevance最终答案和问题相关吗有时候检索没问题模型也能拿到正确上下文但生成的答案偏了这是生成环节的提示词问题。在RAGAS之外实际落地中还要看任务级别的指标检索召回率RecallK正确的信息片段是否在Top K里被召回到。准确率PrecisionK召回的K个片段里到底有多少是真正相关的。端到端任务成功率比如“工单分类正确率”“专利查新命中率”这才是业务方真正关心的指标。5.2 低成本可靠的自建评测流程依赖现成工具是一方面我更建议在自己的业务数据上构建一套小规模的评测集。做法是找业务方配合整理50到100条典型问题每条问题标注标准答案和答案对应的知识片段来源。然后建立评测流程每次改动知识库处理流程或系统参数之后用相同的问题集重新跑一遍对比生成结果与标准答案的匹配度。这种方法不用大量人工但能持续追踪系统质量的变化趋势。实际操作中我会这样做先用RAGAS跑一遍自动评分然后人工抽检Top 10和Bottom 10的问题看看哪些问题评分不准再根据具体现象去排查问题。检查顺序先是检索阶段——把问题输入到Retrieval工具看看返回的Top 5片段靠不靠谱如果检索不中排查分块和Embedding环节如果检索命中但答案不对排查Prompt和生成环节。5.3 指标看懂了之后如何优化RAG效果评测完的下一步是优化。这里有一个效率很高的优化优先级顺序从成本低到高排列先调Prompt检查系统提示词是否清晰说明了“只能基于资料回答”是否需要提示模型遇到不相关内容时选择“不知道”。这行代码成本最低效果可能很明显。再调检索策略尝试Top K从3改到5或8或者加关键词搜索的混合召回观察评测集指标的变化。然后调分块策略如果某个常见问题检索不到查看对应的文档分块结果看信息是否被切碎了。最后才考虑换模型Embedding模型和生成模型对效果影响很大但模型更换成本也最高建议留到确定其他环节都已稳定后再操作。6. 常见问题与排查技巧实录最后把我在这几年RAG/Agent项目里遇到的高频问题和排查经验整理出来按问题的出现频率排序方便你排查时对照。6.1 检索质量太差查不到或查不准这是最常见的问题。现象用户问的问题很明确但返回的片段完全不相关。排查步骤先确认问题本身有没有歧义。有些用户问题很短比如“如何部署”范围太大检索分不清方向。这时候可以用Agent做一个“查询改写”把模糊问题拆成多个子查询或者增加上下文信息。确认分块粒度。如果你的文档一个块是2000字一个块里包含多个主题检索命中的块可能既有相关内容又有大量噪音。试试把块切小一点让每个块只包含一个完整主题。确认Embedding模型和你的业务领域匹配。如果用的通用Embedding模型在专业术语多的场景法律、医疗、专利效果会打折。换领域专用的Embedding模型比如法律领域、医疗领域的微调模型往往能明显提升。6.2 知识库更新了但检索不到新内容常见原因增量写入向量库时没有删除旧版本。有些向量库的更新是追加模式——你上传了一份新版的制度文件旧版也还在库里面检索的时候新老版本混在一起答案自然混乱。解决方法是写入新文档之前先按文档ID或来源字段删除旧的向量记录再写入新的。并且在Payload里带上版本号和生效日期检索时可以做过滤。6.3 模型幻觉撤销不了怎么办即使上了RAG模型有时候还是会“强行输出”上下文里没有的信息。我的排查顺序是检查上下文是否真的覆盖了答案所需的信息。有时候检索返回了相关片段但片段里的信息不足以回答用户的深度问题模型只好自己发挥一部分。检查Prompt里是否有明确的“不知道就直说”指令。很多模型如果没被明确告知“没有依据时不能说”它宁愿编也不愿意承认自己不知道。尝试降低temperature参数。这是最简单的操作通常我建议RAG场景的temperature控制在00.3之间过高会引入随机性增加幻觉风险。6.4 Agent卡死或报错action execution terminated due to error这个报错出现的场景很典型Agent决定调用一个工具但工具执行时抛了异常比如参数格式不对、API超时而Agent的循环机制没有捕获这个异常导致整个流程终止。排查思路工具函数的入参校验一定要做好。大模型生成JSON参数经常出现类型错误比如把数字参数写成字符串。在工具函数入口处做严格校验不符合就返回一个友好的错误消息给模型让模型重新生成参数。给Agent循环加异常捕获工具出错时把错误信息反馈给大模型让它重新尝试或调整策略而不是直接终止。设置最大迭代轮数避免死循环。我一般设置58轮超过就终止并返回当前结果。6.5 上下文窗口不够用怎么办当你用Agent处理复杂任务时多轮工具调用的历史记录会占用大量上下文空间。常见做法是对历史消息做截断只保留最近的几轮。对检索回来的文档做压缩用大模型把长段落总结成要点之后再送入上下文。用外部记忆存储长期特征信息而不是全部堆在上下文里。6.6 关于RAG评测和效果验证的一个实用技巧这是我的一个习惯每次改动RAG系统我都不会只凭几个感觉上的例子判断效果好坏而是固定跑同一套50个问题的评测集记录每个问题在改动前后的答案质量和检索命中情况。这样能尽最大努力避免“改好了一个问题结果带崩了另外几个问题”的情况。我自己有一次把分块大小从400字改成800字测试了3个“感觉不错”的样本觉得效果提升了但跑了完整评测集后才发现召回率下降了不少。从此以后任何改动都要过一遍评测集这已经成为我个人的硬性要求。最后再分享一个小技巧做RAG/Agent项目时尽量让你的知识库文档保留结构信息不要把所有内容扁平化切成一个个块。如果可能在块里加上标题层级路径比如“第一章/第二节/3.1条款”检索的时候不仅能返回内容还能让模型知道这段内容在文档中的位置回答问题时能附带引用来源这在很多严肃场景专利、法律、金融报告里几乎是刚需。这一步看起来费事但做完了整个系统的可信度和可用性都会上一个台阶。