RAG(检索增强生成)最难的地方从来不是「把向量库接上 LLM」这一行代码,而是让「用户问的」能稳定命中「知识里该被引用的那一段」,并且这条链路能随文档更新、随业务扩张而不塌。大多数失败的 RAG 项目,问题都出在检索环节:分块切坏了、召回只走了语义路漏了编号、融合时把两路分数直接相加、上线后没有任何量化指标,最后靠感觉调温度。

本文按一条阅读线把这些主题串起来:先定管线 → 选好 embedding 地基 → 多路召回补语义与字面的短板 → 用 RRF/MMR 融合重排 → 选对向量存储 → 必要时上知识图谱 → 用指标量化 → 用人工门禁守住质量底线 → 顺带看 GEO 与工程化工具;每节都配「三个容易踩的坑」清单,文末附一张速查表。


一、先把管线定下来:文档从进来到可检索的路径

RAG 的基础软件设计不是先选模型,而是先把文档「从进来到可检索」的路径定下来。管线一般是:接入与去重、解析与清洗、分块与向量化、入库与版本、检索与重排。去重要在文件哈希和文本正则化之后做,否则同一份 PDF 改名会被当两份知识。清洗要去掉页眉页脚、目录、水印,保留表格与标题层级。设计里还要把「谁可以看哪份文档」和「这次检索用哪个索引快照」写进元数据。

三个容易踩的坑

  • 去重只看文件名文件去重用内容哈希加来源路径,不要只看文件名。重传时要能覆盖旧块而不是叠一份,否则改名后的同一份文档会被当成两份知识。
  • 清洗分块一刀切。清洗和分块要按文档类型分支:手册按标题、FAQ 按问答、代码按函数。统一切 512 字会把表格拆破。
  • 入库不带版本。入库要带版本号与租户。检索时过滤掉过期块,评估时才能对齐「这一版知识」。

什么时候用:从零搭问答系统时,先把这条管线写成服务而不是 notebook。文档数量少且不变时可先手工清洗;一旦要定期同步网盘和 Confluence,就必须把去重与清洗当成正式作业。不要在清洗还没稳定前就上级 rerank 和多路召回——地基没稳就堆上层,问题会全埋在检索里。


二、语义检索的地基:embedding 模型怎么选

Embedding 模型把文本变成固定维向量,让语义相近的句子在空间里靠近。RAG 的检索、去重、聚类都依赖它。选型看语言、最大长度、维度和能否离线部署。通用榜单只能当参考,必须在自己的问答集上测召回。查询和文档如果分布差很大,要用非对称模型或把查询也写成「文档风格」。维度高占存储,量化能换空间但可能掉点。

三个容易踩的坑

  • 两套空间混用查询和文档用同一套模型与同一套预处理。混用两个空间等于随机检索。
  • 只信榜单名次。在业务集上测 Hit@k,不要只看 MTEB 名次。中文语料优先中文模型。
  • 换模型不重编码。更新模型等于换空间,必须全量重编码。灰度时新旧索引不能混查。

什么时候用:语义检索、相似推荐、去重都要 embedding。关键词精确匹配仍要 BM25。数据不能出境就本地部署。换模型前先算重编码时间和存储,避免线上空窗


三、多路召回:语义路 + 字面路并行,别只走向量

多路召回是 RAG 里同时走几条检索路径,再把结果合并。常见组合是向量召回加 BM25 关键词,再加上标题、FAQ 精确匹配或图谱邻接。单一向量检索在专有名词、编号、条款号上容易漏;纯关键词又理解不了同义改写。多路之后要用 RRF 或加权融合,而不是简单拼接前几条。每路都要能单独评估,否则不知道是哪路在拖后腿。

这一路里的「字面路」通常就是 BM25:它是经典的词袋检索公式,用词频、逆文档频率和文档长度归一化来给查询打分。它不理解语义,但对专有名词、错误码和几乎不改写的问法非常稳,在 RAG 里常作为多路召回的字面通路,补向量检索的漏召回。中文要先分词或用 n-gram 分析器,否则单字 IDF 会乱。调参主要是 k1 控制词频饱和,b 控制长度惩罚。

三个容易踩的坑

  • 只走语义路漏编号。至少准备语义路和字面路。产品型号、错误码、制度文号必须能被字面命中。
  • 每路只取 3 条就合并。各路召回数量要够,融合后再截断。不要每路只取 3 条就合并。
  • 过滤条件某路没生效。过滤条件(租户、时效、权限)要在每一路都生效,避免某路把不该看的文档漏进来。

BM25 的额外坑

  • 分词与业务语料不匹配。分词与同义词表要和业务语料匹配,产品名、型号加入自定义词典。
  • 纯正文 BM25 被长文档淹没。字段加权:标题、条款号的权重应高于正文。
  • 和向量路直接比原始分。与向量路融合时不要比较原始分数,用 RRF 或分别归一化。

什么时候用:知识类型混杂、既有口语问题又有精确编号时,上多路召回。纯闲聊或极小 FAQ 可以只走向量。融合前先看单路 Hit@k,确认两条路真的互补,再引入第三路。制度、手册、工单号、日志错误码这类查询必须有 BM25;纯语义闲聊可以不上。语料经常改写、同义很多时,BM25 只做辅路,且索引要随文档更新重建或增量,过期文档要能删除。


四、融合与重排:RRF 排位融合 + MMR 保多样性

多路召回后要把名单合起来。RRF(Reciprocal Rank Fusion)按排名倒数给分数:1/(k+rank),再把多路名单上的同一文档累加。它不依赖各路分数是否可比,因此很适合向量分和 BM25 分混在一起的场景。k 常用 60,用来压低排名很靠后的噪声。RRF 不管原文相似度,只看相对名次,所以某一路整体分数偏高也不会霸榜。实现时要用稳定的文档 ID 对齐同一条结果。

重排阶段的另一个工具是 MMR(Maximal Marginal Relevance),它解决「前几条都在讲同一段」的问题。MMR 在相关性和多样性之间做权衡:先选与查询最像的一块,再选与查询像、但与已选块不那么像的下一块。λ 靠近 1 更看相关性,靠近 0 更看多样性。知识库里同一文档被切成很多重叠块时,不用 MMR 就会把上下文窗口塞满重复段落。

RRF 的三个容易踩的坑

  • 先没去重就对齐。先在各路内部去重,再用文档 ID 对齐。切块级融合时要对齐到父文档再打分。
  • k 值随手定。k 值过小会让第一名权重过大;过大则各名次差距变平。用评估集扫一遍再定。
  • 把它当最终相关性模型。RRF 之后仍可接一个交叉编码器做精排。它是融合器,不是最终相关性模型。

MMR 的三个容易踩的坑

  • 只在 top-5 里换顺序。先召回一个较大的候选集(比如 30 条),再对前 k 做 MMR,而不是只在 top-5 里换顺序。
  • λ 不调。多样性用已选块的向量相似度来衡量。λ 要按任务调:事实问答偏高,综述生成偏低。
  • 用 MMR 替代过滤。MMR 不能替代过滤。权限、时效和文档类型仍要在检索层先裁。

什么时候用:两路以上召回且分数量纲不同时,优先 RRF;已经有校准好的统一打分模型时,可以直接加权。不要对同一路内部用 RRF,上线后观察是否某一路的长尾总被压掉,必要时给该路加权重。MMR 适合手册、政策、长 PDF 被切得很碎、生成时容易复读同一条款时;短 FAQ 每条本来就独立,直接 top-k 即可。不要把它当成提高召回率的手段;召回差要先改分块和 embedding


五、向量存储选型:Milvus 与 Chroma 怎么分

向量库是 RAG 的落地底座,但两者定位差别很大,选错会要么过度工程、要么撑不住规模。

Milvus 是面向向量检索的数据库,把 collection、partition、index 和标量过滤放在一起。它适合大规模 embedding 存储与 ANN 查询,而不是当普通业务库。写入时要设计好主键、分区键和标量字段(租户、文档 ID、时间),检索才能过滤。索引常见 HNSW 与 IVF 系列,要在召回率和内存之间取舍。运维上要关心 segment 合并、磁盘索引加载和副本。

Chroma 是轻量向量数据库,适合本地原型和中小规模 RAG。它把 collection、embedding 函数和元数据过滤放在很薄的一层里,开发体验接近「给文本加个可搜的盒子」。生产上要关心持久化路径、并发写入和备份,不要默认它能扛百万级 QPS。元数据过滤能力够日常租户隔离,复杂混合检索可能要迁 Qdrant 或 Milvus。embedding 函数要固定,换模型必须重建。

Milvus 的三个容易踩的坑

  • schema 后补。schema 先定:向量维度、度量方式、标量过滤字段。维度错了不能混插。
  • 全表扫描再丢弃。按租户或业务线分区,过滤条件能下推。不要每次全表扫描再在应用层丢弃。
  • 索引参数不压测。索引参数要压测。nprobe、ef 过小会漏召回,过大延迟飙升。删除用 compaction 真正回收。

Chroma 的三个容易踩的坑

  • 中途改维度/度量。collection 的维度和距离度量一开始就定死。中途变更等于新库。
  • ID 让库自动生成。ID 用文档版本拼出来,方便更新时覆盖。不要让库自动生成后再对不上源文件。
  • 持久化不备份。持久化目录要进备份。容器销毁会把本地库一起带走。

什么时候用:向量过百万、要混合过滤和水平扩展时选 Milvus;几千条向量用 Chroma 或内存库即可。不要把原文大字段全塞进 Milvus,文本放对象存储,库里只留引用。单机 Demo、内部知识库、数据量不大时用 Chroma 起步,要水平扩展、量化磁盘索引时再换库。不要和业务事务库混在同一套备份策略里却从不演练恢复,上线前用真实过滤条件测延迟(Milvus 用真实查询测召回而非只看 QPS)。


六、知识图谱路线:用图数据库做多跳检索

用图数据库搭 RAG 知识图谱,是把实体和关系显式存下来,检索时既能向量找相似,又能沿边做多跳。适合制度里的「部门-职责-系统」、设备故障的「症状-原因-备件」。图库常见 Neo4j、Nebula、JanusGraph,选型看数据规模、是否要分布式和 Cypher 生态。图谱质量取决于抽取:实体对齐差,后续推理全错。图 RAG 不是把每句话都变成节点,而是只对稳定的领域对象建模。

三个容易踩的坑

  • 没有本体就抽取。先定本体:有哪些实体类型和关系,再跑抽取。没有 schema 的图谱会迅速变成垃圾堆。
  • 只向量不扩边。检索混合:向量召回候选实体,再按关系扩展子图,把子图文本送给模型。
  • 只追加不失效。更新要同步边。文档改版时,旧三元组要失效,不能只追加。

什么时候用:问题经常是「A 和 C 通过什么关联」或需要全局汇总时,上图谱。纯条款检索用向量加 BM25 即可。实施成本高,先用一个小领域验证抽取准确率,再扩大。不要用图库替代全文检索


七、量化评估:把「感觉还行」变成可复现的数字

RAG 量化评估是把「感觉回答还行」变成可复现的数字。离线侧常用检索的 Hit@k、MRR,以及生成侧的忠实度、答案相关性、上下文精度。线上侧看点踩率、追问率、转人工率和空答率。没有固定问答集和标注标准,量化就是空转。评估要分层:检索差就先别调生成温度;生成胡编就先查是否召回不足。

这里绕不开三个核心指标——Hit@k、Recall@k 和 pass@k,它们都是「前 k 个结果里有没有对的」这类指标,但分母和判定不同。Hit@k 问的是:至少命中一条相关文档就算成功,常用来看检索有没有把答案所在块捞上来。Recall@k 看的是相关文档被找回的比例,相关集合大于 1 时比 Hit 更严。pass@k 多用于代码生成:k 次独立采样里有一次能通过测试就算过。混用这三个词会让评估集无法对比。

评估流程的三个容易踩的坑

  • 没有冻结黄金集。建一个冻结的黄金集。每次改分块或模型都跑同一批题,才能说变好了。
  • 检索和生成指标混看。检索指标和生成指标分开看。只看 BLEU 类分数会掩盖「答得顺但没依据」。
  • 线上指标不能下钻。线上指标要能下钻到文档和意图。某一类制度问答踩得多,就单独加样本。

指标使用的三个容易踩的坑

  • 「相关」没定义。先定义「相关」:人工标的金块、还是能支撑答案的任意块。标准不统一,指标就不可复现。
  • k 和线上不一致。k 要与线上一致。线上只给模型 5 个 chunk,就报 Hit@5,不要用 Hit@50 自我安慰。
  • pass@k 参数漂移。pass@k 要固定温度和采样次数。代码题用单次 greedy 的 pass@1,和温度 0.8 的 pass@10 不是一回事。

什么时候用:准备上线或每次改检索策略时必须量化。Demo 阶段可以先人工看十个例子。不要用模型给自己打的分数当唯一验收,评估流水线要进 CI,避免只在笔记本里跑一次。检索评估用 Hit@k 和 Recall@k;Agent 写代码、做工具调用用 pass@k,生成忠实度不要用这三项替代。评估集要定期更新,避免模型背下旧题;报告时同时给出样本量,小样本的 Hit@k 波动很大。


八、数据标注与人工质量门禁(Human-in-the-loop)

RAG 的数据标注与人工质量门禁(Human-in-the-loop)是把「能不能入库、能不能上线回答」从模型手中拿回来。标注对象通常是问答对、检索是否命中、答案是否忠实、以及文档切片是否切坏。审核流应发生在两个点:文档进入索引前的内容审核,以及生成结果对外展示前的抽检。没有门禁的知识库会把过期制度、内部草稿和错误表格一起喂给模型。

三个容易踩的坑

  • 标注规范不可执行。标注规范要可执行:相关/不相关、忠实/编造、过期/有效。每个样本至少双人抽检争议项。
  • 高风险文档自动入库。高风险文档走人工放行。制度、价格、医疗法务类默认待审,不能自动入库。
  • 抽检不回流。线上抽检要回流。用户点踩的样本进入标注队列,修切片或改答案后再评估。

什么时候用:知识库面向员工或客户、答案会被当真时,必须上门禁。内部 Demo 可以先跳过。不要用模型给自己打满分代替人工。门禁规则要写进流水线,而不是靠某个人偶尔看看后台


九、GEO:让内容被生成式引擎引用

GEO(Generative Engine Optimization)面向「生成式引擎优化」:让内容更容易被大模型检索、引用和摘要,而不是只为传统搜索排名。系统通常包括选题与备料、结构化文章生成、引用与实体标注、以及向站点或知识库的投喂。和 SEO 不同,要优化的是块级可引用性:清晰的断言、可核对的数字、稳定的标题层级。还要监测模型是否引用了你的页面、引用是否正确。没有监测的 GEO 只是批量写稿。

三个容易踩的坑

  • 内容块不自包含。内容块要自包含。每一节都能单独被模型摘走而不丢失主体和出处。
  • 实体数字不结构化。实体、数字、更新日期要结构化。模型更爱引用能核对的句子。
  • 投喂后不抽检。投喂后要抽检:问一组题,看是否命中你的页面、有没有被歪曲。

补充的三个判断

  • 扩写优先于蒸馏。蒸馏优先于扩写。把一手事实变成短断言,再展开,避免空话占窗口。
  • 投喂渠道不可回滚。投喂渠道要可回滚。发错版本必须能从索引撤下。
  • 只看浏览量。用一组固定问题监测引用率与歪曲率,而不是只看浏览量。

什么时候用:品牌希望出现在 AI 问答引用里、或要把站点同步进知识库时,做 GEO 管线。内部文档治理优先于对外灌水。不要用生成内容堆关键词。合规上要标明 AI 辅助,避免把未核实数字送进索引;涉及医疗金融的内容必须人工终审后再投喂。


十、supervision:视觉标注流水线与「模型输出 / 业务规则」分离

注:supervision 是计算机视觉落地工具,与 RAG 检索链路本身没有直接关系;但它体现的工程思路——把「模型输出」和「业务规则」拆开、用配置驱动、记录模型版本以利复现——和上面 RAG 管线的设计原则一致,这里一并列出供参考。

supervision 是面向计算机视觉落地的 Python 工具库,用来把检测、分割、跟踪的结果变成可标注、可统计的流水线。它不训练网络,而是接在 YOLO、Grounding DINO 这类模型后面:画框、统计进出人数、做多边形区域内过滤、把检测结果写成数据集。对现场项目来说,它把「模型输出」和「业务规则」拆开了。

常用流程是读视频、跑推理、用 ByteTrack 跟踪、再按区域计数。多边形区域和类别过滤要写进配置,而不是写死在脚本里。输出视频只适合演示,线上更该写 JSON 或消息。注意抽帧率和跟踪 ID 抖动,漏检会导致计数跳变。把它和模型版本一起记录,复现某次误报才找得到当时的阈值和区域。


附:RAG 工程速查表

场景 用什么 不要用什么
文档要定期同步、量大且会变 先定「接入→去重→清洗→分块→入库带版本」的管线服务 只在 notebook 里随手跑、靠文件名去重
语义检索、相似推荐、去重 业务集上测过的 embedding 模型(中文优先中文模型) 只看 MTEB 榜单名次、查询与文档用两套模型
既有口语问题又有型号/错误码/文号 多路召回:向量路 + BM25 字面路 只走向量检索(会漏编号)、纯关键词(不理解同义)
两路以上、分数量纲不同 RRF 融合(k≈60),后可接交叉编码器精排 直接把各路原始分相加、对同一路内部套 RRF
长文档切很碎、生成复读同条款 MMR 提升多样性(λ 按任务调) 用 MMR 当提高召回的手段、用它替代权限/时效过滤
向量过百万、要混合过滤和水平扩展 Milvus(按租户分区、压测索引参数) 把原文大字段全塞库里、不备份就上线
单机 Demo / 内部知识库 / 数据量小 Chroma 起步 默认它能扛百万级 QPS、和业务事务库混备份不演练恢复
需要「A 通过什么关联 C」或全局汇总 图数据库知识图谱(先定本体再抽取) 用图库替代全文检索、把每句话都变节点
上线前 / 每次改检索策略 冻结黄金集 + 检索指标(Hit@k/Recall@k) 与生成指标分开看,进 CI 用模型给自己打的分当唯一验收、只报 Hit@50 自我安慰
答案会被员工/客户当真 人工质量门禁(入库前审核 + 展示前抽检 + 点踩回流) 模型自动打满分代替人工、门禁只靠人偶尔看后台
希望品牌出现在 AI 答案引用里 GEO:块级自包含、实体数字结构化、可回滚投喂 + 引用监测 堆关键词、发错版本不可撤下、医疗金融内容不终审