资讯动态

RAG实战指南:从零搭建AI Agent知识库与检索增强管道

发布时间:2026/9/29 18:16:35 来源:尧图企业网站定制
做 AI Agent 应用这一年多我越来越觉得最容易被低估、但又最影响最终效果的东西不是模型本体的能力而是它的“知识来源”铺设得够不够扎实。很多朋友把 Agent 搭建跑通之后发现回答还是不准确一问原因大部分都是知识管道这块没做好。这一篇我专门聊聊 RAG——Retrieval-Augmented Generation也就是检索增强生成。RAG 解决的核心问题非常直白让模型在回答问题之前先去外部知识源里查资料把查到的内容作为参考依据再组织语言给出答案。它适合做 AI Agent 开发的技术人、准备落地知识库问答的产品团队以及正在研究检索策略的同学参考。这篇内容包括 RAG 在 Agent 架构里的定位、核心组件拆解、一个可以照抄的从零搭建流程以及我在实际工程里踩过的坑和排错思路。1. 为什么 Agent 必须有一套“知识获取管道”1.1 模型自己的记忆撑不起业务场景大语言模型的训练数据是有截止日期的而且训练语料里绝大多数是通用互联网内容。这意味着两件事第一模型不知道你内部的报价规则、产品参数、审批流程第二模型对组织内部的知识没有概念你问它“我们公司哪个版本的设备支持双协议输出”它要么开始编要么给一个完全不相干的通用回答。更要命的是如果你硬要让 Agent 记住这些细节把信息塞进提示词里提示词会越来越长成本越来越高而且超出上下文长度之后模型会“选择性遗忘”——这不是你调参能解决的是结构性问题。这里就需要一个外置的“记忆”。给模型配一个可以检索的数据库相当于给一个很聪明但没有行业经验的实习生配了一位随时可以请教的老师傅。模型不懂没关系它会先去查查完再回答。这也是 RAG 这类方案能在 2023 年以后成为 Agent 应用标配的原因它把“知识”从模型参数里拿了出来放到了可以随时更新的管道里。1.2 RAG 在 Agent 架构里的位置一个完整的 Agent 系统可以拆成四层模型层、记忆层、工具层、编排层。RAG 属于记忆层和工具层的交叠部分。一方面它是 Agent 工作记忆的扩展——对话中的临时语境放在短期记忆里业务知识放在向量库里需要的时候按相关性取回另一方面它也可以被封装成一个工具Agent 在规划步骤时主动调用。我在实际项目里更喜欢把它封装成工具因为这样 Agent 的学习能力就出来了。比如 Agent 接到的任务是“帮我把 Q3 的售后数据整理成报告”它会自主决定先查售后报告模板再检索具体数据表最后组织输出。过程中它可能多次调用同一个检索工具也可能在发现第一次检索结果太泛之后换一个关键词再查。这个“自己决定查什么”的过程就是 Agentic RAG——它不是简单的“输入问题-召回-回答”三段式而是把检索融入 Agent 的决策回路里。对于刚起步的团队建议先把经典 RAG 跑通再考虑 Agentic RAG否则检索效果的瓶颈还没找到就叠加复杂度排查问题会非常痛苦。1.3 什么类型的业务最适合先用 RAG不是所有场景都需要 RAG。如果你的问题都是模型本身就能回答的常识问题没必要额外搭一套管道。但凡是下面的场景RAG 几乎是刚需企业内部知识库问答制度文档、操作手册、历史方案散落在各处文件里产品参数与报价检索尤其是制造业、软件行业型号、参数、价格体系都是结构化数据但表述方式灵活用户问“那台机器能跑多快”和“最大线速度是多少”是同一个问题本地私有化部署场景比如本地 ERP 系统加 RAG 加 LLM 做产品检索数据不出内网模型可以在云端或本地部署知识库必须本地化客服助手需要实时更新的售后知识模型不可能靠训练时的一个快照覆盖这些场景有个共同特点知识在持续变化而且权威版本存在于文档或系统里不在模型脑子里。RAG 的价值就是把“知识的权威性”留给人维护的文档系统把“表达的灵活性”交给语言模型。2. RAG 管道核心组件拆解2.1 从文档到索引解析与加载很多人以为 RAG 就是把文档扔进数据库实际上第一道坎就是文档加载。不同格式的文档处理差别很大PDF 要处理版面、表格、图片Word 和 Markdown 相对简单网页要清洗噪声内容。如果文档里有扫描件还需要 OCR否则召回出来的内容都是乱码效果自然崩。我在处理 PDF 时总结了一套经验不要把整个页面作为一个 chunk文本块因为 PDF 版面通常包含页眉页脚、图表、多栏内容混在一起检索相关性会非常差。第一步要按版面做布局分析识别出正文区块、表格区块、图片注释再按区块顺序切分。表格这类结构化内容优先转换成 Markdown 表格或者键值对文本这样 embedding 模型能更好理解结构关系。加载完之后必须做一轮清洗去掉多余换行、合并断行的段落、修正 OCR 产生的常见字符错误。这一步粗糙的话后面切分、检索都会放大问题。对于知识库的更新机制要按增量做而不是每次全量重建。我习惯给每个文档记录哈希值文档变动了才重新加载和切分其余的直接复用旧索引。全量重跑一次可能几十分钟但增量更新几分钟就能搞定尤其在一个文档数量过千的知识库里这是运维舒适度的重要分水岭。2.2 文本切分粒度就是召回效果的天花板文本切分是 RAG 管道里最容易被低估的一步。chunk 切得太细单个块内容太单薄缺少上下文比如一个单独的表格行“数量500单位台”检索到也读不出完整业务含义切得太粗块内包含太多无关内容向量表达的语义被稀释召回精准度下降还会挤占模型的上下文窗口。常见的切分策略有三种固定窗口切分、递归字符切分、语义切分。固定窗口简单按字符数硬切风险是切断句子甚至切断词递归字符切分是目前通用场景的默认选项它按分隔符优先级逐级切分——先按段落再按句子再按长词——尽量保证切分边界落在语义相对完整的位置语义切分则依赖 embedding 模型来判断哪里是一个自然语义边界效果更好但计算成本也高。我的经验值是从chunk_size500、overlap100按字符计中英文略有差别起步。overlap 的作用是让切分边界附近的信息不会因为硬切而丢失前后块之间保留一定的“记忆重叠”。如果文档类型是标准操作手册可以尝试加大到 800如果是聊天记录这类短文本建议缩小到 200 到 300。没有绝对正确的参数只有针对你的语料和检索场景反复调出来的相对合理值。切分完之后把 chunk 按顺序编号并保留原始文档路径这一步非常重要——否则后续做溯源和问题排查时根本定位不到内容来源。2.3 Embedding 选型中文场景别直接上通用模型Embedding 模型负责把文本映射成向量向量之间的相似度代表语义相似度。embedding 的效果直接决定检索召回的质量它的重要性怎么强调都不为过。而且一个特别容易被忽略的事实是Embedding 模型和大语言模型是两回事你用的推理模型很强不代表 embedding 模型也强。要找专门的向量化模型来建索引。中文场景下我踩过坑。早期直接用某个英文为主的通用 embedding 模型检索中文技术文档时 hit rate 惨不忍睹——很多明显相关的文档召不回来因为模型对中文语义的理解不够细致。后来切到针对中文优化过的模型效果提升是肉眼可见的。目前中文社区常用的方案有 BGE 系列、M3 系列等都是开源可本地部署的可以在内网跑。选择时关注两个参数向量维度和最大输入长度。向量维度高代表表达能力强但存储和计算成本也高需要平衡最大输入长度决定了你可以 embedding 多长的文本如果切分的 chunk 长度超过了模型上限会被截断语义信息就丢了。另一个容易忽视的细节Embedding 模型的更新。Embedding 模型升级后向量分布可能变化所有索引必须重建否则新旧向量不在同一个语义空间里检索结果会很奇怪。我的做法是每次换 embedding 模型前先在核心测试集上跑一遍 hit rate 对比确认提升了再切换。2.4 向量数据库轻量起步别一开始就上重基础设施向量数据库的选择和你的部署环境强相关。本地单机、数据量不超过几十万条完全可以用 FAISS 或 Chroma——前者是内存索引库速度快但持久化要自己处理后者自带持久化API 简洁适合快速验证。数据量大了、需要高并发和分布式再考虑 Milvus 或 Qdrant 这类服务化方案。很多团队一上来就搭 Milvus 集群结果查询量根本到不了那个水位运维成本却先上来了。我个人的建议是原型阶段用轻量方案数据形态和查询模式稳定之后再评估重方案不要一步到位“上生产配置”。向量检索的核心是近似最近邻搜索常用 HNSW、IVF 这类索引算法。HNSW 在召回率和查询速度上平衡较好适合绝大多数场景。建索引时还有个关键参数是距离度量方式。当前主流的 embedding 模型通常用余弦距离衡量语义相似度但在超大规模场景下内积更快具体选择要和你用的模型匹配。这里有个工程上的注意点很多团队忽略了一个事实向量数据库只是“候选召回”不是最终答案。它的作用是快速缩小范围把大概率相关的 top-N 挑出来后续还有重排和生成步骤。2.5 检索策略从单纯向量到混合检索单靠向量相似度做检索有几个天然短板关键专业术语、型号编码、人名等精确匹配场景向量相似度不一定能处理好用户提问很短语义信息少向量召回容易跑偏。实战中更稳健的是混合检索向量检索 关键词检索BM25并行把两者结果做融合。原理也直观向量负责语义相关的宽松召回关键词负责精确匹配的强约束召回互补性很强。融合的方式很多从简单的加权求和到 RRFReciprocal Rank Fusion都有。RRF 的核心思路是对每个文档在多个检索结果里的排名取倒数求和排名越靠前贡献越大最后按总分排序。实测下来混合检索比单纯向量检索在中文技术文档场景 hit rate 一般能提升 10 到 15 个点值得做。更进阶的检索技巧是 Query 改写和 HyDE。Query 改写是让 LLM 把用户的模糊表达转化成适合检索的多个查询词HyDE 则是让模型先根据问题生成一个假设性回答再用这个回答去做向量检索。原理上假设性回答通常比原问题包含更完整的语义信息在向量空间里更容易匹配到相关文档。这个方法在某些领域效果出奇的好但延迟会增加一次模型生成的开销是否采用取决于你的实时性要求。检索结果出来之后建议加一个 Rerank重排阶段——用交叉编码器模型对召回的候选文档做精细打分重新排序。向量召回是粗筛重排是精排两道关口下来最终给到大模型的 top-K 才有质量保障。3. 实操从零搭一条能跑通的 RAG 管道3.1 整体代码流程与关键配置下面这条管道我尽量保持精简但各个环节都完整你可以直接本地跑通。技术栈是 Python LangChain FAISS 本地 Embedding 模型。如果你偏好 LlamaIndex结构也差不多核心思想不变。from langchain_community.document_loaders import DirectoryLoader, TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import FAISS from langchain_community.llms import Ollama from langchain.chains import RetrievalQA # 1. 加载文档 loader DirectoryLoader( ./knowledge_base, glob**/*.txt, loader_clsTextLoader, loader_kwargs{encoding: utf-8} ) docs loader.load() # 2. 切分 splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap100, separators[\n\n, \n, 。, , , , , , ], length_functionlen ) chunks splitter.split_documents(docs) print(f共切分成 {len(chunks)} 个文本块) # 3. Embedding embedding_model HuggingFaceEmbeddings( model_nameBAAI/bge-large-zh-v1.5, encode_kwargs{normalize_embeddings: True} ) # 4. 构建向量库并持久化 vectorstore FAISS.from_documents(chunks, embedding_model) vectorstore.save_local(./faiss_index) # 5. 检索 生成 qa_chain RetrievalQA.from_chain_type( llmOllama(modelqwen2.5:7b), chain_typestuff, retrievervectorstore.as_retriever( search_typesimilarity, search_kwargs{k: 5} ) ) # 6. 提问 query 设备型号 AT-3000 的最大线速度是多少 answer qa_chain.invoke({query: query}) print(answer[result])这段代码的每一步都不能跳。文档加载用glob控制加载范围切分器里显式指定了中文标点作为分隔符保证句子尽量完整Embedding 开启normalize_embeddings因为很多相似度计算依赖归一化向量。FAISS 支持保存到本地索引是一次构建、多次使用的不需要每次问答都重新构建。3.2 参数为什么这么设置chunk_size500不是拍脑袋。太短会丢失实体间的关联太长会让向量语义不聚焦。500 在中文场景里大约对应两百多字适合承载一个完整段落的信息。overlap100是为了防止关键内容恰好被切在边界处前后块各保留一部分上下文信息不中断。search_kwargs{k: 5}的含义是每次检索取回 5 个最相关的文本块。这个数字直接影响两步一是召回的覆盖面k 太小可能漏掉重要信息二是后续生成阶段的上下文长度k 太大一次塞进去的内容过多模型反而分不清主次。我常用的范围是 4 到 8需要你根据文档粒度和问题复杂度自己调试。值得强调的是向量检索返回的similarity_score并不是置信度——你会看到很离谱的内容也带着高相似度。实践中我一般只把相似度阈值作为过滤条件的参考不依赖它做最终判断因为不同 embedding 模型产出的分数分布差异很大阈值很难通用。关于检索类型的参数search_typesimilarity是直接用向量距离找最近邻。后面如果做混合检索可以换成 MMR 或者自定义检索器。MMR 在相似的基础上增加了多样性约束避免召回的 5 个文本块内容重叠严重。3.3 提示词的完整构造逻辑RAG 的最后一步是生成提示词的质量决定了模型能不能正确使用检索结果。这里我给出一套我常用的提示词模板核心逻辑是“身份 依据 约束 兜底”。你是企业知识库助手。请严格依据下面提供的【参考文档】回答用户问题。 【参考文档】 {context} 【用户问题】 {question} 要求 1. 如果参考文档中有明确答案给出准确回答并标注信息来源文档名/编号。 2. 如果参考文档中没有答案直接回答“知识库中未找到相关内容”不要编造。 3. 回答时先给出结论再补充说明。 请回答这套提示词的几个细节很关键。第一条“严格依据参考文档”是从生成侧压住幻觉的源头。第二条“没有答案就直说”是对检索失败的保护——宁可直接告诉你没找到也不要让模型强行编一个。第三条“先给结论再补充”是面向实际业务场景的表达习惯用户通常只想要一个准确判断不需要模型绕圈子。{context}是检索结果按序拼接的上下文{question}是原问题。如果你用的是 LangChain 的 PromptTemplate这两个变量名要保持一致。这里有一个我踩过的坑不要简单地把所有检索结果全部塞进{context}。如果切分粒度不一致塞进去的内容可能包含重复段落或同一来源的多段信息模型容易产生重复输出。建议先做一步去重和排序保持来源文档的原始顺序再拼接。3.4 从“管道能跑”到“效果达标”的闭环能跑通只是起点。真正让 RAG 管道在业务里稳定发挥需要一个评估闭环。我建议每一次问答都记录这三个东西原始问题、召回的前 K 个文档、最终回答。每周做一次回顾统计“回答中引用的信息是否真的在召回文档里”。如果回答内容好但引用内容不在召回集里说明提示词层面模型可能自行发挥了要检查是不是“泄漏”了训练知识如果召回集里明明有正确答案但回答错误问题出在生成阶段或提示词设计如果召回集里本来就没有相关信息那就得回头优化切分、embedding 和检索策略了。骑驴找马先把闭环跑起来再逐步替换瓶颈。我的体会是RAG 项目最容易死在“一步到位”的想法——想着一开始就做 Agentic RAG、GraphRAG、语义缓存这些高级功能结果基础检索质量还没验证所有问题混在一起根本无法定位。先把最普通的管道做好、评估好再往上面叠加复杂度才是务实的路径。4. 效果评估与高频问题排查实录4.1 Hit Rate 到底怎么算怎么用RAG 社区里现在越来越多人把 hit rate 作为核心指标。含义很简单针对一组测试问题如果某个问题对应的标准答案文档出现在召回结果里就算命中命中数除以总问题数就是 hit rate。比如准备 100 个你标注过标准答案的问题每次检索取回前 5 个文档正确答案文档出现在这 5 个里的次数是 80 次hit rate5 就是 80%。为什么 hit rate 比最终回答的“感觉准确”更有价值因为它拆开了 RAG 管道的责任边界。最终回答不准确可能是检索没召回也可能是模型没用上召回内容或者是提示词引导不当。你没有这个指标问题来了的时候只能盲猜。而一旦 hit rate 没问题但回答仍然不对你就能很确定地说这一步问题不在检索在生成。实际执行时我建议这样做用真实业务问题构建一个 200 到 500 条规模的评估集每条对应一段标准答案原文。定期跑一遍记录 hit rate5、MRRMean Reciprocal Rank看正确答案排多前和回答的忠实度。忠实度可以人工标注也可以让另一个模型做裁判判断“回答是否被召回内容支持”。然后再定期做增量更新确保测试集覆盖新增的业务知识。MRR 的计算也很简单正确答案在候选列表里的排名取倒数多个问题取平均。第一个位置就是 1第三个位置就是 1/3。4.2 检索不到相关内容先查查是不是“源”就错了知识库检索不到可用内容原因通常按优先级排列第一源文档本身没有被正确加载。我见过好几次“检索不到”问题最后定位出来是文档编码错误BOM 头没处理正文乱码embedding 出来全是噪声。先用简单的关键词直接在原始文档里搜索如果关键词能找到而向量检索找不到那问题出现在向量链路如果关键词直接找不到源数据可能压根没进库。第二切分把关键信息拆散了。比如一句话的主语在 chunk A谓语和宾语在 chunk B向量模型对这种跨块信息无能为力。这时可以检查 chunk 边界调整 overlap 和切分策略尤其是遇到“答非所问”的现象时要重点检查。第三Embedding 模型对领域词的理解不到位。专业缩写、型号编码、方言化表达通用模型往往没学过。对应办法是换领域微调的 embedding 模型或者在检索侧增加关键词精确匹配来兜底。4.3 检索到了但回答不对问题多数在生成侧当你确认召回结果里已经包含正确答案但 Agent 给的回复依然不对常见的坑有三个。第一个是提示词太弱模型没有“必须依据参考文档”的强约束。第二个是上下文拼得太多正确答案被淹没在无关文本里。模型注意力资源有限塞 10 个文档进去不如精选 3 个高质量的管用。第三个是模型本身的服从性——小参数模型容易在长上下文里“跑神”回答越来越跳跃。这种情况我试过把模型从 7B 换成 14B效果提升明显。还有一个隐蔽问题被截断的上下文。很多框架默认对上下文做截断前一段被截掉的信息可能恰好是答案所在位置。排查方法很简单把context完整打印出来看一遍你是不是真的看到了答案。4.4 进阶方向Agentic RAG、GraphRAG 与知识割裂如果你的 RAG 管道已经稳定运行一段时间评估指标也到了阈值这时候再考虑进阶形态。当下有三个主要方向Agentic RAG让 Agent 自主决定“要不要检索”、“该用什么词检索”、“检索一次还是多次”。它解决的是复杂问题拆解场景——用户问“对比 A 方案和 B 方案的优缺点”一次检索很难既覆盖 A 又覆盖 BAgent 可以分两次检索再合并结果。GraphRAG在文本切分的基础上额外构建实体关系图把分散在不同文档里的同一实体关联起来。它隐式解决的是跨文档知识聚合问题比如多个文档分别提到某个型号的参数、售后政策、备件清单传统向量检索很难一次都召回图结构可以沿着关系路径找齐。Ontology RAG本体增强 RAG在知识源之上叠加业务语义模型定义实体、属性、关系。适合领域知识体系完备、对精确性要求高的场景比如医疗器械、工业设备。这些方向的本质都在回答“知识割裂”问题——同一个知识实体分散在多个文档、多个流程、多个系统里单点检索无法形成完整答案。但如果基础的切分、embedding、检索还没调明白直接上这些形态会适得其反。4.5 独家避坑技巧清单最后分享几条只有实际做项目才会碰到的经验不要把全部文档一次性灌进索引。先抽样一个模块或一个子目录跑通流程再扩容。否则第一次全量索引的报错会让你分不清是数据问题还是代码问题。定期重建索引而不是只增量更新。数据量上来后旧索引里的向量可能因为 embedding 模型微调、切分策略调整而不一致建议每月做一次全量重建。所有检索日志保留原始 query。这些数据是你调优评估集的第一手来源也是排查“用户觉得答案不对但技术上没错误”的关键线索。为每个文本块保留“来源路径原文片段”。用户追问“你根据什么说的”时溯源就是知识库可信度的证明。这一步一开始就要设计进去等系统上线后再补源信息会非常痛苦。我个人在这几个坑里栽过不少跟头尤其是“回答错误”的排查思路一度被带偏到调推理模型浪费了很多算力。现在形成的条件反射是先看 hit rate再打印 context最后才动提示词和模型。检索问题的归检索生成问题的归生成这条分界意识比任何技术技巧都值钱。RAG 这个方向做好之后后面再看 Agent 的复杂编排就会轻松很多——知识的底座稳了Agent 的上层建筑才谈得上可靠。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑