ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

检索增强生成(RAG)原理与工程化实操:AI Agent知识管道搭建指南

2026/9/28 20:34:18 拓冰建站 浏览量
检索增强生成(RAG)原理与工程化实操:AI Agent知识管道搭建指南 做了不少 Agent 项目的朋友应该都有同感模型本身的记忆是有“保鲜期”的训练数据截止日之后的业务文档、内部知识库、实时数据它一概不知道。你说把文档写在系统提示词里塞个几十万字既浪费 token效果也一塌糊涂。这时候就需要一条知识获取管道把外部知识在生成前送进模型的视野里。这条路最成熟、最基础、最好落地的方案就是 RAGRetrieval-Augmented Generation检索增强生成。这一篇我打算把 RAG 从原理到工程化实操完整拆一遍重点讲清楚它在 AI Agent 里的定位、标准管道的四个环节、基于 LangChain 的最小可跑通实现以及为了检索质量我踩过的一堆坑。适合正在从“调 API 聊天”转向“认真做 AI Agent 应用”的开发者也适合那些刚接触 RAG 词、被各种概念绕晕的人。1. 为什么要给 AI Agent 接一条知识管道1.1 模型天生缺失的三块拼图很多人一开始会把大模型当成一个“什么都知道的超级专家”实际工程做多了就会发现它距离一个合格的知识库还有三块硬伤。第一是知识时效性。大模型的知识取决于训练集的截止日期。哪怕是最新的开源模型它也不知道昨天发生的事、昨天发布的论文或者你们公司昨天刚更新的内部流程。对 Agent 来说时效性往往就是生命线比如客服机器人需要知道最新的退换货政策如果模型记住的是一年前的旧规则那它给出的答案不但没用还会误导用户。第二是私域知识盲区。所有公开模型的知识边界是公开互联网你们公司的数据库、内部 Wiki、行业研报、产品手册、用户反馈这些内容模型根本没学过。想让它基于这部分内容回答唯一靠谱的路径不是继续训练而是在生成时把相关内容“临时喂”给它。第三是幻觉。模型在遇到不确定的问题时往往会用“听起来合理”的方式编造答案。这个特性对闲聊影响不大但对 Agent 这种需要处理真实任务、给出可执行结论的系统幻觉是不可接受的。RAG 提供的外部证据是遏制幻觉最务实的手段因为答案的每个关键论断都能追溯到具体的原始文本。这三块拼图恰恰说明一个问题把 Agent 接到外部知识管道不是锦上添花而是刚需。设计一个 AI Agent 时如果只考虑“怎么让模型更好地推理”不考虑“它哪里来的知识”那做出来的东西大概率是个中看不中用的 Demo。1.2 RAG 在 Agent 里的角色定位用一个生活化的类比来说RAG 相当于给 Agent 配了一本可以快速翻查的大百科全书并且教会了它在回答问题前先查书然后基于查到的内容作答。传统的 Prompt 工程是“闭卷考试”模型只能依靠自己内存里的知识回答。RAG 是“开卷考试”系统先把相关资料检索出来作为参考材料放进上下文中模型再根据这些材料生成答案。这中间的差别决定了答案的准确性和可控性。在 Agent 的完整架构里RAG 通常不是主角但它是主角手里的“工具”。Agent 负责拆解任务、规划步骤、决定下一步调用什么工具而 RAG 就是那个“根据查询去检索知识”的工具。放到 AI Agent 的知识获取管道里RAG 一般以两种形态出现单轮知识问答用户问一个问题Agent 决定知识检索工具系统执行检索把结果交给大模型生成。典型如“根据我们公司产品手册A 型号和 B 型号的区别是什么”。多跳知识获取Agentic RAGAgent 先根据初步结果意识到还需要更多细节于是再发起第二次、第三次检索或者改写查询词后重新检索最终把多次检索到的碎片知识整合成答案。典型的场景是“对比我们三个服务套餐然后给我推荐一个适合 5 人团队的方案”如果知识被拆在三个文档里初次的单次检索很可能不全需要多步才能凑齐信息。基础 RAG 是我们这一篇的重点而理解基础 RAG 是理解 Agentic RAG 的前提。就像写代码前先学会怎么用列表、循环、函数才能去设计复杂的程序流程。2. RAG 管道的骨架拆解从文档到回答的四段流程标准 RAG 可以压缩成一句话把文档切碎、向量化、存起来查询时把问题也变成向量找到最相关的碎片连同原文一起交给模型生成答案。听起来简单但每个环节都藏着影响最终效果的决定性细节。2.1 切分Chunking不是简单按字数切切分是把长文档拆成若干个小片段chunk。这一步常被新手忽略但它的质量直接决定了检索的召回效果。一个常见理解误区是“切得越小越精确”。实际上片段太小比如一段只有一句话虽然定位精确但上下文信息往往不足模型回答时缺少足够的背景片段太大比如一个整章几千字检索虽然命中率高了但会导致拼接后上下文爆炸浪费 token而且可能把不相关的内容带进来干扰答案。真正合理的切分策略要从三个层面来考虑语义边界优先尽量在标题、段落、句子、表格自然的边界处切开保证每个 chunk 是一个相对完整的信息单元。用固定长度硬切最大的问题是会切断语义比如把一个表格从中间切开或者把一个问答对拆成两半。重叠overlap策略相邻 chunk 之间留一部分重叠文本通常 10%-20% 是合理范围。这能避免关键信息恰好落在边界上导致两边都搜不到的情况。比如 chunk_size512overlap64既保证上下文连续又能多一层边界保护。元数据保留切分时一定要记录每个 chunk 来源文档名、章节路径、页码等元数据。后面做过滤、溯源、凭证展示全靠它很多人一开始忽略等要在答案下面加“参考资料”的时候再回补成本非常高。切分的具体参数不能生搬硬套要跟你实际文档的类型挂钩。如果是产品说明书按章节语义切分效果远好于固定窗口如果是聊天记录就需要按对话轮次切分而不是按字切。这块值得花时间做小规模实验用几个典型问题去验证不同切分参数下的检索命中情况。2.2 向量化与索引Embedding 模型怎么选切分完成之后需要把每一个文本碎片变成计算机能算相似度的向量这一步用的就是 Embedding 模型。Embedding 模型承担的任务是“语义编码”它把一段文字映射到一个高维空间让语义相近的文字在高维空间中距离更近。比如“冰箱制冷效果差”和“冷藏室不凉了”字面上差得很远但 Embedding 向量距离却很近这正是 RAG 能突破关键词匹配限制的原因。选 Embedding 模型要看的指标主要有三个语义表示能力能不能理解行业术语、长句和反义关系。拿自己的业务场景里的几个典型问答去对比测试是最靠谱的。向量维度维度越高表达力通常越强但存储和计算成本也越高。常见的有 384 维、768 维、1024 维甚至更高不是维度越高越好得看实际效果和成本均衡。中文场景的适配性如果业务以中文为主一个通用的多语言模型可能不如一个专门优化过中文的模型。多语言模型对中文的语义理解往往比不过专门的中文模型这是我在实际对比中得出的一个比较明显的结论。索引侧目前主流做法就是用向量数据库来存储向量和原始 chunk 的对应关系。选择有几个方向一种是托管的向量数据库省心、自带可视化和批量处理另一种是开源自托管的方案比如 Chroma、Qdrant、Milvus适合本地开发和数据敏感场景。做实验跑 Demo用 Chroma 就很快一句pip install加几行代码就能起服务生产环境则要考虑数据体量、并发查询量和扩展能力通常需要更重的方案。另一个值得注意的细节是向量库里存的不只是向量通常还要存原文 chunk、文档元数据、唯一 ID。因为最终模型看到的必须是原始文本而不是向量本身所以检索完向量后要能立刻回查到原文并把原文拼进上下文。2.3 检索RetrievalTop-K、相似度阈值还有混合检索检索是整个 RAG 管道里最影响“上限”的环节。检索这一步如果召回的片段不对头你后面提示词写得再好、模型再强也救不回来。最朴素的检索方式是向量相似度检索把用户的查询问题也 Embedding 成向量然后在向量库里找距离最近的 K 个切片。这里面有两个参数经常被忽略Top-K要返回多少个切片K 太小容易漏信息K 太大会混入不相关的内容。实践中可以按文档平均长度来调常见的起点是 K4 到 8再根据检索结果的实际相关性做增减。相似度阈值返回结果前设置一个最低分数线低于阈值的就算距离近也不返回。这能避免“矮子里拔高个”问题是用户问的内容文档里根本没有硬检索也会返回一堆不相关的内容设了阈值就能返回空结果正确的处理是告诉用户“知识库中暂时没有相关信息”而不是让模型瞎编。向量检索的问题也很明显它对短文本、关键词不敏感在遇到专业缩写、专有名词时效果往往不太好。比如用户查询“QPS 上不去怎么办”如果文档里写的是“每秒查询数不足”或者是“性能瓶颈”向量可能匹配不到。解决这个问题的主流方案是混合检索Hybrid Search把关键词检索BM25和向量检索的结果做融合。BM25 擅长精确匹配专有名词和代码片段向量检索擅长语义扩展两者各有优势互补后的效果在真实业务里几乎总是优于单一检索方式。实现上可以先分别拿到两个 Top-K 集合再用 RRFRank Reciprocal Fusion或者分数加权的方式合并排序。我自己测试下来混合检索在技术文档类知识库上的命中率提升非常明显代价只是多一次索引和一二十行融合代码属于性价比极高的优化。比较进阶的做法会在检索之后再加一层重排序Rerank。第一轮用一个轻量快速的方式召回 20-50 个候选然后用一个更强大的模型逐一计算 query 与 chunk 的相关性分数只留分数最高的几个。这个环节对最终质量的提升比调任何 Prompt 都大但是要注意延迟和成本毕竟多跑了一个模型调用。3. 实操基于 LangChain 快速搭一条最小可用 RAG 管道理论讲再多不落地都是空的。这里用 LangChain 搭一条最小可用的 RAG 管道代码量很精简同时每一步都对应上面讲的流程让你能一条线看懂。3.1 环境准备与依赖安装先准备好 Python 环境建议 3.10 以上。我用的核心依赖如下pip install langchain langchain-community langchain-openai chromadb这里解释一下为什么选 LangChain。虽然很多资深工程师对抽象封装有意见但作为一个“基础管道”来说LangChain 把文档加载、切分、向量化、检索、提示词组织这几个环节封装成了统一接口极大降低了实验成本。如果你只是想验证 RAG 在业务里的效果先跑通再决定要不要重写底层是最高效的路线。Qdrant、Milvus 等向量库在 LangChain 里也叫VectorStore接口所以后续想换库改动很小。顺带说明一下我会用 OpenAI 兼容接口来做示例你在实际中完全可以用本地部署的模型或者国内大模型平台的接口替代只要保持兼容就行。3.2 索引侧代码加载、切分、写入向量库索引侧的目标很简单把本地文档变成向量库里的记录。我这里以一个 markdown 格式的产品手册片段作为示例演示整个流程。from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import Chroma # 1. 加载文档 loader TextLoader(./product_manual.md, encodingutf-8) documents loader.load() # 2. 切分文档 text_splitter RecursiveCharacterTextSplitter( chunk_size512, chunk_overlap64, separators[\n\n, \n, 。, , , ., !, ?, , ,, , ], ) chunks text_splitter.split_documents(documents) print(f切分完成共 {len(chunks)} 个文本片段) # 3. 初始化 Embedding 模型 embeddings OpenAIEmbeddings( modeltext-embedding-3-small, # 换成你实际使用的 embedding 模型 ) # 4. 写入向量库并持久化到本地 vectorstore Chroma.from_documents( documentschunks, embeddingembeddings, persist_directory./chroma_db, ) print(向量库写入完成)这套代码里几个细节要特别注意separators的层级顺序代表了切分时的优先级。我优先在段落边界切然后是句号、感叹号、问号这些句子边界最后是逗号和空格。这比纯固定长度切出来的 chunk 语义完整性高不少。chunk_overlap64不是随便写的。它保证每个 chunk 末尾跟下一个 chunk 开头有一小段重叠降低关键信息被边界切断的概率。对于 512 的 chunk64 大概是 12% 的重叠比例属于一个经验上比较稳的区间。向量库目录./chroma_db会持久化到磁盘下次启动不用重新索引。做增量更新的时候要注意去重策略否则同一文档反复跑会导致重复存储。索引完成后你可以随便查一下向量库的内容确认 chunk 数量和文本内容符合预期。这个节奏很重要先确认索引没问题再去写查询代码避免问题叠加导致排查困难。3.3 查询侧代码检索、组装提示词、生成查询侧是 RAG 真正发光的地方拿着用户问题去检索把结果跟提示词拼在一起送给大模型。from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnablePassthrough from langchain_core.output_parsers import StrOutputParser vectorstore Chroma( persist_directory./chroma_db, embedding_functionOpenAIEmbeddings(modeltext-embedding-3-small), ) # 检索器每次取 top_k 个文档片段 retriever vectorstore.as_retriever( search_typesimilarity, search_kwargs{k: 5}, ) # 提示词模板把检索到的上下文作为参考资料 prompt ChatPromptTemplate.from_messages([ (system, 你是一个基于参考文档回答问题的助手。\n 请优先使用下面的参考资料回答用户问题如果参考资料中没有答案请直接说明“知识库中没有相关信息”。\n 回答时请保持简洁准确列出关键结论即可。\n\n 参考资料\n{context}), (user, {question}), ]) def format_docs(docs): return \n\n.join(f【来源】{d.metadata.get(source, 未知)}\n{d.page_content} for d in docs) rag_chain ( {context: retriever | format_docs, question: RunnablePassthrough()} | prompt | ChatOpenAI(modelgpt-4o-mini, temperature0) | StrOutputParser() ) # 测试查询 question 这款设备支持的最大并发连接数是多少 answer rag_chain.invoke(question) print(answer)几个工程化要点temperature 设置为 0减少模型发挥的空间。RAG 场景下我们期望模型尽量忠实于检索到的原文而不是自由创作。把它调整到 0.3 以上模型就会开始“不听话”。提示词里明确兜底策略。我写了“如果参考资料中没有答案请直接说明”这句话能堵住很多幻觉。实际测试中不加这句话模型常常会生硬地把不相关的内容也要扯上关系。把来源信息拼进上下文。在format_docs里我把每个 chunk 的来源也带上了这有两个好处模型回答时更容易引用对应来源方便后续做答案溯源也会在结果展示时帮你自然地拿到 reference。检索结果拼接的顺序。按照相似度得分从高到低排列通常最相关的内容会出现在上下文的前部模型相关的注意力也会更强。跑一遍上面的代码你就拥有了一个能回答产品手册问题的最小 RAG 系统。这个系统虽然简陋但五脏俱全涵盖了基础 RAG 的全部链路。4. 检索质量上不去先排查这四个环节实践中你会发现 RAG 系统的效果天花板往往不在模型而在检索。接下来的内容是这几年实打实踩坑换来的经验适合当作排查手册使用。4.1 召回率与精度的失衡最典型的症状是回答内容“好像对”但引用的是不太相关的内容或者明明文档里就有精确答案却检索不到。这里我建议最先去看检索返回的原始 chunk而不是直接调模型。先把retriever.invoke(query)的结果打印出来人工看一眼返回的前几个 chunk 是不是真的相关。这一步相信我特别有用因为很多问题从源头看就会发现答案是“检索出来的东西不对”后面所有优化都是无效的。如果发现检索结果相关性不好按优先级做这些事先排查切分质量是不是把完整语义切碎了检查 chunk 里有没有出现“句子在中间断掉”的现象。然后看混合检索BM25 召回的结果是否有向量检索漏掉的专有名词把混合检索加上通常会有立竿见影的效果。再试 Top-K 调优K 从 5 调到 8 或者 10看看是否能把正确答案“包含进来”。有时候答案没召回纯粹是因为 K 太小。但注意 K 过大也会引入噪声所以要在精度和召回之间做权衡最理想的方案是做两阶段检索第一轮大 K 召回第二轮用 Rerank 压缩成高质量的小集合。简单说检索问题优先在检索引擎层面解决不要一上来就让大模型“努力从上下文里找线索”。4.2 Embedding 模型和查询词的语义鸿沟第二个常见问题是检索质量和意图理解不匹配。尤其表现在用户问“怎么收费”但文档里写的是“定价方案”“价格表”“订阅计划”这类业务词汇。这背后其实是 Embedding 模型和业务术语之间的语义鸿沟。通用 Embedding 模型学的是互联网级别的语义关联对特定行业的行话、缩写、内部黑话理解有限。不要指望一个通用模型能替你消化所有业务场景。我的处理经验是业务场景先在检索前做查询改写Query Rewrite。在进入检索器之前先让大模型把用户的问法转换成更标准、更接近文档表述的问法。比如用户说“多少钱”可以改写为“定价方案 价格 套餐费用 收费标准”。这属于标准 RAG 到 Agentic RAG 之间最便宜的进阶手段代码不到二十行但效果很实在。建立同义词词典和别名表。对于产品名、型号、内部术语维护一张专有名词映射表在查询时用字符串替换或者 BM25 的扩展检索去适配。这个方法很土但很有效尤其适合文本结构化高的文档。在 Embedding 模型选型阶段就做业务测试。拿 20 道典型的领域问题用不同的 Embedding 模型跑离线召回率对比干脆用数据说话。很多团队几个不同模型相比一测就知道哪个更适合自己的文档。语义鸿沟问题不会通过一次优化就彻底消失因为语言表达方式太多变了。但通过查询改写和多路召回可以把命中率从“看运气”提升到“基本稳定”。4.3 提示词模板与上下文管理的细节检索没问题上下文也拼接好了最后还是答得不够好就要看看提示词交互这个环节。RAG 的提示词其实比普通对话提示词更容易犯两类错误。第一类是上下文过长导致注意力稀释。检索返回多个 chunk 时如果相关性分数最高的内容被一堆噪声片段包围模型可能“找不着重点”。应对办法是在组装 Prompt 前先按分数过滤一遍只留下够用的上下文。比如检索时召回 8 个但真正拼进提示词的只有 4 个保留一个额外的候选空间在融合排序后只取头部。第二类是没有告诉模型如何对待“不确定”。有些答案需要明确列出依据有些则要给出引用片段这些都应该在 system prompt 里写明。我常用的模板结构是这样的你是知识库助手请仅依据提供的【参考材料】进行回答。 要求 1. 如果参考材料中包含答案请直接回答并在每个关键结论后注明来源片段。 2. 如果参考材料中没有答案请回复“资料库中暂无相关信息”不要自行推测。 3. 当参考材料信息冲突时请明确指出存在冲突并列出所有不同说法。第三条容易被忽略但非常实用。RAG 系统面对的多文档场景不同文档之间经常有版本差异、口径不一这时让模型直接判断对错只会越描越黑正确的做法是让它如实暴露冲突把判断交给人工。上下文管理还有一层是要考虑 token 预算。不管检索多准如果上下文已经让模型超长API 报错或者花钱如流水都是家常便饭。在实践中我会给每个答案设定一个上下文大小上限比如最大 3000 token如果候选片段超出就只保留与 query 得分最高的几个严格掐断无用内容。4.4 常见错误速查表这里把我看到最多的初级错误整理成一个速查表值得收藏。现象可能的根因处理方案问什么都答“不知道”检索 Top-K 太小或相似度阈值太高调大 K调低阈值检查向量库是否被正确写入答案看着对但引用了无关文档混合检索排序权重不合理或上下文混入噪声增加 Rerank 环节或降低噪声 chunk 的排序权重专有名词、型号检索不到Embedding 对精确匹配不敏感加 BM25 混合检索或用查询改写把缩写展开回答内容正确但语气/格式不统一提示词约束不足在提示词中明确答案格式增加输出结构约束反复触发 API 限流同时调了检索、重排、生成三个模型给检索和重排加缓存或把第一轮召回集合减小更新知识库后检索结果没变化持久化目录冲突或者删旧数据不彻底清空向量库目录重建索引并确认写入前是否清掉了旧记录长文档的中间部分永远召回不到切分时丢失了上下文或 embedding 模型对长文本理解弱采用语义窗口切分按章节切并保留标题路径作为前缀这张表不见得覆盖所有情况但排查 RAG 问题时只要按“先看检索再看拼装最后看生成”的顺序走大多数问题都能在二十分钟内定位。5. 从基础 RAG 走向 Agentic RAG让管道变成 Agent 的一门技能5.1 为什么标准 RAG 在 Agent 里不够用基础 RAG 有一个隐藏的前提假设用户一个回合的查询足以从文档中找到所有需要的答案。然而真实场景中用户的问题往往是多步骤的、模糊的甚至是需要主动追问的。举个例子。用户问“公司有哪些套餐适合我们团队”这个问题的那个“适合”本身就需要清楚“我们的团队人数是多少、预算多少、需要什么功能”而知识库里根本没有用户侧的信息。标准 RAG 做不到交互式澄清它只会把现有文档里所有套餐都检索出来一股脑交给模型让模型“猜”一个推荐。再比如多文档综合类问题“对比我们 2023 版和 2024 版的产品说明功能变更有哪些”如果两版文档分别存储单次检索往往只召回旧版或只召回新版的相关片段模型很容易只看到一面之词。要回答这个问题的正确姿势是分两次查先查 2023 版再查 2024 版然后把变更清单合并出来。这正是 Agentic RAG 的出发点把“检索”从一条固定的管道升级为 Agent 手里可编排的工具。Agent 判断何时检索、检索几次、是否要改写查询、检索结果不足时是否再查这些决策都由 Agent 自己做。5.2 三个立即能用的升级方向从基础 RAG 向 Agentic RAG 升级的过程不必一步到位。这里分享三个渐进增强的方向每一个都不改变原有架构但能把系统的智能度往前提一个台阶。第一个方向是查询改写Query Rewriting。在进入向量检索之前增加一个轻量级的改写步骤用一个较小的模型把用户问题在知识库语境下重写一遍。比如“推荐一下我们店里适合油皮的护肤品”改写为“油性皮肤 护肤品 清爽控油 产品推荐”。这能显著提高 recall尤其是用户说法和文档术语不一致的场景。实现上只需要在 RAG 链前加一步模型调用成本低、见效快。第二个方向是多路召回与融合Multi-Route Retrieval。同时走向量检索、关键词检索、按元数据过滤的窄域检索再使用 RRF 或加权评分融合三个结果集。比如一份知识库里有产品手册、售后常见问题、价格表三类文档按元数据限定“只要售后文档”这类过滤通常比全库混检效果更好。在多路召回之后加一个重排就能拿到质量很高的最终上下文。第三个方向是给 Agent 安装“要不要检索”的判断器。不是所有问题都需要检索。简单闲聊、模型本身就能回答的常识问题强行接 RAG 反而是浪费 token 和延迟。通过一个分类器让 Agent 先判断是否需要检索如果答案直接由模型给就行就不要走检索通道。这个判断可以减少一半以上的无效检索同时还让响应速度更均匀。再往外延伸就是这阵子听到比较多的 GraphRAG、Ontology RAG 这些概念它们本质上是改变知识的组织方式用图谱或本体去建模实体间的关系让 Agent 能通过关系路径获取知识而不是只依赖向量相似度。这类方案对复杂关系型问题有显著优势但实现复杂度也更高属于基础 RAG 完全跑通之后再考虑的事。我个人在实际项目里验证过的经验是RAG 系统的复杂度一定要跟业务问题的复杂度匹配。跑通基础 RAG 之后先花一两周积累真实用户问题集把这些问题分类统计哪一类是单次检索就能解决的哪一类常常需要改写或多步查询然后再决定是否引入 Agentic RAG 的机制。最忌讳的是上来就搭建一套复杂的多智能体知识系统结果连检索质量问题都还没定位清楚反而把问题藏进了复杂的架构里后患无穷。如果这篇文章能帮你把基础 RAG 的每个环节都看得明明白白那我这几年的坑就没有白踩。