
走近 AI Agent 第四篇知识获取管道——RAG 基础在 AI Agent 的完整链路里如果说规划Planning、工具调用Tool Use、记忆管理Memory是大脑和四肢那知识获取管道就是让 Agent“吃饱饭”的消化系统。这一篇我们聚焦 RAG——检索增强生成也就是把外部知识库接入大模型的标准做法。先不用说复杂理论直接进入场景。假设你在做一个客服 Agent用户问“我们公司的报销流程是什么”模型没有见过你的内部文档它只能瞎编。RAG 做的事情很简单先把公司文档切成小块、向量化、存进数据库用户提问时把问题也向量化去库里捞最相关的几段再把这几段原文塞进提示词让模型“带着资料回答”。就这么一个朴素的闭环成了绝大多数 AI Agent 产品落地时不可或缺的组件。这篇内容适合谁如果你正在从 0 到 1 搭建一个带知识库的 Agent或者已经跑通了最简单的 RAG Demo 但发现效果很不稳定想系统化地把每个环节调明白那这篇就是给你准备的。我尽量把原理、取舍、实战参数都摊开讲不绕弯子。1. 为什么 Agent 需要一条“知识获取管道”从模型局限到 RAG 的定位聊 RAG 之前我必须先把“为什么要折腾这么一条管道”说清楚不然你很容易在后续调优时迷失方向。很多人以为 RAG 只是给模型“加点资料”这理解没错但太浅了。RAG 解决的是 Agent 落地时三个绕不开的硬伤。1.1 三个硬伤幻觉、知识截止、私有数据隔离第一个硬伤是幻觉。大模型的本质是“概率接龙”它不知道“不知道”这件事。你问它一个超出知识范围的问题它为了连贯性会编一个看起来靠谱的答案。前面提到的报销流程模型会说“根据公司规定您需要填写OA表单”——如果你司根本没有 OA这就是一次错误答复。RAG 通过把检索到的真实文本块放进上下文强制模型基于事实材料作答幻觉出现的概率会被明显压制。第二个硬伤是知识截止。模型训练快照在某个时间点就结束了。今天是 2026 年你用的模型可能只训练到 2025 年中的语料。凡是之后发生的产品更新、政策调整、行业数据模型一概不知。RAG 让知识库和模型解耦资料更新不需要重训模型换个文档或重新索引一遍就行。这也是 RAG 在工程上最受欢迎的地方——低成本、可迭代。第三个硬伤是私有数据。企业内部文档、客户工单、标注数据都不能进公网模型。RAG 把知识放在你自己的向量库里模型只拿检索结果作为上下文生成原始资料可以做到不泄露当然提示词注入的风险另说。这让 Agent 能安全地碰敏感数据而不是把所有家底都灌进模型权重。1.2 RAG 在 Agent 架构中的位置从“资料库”到“管道”我见过很多人做 Agent 时把 RAG 当成一个孤立的“查资料接口”写个函数search_db(query)就完事了。这在小 Demo 里没问题但放在真实 Agent 系统里知识获取应该被设计成一条完整的管道而不是一次性的检索调用。为什么强调“管道”因为 Agent 是动态的。用户的原始问题往往需要改写、拆解或多轮追问才能变成一两个真正适合检索的 query检索回来的文本块可能要经过重排、过滤、去重才能组成高质量上下文如果第一轮检索结果不理想Agent 还得决定换关键词、换 filter 条件再查一轮。这些环节串起来就是一条有入口、有中间处理、有出口的知识获取管道。我自己的习惯是把这条管道拆成五段来看摄取Ingestion原始数据进库前的清洗、切分、向量化存储Storage向量库/文档库的选型与索引设计检索Retrievalquery 处理、召回、多路融合增强Augmentation上下文组装、压缩、重排生成Generation提示词策略、引用溯源、答案校验这篇是基础篇我重点展开摄取、检索、增强、生成四段存储会在相关内容里一带而过。后续系列有机会再单独讲向量库选型和索引优化。1.3 一个简单类比图书馆里的参考咨询台如果你觉得上面这些概念还是有点抽象我再用图书馆类比帮你把 RAG 的位置钉在脑子里。把 Agent 想象成一个刚入职的实习研究员。他没读过你们公司的任何文件没训练过私有数据但聪明确实很聪明模型能力强。你要让他回答“报销上限是多少”直接把整本员工手册丢给他读他会被几千页信息淹没找不准重点。这就是“塞满上下文”的粗暴做法。RAG 的优雅之处在于你先给手册编好索引切片 向量化实习研究员带着问题去目录里查检索只把最相关的三五页复印出来召回然后基于这三五页作答。他不需要读完全书答案的质量却远比“瞎猜”或“通读全文”靠谱。这个类比能帮你理解后续所有参数调优——切多小、查多少、怎么排序本质上都是在优化“复印哪几页最有用”这件事。2. 摄取这一步藏着整条管道的成败数据清洗、切分策略与向量化细节很多教程上来就让你跑一个load_document() - split_text() - embed_and_store()的三步脚本看起来很容易但真实知识库质量的高低八成的决定因素在摄取阶段就定死了。检索效果不好你后面怎么调提示词都救不回来。所以这一节我可能要唠叨多一点。2.1 数据清洗优先级宁可少不要脏摄取之前最重要的一件事不是上切分器而是清洗数据。我从实战中总结出一条知识库宁可内容少一点也绝对不能又杂又脏。脏数据带来的问题很隐蔽——不是“答案找不到”而是“找到了错误段落但看起来很有道理”。清洗的优先级我建议这样排第一优先去除模板噪声和导航残留。从网页抓来的文档经常带着页眉页脚、站内导航、公告栏。这些文本一旦被切成块并向量化就会在检索时成为巨大的干扰源。比如用户问“怎么退款”可能把“©2026 某某公司版权所有”这种块召回来因为它的向量和页面其他部分距离很近。我常用的做法是先做一轮正则清洗把页脚、版权、固定导航栏统一剔除。第二优先统一文档格式和编码。Word、PDF、Markdown、网页、扫描件每种格式进了管道都要先转成纯文本。PDF 分栏文档经常把阅读顺序打乱用pdfplumber或PyMuPDF提取时要留意版面顺序扫描件得先做 OCR而 OCR 错字是检索效果的大敌。这里没有银弹只能按文档类型各写各的处理函数。第三优先去重和版本清理。企业内部常见的场景是一份制度文件同时存在三个版本v1.2、v1.3、最终版。如果不去重不清理检索时可能同时召回两个版本模型可能取最新的也可能取中间的取决于向量距离。我建议在摄取前先做一轮基于文件哈希和标题匹配的去重把历史版本单独归档而不是直接入库。2.2 切分策略chunk size、overlap 与结构化感知清洗完就是切分这是摄取阶段最值得反复实验的环节。切分决定了检索的最小粒度切太大了召回段落包含太多无关信息模型容易被带偏切太小了语义不完整模型得不到足够的上下文。这两头都得拿捏。我实测下来中文场景里比较稳的起点是新兴模型上下文窗口普遍较大但块大小不宜盲目调大。常规小知识库建议chunk_size512按 token 算或者300-500 字overlap50-100重叠区域。为什么要有 overlap因为信息经常跨块分布——一句话在块 A 结尾它的语义答案却在块 B 开头。一点重叠能让边界处的语义不至于被生硬切断。但固定窗口切分只是兜底方案。如果你处理的文档有清晰结构就更应该做“结构化感知切分”。最简单的做法是先按 Markdown 标题或 Word 大纲把文档切成章节再对过长的章节按段落切。这样每个入库块都自带“章节归属”后面做元数据过滤metadata filter你会非常感谢当时的自己。下面给一个基于 LangChain 的结构化切分示例from langchain_text_splitters import MarkdownHeaderTextSplitter from langchain_text_splitters import RecursiveCharacterTextSplitter # 先按标题层级切 headers_to_split_on [ (#, H1), (##, H2), (###, H3), ] markdown_splitter MarkdownHeaderTextSplitter(headers_to_split_onheaders_to_split_on) sections markdown_splitter.split_text(markdown_doc) # 对每个章节再按固定大小二次切分 text_splitter RecursiveCharacterTextSplitter( chunk_size512, chunk_overlap50, separators[\n\n, \n, 。, , , , , , ], ) chunks [] for section in sections: sub_chunks text_splitter.split_text(section.page_content) for sub_chunk in sub_chunks: sub_chunk.metadata.update(section.metadata) # 保留标题路径 chunks.append(sub_chunk)注意separators的顺序是有讲究的。把\n\n放最前是希望优先在段落边界切如果还是太长再退到句子标点句号、问号去切。交替用、 等切出来的块语义完整性会明显好于纯字符流硬切。2.3 向量化embedding 模型选型与正反例切片之后每个块要变成一个向量。这一步的选择直接决定检索召回的下限。选 embedding 模型时我建议关注三件事语义理解能力、维度对存储成本的影响、以及对中文的支持程度。这三个维度往往相互矛盾。能力强的模型维度通常也更高比如 3072 维甚至更高存储和计算成本随之上涨中文支持不好的模型英文检索效果不错一到中文就有点失灵。我的选型原则中英混杂语料优先选专门针对多语言优化的 embedding业界常见的几种中文/多语言模型都可以整体上大陆服务商或开源模型里近两年新出的 1k 维度档位选择比较平衡垂直领域语料通用 embedding 在专业术语上的效果可能不够用。有条件就收集几万条领域内的“相似片段对”在通用模型基础上做微调或训练领域适配模型没条件至少要用领域语料做一次模型效果评估选最合适的性价比考虑个人练手项目不要一上来就追求最贵的模型先跑通流程再评估到底值不值得升级模型向量化过程中的实操细节批量 embedding几千个 chunk 逐个跑接口非常慢建议每次请求带着几十个文本一起传。多数 SDK 支持批量参数。保存文本原文向量库里只存向量是不够的还得把原始 chunk 文本和 metadata 一起存否则检索到了向量你拿什么拼进提示词归一化如果向量做了 L2 归一化点积和余弦相似度等价有些向量库会要求/推荐这样处理。2.4 摄取排错的经典案例为什么引入了大量“看似相关”的脏数据这里分享一个我早期踩过的坑。当时在做一个法律政策知识库我抓了一大批地方法规文本没清洗就直接切分入库。上线后测试“北京社保缴纳比例”这类问题返回的内容混进了大量其他省份的相似条款。模型甚至会把深圳的政策和北京的混在一起作答。后来排查发现问题出在两条各地方规的条款结构高度相似向量上天然接近抓取时没保留“省份”metadata无法在检索时做地域过滤修复方案并不神秘清洗时保留原始条例的省份、年份、文号写入 metadata检索时强制加一条 filter比如province 北京。效果立刻改善。这个坑说明摄取阶段的每一份辛苦都会在检索阶段获得回报摄取阶段的偷懒也会在检索阶段加倍偿还。3. 检索不是“查一下”这么简单从向量相似度到混合检索与重排管道走到检索段你会发现“效果差”不一定是因为模型不行很可能是因为你召回的方式本身就不够稳。很多人以为 RAG 检索就是把用户问题向量化后跟库里比对一下取 top_k 拼进提示词。这个“一下”里头可调的东西非常多。3.1 稀疏检索和稠密检索两种互补的信号“向量化后算相似度”这种路径叫稠密检索dense retrieval它擅长理解语义层面的相似——你说“怎么报销”能找到“差旅费用申请流程”。但稠密检索有个弱点它对关键词的精确匹配不敏感。如果你问的是精确型号“A-100-B”而文档里恰好也写了“A-100-B”向量匹配未必把它排到最前因为整句的语义重心可能落在别处。这时候稀疏检索sparse retrieval经典做法是 BM25就有用了。BM25 基于词频和逆文档频率来打分擅长处理“关键词精确命中”的情况。实际效果上BM25 在专有名词、型号、人名、编号这类场景往往比向量检索还要准。所以你真正需要的是混合检索hybrid search——同时跑向量检索和 BM25然后把两家结果合并排序。这也是为什么向量数据库/检索平台这两年都在主推 hybrid 能力。维度稀疏检索BM25稠密检索向量匹配原理词汇精确匹配语义向量距离优势精确词、型号、稀有名词同义句、口语化表达劣势无法处理“换个说法”对精确字符不敏感典型问题“A-100-B 规格”“怎么报销”具体的合并方式有很多种最简单的是RRFReciprocal Rank Fusion给每个文档在两个结果列表里分别记一个排名分最后按名次的倒数加权求和。RRF 的好处是不需要调权重而且对两边的分数尺度不敏感。下面给一个轻量的示例def reciprocal_rank_fusion(results_dict, k60): fused_scores {} for query_results in results_dict.values(): for rank, doc_id in enumerate(query_results): if doc_id not in fused_scores: fused_scores[doc_id] 0.0 fused_scores[doc_id] 1.0 / (k rank 1) reranked sorted(fused_scores.items(), keylambda x: x[1], reverseTrue) return reranked示例里的results_dict是“每种检索方式向量/BM25返回的 doc_id 列表”组成的字典。k是个平滑常数经验上取 60 左右。这个函数会把两个列表的名次信息合并成一个总分按总分降序得到最终召回顺序。3.2 query 改写原始问题往往不适合直接检索用户的问题很少是完美的检索 query。举几个典型场景口语化问题“那个报销最多能报多少啊”——直接拿去向量化匹配效果很差指代模糊“它支持远程吗”——“它”指什么上下文里才有多角度问题“售后服务政策和退换货流程分别是什么”——这其实适合拆成两个子问题进行检索所以成熟的 RAG 管道里检索前最好有一个 query 处理环节。最轻量的做法是规则改写去掉语气词、补全主语、提取核心名词。高级一点的做法是“用 LLM 做 query 改写”比如让模型分析对话历史生成检索用关键词。Agentic RAG 的核心诉求之一就是让 Agent 自己决定“要查什么、查几次、怎么查”。我自己的经验是query 改写不是每轮都需要过度改写反而会引入噪声。可以设一个简单的判定逻辑——如果问题长度很短且包含明显指代词才触发改写如果问题本身已经很具体直接检索即可。3.3 元数据过滤与 top_k 选择精确打击而不是大海捞针检索前先想清楚你的知识库有哪些维度可以过滤前面那个法律知识库的province就是典型。其他常见的过滤字段包括时间范围、文档类型、部门、产品线、语言版本。过滤的意义有两层。第一层是硬约束——“只要北京的数据”直接从源头排除其他省份的干扰。第二层是软排序——某些条件下比如最新版本优先可以通过 score 加权实现。至于 top_k 怎么定要结合块大小和模型上下文一起看。一个我常用的计算方式上下文目标占用 模型窗口的 30% 左右 top_k 上下文目标占用 / 平均块大小假设一个模型的窗口是 8k token你想留 2.4k token 给检索结果如果平均块大小是 400 tokentop_k 取 5-6 比较合适。取太少了可能不够覆盖问题的多个方面取得太多模型会“淹没”在信息噪声里反而答不好。这个话题我在下一节还会展开。3.4 Rerank检索的最后一公里很多人在检索后直接拼上下文效果始终差口气。先别急着怀疑模型试试加一个 rerank 环节。Rerank 和初检不同初检是拿“问题向量”和“块向量”做批量比对速度快但精度有限rerank 是拿“完整问题文本”和“召回的几十个候选块原文”交给一个专门的交叉编码器cross-encoder逐对精算相关性再按精算分数重排。这就像先让助手从全图书馆挑出 50 本可能相关的书初检再让专家逐一翻目录和摘要选出最相关的 5 本rerank。精度提升非常明显代价是速度变慢、成本增加。在 LangChain 里rerank 可以通过ContextualCompressionRetriever接入from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import CrossEncoderReranker from langchain_community.cross_encoders import HuggingFaceCrossEncoder rerank_model HuggingFaceCrossEncoder(model_nameBAAI/bge-reranker-base) compressor CrossEncoderReranker(modelrerank_model, top_n5) compression_retriever ContextualCompressionRetriever( base_compressorcompressor, base_retrievervectorstore.as_retriever(search_kwargs{k: 50}) )这个示例里的逻辑是先用向量库召回 50 个候选块再由 bge-reranker 模型精算把最相关的 5 个选出来拼进上下文。候选集取多大、最后保留多少个需要按你的准确率和延迟要求去调。候选集太小rerank 巧妇难为无米之炊太大rerank 本身的耗时和费用会变成瓶颈。4. 增强与生成提示词组装、引用溯源和幻觉抑制到了这一段管道已经拿到了高质量的候选块剩下的事情是把它们组织成模型能高效利用的上下文并让模型稳定地“基于资料而非脑补”作答。这里细节很多但每一条都能直接影响回答质量。4.1 上下文组装顺序别把最重要的信息埋在长文中间大模型对上下文中间部分的注意力往往弱于开头和结尾这是很多人的切身体会业界把它戏称为“lost in the middle”。组装上下文时不要随手把检索结果按分数从高到低排好就完事。我建议的组装策略是把相关性最高、一定需要模型看到的块放在最后靠结尾把次高相关、可能有用的块放在最前靠开头可选但可能混淆的块干脆不放也就是说不要让模型在“信息堆”里自己寻找重点你要用组装顺序替它标好重点。实操时我甚至会写一个简单的重排函数让 top-1 的块永远出现在检索材料的最后一段。这个细节对长文档场景的效果提升非常显著。4.2 提示词注入的写法明确的指令 硬性约束上下文准备好后提示词是决定生成质量的关键。RAG 提示词的核心是“边界”两个字告诉模型什么能做什么不能做而不是让模型自由发挥。我常用的模板结构是这样的你是一个{角色}。请严格根据以下资料回答用户的问题。 参考资料 {context} 规则 1. 只依据参考资料作答不要使用参考资料以外的知识 2. 如果参考资料不包含答案直接回答“资料未提及” 3. 回答时尽量引用资料的原文表述不要自行改写关键数据 4. 如果资料之间存在矛盾指出矛盾并给出你依据的版本 用户问题{question}这里“只依据参考资料作答”不是一句空话。你要考虑在生成逻辑里加上一层校验比如让模型在回答末尾附上引用来源文档名和段落位置方便用户核查。这不仅是增强可信度也会潜移默化地让模型不敢乱编——因为它知道自己被要求“展示引用”。顺带说一个细节模型看到“资料未提及”这个选项后胡编的概率会显著下降。这相当于给它一条体面的退路而不是逼着它在“编一个答案”和“承认不懂”之间被迫选择前者。4.3 压缩长上下文不是召回越多越好前面提过 top_k 不是越大越好。但实际场景中召回内容本身也可能包含冗余信息比如同一份政策在多个块里重复出现。直接全部拼进去浪费宝贵的上下文窗口是小事让模型被重复信息带偏才是大事。这个问题通常用上下文压缩context compression来解决按相关性过滤给候选块设定一个相关性分数阈值低于阈值的直接丢弃按内容去重如果多个块高度相似比如重复的制度条款只保留最完整的那个用 LLM 做摘要压缩让模型先读一遍候选块输出一段简明摘要再拼进正式上下文第三种方式对长文档特别有用。比如你召回了 10 个块加起来 4000 token直接塞进上下文对一个 8k 窗口的模型来说太拥挤让模型先压缩成 800 token 的要点综述再让它基于综述回答效果通常更好。代价是多一次 LLM 调用延迟和成本会上升需要你自己权衡。4.4 生成环节的幻觉抑制一道最后的检查闸门即使提示词写得再好模型偶尔还是会产出超出资料范围的推断。更稳妥的做法是在生成后加一道自检逻辑——把模型的回答和原始检索块再发给模型一次或用一个轻量的“忠实度判别”调用要求模型判断回答中的每一句是否都有资料依据。我看到不少开源框架也在做类似的事比如让模型输出“fully supported / partially supported / unsupported”的标注。这个做法的核心逻辑是让模型去过一遍verify成本不高但能兜住绝大多数基于幻觉的瞎编。当然自检也不是万能的。如果检索块本身就有错误信息自检大概率会“忠实”地放过这些错误。所以还是那句话管道前置环节的质量决定后置环节的天花板。5. 效果度量与调优顺序没有指标你就是在盲调RAG 管道搭起来以后最常听到的问题就是“效果不太好”——哪里不好、怎么不好、离目标差多少往往说不清。不量化的结果是每次改动都是玄学。我这里建议一套务实的度量体系从粗到细能让你知道问题到底出在哪个环节。5.1 检索层指标先看能不能捞到再看捞得准不准检索层的指标无需复杂先看能不能捞到命中率、hit rate再看捞得准不准MRRHit Rate命中率对测试集中的每个问题人工标注出“正确答案对应的文档块 ID”。如果这个 ID 出现在 top-k 检索结果里就算一次命中。所有问题里命中占比就是 hit rate。这个指标衡量的是系统到底有没有能力把正确答案找出来。MRR (Mean Reciprocal Rank)不仅看有没有命中还看正确答案排在第几位。比如正确答案排名第 1贡献 1.0排名第 3贡献 1/3。MRR 对排序质量更敏感。实操上我建议至少准备 50-100 条覆盖各业务场景的标注测试集太小了区分度不够。每条要标注“期望检索到的块 ID 列表”哪怕是粗粒度标注也行。跑完一轮评估你基本能知道到底是检索召回不行还是生成阶段没用好材料。下面给一个简单的命中率计算逻辑帮你跑通这个流程def evaluate_hit_rate(qa_pairs, retriever, k10): hits 0 for item in qa_pairs: query item[question] expected_ids set(item[gold_chunk_ids]) retrieved retriever.invoke(query, kk) retrieved_ids {doc.id for doc in retrieved} if expected_ids retrieved_ids: hits 1 return hits / len(qa_pairs)这个示例里需要注意retriever.invoke(query, kk)是假设你的检索器实现了这样一个方法按语义返回带.id属性的相关文档对象gold_chunk_ids来自你的标注集。对齐 ID 体系是这里最花精力的一步——你需要保证标注时用的 chunk ID 和检索返回的 ID 是一套否则全部白算。5.2 生成层指标答案质量怎么看生成层更偏“主观”但也可以结构化。我常用的三个维度忠实度Faithfulness回答里的每个关键信息点是否能在检索块里找到出处。可以人工标注也可以用 LLM 判分。完整性Completeness用户问题的所有子问题/关键方面回答是否覆盖到。比如用户问了“怎么申请 需要什么材料”如果回答只说了流程没说材料完整性就会扣分。有用性Helpfulness综合判断回答是否真正解决了用户问题是否清晰、可执行。我的评估做法是建一个打分表每题按这三个维度分别打 1-5 分记录每次改动前后的分数变化。只要改动方向是对路的分数通常会有规律地升降而不是凭感觉觉得“好像好了一点”。5.3 调优顺序物料大于检索器大于提示词如果你做了一轮评估决定开始调优我建议严格按这个顺序来不要跳步第一步检查数据问题。先看测试集的失败案例检索有没有召回正确答案如果正确答案根本没有入库或者入库了但元数据不对那调什么参数都是白搭。先改数据、清洗、元数据。第二步调检索层。在数据没问题后如果 hit rate 低于 0.7优先调能否加混合检索能否加 reranktop_k 是否合理切分粒度能不能优化这层的问题不解决生成层无论如何都不会好。第三步调生成层。hit rate 上去了但忠实度/完整性还是低这时再动提示词、压缩策略、引用要求。这里的逻辑很简单生成层的天花板由检索层决定检索层的天花板由数据质量决定。从上游到下游逐层排查找问题才有确定性改完才知道哪里起了作用。5.4 一个“效果爆炸”的调优示例我做过一个内部运维知识库的 Agent。第一版就一个向量检索 top_k5 拼进提示词hit10 只有 0.42。盲调了一周时好时坏。后来老老实实做评估发现失败案例主要集中在设备型号查询“S5800 交换机堆叠配置”——向量检索对精确型号不够敏感。于是做了两步加上 BM25 混合检索前缀内联某类带规格的问题hit10 直接跳到 0.78加了 rerankcross-encodertop_k 从 50 精排到 5hit5 稳定在 0.72 左右这之后我再回头优化提示词忠实度指标才有明显变化。整个过程验证了一件事评估数据先于一切调优没有数据你根本不知道把力气花在哪。6. 走向 Agentic RAG管道从“固定流程”升级为“Agent 决策的一环”基础 RAG 稳定跑通之后你一定会在实际使用中撞到它的天花板——不是效果不够好而是流程过于死板。基础 RAG 管道的模式是用户问题进来查一次拼上下文生成答案。但真实世界的问题不会总是那么配合。这一节算是给想更进一步的同学抛块砖Agentic RAG 是怎么把“知识获取”变成 Agent 的自主决策过程的。理解这一点对你回看基础 RAG 的设计也会更清醒。6.1 基础 RAG 的三个“不够聪明”第一检索一次就结束不回退。当第一次检索结果不理想时基础 RAG 不会重试。但实际对话中你可能需要先查“产品文档”发现用户问的是“售后”还要换到“售后政策”库里查。Agentic RAG 允许 Agent 判断“这次检索不够好”然后改写 query、换过滤条件、甚至换数据源再查一次。第二intention 不识别。有些问题适合直接查知识库有些问题适合调用工具比如查实时库存、算价格有些问题两样都要。基础 RAG 不管三七二十一都进检索管道Agentic RAG 会让 Agent 自己决定这个问题要不要检索检索哪个库要不要结合工具结果一起回答第三知识库单一。大型 Agent 系统基本不可能只有一个知识库。人事文档、产品手册、技术支持库、合同模板各是一套数据。基础 RAG 要么全塞一个库语义互相污染要么你手动指定。Agentic RAG 会让 Agent 根据问题内容自主路由到正确的知识库甚至多个库联合检索再融合。6.2 从“检索调用”到“工具调用”如果你把知识库检索封装成一个工具比如search_knowledge_base(query: str, category: str)RAG 就从一条固定管道变成了 Agent 可以按需调用的技能。这和调用天气 API、调用计算器在架构上没有本质区别——都是工具调用tool calling的一部分。我自己做 Agent 时习惯把“检索知识库”当成和其他工具并列的一个 function而不是把它特殊化。Agent 每次需要知识时自己决定调用哪个检索工具、传什么参数、根据结果决定是否还需要再查。这个变化会带来很好的可扩展性新知识库接入只是新增一个工具定义主流程不用改。下面是一个工具定义的最小示例JSON Schema 风格你可以把它接入支持 function calling 的 Agent 框架{ type: function, function: { name: search_hr_policy, description: Search the HR policy knowledge base for employee handbook, reimbursement, leave policies., parameters: { type: object, properties: { query: { type: string, description: The search query, e.g., 报销流程 }, category: { type: string, enum: [reimbursement, leave, onboarding, payroll], description: Optional category filter. } }, required: [query] } } }这个示例的精髓在于description写得足够清楚模型才能判断“该不该调用这个工具”。工具描述就相当于文档注释写得好不好直接决定模型用得对不对。6.3 RAG 与 Skill 的结合知识获取不只是“查资料”近几年业界的另一个趋势是把 RAG 和“技能Skill”拆开来理解。我自己的看法是Skill是 Agent 的“能力”它知道怎么执行一个任务比如写 SQL、做图表、调用 API。这更接近 agent 的“技能包”RAG是 Agent 的“记忆供给”它提供完成任务所需的背景知识这两者经常是配合关系。比如 Agent 要写一份项目总结报告技能它需要先查历史工单、查项目进度RAG再把这些材料组织成结构化文档。单纯把 RAG 当“查资料接口”很多有价值的场景是覆盖不到的但把 RAG 塞进 Agent 完成复杂任务的中间步骤价值就出来了任务拆解时Agent 通过 RAG 获取领域规则执行步骤时Agent 每步都用 RAG 校验自己用的参数和口径是否准确结果生成时Agent 通过 RAG 补充背景和案例这也是为什么“skill 和 rag 怎么结合”成了从业者社区里频繁被问起的话题。我的建议是先别急着搞复杂的结合把你现有的技能拆出几个“需要背景知识”的节点在这些节点上接入检索管道跑通后再想更大规模的编排。6.4 前瞻从 RAG 到知识图谱再到分类体系基础 RAG 有一个隐含问题向量相似度管不了复杂的逻辑关系。比如用户问“A 产品的升级会不会导致 B 模块不兼容”——这需要 A、B、C 之间的依赖关系纯向量索引很难表达。这时你可能会往“Ontology RAG”或“GraphRAG”方向扩展把实体和关系建进图谱检索时不仅找相似文本还顺着关系链找相关实体。这类方案威力很强工程复杂度也不低。但我不建议新手一上来就上图谱。先把基础 RAG 管道的每个环节做扎实、打准指标基线再逐步叠加知识图谱等进阶能力会更稳妥。因为图谱 RAG 的效果依赖实体抽取的准确性抽取质量不稳定时图谱中间件反而会把错误放大。7. 系列视角下的实践清单与常见问题排查最后把这一篇的内容收敛成一张可照做的清单再加上几个高频问题的排查路径。这个部分建议你收藏起来等真正动手搭 RAG 时对着操作。7.1 从零搭一个基础 RAG 的最小实践清单按这个顺序走不容易漏环节准备 10-20 篇领域文档先手工清洗成干净文本保留元数据标题、来源、日期、分类结构化切分chunk_size512、overlap50起步保留标题作为 metadata批量调用 embedding 接口把文本 向量 metadata 一起存入向量库建立测试集20-30 条问答对标注每条对应的期望块 ID搭一个最简检索函数向量检索 top_k5 起步跑一轮记录 hit5 / hit10加 BM25 混合检索比较指标是否提升加 rerank候选池取 20-50最终保留 3-5优化上下文组装顺序最重要的放最后按模板写提示词加引用要求加生成后的忠实度自检回到步骤 4扩大测试集到 50-100 条做一轮完整评估记录各项分数7.2 高频问题的快速排查表症状优先检查参考解法回答像在“猜”检索到的块是否与问题相关性足够加混合检索、加 rerank、检查测试集命中率回答总是缺要点问题是否包含多个子问题query 拆解、调大 top_k、增加(query 改写)回答有错误事实检索块本身是否权威、是否旧版本检查数据清洗、元数据过滤、版本去重检索很慢是否 filter 没建索引、候选集过大建字段索引缩小 top_k限制候选同一个话反复说上下文压缩不够、去重不生效增加去重压缩后再拼提示词引用对不上检索块 ID 与生成引用 ID 不同源统一 ID 体系用块原文做引用锚点7.3 一个容易忽略的工程提醒管道的可观测性最后说个工程上的心得RAG 管道一定要留日志。每一步干的事、用了什么参数、召回了哪些块、分别多少分全都记下来。没有日志你会在调优时陷入“刚才那次效果到底是改了提示词变好的还是换了 cut 参数变好的”这种纠结中无法自拔。我自己的做法是在每次试跑时顺便输出一个流水记录[query] 员工报销上限是多少 [rewrite] 不触发直接用原始 query [retrieve] hybrid: dense top10.81, bm25 top122.5 [rerank] top1: score0.95, chunk_idc123, source员工手册v3.md [context] chunks5, total_tokens2100 [answer] 员工市内交通报销上限为每日80元...有了这份记录你能很快看出是检索没召回、rerank 把正确答案排掉了还是上下文组装时被无关块干扰了。看起来笨拙但在实际项目中这比任何 fancy 的可视化工具都管用。7.4 基础篇的边界什么东西本篇没覆盖但你在真实项目会用到如果你准备在正式项目里落地 RAG以下几个方向本篇没有展开但迟早会碰到向量库选型与索引调优HNSW、IVF 等索引参数内存/磁盘平衡分片策略——这是存储层的大话题多路召回与复杂融合策略RRF 之外还有加权融合、基于学习的排序模型learning to rank增量更新与数据版本管理文档更新后如何只重索引变更部分而不是全量重建安全与权限知识库级、块级甚至行级权限控制防止低权限用户检索到高权限内容延迟与成本优化缓存、批量预计算、轻量 rerank 模型的选择这些话题我会在后续的系列里逐个展开。这一篇作为“走进 AI Agent”系列的 RAG 基础核心目标是把管道思维立起来摄取、存储、检索、增强、生成每个环节都值得单独打磨但每个环节都得受最终指标的约束。用我自己的话说RAG 这个技术看着简单真正把它做得“稳”而不是“能跑”需要你在每个环节都踩过坑、量过数、调过参。希望这篇能帮你少走几段弯路。下一篇我打算聊聊 Agent 的规划与记忆机制那是从“会查资料”走向“会自己干活的 Agent”的关键一步。