ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

双层RAG实战:结构化知识条目与原始切片协同检索方案

双层RAG实战:结构化知识条目与原始切片协同检索方案 1. 为什么单层向量库开始不够用了做过RAG检索增强生成的朋友大概都有过这种体验知识库刚上线时效果惊艳问什么都能答上来demo演示一遍过。但用着用着就发现不对劲了——用户问一个稍微需要归纳的问题比如“我们产品的退款政策分几种情况”系统检索回来一堆零散片段每段都沾点边但拼起来就是不成体系。模型拿着这些碎片去生成要么漏掉关键分支要么把不同场景的规则混在一起答得似是而非。这个问题的根源不在于Embedding模型不够强也不在于向量库选得不对而在于单层向量库的检索粒度天然是“碎片化”的。它擅长的是“找到和问题最相似的文本块”但它不理解“这些文本块之间是什么关系”。你问一个需要跨片段归纳的问题它只能给你一堆平行切片剩下的归纳工作全压在生成模型身上。生成模型上下文窗口有限注意力也有限碎片一多就开始丢信息。我最初做知识库问答时也是单层向量库一把梭后来被现实反复教育才慢慢摸索出“双层RAG”这套结构。所谓双层第一层是结构化知识条目第二层是原始切片。两者各司其职配合起来用效果比单纯堆向量库好一大截。这篇文章就把我这套方案的完整思路、实现细节、踩过的坑全部摊开讲清楚适合正在做RAG应用、被检索质量困扰的开发者参考。2. 双层RAG的整体设计思路拆解2.1 单层向量库的三个硬伤先说说为什么单层不够。我把实际项目中遇到的问题归成三类。第一类是归纳类问题失效。用户问“XX功能有哪些限制”正确答案可能分散在五六个文档段落里每段讲一个限制。单层检索按相似度召回可能只召回其中三段另外两段因为措辞和问题不够相似被漏掉。生成模型拿到三段就答三条用户以为只有三条实际上有六条。这种漏召回在单层结构里几乎无法根治因为你没法保证“所有相关片段”的相似度都排在前K。第二类是关系丢失。知识库里很多信息是有层级和关联的比如“A方案包含B、C两个子步骤B步骤依赖D前提”。单层切片把这些关系切断了检索回来的片段是孤立的模型看不到“B依赖D”这层关系生成的答案就可能给出一个缺少前提的操作步骤。第三类是噪声干扰。为了不漏召回很多人把TopK调大结果召回一堆弱相关片段。这些片段本身没错但和问题关系不大反而稀释了真正有用的信息模型容易被带偏。TopK调小又漏召回调大又引入噪声这个矛盾在单层结构里很难平衡。2.2 双层结构的分工逻辑双层RAG的核心思路是让结构化的归结构化让原始细节的归原始细节。第一层“结构化知识条目”是对原始知识的提炼和归纳。它把散落在多个切片里的信息提前整理成一条条独立、完整、自包含的知识条目。比如“退款政策”这个主题我会整理成一条结构化条目里面列清楚所有退款情形、每种情形的条件、所需材料、处理时效。这条条目本身就是一个完整的答案骨架。第二层“原始切片”保留原文的细节和上下文。当结构化条目命中后我再根据条目里关联的原文位置去第二层召回对应的原始切片补充具体措辞、示例、边界情况。打个比方结构化条目像是书的目录和章节摘要原始切片像是正文。用户问一个宏观问题先查目录定位到章节再翻正文看细节。单层向量库相当于只有正文没有目录用户问宏观问题你只能从正文里随机翻几页给他看。2.3 两层之间怎么关联两层不是孤立的中间需要建立映射关系。我的做法是每条结构化知识条目都带一个source_refs字段记录这条条目是从哪些原始切片归纳出来的存的是切片ID列表。检索时先命中结构化条目再通过source_refs去第二层拉取对应的原始切片。这个映射关系在构建阶段就要建立好。我通常的做法是先对原始文档做切片得到切片库然后人工或半自动地基于切片库归纳结构化条目归纳时记录用到了哪些切片。半自动的方式是用大模型辅助归纳让模型输出条目内容的同时输出引用的切片ID人工再校验一遍。注意结构化条目不要做得太细否则就退化成另一种切片了。一条条目应该对应一个相对完整的知识单元能独立回答一类问题。我一般控制一条条目覆盖3到10个原始切片的信息量。3. 结构化知识条目的构建实操3.1 条目该长什么样结构化条目不是随便写一段话它得有固定的结构方便检索和后续处理。我用的字段结构大概是这样{ id: kb_refund_001, title: 退款政策总览, category: 售后服务, summary: 涵盖全部退款情形、条件、材料与时效, content: 本产品支持三种退款情形..., keywords: [退款, 退货, 售后, 时效], source_refs: [chunk_1024, chunk_1025, chunk_1031], updated_at: 2025-01-15 }title和summary是给检索用的content是给生成用的source_refs是连接第二层的桥梁keywords辅助关键词检索。category用于分类过滤当知识库很大时可以先按类目缩小范围。这里有个经验summary要写得像“这个条目能回答什么问题”而不是“这个条目讲了什么”。前者更贴近用户的提问方式检索命中率更高。比如写“涵盖全部退款情形、条件、材料与时效”比写“退款政策说明”要好因为用户提问往往是“退款需要什么条件”“退款要多久”前者包含了这些提问词。3.2 归纳条目的操作流程我的归纳流程分四步。第一步是聚类。把原始切片按主题聚成若干簇。小知识库可以人工看大知识库用Embedding聚类加人工校验。聚类的目的是找出“哪些切片讲的是同一件事”这些切片就是一条结构化条目的素材。第二步是归纳。对每一簇切片让大模型生成一条结构化条目。Prompt里明确要求输出完整的知识单元覆盖簇内所有切片的关键信息不要遗漏分支情况同时输出引用的切片ID。模型输出后人工过一遍补漏纠错。第三步是去重合并。不同簇可能归纳出内容重叠的条目需要合并。合并时保留信息更全的那条把另一条的source_refs并进来。第四步是质量校验。逐条检查这条条目能不能独立回答一类问题有没有遗漏重要分支source_refs指向的切片是否真的支撑这条内容校验不通过的打回重做。这套流程听起来繁琐但知识库构建是一次性投入后续维护成本很低。我做过一个两千多切片的知识库归纳出约一百五十条结构化条目整个流程走下来大概两天之后检索质量提升非常明显。3.3 条目粒度的把控粒度是结构化条目最容易做错的地方。做太粗一条条目塞太多内容检索命中后生成模型要处理的信息量太大反而容易丢细节做太细条目退化成切片失去归纳价值。我的判断标准是一条条目对应一类问题。如果两类问题的答案有明显不同的结构就拆成两条如果两类问题只是措辞不同、答案结构一致就合并成一条。举个例子。“退款条件”和“退款时效”是两个不同的问题类型前者答案是条件列表后者答案是时间范围应该拆成两条。“个人退款条件”和“企业退款条件”如果条件结构一致只是主体不同可以合并成一条在content里分主体说明。粒度把控没有绝对标准我的经验是一条条目的content控制在200到500字之间比较合适。低于200字可能信息量不够独立成条高于500字可能该拆了。4. 原始切片的处理与两层协同检索4.1 切片策略的调整有了结构化条目兜底原始切片的策略可以更激进一些。单层结构时切片要尽量大生怕切碎了丢上下文双层结构下切片可以切得细一点因为宏观信息已经被结构化条目覆盖了切片只需要保留细节。我一般用语义切片而不是固定长度切片。按段落、按标题层级、按语义完整性来切每片控制在300到800字。切的时候保留一定的重叠避免边界信息丢失。重叠量我设的是切片长度的15%左右实测下来既能保住边界信息又不会引入太多冗余。每个切片除了文本内容还要存元数据所属文档、章节路径、前后切片ID。前后切片ID在检索时有用命中一个切片后可以顺带拉取它的前后邻居补充上下文。4.2 两层检索的触发逻辑检索时的流程是这样的用户提问先在第一层结构化条目库里检索召回Top3到Top5条目。判断召回条目的相关度。如果最高分低于阈值说明这个问题结构化条目覆盖不到直接走单层切片检索兜底。如果命中结构化条目取条目的source_refs去第二层拉取对应的原始切片。把结构化条目内容和原始切片一起塞进生成模型的上下文。这里的关键是阈值判断。不是所有问题都能被结构化条目覆盖有些细枝末节的问题只有原始切片里有。如果强行用结构化条目回答可能答非所问。所以需要一个兜底路径结构化条目相关度不够时退回单层切片检索。阈值怎么定我用的是余弦相似度阈值设在0.75左右。这个值需要根据你的Embedding模型和实际数据调。调的方法是拿一批测试问题跑一遍看结构化条目命中的准确率阈值调高准确率上升但覆盖率下降调低反之找一个平衡点。4.3 上下文组装顺序组装上下文时顺序很重要。我的做法是结构化条目在前原始切片在后。原因是生成模型对上下文开头和结尾的信息注意力更强中间容易忽略。结构化条目是答案骨架放前面让模型先建立整体框架原始切片是细节补充放后面让模型按需取用。如果反过来模型先看一堆碎片可能还没看到骨架就已经开始生成答案了。结构化条目和原始切片之间加一个分隔标记比如--- 以下是原始文档细节 ---让模型清楚哪部分是归纳、哪部分是原文。这个标记看起来不起眼但实测对生成质量有影响模型能更好地区分“该归纳的内容”和“该引用的内容”。5. Embedding模型选型与检索参数调优5.1 两层用不同的Embedding模型双层RAG的一个优势是两层可以用不同的Embedding模型各取所长。结构化条目层我倾向于用语义理解强、对短文本敏感的模型。因为条目本身是归纳过的短文本title和summary都很精炼需要模型能准确捕捉语义。这类模型通常在中英文语义相似度任务上表现好对同义改写、意图识别比较准。原始切片层我倾向于用长文本表征好、细节保留强的模型。切片是原文长度较长需要模型能处理长文本且不丢失细节信息。有些模型对长文本会做截断或池化导致细节丢失这类就不适合切片层。具体选哪个模型我不在这里给具体名字因为模型迭代太快给了也很快过时。选型方法是拿你自己的测试集把候选模型都跑一遍看检索命中率和最终生成质量。测试集要包含归纳类问题、细节类问题、边界问题覆盖真实使用场景。5.2 检索参数的计算与选择几个关键参数需要调TopK。结构化条目层TopK我设3到5因为条目总数不多TopK太大引入无关条目。切片层TopK我设5到10因为切片数量大需要多召回一些保证覆盖。但切片层TopK不是越大越好超过10之后噪声明显增加生成质量反而下降。相似度阈值。结构化条目层阈值0.75切片层阈值0.6。切片层阈值低一些因为切片是原文措辞和问题可能差异较大阈值太高会漏召回。重排序。如果检索质量还不够可以加一个重排序模型。先召回Top20再用重排序模型精排取Top5。重排序模型比Embedding模型慢但准。我一般在切片层加重排序结构化条目层不加因为条目层召回量本来就小。提示参数没有万能值一定要用你自己的数据调。我见过有人直接抄别人的TopK10结果他的知识库切片粒度不一样效果差很多。调参的投入是值得的检索质量提升一点生成质量提升一大截。5.3 混合检索的补充纯向量检索有个短板对精确匹配不敏感。用户问一个专有名词、一个编号、一个特定型号向量检索可能召回一堆语义相似但实体不对的片段。这时候需要关键词检索来补充。我的做法是向量检索和关键词检索并行各召回一批然后融合。融合用RRF倒数排名融合比较简单不需要调权重。两路各取Top10融合后取Top5。实测下来混合检索比纯向量检索在实体类问题上准确率高不少。结构化条目层也可以用混合检索但条目层的关键词字段是人工整理的质量比切片层的关键词抽取高所以关键词检索在条目层效果更好。6. 常见问题与排查技巧实录6.1 结构化条目命中率低怎么办这是最常见的问题。用户提问结构化条目层没召回相关条目走了兜底路径效果退回单层水平。排查思路分三步。第一步看条目的title和summary是否贴近用户提问方式。很多条目写得太“官方”用户提问太“口语”两者语义空间对不上。解决办法是把summary改写成用户可能问的问题形式或者给条目加同义问法。第二步看Embedding模型是否适合短文本。有些模型对长文本好对短文本反而弱。换一个短文本强的模型试试。第三步看条目数量是否太少。条目太少覆盖面不够很多问题自然命中不了。这时候需要补充条目把兜底路径里高频出现的问题归纳成新条目。6.2 生成答案还是漏信息命中了结构化条目但生成答案还是漏了分支。可能原因有两个。一是结构化条目本身就不全。归纳时漏了某些切片的信息。回去检查条目的source_refs看引用的切片是否覆盖了所有相关切片。如果漏了补充进去。二是原始切片没拉全。source_refs里的切片ID可能因为切片库更新而失效或者拉取时出了错。检查切片库和条目库的同步机制确保source_refs指向的切片都存在。三是上下文太长模型注意力不够。结构化条目加原始切片可能超过模型的舒适上下文长度。解决办法是精简原始切片只拉和问题最相关的几片而不是把source_refs里的全拉出来。6.3 两层更新不同步知识库更新时原始切片变了但结构化条目没跟着更新导致条目里的信息和切片对不上。这是双层结构特有的问题。我的解决办法是建立更新联动机制。切片库更新时记录哪些切片变了。然后检查这些切片被哪些结构化条目引用把这些条目标记为“待复核”。人工复核后决定是更新条目内容还是只更新source_refs。这个机制需要一点工程投入但值得。我见过有人没做联动切片更新了半年结构化条目还是老的检索出来的答案和原文对不上用户投诉才发现。6.4 常见问题速查表问题现象可能原因排查方向解决动作归纳类问题答不全结构化条目未命中看条目层召回分数优化summary措辞或补条目答案与原文不符条目与切片不同步比对条目content与source_refs建立更新联动机制实体类问题答错纯向量检索实体不敏感看召回片段实体是否匹配加关键词检索混合生成答案冗长上下文噪声多看召回片段相关度分布降TopK或加重排序边界问题答偏切片层召回不足看切片层召回分数降阈值或增TopK6.5 几个实操心得第一个心得结构化条目要定期复盘。用户提问分布会变半年前归纳的条目可能覆盖不了现在的高频问题。我一般每个月看一次兜底路径的日志把高频兜底问题归纳成新条目。第二个心得不要追求100%结构化覆盖。有些长尾问题就是没法归纳强行归纳反而让条目变得臃肿。留20%到30%的问题走兜底路径是正常的兜底路径的效果也不差只是比结构化路径稍弱。第三个心得条目content里保留原文关键措辞。归纳时容易把原文的具体措辞改成自己的话但有些措辞是用户会搜的关键词改了就搜不到了。我的做法是归纳时保留关键实体和术语的原词只改连接和表述。第四个心得两层检索的日志要分开记。哪层命中、命中分数多少、走了哪条路径这些日志对调优至关重要。我一开始没分开记出了问题根本不知道是哪层的锅后来分开记之后排查效率高很多。7. 什么场景适合上双层RAG双层RAG不是银弹它有适用场景。知识库规模小、问题类型单一的场景单层向量库就够了上双层是过度设计。知识库规模大、问题类型多、有大量归纳类问题的场景双层RAG的优势才明显。具体判断标准如果你的用户提问里有超过30%是“有哪些”“分几种”“整体上”这类需要归纳的问题单层结构大概率撑不住值得上双层。如果用户提问基本都是“XX是什么”“XX怎么做”这类点状问题单层结构够用。另外双层RAG的构建成本主要在结构化条目的归纳上。如果知识库更新频繁条目维护成本会比较高。更新不频繁的知识库更适合上双层。我自己在实际项目里的体会是双层RAG的投入产出比在知识库规模超过五百个切片之后开始显现。低于这个规模单层调调参数也能凑合超过这个规模单层的漏召回和噪声问题会越来越明显这时候双层的价值就体现出来了。最后再分享一个小技巧结构化条目的ID命名带上类目前缀比如kb_refund_001这样在日志里一眼就能看出命中的是哪类条目排查问题时省很多事。
返回列表