Agent外挂知识管道:RAG基础、分块与检索评估实战

发布时间:2026/9/30 5:39:49
Agent外挂知识管道:RAG基础、分块与检索评估实战 做Agent的都知道一个尴尬场景模型推理能力再强你问它一个私有文档里的细节或者一个训练截止日期之后的新消息它就开始用那种特别自信的语气编答案。你换更大的模型也解决不了因为问题根本不在推理能力而在“它没有那条知识管道”。所以从这篇开始我们把视角从模型内部挪到模型外部聊聊Agent怎么把外部知识接进来。RAGRetrieval-Augmented Generation检索增强生成正是这条知识获取管道的地基。这篇是“走进 AI Agent”系列的第四篇内容定位是RAG基础。我会先讲RAG在Agent架构里到底承担什么角色、边界在哪再手把手带你跑通一条最小可用的RAG管线然后是检索质量的量化方式Hit Rate、召回率这些到底怎么看最后聊Agentic RAG和图谱增强这两个进阶方向。整个过程中会穿插我实际做项目时踩过的坑尤其是中文场景下的那些坑。这系列前面的文章如果有小伙伴没看不影响这篇的阅读核心概念我会都解释一遍。但如果已经理解Agent的规划、工具调用这些基础这篇读起来会特别顺。1. Agent为什么要外挂知识RAG的定位与边界1.1 语言模型的“记忆天花板”与Agent的知识来源先抛一个反直觉的结论大模型不是你想象中的“百科全书”它更像是“把海量信息压进大脑参数的推理引擎”。的训练目标只是“预测下一个词”它的知识是隐式地锁在几百亿参数里的不是像数据库那样一条条能查的记录。这就带来三个固有毛病。第一参数化知识有截止时间训练数据之后发生的事它一概不知道。第二参数化记忆是“模糊记忆”它记得一个概念大概是什么样但记不住精确的数值、编号、专有名词的准确拼写哪怕见过多次也会混淆。第三幻觉问题当它被问到一个知识盲区时大脑机制会倾向于“编一个听起来合理的答案”而不是老老实实说我不知道。所以Agent想真正落地到业务里面对的一定是“最新数据 私有文档 领域知识”这种组合而这些恰恰是模型参数里缺失的部分。Agent的知识来源实际上有四条路预训练权重模型出生自带的通用知识覆盖广但不精确、不实时。微调把特定知识烙进权重代价高、更新慢适合风格迁移和高频行为对齐不太适合塞事实类知识。工具调用/函数调用适合“精确计算、实时查询、操作外部系统”比如查天气、算订单金额、调员工API。RAG检索增强适合“从大量非结构化文本中找到相关信息并让模型基于这些信息作答”。我自己的经验是这四条路不是互斥的而是一个Agent系统的不同闸门。RAG的核心价值是把“模型不知道的事实性问题”从参数记忆里剥离出来交给外部索引。模型的任务从“回忆你知道的”变成了“阅读我给你的材料然后推理”这其实是一个工作量上的巨大转移也是目前控制幻觉最有效的手段之一。1.2 RAG管道的基本结构离线索引与在线检索RAG这个名字听起来学术但拆开看就是一个很朴素的思想回答问题之前先去资料库里翻找相关资料把找出来的资料贴在上下文里再让模型作答。整个结构分成两半。一半是离线的知识索引管线把文档载入系统做解析切成合适大小的块chunk用embedding模型把每个块编码成向量然后把向量连同原文、元数据一起存进向量库。这个过程只需要在文档变更时执行。另一半是在线的检索问答管线收到用户问题后把问题编码成同样的向量空间里的查询向量去向量库里做相似度检索拿回Top-K个相关片段经过重排序拼装成上下文送给大模型生成答案。这个“先索引、后检索、再生成”的流程就是你在各种博客、框架文档里看到的经典RAG架构。虽然现在衍生出了很多变体比如Self-RAG、Agentic RAG、GraphRAG但核心的骨架没有变过。你可以把索引管线理解成“给所有知识文件建立了一个带语义坐标的地图”把检索管线理解成“用地图找到相关坐标把对应段落取回来交给读者”。我见过不少新手一上来就急着接LangChain的RetrievalQA把文档灌进去就开始问问题结果效果一塌糊涂。原因几乎都是把RAG当成一个“黑盒工具”而不是“一条可以逐环节调优的管道”。一旦你心里有这条管道的完整结构调试起来就会顺畅很多。1.3 RAG的适用边界什么场景别用RAG聊完RAG能做什么也说说它不擅长什么这个部分其实能帮你省很多时间。RAG擅长的是“非结构化文本的语义召回”典型场景包括企业知识库问答、产品文档助手、客服知识检索、合同审查辅助、科研文献综览。它本质上假设的是“答案可以被一段连续文本覆盖”所以问题越适合在文本里找答案效果越好。但有两种场景我基本不推荐硬上RAG。第一种是强结构化的精确查询比如“上个月华东区销售额多少”这种用自然语言转SQL、或者直接Function Call查数据库结果又准又稳定。你用RAG去召回“销售数据”相关的文档大概率召回一堆总结性的报表描述里面数字还不一定对。第二种是高频、时效性极强的操作类查询比如“帮我提交工单”“订这周日的会议室”这类应该走工具调用让Agent直接操作业务系统而不是在文档里翻操作指南然后假装执行它。还有一个我踩过的坑是“给RAG喂知识库时全量灌入、鱼龙混杂”。RAG检索器很难自动判断“哪份文档权威”如果你把销售周期PPT和财务SOP放在同一个索引里检索时它们会因为“都是公司文档”而被同时召回回答就会串味。所以在索引设计阶段就应该用元数据区分文档类型和权限范围甚至为不同业务域建不同的索引。2. 一条最小可用的RAG管线分块、向量化与召回实战2.1 文档加载与解析边界问题最多的起点很多人觉得RAG的第一个难点是向量检索但我实际做下来真正最耗时间的反而是最不起眼的文档解析。你永远不知道用户会给你什么样的PDF有的是文字层正常的“好文档”有的扫描件有的是PPT导出的读版式PDF还有带双栏排版的论文。我现在的处理习惯是这样的优先用pymupdffitz提取PDF文本层顺手把每页的标题层级也带出来对于扫描件接OCR服务但要注意OCR之后的文本是“扁平”的很容易丢失标题层级。Word文档用python-docx按段落结构提取尽量保留Heading层级这对后面的结构化分块特别重要。HTML和Markdown则简单一点转成纯文本后利用标签切分。文档解析的目标不是“把所有字变成字符串”而是“尽量保留语义结构”。我见过一个最典型的失败案例某PDF合同里的“违约责任”条款恰好被放在一个表格里用普通文本抽取直接丢了表格内容导致RAG对“违约责任”类问题的召回结果几乎为空。这种问题你在检索层怎么调参都救不回来只能在解析层解决。所以我给团队定的规矩是每接入一类新文档格式先做10个代表性样本的“解析到文本”人工检查看格式损不损失再看切块后的语义完整性确认没问题再全量灌入。2.2 分块策略检索效果的大半分块chunking是RAG管道里被低估最多、但影响最直接的一环。为什么要分块因为向量检索是一个“用稠密向量表示语义”的过程而embedding模型对输入长度极其敏感。你如果拿一整篇30页的合同去编码向量会被稀释成“这是一份合同”什么都检索不出来如果切成小块每块就能保留一个稳定主题检索时更容易命中具体信息。分块策略我按优先级排会是这样一个顺序按文档结构分块利用Markdown/PDF的标题层级把章节当作天然分块边界。这是最优解因为语义边界是文本自己的。固定大小窗口 重叠没有结构信息时按token或字符数固定切块并设置10%~15%的重叠区域避免语义断层。父子分块小块负责精确召回大块负责给模型足够上下文。这个策略改天单独聊但基础RAG里已经可以让你们先了解。我目前的默认配置是父块按标题层级得到的章节块子块512字符左右、带64字符重叠、按段落边界切分。索引里同时存父子两层结构检索时用子块匹配返回结果时用父块组装上下文。这样做的好处是精确匹配到具体段落但喂给模型的上下文带上章节全局信息回答质量会明显更自然。这里给一段父子分块思路的示意代码import hashlib def build_parent_child_chunks(text: str, heading_pattern, max_child_chars512, overlap64): 根据标题层级切父块再按段落与窗口切子块。简化演示版。 parent_chunks split_by_headings(text, heading_pattern) # 按结构得到父块 chunks_with_meta [] for parent in parent_chunks: doc_id hashlib.md5(parent.encode(utf-8)).hexdigest() sub_chunks [] start 0 while start len(parent): end min(start max_child_chars, len(parent)) sub parent[start:end] sub_chunks.append({text: sub, parent_id: doc_id}) start end - overlap if end len(parent) else len(parent) chunks_with_meta.append({doc_id: doc_id, text: parent, children: sub_chunks}) return chunks_with_meta这里我只是演示逻辑真正工程里还要处理很多细节比如如何避免把一句话从中间截断。我的经验是能按结构就按结构没有结构时至少要在段落边界处切块绝不硬按字符数从左往右切。因为切分边界处丢失的往往是关系最紧密的前后文。2.3 向量化选型中文场景怎么做Embedding分块之后要做embedding这是把文本变成向量的一步。核心问题只有一个选什么模型。对中文场景我实际测试过多个模型的检索效果结论是BGE系列的中文召回能力很扎实尤其是BGE-M3这种多语言模型它对中文、英文、代码混合的文档支持得很好阿里的text-embedding系列在中文文档类的语义匹配上也表现优秀。这些模型基本都支持把文本映射成1024维左右的向量并且有专门针对文本检索场景的“query指令”比如BGE的query指令在编码用户问题时需要加上这是一个很多新手会漏掉的细节。选向量模型的几个硬标准按优先级排领域适配性你的文档是法律合同、医疗报告还是代码文档拿一小批真实数据做召回测试比看榜单分数靠谱得多。文本长度支持输入上限至少512个token最好能到1024否则分块策略会受限制。维度与存储向量维度越高存储和计算成本越大。如果不差钱无脑选上游如果本地部署考虑轻量模型比如BGE-small这种。一个很常见的错误是只用API模型做embedding然后所有文本不管什么场景都直接用默认配置。实际上不同文档类型对向量模型的要求差别很大跑一批真实RAG评估集去对比模型是唯一可靠的方法。我在项目里通常会准备50~100条“问题期望命中的段落”的测试集然后对比2~3个embedding模型的Hit Rate谁高用谁。2.4 向量库选择与混合召回向量库是整个RAG的存储层。选择逻辑很简单我一般这样判断数据量在万级以下、开发验证阶段直接用FAISS。它只是一个内存索引库不是真正的服务但足以让你快速跑通流程。需要持久化、支持元数据过滤、并发查询选Qdrant或Milvus。场景里要求极低延迟、海量数据Milvus/Zilliz那套更重但也更能扛。我第二常用的是Qdrant因为它部署简单一个Docker就起来元数据过滤和Payload存储都很直观。下面是一个用FAISS做检索的示例方便你理解“向量检索”的本质import numpy as np import faiss def build_faiss_index(chunk_vectors: list[np.ndarray], dim: int): 把chunk向量灌入FAISS索引返回index对象。 arr np.vstack(chunk_vectors).astype(float32) index faiss.IndexFlatIP(dim) # 内积相似度等价余弦 index.add(arr) return index需要提醒的是向量检索只是RAG检索引擎的第一层也是基线层。长期使用的话我会建议在向量之路之外再叠加一层混合检索BM25关键词检索 向量语义检索然后合并打分。原因是向量检索对“同义改写”很友好但对“精确名词、编号、代码片段”的匹配不如BM25。比如用户问“服务器IP是啥”的精确IP地址向量检索往往召回“服务器列表”的段落BM25直接命中那个IP出现的段落。两条路合并召回再用重排序模型统一打分是目前工程上最稳的配置。重排序Reranking是召回质量的关键一环。通常用Cross-Encoder的rerank模型比如bge-reranker系列把“查询候选段落”成对输入模型输出相关性分数然后重新截取Top-K。这一步能让结果集的准确率上一个明显的台阶。我的建议是Top-50候选进入rerank截取Top-5喂给大模型这个配置在大多数场景下性价比最高。3. 检索质量怎么量化Hit Rate、召回率与失败诊断3.1 指标先行先定义什么是“好检索”很多RAG项目调了半天效果忽好忽坏原因就是没定义“什么是好”。你应该先有一套离线评估集再谈调参。检索评估里最有实用价值的指标是Hit Rate也叫Recallk对每一个评估查询我们人工标出“应该被召回的那个标准答案段落”然后看系统召回的Top-K里有没有包含它。如果有就算一次命中。Hit Rate 命中查询数 ÷ 总查询数是衡量“检索器到底有没有把答案捞出来”的第一指标。举例说明。假设我准备了50个查询每个查询标了它在知识库里的标准段落。设置K5系统运行一轮50个查询里有42个的标准段落出现在Top-5结果里那HitRate584%。这意味着还有8个查询是“即使答案在库里也捞不出来”属于检索器的硬伤无论后面生成模型多强都救不回。除了Hit Rate还有几个指标按需使用。Precisionk衡量Top-K里有多少是有用的MRR倒数排名衡量标准答案在结果里排得够不够靠前NDCG则考虑位置折扣适合理解“排序质量”。但作为日常迭代指标我建议只看两个HitRate5监控整体召回能力Top-1相关性抽样检查排序质量。3.2 检索失败诊断不看坏case等于白调指标只能告诉你“哪里不行”不能告诉你“为什么不行”。想要提高指标必须去看那些失败的坏case。我在实际项目里有一套固定的排查流程。第一步把失败的查询和实际召回的Top-K片段打出来人工看一遍。大概率会发现一类常见问题查询是“服务器如何扩容”知识库里相关的段落标题叫“资源扩容规范”词汇完全不同向量语义匹配失败。这种问题靠切分无法解决得走查询侧优化——比如加同义词扩展、query改写或者用混合检索把关键词召回补上。第二步对召回结果做相似度分数分布分析。如果所有候选片段的相似度都低于0.3大概率是分块粒度的问题块太大向量被稀释或者embedding模型与领域不匹配。如果分数普遍很高但命中内容不对那要怀疑分块切到了语义断层处两个话题被强行包进一个块。第三步回到索引侧检查。我遇到过一个很隐蔽的问题某文档的标题和正文被解析成了两个独立块而正文块丢掉了标题里的“附件四”导致检索时全部正文块都匹配上了“附件四”相关的查询但标题块排名很低答案自然不对。这种问题只能在解析层连带修复比如保留段落内的标题字段让它随正文块一起进入索引。这里特别提一个困扰很多人的问题为什么答案明明在文档里模型却总说“找不到”十次里八次是切分问题——标准答案跨了两个块的边界检索器把两块都召回了但你的上下文组装可能默认只取命中的第一个块第二个块没进去于是上下文缺少关键后半句。解决方法是父子分块或取Top-K后按原文档顺序拼接段落别只取单个碎片。4. 从单路到多路Agentic RAG与图谱增强路径4.1 Agentic RAGAgent不再只是搬运上下文基础RAG可以概括为“一个查询进去一批文档出来拼上下文生成答案”。这个模式的问题在于查询本身往往是复杂的、含糊的、多跳的。比如“帮我总结一下A项目的合同里关于违约责任和甲方付款义务的区别”这种查询本质上需要拆解成多个子问题、检索多个文档、跨越多个文档做推理。于是就有了Agentic RAG——把RAG检索器当成Agent手里的一个可调用工具由Agent自由决定“查几次、查什么、怎么组合结果”。相比基础RAG的固定管道Agentic RAG里多了一个“思考-检索-再思考”的控制循环Query策略Agent可以改写原查询、拆解成子查询、并行召回。检索决策Agent可以决定是“直接向量检索”“走混合检索”“先查数据库拿元数据再过滤文档”。多项证据的权衡当多份文档说法不一致时Agent能做交叉确认甚至调用别的工具来验证。自校验这里的思路很像Self-RAG生成前先判断“我有足够材料吗”推理后再判断“我的回答是否忠于材料”拿不准就再查一轮。你可以想象一个做法律调研的Agent收到问题后先自己拆成“合同条款检索”“判例检索”“法条检索”几个分支每个分支都走各自的RAG索引最后再综合成一份含引用的报告。这就是典型的Agentic RAG。在做项目时我的经验是不要一上来就做Agentic RAG。先把单路RAG跑通、把索引质量和指标建好再逐步放开控制权。因为没有可靠的“基准检索”Agent的每一步决策都是在沙地上盖楼。4.2 知识割裂怎么解GraphRAG与本体RAG的取舍基础RAG还有一个结构性问题业内常叫“知识割裂”——知识在物理上是分散的在逻辑上却是连着的。一个客户实体、一个项目、一条职责链它们的信息打散在各个文档的不同段落里向量检索只能按语义相似度找回“局部”很难跨文档把这些碎片串成一张完整的图。近两年的GraphRAG和本体RAG就是冲着这个问题去的。GraphRAG的做法是先让大模型从文档里抽取实体比如“甲项目部”“合同编号A-2024”、抽取关系甲项目部负责A-2024的履约”和属性存进图数据库再把图按社区的聚合关系做分层摘要。回答时先走图谱找到相关实体和多跳路径把这些“有结构的证据”结合原文一起交给大模型。本体RAG更进一步它不是让模型自由抽取实体关系而是先定义一套业务本体schema比如“合同”“项目”“风险条款”“负责人”等类型以及这些类型之间的约束关系然后让抽取过程往这套schema上靠。这个思路对垂直行业特别有价值因为业务的实体和关系本来就是有限的、明确的定义好本体之后检索结果可以严格对齐业务语义避免模型自由发挥。但是注意图谱不是银弹。我测过的小规模场景里如果把文档数量控制在几千篇且以单文档内问答为主GraphRAG的收益并不明显成本却高得多——你要维护抽取质量、图结构、更新机制。它真正发威的场景是跨文档多跳问答、强关联推理、需要全局概览与聚类总结的大型知识库。我给团队的建议是先看你的用户问题里“跨文档关联”的占比有多少低于三成不要上图谱先把向量混合检索做到极致。5. 工程落地盘点我踩过的坑和绕过的路5.1 增量更新比首次入库难十倍接RAG项目时最容易低估的不是首次建索引而是后续的增量更新和权限变化。文档是活的今天改了一版合同明天上线了一份新规后天某个部门的数据要全部下线。如果你的知识库不支持增量更新每次全量重灌成本和时间都会失控。几个实用的做法。第一每个chunk带版本号或更新时间字段重建时同一doc_id先逻辑标记为下线墓碑机制新版本灌入后通过元数据过滤只查有效版本。第二向量库的删除不是即时的要靠后台任务处理不要在业务查询路径上做全量删除。第三文档的下线比增加更重要被删文档的chunk如果不去掉下一次检索必然召回“过时资料”模型会一本正经地引用一个已经不存在的版本。5.2 上下文爆炸与引用可信度RAG项目到了生成阶段会遇到另一个坎上下文太长。Top-K每个块取512字符Top-5就是2500字符的上下文再叠加上System Prompt和对话历史模型很快吃满上下文窗口。我常用的降载方案有三层第一层召回后在rerank阶段严格截断比如只保留得分最高的Top-3每个块限制在400字符以内。第二层对长块做“上下文压缩”抽取出关键句子或摘要而不是整块塞进去。第三层对某些确定性高的场景直接用“引用列表”替代大段原文让模型基于引用的片段走推理。这个压缩层是现在RAG优化的蓝海做得好能让系统多扛好几轮对话。引用可信度是我特别想强调的一点。RAG不是万能的护身符模型仍然会幻觉尤其当召回了内容模糊的相关段落时。我在生产系统里强制要求模型的回答必须附带召回来源的文档ID和片段定位前端渲染成可点击的引用角标。一旦用户能点开引用验证你会惊讶地发现错误率直线下降——因为用户自己会交叉核对了而模型的压力也小了很多。5.3 中文场景的预处理细节和其他杂项中文RAG里一些看似小的细节对你的Hit Rate影响比想象中大得多。检查你的文本要不要做简繁体归一全角半角统一遇到“B2B”这种术语和“B2B平台”这类词的切分中文分词器的词表直接影响BM25召回一定要用领域词表做扩充再走关键词检索。另外一个被忽略的是Query侧的正则化。用户可能把“2024-08-01”写成“2024/8/1”“身份证号”里带不带空格这些在你要走精确匹配时会造成召回失败。建议在缓存检索结果和做评估前先统一规范化文本。5.4 评估体系建设建议先建评测集再优化这一篇反复提到“评估集”最后分享一下怎么低成本建立一个能持续用的RAG评测集。不用一开始就标几百条人工数据那样成本太高。我的做法是从线上日志里拿真实用户query挑出离线的种子问题集然后用一个较强的LLM对每个问题生成“答案摘要”再人工抽样纠正一批作为种子标注集。每个月再从新日志里抽一批新query加入。有了评测集优化顺序就清晰了先跑一遍现有链路算HitRate5然后看失败case归类原因改进某一段解析/分块/embedding/重排后再重算指标。这个闭环一旦跑起来后面几天你都能感受到效果稳定改善。我见过最快的一次仅靠把分块从“固定窗口”改成“按结构切块”HitRate5就从58%升到了79%效果极其明显而之前大家还在反复换模型。做个简单总结基于我自己的实操经验RAG这条管道的核心价值不是“炫技”而是给Agent一个可靠、可观测、可迭代的知识入口。无论接下来你准备尝试GraphRAG、Agentic RAG还是把它嵌入到某条业务流水线都绕不开基础的索引、召回、评估这三个环节。先把地基打稳进阶的路会顺很多。