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

资讯详情

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

RAG知识获取管道全解析:为Agent打造第二套记忆系统

RAG知识获取管道全解析:为Agent打造第二套记忆系统 从上一篇文章聊完 Agent 的规划与工具调用之后有不少朋友在评论区问我我的 Agent 骨架已经能跑起来了也能调外部 API 了但一遇到需要回答“我们公司内部资料里怎么说”“这个产品最近有没有更新”“某个领域的最新结论到底是什么”这类问题就开始胡说。这其实就是当前很多 AI Agent 项目的真实瓶颈——Agent 有嘴但没有可以张嘴就来的知识来源。这篇是“走进 AI Agent”系列的第四篇专门聊知识获取管道也就是 RAGRetrieval-Augmented Generation检索增强生成。核心关键词是“知识获取管道”我把 RAG 定位成 Agent 的“第二套记忆系统”模型参数里装的是训练截止日期前的世界而 RAG 负责把最新的、私有的、领域化的知识实时接进来。本篇文章会从原理、架构、最小可跑通实现、参数调试、常见坑五个维度展开适合刚搭完 Agent 骨架、正在纠结“怎么让 Agent 变专业”的开发者参考。我会尽量写得细一点把我实际调过的参数、踩过的坑都放进来希望能帮你少走弯路。1. 为什么 Agent 需要一条“知识管道”1.1 模型参数的边界与 Agent 的“失忆”困境先说一个很基础但很多人容易忽略的事实LLM 的知识是有保质期的。模型训练完成的那一刻它知道的“世界”就定格了。今天发生的新闻、你公司的内部流程、最新的产品文档、昨天刚改的接口说明模型一概不知。如果 Agent 只能靠参数内存知识回答问题那它本质上就是一个“记忆力被永久冻结的顾问”你问他历史常识还行一问具体业务细节就露馅。更麻烦的是幻觉问题。当模型确实不知道某个答案时它不会老老实实说“我不知道”而是会基于概率把最可能的词串起来生成一段看起来很自信、实际完全不对的内容。这个特性在闲聊场景里无所谓但在 Agent 处理真实任务时很致命——比如让 Agent 根据最新的商品目录回答客户报价如果目录数据不在模型参数里它编出来的价格可能就是灾难。所以 Agent 必须要有一条外接的知识获取管道。“外接”的意思是知识不放在模型参数里而是放在 Agent 可以主动去查的地方在回答问题、执行任务的当下动态取用。这就像你雇了一个知识渊博但记忆固化在去年的顾问现在给他配了一个实时更新的资料库和搜索引擎他每次回答前先去查资料再基于查到的内容作答。这个“先查再答”的动作就是 RAG 的核心。1.2 知识获取的三种形态提示词内联、RAG 与微调给 Agent 接入知识主流有三条路把知识写进提示词、用 RAG 检索外部知识库、微调模型参数。三者的适用场景差异很大。提示词内联最简单直接把相关资料粘贴进系统提示词里让 Agent“看着资料回答”。优点是零开发成本适合一次性小文本。缺点是受上下文窗口限制塞不下大量文档而且每次调用都要重复传递大段文字成本高、延迟高。我见过有团队想用这个方案做企业知识库结果几千页的文档根本塞不进去只能放弃。微调则是把知识“写进”模型的参数里通过大量领域数据训练让模型本身变得专业。优点是回答时无需外部检索速度稳定。缺点是训练成本高、迭代周期长而且知识一旦固化就又面临过期问题。对绝大多数 Agent 项目来说微调更像锦上添花不适合作为主要的知识获取手段。RAG 的定位正好在两者之间文档存在外部索引里每次问答前先检索出最相关的一小段再注入到提示词里。它既不受上下文窗口限制又能保证知识实时更新还不用花高昂的训练成本。用生活类比解释提示词内联是“把整本参考书摊在桌上翻到哪页算哪页”微调是“把参考书内容背进脑子”RAG 则是“配一个高效检索员你要什么他立刻翻给你”。对于知识量大、更新频繁、内容私有的 Agent 场景RAG 是当前性价比最高的方案。2. RAG 管道的三个核心环节拆解2.1 入库阶段切分、嵌入与向量库很多朋友第一次搭 RAG上来就问“用什么向量库比较好”其实这是顺序搞反了。RAG 管道真正的地基在入库阶段也就是文档进来之后的三步切分、嵌入、存储。切分chunking是把长篇文档切成小块。为什么要切因为嵌入模型和生成模型都有输入长度限制而且检索的基本单位不是“整本电子书”而是“某一段相关文字”。切得太粗比如整章作为一个 chunk检索时会把大量无关信息一起捞进来稀释了答案的精确度切得太细比如一句话一个 chunk语义可能不完整检索时也容易漏掉关键上下文。我实际用下来通用场景下 chunk_size 在 300~800 字中文之间比较稳同时设置 chunk_overlap 50~100 字让相邻块保留重叠部分避免一句话被拦腰截断而丢失上下文。如果是代码文档、结构化表格切分策略要另行调整——表格最好整块保留代码类文档则按函数或模块切这些后面细说。嵌入embedding是把文本变成向量。这一步选择了什么嵌入模型基本决定了检索质量的上限。常见的开源选择有 BGE 系列、M3E、text-embedding 系列等。这里有一个非常关键但常被忽略的经验嵌入模型一定要和你的文档领域、语言匹配。我见过有人用纯英文通用 embedding 去检索中文金融文档检索效果差到还不如直接关键词搜索后来换了中文优化的模型命中率立刻翻倍。不要盲目追求大模型先用一个领域匹配度高的模型跑通效果不够再换。向量库负责存储向量并提供相似度检索。市面选择很多小规模项目直接用 FAISS 这种本地库几十万条向量毫无压力生产环境一般用 Milvus、Qdrant、pgvector 或 Elasticsearch。选型时可以看几个维度是否需要持久化、是否需要过滤比如按部门/权限过滤文档、数据量级、团队熟悉度。我的建议是先从最简单的 FAISS 跑通逻辑等真到了并发和运维压力再说换库的事过早引入分布式向量库属于过度设计。2.2 检索阶段从向量相似度到混合检索文档入库之后RAG 的第二个环节是检索。用户的提问进来先转成查询向量再去向量库里找最相似的若干条 chunk。听起来简单但实际用下来单靠向量相似度很容易出问题用户问“这个产品支不支持批量导入”文档里写的是“批量导入功能已在 3.2 版本上线”两边措辞完全不同但语义相近向量检索能处理这种语义匹配反过来如果用户问的是精确的产品型号“A-300 订单查询接口怎么调”向量检索反而不如直接做关键词匹配。这就是为什么现在的工程实践普遍采用混合检索Hybrid Search向量检索负责语义召回BM25 这类稀疏检索负责精确词匹配两边结果做融合。具体融合策略有加权求和、RRFReciprocal Rank Fusion等。我个人的经验是在 Agent 场景里大部分知识库问题都带有明确的实体词比如产品名、接口名、人名、编号混合检索基本是标配而不是可选优化。检索阶段还有一个常被忽略的环节重排序Rerank。第一阶段无论向量还是混合检索候选集一般取 top 20~50为的是保证召回率但这批结果里往往夹杂着不少不完全相关的条目。重排序模型是一个 cross-encoder会把查询和每条候选文本做深度交互打分相关性判断明显比向量相似度更准确。经过重排序后只取 top 3~5 条送入生成环节回答质量会有肉眼可见的提升。代价是增加了一些延迟和计算量但在追求质量的场景这笔花销值得。2.3 生成阶段把检索结果整合进 Agent 的上下文RAG 的最后一个环节是生成。这一步的原理不复杂把检索到的 chunk 作为“参考资料”拼进提示词告诉语言模型“请基于以下资料回答用户问题不要编造”然后把拼好的完整提示词交给模型生成最终回答。但放在 Agent 场景里生成阶段比普通 RAG 多了一个层次。Agent 不是简单的一问一答它往往有任务目标、有多轮对话、有工具调用流程。所以检索出来的内容不光要拼进“回答用户问题”的提示词里还要让 Agent 在规划任务时就知道自己手上有哪些可用知识。我常用的做法是把检索结果放进上下文的一个专门区域标注为“参考资料区”系统提示词里写明“当你在规划步骤、调用工具、判断结果时都要优先考虑参考资料区的内容”这样检索结果不只是最后的措辞素材而是参与到了 Agent 的决策过程中。还要注意引用溯源。Agent 回答完问题最好能在后面附上信息来源的标题或 chunk 片段一来方便用户验证真伪二来出现错误回答时能够回溯排查是哪条检索内容误导了模型。这个习惯我在实际项目中吃过大亏才养成早期没有引用溯源模型基于一条过期文档回答了报价事后追责死活查不到是哪里出了问题。3. 给 Agent 装上 RAG落地选型与最小实现3.1 先评估清楚Agent 到底需要哪种知识动手写代码之前我建议花点时间想清楚你的 Agent 需要的是哪一类知识因为它决定了管道形态。第一类是静态的私有文档知识比如公司制度、产品手册、历史报告。这些内容相对稳定、数量大、格式杂乱适合走标准的 RAG 流程离线切分入库在线检索。第二类是实时变动的外部知识比如天气、股票行情、最新的接口返回。这类知识的特点是你根本没有整块文档可以入库适合直接做成 Agent 的工具调用让它实时去请求 API。很多时候这类知识与 RAG 无关不要硬套。第三类是结构化业务数据比如数据库里的订单记录、用户信息。这类数据与其塞进向量库不如走自然语言转 SQL 的工具调用链路让 Agent 直接查库。我在实际项目中见过最多的误用案例是有人把第三类结构化数据硬切成 chunk 塞进向量库结果检索出来的信息早就不是最新状态了。所以落地前先做知识类型盘点决定哪些走 RAG、哪些走工具调用、哪些走混合方式。这个判断做对了后面能省很多事。3.2 一个最小可跑通的 RAG 实现说再多理论不如直接给一个能跑的最小实现。下面这个示例我用的是最简的技术栈一个小型嵌入模型 FAISS 向量库 一个支持长上下文的 LLM API。代码只演示流程生产场景请替换成你的真实文档和模型配置。from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.document_loaders import DirectoryLoader from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import FAISS from langchain_core.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI # 1. 加载文档 loader DirectoryLoader(./docs/, glob**/*.md, show_progressTrue) docs loader.load() print(f加载文档数: {len(docs)}) # 2. 切分 splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap100, separators[\n\n, \n, 。, , , ., !, ?, ], ) chunks splitter.split_documents(docs) print(f切分后 chunk 数: {len(chunks)}) # 3. 嵌入 embeddings HuggingFaceEmbeddings(model_nameBAAI/bge-base-zh-v1.5) # 4. 建库 vector_store FAISS.from_documents(chunks, embeddings) vector_store.save_local(./faiss_index) # 5. 检索 query 这个产品支持批量导入吗 retriever vector_store.as_retriever(search_kwargs{k: 5}) candidates retriever.get_relevant_documents(query) # 6. 生成 prompt ChatPromptTemplate.from_messages([ (system, 你是一个严谨的助手。请优先依据以下参考资料回答问题 如果参考资料中不包含答案请明确说不知道。\n\n参考资料:\n{context}), (human, 问题: {question}) ]) llm ChatOpenAI(modelgpt-4o-mini, temperature0.2) context \n\n.join([f[来源: {d.metadata.get(source, unknown)}]\n{d.page_content} for d in candidates]) response llm.invoke(prompt.format(contextcontext, questionquery)) print(response.content)这段代码就是一个完整的 RAG 闭环注释里已经把流程标清楚了。跑完你就能直观感受到什么是“先查再答”。需要注意几个细节RecursiveCharacterTextSplitter的separators参数要按“从小粗到小细”的顺序给中文标点这样切分时优先保留完整段落再降级到句子。中文场景如果不加。”这些分隔符切出来经常把一句话拦腰截断检索质量会明显变差。k值这里设了 5实际要根据你的文档粒度调整chunk 短可以适当加大chunk 长就减小。后面 3.3 会讲怎么用验证集来调。生成时我在每段资料前加了[来源: xxx]标记后续 Agent 回答问题时可以顺带输出这个来源方便追踪。3.3 Agent 场景下的检索参数调节参数是最容易被忽略又最影响效果的部分。我把常用参数的经验设置整理成一张表方便你对照初调参数建议初始值调节方向备注chunk_size500中文回答空泛则减小上下文断裂则增大以语义完整为第一优先级chunk_overlap80~100检索结果上下文不完整时加大建议不超过 chunk_size 的 20%top_k5重排序后答案不准时先增大到 10~20 看召回配合 Rerank 使用效果更稳相似度阈值0.5视模型而定检索出明显无关内容时上调阈值过高会漏检谨慎embedding 模型bge-base-zh-v1.5中文文档不要用纯英文模型领域匹配远比模型大小重要temperature0.2答案太发散则继续调低知识问答场景建议 0~0.3“怎么知道调没调对”是最常被问到的问题。我的做法是留出 30~50 个真实问答对每调一个参数就跑一遍这些题目手动打标“回答是否可用”然后看整体通过率。这比任何理论推导都直观。你会发现自己很快就能找出当前系统的真正短板到底是该调切分、换嵌入模型还是该加混合检索和 Rerank。没有验证集就盲目调参本质上只是凭感觉在猜。4. 进阶编排RAG 从固定管道走向 Agentic4.1 静态 RAG 的问题检索行为是死的上面讲的 RAG 流程是一个固定管道用户问题进来检索一次生成答案。这对知识问答场景够用但放在 Agent 里就差了一口气。原因很简单Agent 的任务往往是多步骤的它可能先要理解客户咨询背景再查产品资料然后结合订单信息给出解决方案。如果每一步都只能“问题进来检索一次生成答案”Agent 就退化成了一问一答的聊天机器人完全没有体现出“自主执行”的价值。举个例子用户问“我们公司的两批订单里分别用了哪些过时的产品型号需要怎么替换”这种问题直接检索一次几乎不可能找到完整答案因为里面涉及多轮信息收集先定位两批订单的产品清单再查每个产品型号的当前状态和替代品最后整理替换方案。固定管道做不了这种事。所以就有了 Agentic RAG 的概念。简单说把检索从“一个固定的调用步骤”升级为“Agent 可自主决策的一项工具”Agent 根据任务进展自己决定要不要检索、检索什么、检索几次、怎么组合检索结果。用户热词里反复出现的 “Agentic RAG” 指的就是这条进阶路径。4.2 查询改写、多路召回与反思机制Agentic RAG 相比静态 RAG我在实际项目中感触最深的是三个能力第一个是查询改写。原始用户问题往往是一个复杂表述直接拿去检索效果通常不理想。Agent 可以先拆解出检索子问题比如把“我们的库存管理有什么问题以及怎么优化”拆成“库存周转率现状”“库存管理常见风险”“优化方案案例”三个检索意图再分别检索合并。这一步能明显提升召回命中率。第二个是多路召回。对于复杂问题不要只从一个知识库捞内容。一个 Agent 可以同时挂了产品知识库、技术文档库、历史工单库甚至外部搜索引擎。检索不再是从一个向量库里拿 top_k而是多个来源的结果汇聚到上下文里再由模型综合判断。这是我在真实项目里最常遇到的形态企业知识往往是分散在不同系统里的。第三个是自我反思与再检索。第一轮检索结果如果不够支撑答案Agent 可以自我评估再生成更精确的查询词去补检索。工程上叫“反思/再检索循环”效果显著代价是延迟和 token 消耗会增加。我建议只在 Agent 对答案信心不足时启用这条路径不要每次都触发否则成本会很难看。4.3 与 Skill/工具体系的融合在 Agent 架构里RAG 很少单独存在它应该以“知识检索”为名被封装成一个 Skill 或工具和调用 API、查数据库、执行计算这些工具放在同一个技能列表里。Agent 会基于当前任务自主判断“这一步该查知识库还是该调 API”。很多人问“Skill 怎么和 RAG 结合起来”我的实践经验是把检索行为精细化成不同 Skill。比如“product_qa”这个 Skill 专门检索产品文档“tech_support_qa”专门检索工单库“knowledge_search”则是通用混合检索。每个 Skill 内部封装独立的检索配置、参数和提示词模板。Agent 在面对不同任务时选择不同 Skill而不是一个漏斗收到所有检索需求。这种“检索即工具”的思路有一个附带好处你可以给每个检索 Skill 加上权限控制、日志审计、来源追溯。这在企业内部 Agent 落地时几乎属于刚需因为不是所有知识对所有用户都是可见的。把 RAG 封成工具而不是散落在代码各处治理起来才可控。5. 常见问题与排查技巧实录5.1 回答质量差先区分是“没找着”还是“没用对”接到“RAG 回答不准”这个问题时我每次都要求先把问题拆成两层检索层和生成层。检索层的问题是“相关资料根本没被捞出来”生成层的问题则是“资料捞出来了但模型没用对或答跑偏了”。两层混在一起排查效率极低。区分方法很简单把系统里实际检索到的几条 chunk 打印出来人工看一眼。如果 chunk 内容确实包含答案线索那问题在生成层调整提示词或 rerank 的排序即可如果 chunk 内容根本不沾边那问题在检索层需要调切分、换 embedding、加混合检索。我见过太多人一上来就改提示词结果根本原因是向量检索把完全无关的文档捞了上来怎么改提示词都没用。5.2 命中率hit rate与答案可用率两个指标分开看如果你想用数据监控 RAG 状态建议关注两个指标。一个是命中率hit rate指在验证集里的问题中检索结果里包含正确答案的比例。另一个是答案可用率指模型最终生成的答案被人工判定为可用的比例。两者存在明显差距说明瓶颈在生成层两者都很低则瓶颈大概率在检索层。我在项目里还会记录失败样例的分布有多少是因为检索漏召回有多少是因为切分破坏了语义有多少是因为知识本身缺失。这个分布会告诉你接下来该优化哪个环节。比如我发现一次失败中有 40% 是因为新旧版本文档同时在库、检索到过时内容导致答错于是加入了“按文档版本过滤”的元数据规则效果立竿见影。没有失败样例画像优化就是瞎猜。5.3 直接可用的避坑清单最后整理几条我在真实项目里反复踩过、现在每次评审都会检查的坑。第一不要在切分前把 PDF 当纯文本处理。扫描件、表格 PDF 直接进切分器出来的全是乱码或残缺文本检索质量必崩。这类文档要先做 OCR 或结构化解析再走后续流程。第二嵌入模型不能瞎换也不能混用。向量库里的所有向量必须来自同一个嵌入模型的同一个版本一旦换了模型历史向量全部作废需要全量重建。否则新旧向量语义空间不一致检索效果会变成玄学。第三不要把多个知识领域的文档塞进同一个索引。产品资料和技术文档的措辞、主题差异太大检索时容易互相干扰。最好按领域拆成多个独立索引由 Agent 按任务场景选择或使用元数据过滤来缩小范围。第四验证集不能只看对的样本更要多收集错的样本。每次线上用户反馈“回答不对”时把当时的输入、检索结果、模型输出存下来定期复盘。这些错误样本是调优系统的最大财富比任何公开基准都更能反映你的真实场景。第五监控不能只盯 CPU 或延迟要盯“检索内容漂移”。当文档库更新频繁时定时检查固定测试集的命中率是否出现波动。有一次我们的命中率掉了一半最后发现是知识库更新文档时把旧版目录覆盖了某些文档被误删。没有监控这类问题可能在线下潜伏好几周才发现。RAG 这条路从“把文档塞进向量库”到“让 Agent 真的会查、会选、会判断”中间隔着的正是这一堆琐碎但决定成败的工程细节。希望这篇文章能把基础管道讲清楚也让你少走几步弯路。
返回列表