Article / 检索与 RAG
混合检索与 RAG 工程
从 CitedRAG 的真实流水线讲清切分、dense 与 BM25、RRF、重排、父扩展与向量库选型。
大模型知道很多事,但不知道你的文档里写了什么,也不保证说的对。RAG(Retrieval-Augmented Generation,检索增强生成)用一次检索把外部资料放进上下文,让模型基于证据回答,实现起来几乎全是工程问题。检索质量决定了回答质量的上限,混合检索是提升这个上限最有效的手段之一,CitedRAG 的整条流水线正好可以做案例。
LLM 有三个绕不开的限制:训练数据有截止日期;看不到私有文档;在没有依据时也可能生成流畅的错误内容。微调能改变风格与能力,但「随时更新的事实性知识」不是它的强项。RAG 把问题拆成两步:检索器先从文档库里找到相关证据,生成器再依据证据作答,答案的上限因此由检索决定。
一个常见的误解是 RAG 只是提示词技巧。实际的 RAG 系统里,提示词只是最后一环:切分决定引用能不能落到页码,索引决定召回的上限,融合与重排决定进入上下文的证据质量,校验链路决定回答是否可信。CitedRAG 的约束更严格:每个关键结论必须带编号角标,证据不足或引用无法验证时必须拒答,而不是给出一个看起来合理但没法核对的答案。
切分与索引
引用能不能指向准确页码,不取决于生成模型,而取决于切分时有没有把页面信息绑定到每个片段上。CitedRAG 的每个 chunk 在切分阶段就带上来源、页码与页内序号;PDF 路径进一步保留章节与父块 id,图注单独成为一类片段,并附带同一页邻近正文的上下文。这种「页码在切分时固化」的契约,让前端跳转原文时不需要反查 PDF,也不需要模型在生成后回忆页码。
切分粒度直接影响检索精度。片段太大,语义被稀释、向量不聚焦;太小,一句话离开上下文就失去意义。项目最初用 1200 字符,评测发现学术文档的检索精确度不理想,降到 800 字符后明显改善——这个数字来自评测集上的对照结果。相邻片段保留一定重叠,避免答案恰好被切在边界上。

上图是系统的就绪检查:API、模型提供方、文档与索引四项全部就绪,才允许提问。就绪检查之外,回答质量由「解析—切分—索引—检索」这条流水线决定,其中每一步都有常见问题。
索引工程的常见问题大多与「看不见的状态」有关。最早每上传一篇文档就全量重建索引,上传 N 篇的复杂度是平方级;改成增量段后,新文档写成独立小索引,删除按墓碑标记,只有段数量达到阈值才压实合并。嵌入模型的批处理也有陷阱:句子向量模型会把一个 batch 补齐到最长样本,一段九千字符的表格会把同批几十个短片段一起拖慢,按长度分桶编码后时间显著下降。索引发布则必须是原子的:构建在临时目录完成,校验向量数与元数据行数一致后用目录改名发布,失败时回滚快照——索引与数据库一旦不一致,引用就会指向不存在的内容。
dense 与 BM25 的互补
dense 检索把查询和文档都编码成向量,用余弦相似度找语义相近的片段。它的强项是「换个说法也能找到」:问题问「怎么降低幻觉」,文档里写的是「减少无依据生成」,向量空间里两者依然接近。但 dense 的弱点是精确匹配——模型编号、缩写、公式、数字,这些在向量空间里往往不够敏感。
BM25 是经典的关键词相关性算法:一个词在当前片段中出现得越多、在整个语料里越稀有,得分越高。它不理解语义,却恰好补上 dense 的短板,查一个冷门缩写或者「99.8%」这样的数字时,词法通道几乎是唯一可靠的路。混合检索(hybrid retrieval)把两条路都跑一遍再合并,利用的就是这种互补。
工程实现上,词法通道从内存版 BM25 换成了 SQLite FTS5 全文索引:旧方案要常驻全量词表并逐条打分,查询延迟和内存都随语料线性增长;FTS5 把索引放在磁盘上,只对命中的行打分。
SELECT chunk_id, bm25(chunk_fts, 1.5, 0.75) AS score
FROM chunk_fts WHERE chunk_fts MATCH ? ORDER BY score LIMIT ?;
中文分词两边共用同一套规则:英文与数字按词切,中文同时产出单字与相邻双字组合,停用词刻意保留,因为 BM25 的 IDF 会自动压低常见词权重,查询侧再删反而让两个词表对不上。一条词法索引,入库与查询共用,是小系统里保证一致性的省力做法。
融合与重排
两路检索的结果不能把分数直接相加:向量相似度和 BM25 分数量纲不同,相加等于让某个通道的噪声主导结果。RRF(Reciprocal Rank Fusion,倒数排名融合)只使用名次:每个片段在每个通道里的得分是 1 / (k + rank),多路结果累加。它不需要训练、不需要调参,对分数尺度不敏感,是混合检索的常用做法。项目里还叠加了章节权重——摘要、方法、实验章节略微加权,参考文献明显降权——因为「哪一章更可能包含答案」是稳定的先验。
RRF 的分数是排名信号,没有绝对阈值语义。后续所有需要判断「这条证据够不够好」的环节,都要知道当前的分数是哪种尺度,否则阈值会失效。这个约束直接影响了评测口径与纠错逻辑的设计。
第二阶段是 CrossEncoder 重排。dense 检索用双塔编码,查询与文档分别编码、速度快但交互浅;CrossEncoder 把查询和候选拼在一起过一遍模型,逐对打分,慢得多但准确得多。所以流水线分两阶段:先用双通道召回几十条候选,再让重排器精选。重排输出经 sigmoid 校准成 0 到 1 之间的置信度,低于 0.40 的弱证据被丢弃。这里有一个刻意保留的例外:如果所有候选都低于阈值,保留原第一名——给一条弱证据比整题无依据更好,拒答与否交给后面的引用校验链决定,而不是由重排阈值单独决定。
重排之后还有 MMR(Maximal Marginal Relevance,最大边际相关性)做多样性:如果几条候选是同一段话的近似重复,它们挤占的是上下文预算而不是增加信息量。MMR 在相关性与新信息之间取折中,把近重复替换成覆盖不同事实点的证据。
父扩展、去重与纠错
检索到的片段不会原样送进生成器,中间有一层确定性的上下文工程,其中一个关键步骤是父文档扩展:切分时同一章节的片段共享父块 id,检索命中某个小片段后,系统把该章节的相邻片段按阅读顺序拼回来,上限约 1200 字符,且命中片段排在最前——检索用小块保持精确,生成用大块补全语义,也就是常说的 small-to-big。回填会带来高重叠,于是还有一层去重:文本相似度超过阈值的片段只留一条;然后按「中间遗忘」规律重排,把最强证据放在上下文首尾。全程不调用 LLM,纯粹是确定性规则。
CRAG-lite 是证据不足时的本地纠错:完整版 CRAG 会在检索质量差时发起网络搜索,而本地系统限定证据只能来自用户文档,所以只做「扩大候选、放宽过滤、换查询重试」这类本地动作,评分器也是确定性的——看证据条数与最高分,刻意不花额外模型调用。由于 RRF、sigmoid 概率、余弦相似度是三种不同尺度,判分阈值必须按尺度分别设定,其中一种尺度直接视为排名信号通过。纠错之后仍然不足,就由引用校验链路兜底拒答。
查询理解有多个可选组件——多查询改写、元数据自过滤、跨语言改写——默认全部关闭,理由都来自评测:中文到英文的学术检索词改写被实测证明「方差大、没有净增益且每题多花一到两秒」,于是从默认链路里移除,只留给需要的人按需开启。生成端消费这些证据的方式——谁在什么时机调用检索、证据如何进入回答预算——属于编排层的问题。
用评测决定参数
检索系统的参数很多,可行的做法是让评测集决定。CitedRAG 用 50 道题的金标集(45 道可答加 5 道应拒答)跑矩阵评测:固定语料,逐个开关混合检索、重排、父扩展、多查询、CRAG 等变体,比较来源命中率、页码命中率与答案通过率。混合检索加两阶段重排与父扩展的组合在可答题上通过 41/45,来源命中达到 100%,页码命中 93%。一些参数是评测直接改出来的:检索条数从 5 提到 8,通过数从 40 升到 41;MMR 的折中系数从 0.75 调到 0.88,从 38 升到 40。
评测口径本身也要小心。同一份报告里「41/45」与另一种模式的「44/50」分母不同,前者排除了拒答题,后者把拒答题按合理拒答计入,直接比较没有意义;拒答判定在 RRF 尺度上甚至不参与评分,因为倒数排名之和没有绝对阈值。把分数当设计约束的前提,是先搞清楚每个数字在度量什么。
向量库选型与适用边界
向量库的选型经常被过度讨论,其实先问四个问题就够:语料多大、并发多少、要不要和业务数据做事务、团队愿意维护多少组件。FAISS 是向量检索库,不是服务:索引是本地文件,性能好、依赖少,但元数据管理、持久化、并发控制都得自己做,适合单机规模。pgvector 是 PostgreSQL 的向量扩展,向量和业务数据同库同事务,运维最省心,适合已经在用 Postgres、规模中等的团队。Elasticsearch 是搜索引擎,天然带 BM25,向量检索后来补齐,混合检索体验最顺,代价是集群运维。Milvus 是专用分布式向量数据库,面向大规模、多租户与云原生场景,能力最强,组件也最多。单机几万到几十万片段的规模,FAISS 文件加一份清单和原子发布就足够支撑——项目正是这么做的,同时预留了 Postgres 后端的位置。
反过来,有些场景不需要 RAG。语料小到能直接放进上下文时,做索引是多余的;知识稳定且不超出模型能力时,检索只会引入噪声;任务本质是计算或操作而不是查询知识时,更适合用工具而不是检索;当语料无法覆盖问题、而系统又必须给出答案时,RAG 也无法解决——那种场景需要的是换数据或明确拒答,而不是调大检索条数。
最后是几个常见的检索质量陷阱:只用向量检索、丢掉词法通道,数字与缩写会系统性漏召回;不做重排,让相似度直接决定证据质量;跨尺度复用阈值,RRF 上有效的数字搬到 sigmoid 概率上就失效;没有评测集,参数靠感觉;索引与数据库不做一致性设计,删除文档后引用指向孤儿片段;上下文里塞满近重复片段,浪费预算还稀释注意力。混合检索与 RAG 工程是测量驱动的系统工程:先用指标把问题定义清楚,再让每一步都有可验证的收益。