
做 RAG 应用的朋友大概率都经历过这个阶段一开始兴冲冲地把文档切块、灌进向量库、接上大模型Demo 跑得挺漂亮结果一上真实业务就露馅——用户问这个设备的保修期是多久检索回来的却是三段不相关的产品介绍问报销流程分几步模型答得头头是道但步骤顺序全乱了。问题往往不在模型而在检索这一层。我前后搭过七八套不同规模的 RAG 系统从个人知识库到企业级文档问答都趟过。踩坑踩到后来我逐渐收敛到一个相对稳定的架构思路双层 RAG——底层保留原始切片做语义召回上层再叠一层结构化知识条目做精确命中两层各司其职。这套东西听起来玄乎其实核心就一句话让该精确的地方精确该模糊的地方模糊。下面我把这套架构的来龙去脉、设计取舍、落地细节和踩过的坑完整拆一遍适合已经跑通过基础 RAG、但被召回质量折磨过的同学参考。1. 单层向量库到底卡在哪从三次翻车说起1.1 第一次翻车语义相似不等于答案正确最早我做的是一个内部制度问答库几百份文档切完大概两万多个 chunk用当时主流的 embedding 模型灌进向量库。测试阶段问年假怎么算召回的前三条全是讲考勤、讲调休的段落语义上确实像但没有一条真正回答了年假天数怎么计算。这就是单层向量检索的第一个硬伤它优化的是语义相似度不是答案相关性。embedding 把文本压成一个稠密向量相似度高的文本在语义空间里靠得近但讲年假和回答年假天数是两回事。前者是主题相关后者是事实命中。向量检索天生擅长前者天生不擅长后者。更麻烦的是当文档里存在大量主题相近但细节不同的内容时比如十个产品的说明书向量空间里它们挤成一团检索器根本分不清你要的是哪一个。你问 A 产品的参数它可能把 B 产品的段落排到前面因为两者语义太接近了。1.2 第二次翻车切片粒度是个死结第二个坑更隐蔽。切片大小这个参数我调了无数遍始终找不到一个万能值。切得太小比如 128 token单个 chunk 信息不完整模型拿到手拼不出完整答案切得太大比如 1024 token一个 chunk 里混了好几个主题向量被平均掉了检索精度直线下降。而且真实文档的结构千奇百怪——有的是一段话讲一件事有的是表格有的是分点列表用同一套切分规则硬套必然有一批 chunk 是畸形的。我试过按标题切、按段落切、按固定长度切、加滑动窗口重叠每种方案都能解决一部分问题又都会引入新的问题。本质上切片是在信息完整性和检索精度之间做取舍而单层架构逼你必须二选一。1.3 第三次翻车多跳问题和聚合问题无解最让我头疼的是这类问题对比 A 产品和 B 产品的续航差异、把这三份文档里提到的风险点汇总一下。这类问题需要跨多个 chunk 聚合信息单层向量检索只能召回一堆零散片段模型拿到后要么漏、要么编。到这里我彻底想明白了指望一层向量检索解决所有召回问题本身就是个错误假设。向量检索是个好工具但它只是工具箱里的一把螺丝刀不是万能钥匙。真正稳的架构应该是多种检索手段协同各管一段。2. 双层 RAG 的分工逻辑谁负责模糊谁负责精确2.1 两层各自解决什么问题双层 RAG 的核心思想是把召回这件事拆成两种性质完全不同的任务第一层原始切片 向量检索负责模糊的、开放的、语义层面的召回。用户问得含糊、口语化、或者问题本身就需要大范围语义匹配时这一层兜底。第二层结构化知识条目 精确检索负责明确的、事实性的、需要精准命中的召回。用户问的是具体参数、具体流程、具体数值时这一层直接命中。打个比方第一层像是图书馆里按主题找书你告诉管理员我想看点关于心理学的他给你抱来一堆相关的第二层像是查字典你问这个词什么意思他直接翻到那一页给你看。两种需求两种检索方式硬要用一种方式满足必然有一头是瘸的。2.2 结构化知识条目长什么样这里的关键是结构化知识条目到底怎么定义。我的做法是把文档里那些一问一答式的事实抽成独立的、带元数据的条目。一条结构化知识条目通常包含这几个字段字段说明示例主体这条知识讲的是谁/什么X100 型号设备属性/关系讲的是它的哪个方面保修期值具体内容整机 2 年核心部件 3 年来源出自哪份文档哪一段售后手册 v2.1 第 3 章置信度抽取时的可靠程度0.95有了这个结构用户问X100 保修多久系统可以直接在条目库里做精确匹配主体X100属性保修期秒中而且答案干净、可溯源。这比在向量库里大海捞针靠谱得多。2.3 为什么两层要并存而不是二选一有人会问既然结构化条目这么好用为什么不全部结构化把向量库扔了因为结构化抽取是有损的、有边界的。你能抽出来的是那些明确的事实但文档里大量的内容是描述性的、解释性的、需要上下文才能理解的这些抽不成条目只能靠原始切片兜底。而且抽取过程本身会引入误差漏抽、错抽在所难免这时候原始切片就是真相备份。反过来只留向量库的问题前面已经讲透了。所以两层不是替代关系是互补关系结构化条目负责高精度命中原始切片负责高召回兜底。一个管准一个管全。3. 结构化条目怎么抽从文档到条目的完整链路3.1 抽取策略规则优先模型兜底抽取这一步我的经验是不要一上来就全交给大模型。大模型抽取灵活但贵、慢、还不稳定同一份文档跑两遍结果可能不一样。更稳的做法是分层处理规则能搞定的用规则。比如表格类文档、字段明确的表单、带固定格式的 FAQ用正则或解析库直接抽又快又准。规则搞不定的用模型抽。比如大段叙述性文字里隐含的事实这时候上大模型配合结构化输出JSON schema 约束。模型抽完的做校验。用规则反查一遍比如数值范围是否合理、主体是否在文档里出现过过滤掉明显错误的条目。我实测下来纯规则能覆盖大概 40% 的高价值条目模型再补 40%剩下 20% 是那种模棱两可、抽不干净的宁可不要也别塞进去污染条目库。3.2 抽取时的 prompt 设计要点用模型抽取时prompt 有几个坑必须避开别让它自由发挥。明确告诉它只抽取文档中明确陈述的事实不要推理、不要补充常识。我早期没强调这点结果模型把一般设备保修都是两年这种常识也塞进来了污染严重。强制结构化输出。用 JSON schema 约束输出格式字段固定避免它一会儿输出列表一会儿输出段落。要求带原文出处。每条条目必须附上它来自原文的哪句话方便后续核对和溯源。分批处理。一次别喂太多文本长文档切成小段分别抽避免模型看花眼漏抽。一个简化版的抽取 prompt 大概长这样你是一个知识抽取器。请从下面的文本中抽取所有明确陈述的事实 每条事实包含主体、属性、值、原文依据。 要求 1. 只抽取文本中明确写出的内容禁止推理和补充。 2. 如果某条信息不完整跳过不要猜。 3. 输出 JSON 数组每个元素包含 subject、attribute、value、evidence 四个字段。 文本 {chunk_text}3.3 条目去重与冲突处理抽取出来的条目一定会遇到重复和冲突。同一件事在不同文档里说法不一样或者同一份文档里前后表述有出入这太常见了。我的处理原则是完全相同的条目合并来源字段记录多个出处。值冲突的条目都保留但标记冲突。检索时如果命中冲突条目把多个版本一起返回给模型让它根据上下文判断或者直接提示用户存在多个版本。新旧版本冲突按文档版本号或时间戳取新。这个要在元数据里带上版本信息。提示冲突处理千万别自作主张选一个对的尤其是在制度、参数这类场景选错了比不选还糟。把冲突暴露出来让下游决定。4. 检索层怎么协同路由、融合与重排4.1 查询路由先判断该走哪条路两层都建好了接下来的问题是用户一个问题进来该走结构化检索还是向量检索我的做法是加一个轻量的查询分类器判断查询类型事实型查询问具体参数、数值、定义→ 优先走结构化条目检索。开放型查询问怎么做为什么介绍一下→ 优先走向量检索。混合型查询→ 两条路都走结果融合。分类器不用太复杂一个小模型甚至规则匹配就能干。比如查询里出现多少几天什么型号这类词大概率是事实型出现怎么如何为什么大概率是开放型。当然更稳的是训一个小分类模型或者直接用大模型做 zero-shot 分类成本也不高。4.2 结果融合怎么把两路结果拼一起两路检索各自返回一批结果后需要融合成一个统一的上下文喂给模型。这里有几个策略加权拼接结构化条目优先级高排前面向量切片排后面兜底。简单粗暴但有效。去重合并如果结构化条目和某个切片讲的是同一件事去掉冗余的切片避免上下文重复。按相关性重排用一个重排模型rerank对合并后的结果统一打分排序取 top-k。我一般用加权拼接 重排的组合先按来源给个基础分结构化条目基础分高再用 rerank 模型精排。实测下来这套组合比单纯向量检索的命中率能提升一大截。4.3 重排这一步别省很多人为了省事检索完直接取 top-k 就喂给模型了。我强烈建议加一步重排。原因很简单向量检索的相似度分数和这段内容对回答这个问题有没有用是两码事。重排模型cross-encoder 类能同时看查询和候选判断相关性更准。重排的代价是慢一点但换来的是上下文质量的大幅提升。在双层架构里重排尤其重要因为它能把两路来源的结果拉到同一个标准下比较。5. 落地时的参数与工程细节5.1 切片策略双层架构下的新思路有了结构化条目兜底原始切片的切法可以更放飞一点。我的建议是切片可以适当放大比如 512 到 800 token因为精确命中交给条目层了切片层只需要保证语义完整、上下文够用。保留结构信息。切片时把标题、章节号、页码这些元数据带上检索时能作为过滤条件也能在答案里做溯源。重叠窗口别太大。10% 到 15% 就够了太大反而引入冗余。5.2 向量库和条目库的选型向量库这块选择很多我的经验是场景推荐方案理由个人/小规模本地向量库 轻量 embedding部署简单成本低中等规模主流向量数据库支持过滤、混合检索大规模/企业分布式向量库 独立条目存储可扩展支持复杂查询条目库其实用普通的关系库或文档库就够了因为条目是结构化的查询也简单按主体属性查不需要向量能力。别为了统一技术栈硬把条目也塞进向量库那是自找麻烦。5.3 embedding 模型的选择embedding 模型直接决定向量检索的质量。选型时看几个点中文场景优先选中文优化过的模型通用多语言模型在中文上往往不如专门优化的。维度不是越高越好。高维度检索慢、存储贵很多场景 768 维就够用了。关注模型的最大输入长度要和你切片大小匹配切 800 token 结果模型只支持 512那就白切了。注意换 embedding 模型意味着整个向量库要重建所以选型阶段多花点时间测试别上线了才后悔。6. 那些文档不会告诉你的坑6.1 条目抽取的过度自信问题模型抽取条目时特别容易过度自信——文档里没明说的它给你推理出来还标成事实。我踩过最狠的一次是它把建议每 6 个月保养一次抽成了必须每 6 个月保养一次建议变强制性质完全变了。对策就是前面说的强制要求带原文依据并且做规则校验。凡是 evidence 字段对不上原文的条目一律打回。宁可少抽不可错抽。6.2 两层结果打架怎么办有时候结构化条目和向量切片给出的信息是矛盾的。比如条目说保修 2 年但某个切片里写着保修 3 年可能是旧版本。这时候别慌把两个都返回让模型在生成时说明存在不同说法或者直接触发人工复核。永远不要在下游悄悄选一个那是在埋雷。6.3 维护成本被严重低估双层架构比单层多了一套条目库维护成本是实打实增加的。文档更新了条目要跟着更新条目和切片要保证同步不然会出现切片更新了条目还是旧的这种尴尬。我的做法是把条目抽取做成文档入库流程的一部分文档一更新自动触发重新抽取而不是靠人工维护。同时给条目加版本和有效期字段过期条目自动降权。6.4 别指望一次调好这套架构的参数切片大小、召回数量、融合权重、重排阈值没有一组标准答案必须结合你的数据反复调。我一般会准备一个几十到上百条的评测集每次调参跑一遍看命中率和答案质量的变化。没有评测集的调参就是瞎调。7. 什么场景值得上双层什么场景别折腾7.1 值得上的场景文档里有大量明确事实产品参数、制度条款、操作步骤、数值指标。这类内容抽成条目收益最大。查询以事实型为主用户主要问是什么多少怎么做而不是聊聊介绍一下。对答案准确性要求高比如客服、合规、医疗辅助这类场景答错代价大值得为精度投入。7.2 别折腾的场景纯开放型问答用户就是想让模型基于文档自由发挥那单层向量检索加个好点的重排就够了。文档量很小几百个 chunk 的规模单层完全够用上双层是杀鸡用牛刀。文档结构极不规范全是散文、访谈记录这种抽不出干净条目硬抽只会污染库。我的判断标准很简单如果你的痛点主要是召回不准、答非所问且文档里有可抽取的事实那就值得上双层如果痛点是模型不会组织语言那是生成层的问题跟检索架构无关。8. 一个最小可跑的验证思路想验证双层架构对你有没有用不用一上来就搞全套。可以先用最小成本跑个对照实验拿你现有的单层 RAG准备 50 条真实查询记录命中率和答案质量。从文档里手工抽 100 条结构化条目先手工验证价值再自动化建个简单的条目表。加一个查询路由事实型查询先查条目表查不到再走向量。同样的 50 条查询再跑一遍对比结果。如果命中率有明显提升再投入做自动化抽取和融合重排。这个验证过程一两天就能跑完比直接上全套架构试错成本低得多。我自己第一次做这个对照实验时事实型查询的命中率从六成出头提到了九成以上那一刻就知道这条路走对了。当然开放型查询的提升没那么明显这也符合预期——两层架构本来就是冲着精确命中去的。后续如果要把这套东西做扎实重点会落在条目抽取的自动化质量控制和两层融合的权重调优上这两块是最花时间也最见功力的地方。