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:块级自包含、实体数字结构化、可回滚投喂 + 引用监测 | 堆关键词、发错版本不可撤下、医疗金融内容不终审 |
RAG 工程实践:从管线、召回、重排到评测与门禁
https://lautung.com/archives/rag-engineering
评论