1. 先搞清楚:Embedding RAG 到底卡在哪了
这两年做 RAG 的人越来越多,但真正把系统跑上线、跑稳定、跑出业务价值的人,聊到后面基本都会落到同一个问题上:Embedding 这一层,还值不值得继续投入精力去优化?
我自己的答案是:值得,但方向变了。早期大家做 RAG,思路很朴素——把文档切块、丢给 Embedding 模型、存进向量库、用户提问时做相似度检索、拼进 Prompt 让大模型回答。这套流程在 Demo 阶段效果惊艳,一旦进入真实业务场景,问题就集中爆发:召回不准、答非所问、相似但无关的内容被排在前面、专业术语检索不到、多义词混淆、长文档语义漂移。于是很多人开始怀疑是不是 Embedding 模型不行,换模型、换维度、换向量库,折腾一圈发现提升有限。
问题往往不在 Embedding 本身,而在于整个检索链路的设计。Embedding 只是召回环节的一个组件,它负责把文本映射到语义空间,但语义空间里的“近”不等于业务意义上的“相关”。这就是为什么现在讨论 RAG 优化,话题会自然延伸到 BM25、混合检索、重排模型、Agentic RAG、Ontology RAG 这些方向。单纯优化 Embedding 模型,收益已经进入边际递减区间;而优化检索架构和重排策略,收益往往更直接。
这篇文章面向的是已经跑过基础 RAG、正在被召回质量困扰、想系统性地把检索链路做扎实的开发者。我会从 Embedding 的定位讲起,拆解 BM25 与向量检索的互补关系、重排模型的引入时机、混合检索的工程实现、参数调优的计算逻辑,最后给出一套可以直接参考的优化路径。不堆概念,讲的是我实际踩过坑之后沉淀下来的判断。
2. Embedding 在 RAG 里的真实定位与能力边界
2.1 Embedding 到底解决了什么问题
Embedding 模型的核心作用,是把一段文本压缩成一个固定维度的稠密向量,使得语义相近的文本在向量空间中距离更近。这个能力解决的是语义泛化匹配问题。比如用户问“怎么退钱”,文档里写的是“申请退款流程”,两者字面不重叠,但 Embedding 能把它们拉到相近位置,传统关键词匹配做不到这一点。
但要注意,Embedding 的“语义相近”是统计意义上的,它来自训练数据中词语共现的模式。这意味着它对领域专有概念和细粒度区分往往力不从心。我做过一个测试,在一个法律知识库里,用户问“不可抗力条款的免责范围”,Embedding 检索出来的 Top5 里混进了“不可抗力定义”“不可抗力通知义务”这些相关但不直接回答问题的段落。它们语义相近,但业务相关性不够。
2.2 为什么单靠 Embedding 不够用
单靠 Embedding 做召回,有几个绕不开的硬伤。
第一是精确匹配失效。产品型号、错误码、人名、专有名词这类 token,Embedding 模型在训练时见得少,映射出来的向量区分度低。用户搜“ERR_4032”,向量检索可能返回一堆错误码相关的文档,但就是找不到那个精确的。BM25 在这种场景下反而稳得多,因为它直接基于词频和逆文档频率打分,精确 token 匹配是它的强项。
第二是多义词与上下文漂移。同一个词在不同段落里含义不同,Embedding 把它压成一个向量,信息损失不可避免。尤其是长文档切块后,块与块之间的语义边界模糊,检索时容易召回“看起来像但实际不对”的内容。
第三是相似度阈值的不可解释性。余弦相似度 0.82 和 0.79 到底差在哪?很难说清楚。业务方问“为什么这条没召回”,你没法用相似度分数给出有说服力的解释。而 BM25 的分数可以拆解到词级别,可解释性强得多。
2.3 Embedding 模型选型的现实考量
现在 Embedding 模型排行满天飞,MTEB 榜单上的分数差距看起来不大,但实际业务里的表现差异可能很明显。我的经验是,选型不要只看榜单,要看三个维度:领域适配度、维度与成本、推理延迟。
领域适配度方面,通用模型在通用语料上表现好,但垂直领域(医疗、法律、金融、工业)往往需要领域微调或者选用在该领域训练过的模型。我试过用通用模型直接跑工业设备手册的检索,召回率比经过领域适配的模型低了将近 20 个百分点。
维度与成本方面,高维度向量检索精度更高,但存储和计算成本也更高。768 维和 1536 维在实际业务中的召回差异,往往没有榜单上那么大,但存储成本差一倍。如果知识库规模在百万级以下,768 维通常够用;千万级以上再考虑更高维度,同时要评估向量库的索引类型和内存占用。
推理延迟方面,Embedding 模型要在查询时实时编码用户问题,延迟直接影响用户体验。大模型编码质量好但慢,小模型快但精度可能不够。实际部署时可以考虑查询侧用小模型、文档侧用大模型离线编码,这是一种常见的折中方案。
注意:Embedding 模型一旦确定,文档侧的向量需要全量重算。所以选型阶段一定要做充分的离线评测,不要上线后才发现要换模型,重算成本很高。
3. BM25 与向量检索:不是替代,是互补
3.1 BM25 的底层逻辑与适用场景
BM25 是一个基于概率检索模型的排序函数,核心思想是:一个词在当前文档中出现次数越多,文档越相关;但这个词在整个语料库中越常见,它的区分度越低,权重应该被压低。同时,BM25 还做了文档长度归一化,避免长文档因为词多而占便宜。
公式本身不复杂,但它的工程价值在于:对精确 token 匹配极其敏感,且完全可解释。用户搜“iPhone 15 Pro Max 电池容量”,BM25 会精确匹配这些词,把包含这些词的文档排前面。Embedding 可能因为“电池容量”和“续航时间”语义相近,把讲续航的文档也召回来,反而稀释了精确结果。
BM25 的弱点是词汇鸿沟。用户用词和文档用词不一致时,BM25 直接失效。比如用户问“怎么把图片变小”,文档写的是“图像压缩方法”,BM25 匹配不上。这正是 Embedding 的强项。
3.2 混合检索的工程实现方式
混合检索的思路是:同时跑 BM25 和向量检索,然后把两路结果融合。融合策略主要有两种:加权分数融合和倒数排名融合(RRF)。
加权分数融合需要把 BM25 分数和余弦相似度归一化到同一量纲,然后按权重相加。问题是两者的分数分布差异很大,归一化方式对结果影响显著。我试过 min-max 归一化和 z-score 归一化,在不同数据集上表现不稳定。
RRF 更稳健,它不依赖分数绝对值,只看排名。公式是:对每个文档,累加1 / (k + rank),其中 rank 是它在某一路检索中的排名,k 是平滑常数,通常取 60。RRF 的好处是无需调权重,对分数分布不敏感,工程上更省心。我现在的项目默认用 RRF,只有在明确知道两路检索质量差异很大时,才考虑加权融合。
def rrf_fusion(bm25_results, vector_results, k=60): scores = {} for rank, doc_id in enumerate(bm25_results): scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1) for rank, doc_id in enumerate(vector_results): scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1) return sorted(scores.items(), key=lambda x: x[1], reverse=True)这段代码可以直接用,bm25_results和vector_results是两路检索返回的文档 ID 列表,按相关性降序排列。RRF 融合后重新排序,取 TopN 进入下一阶段。
3.3 什么场景该偏向 BM25,什么场景该偏向向量
不是所有场景都需要混合检索。判断标准是查询类型分布。
如果用户查询里大量包含精确标识符(订单号、设备编号、错误码、产品型号、人名),BM25 的权重应该调高,甚至在某些查询上直接走 BM25 通道。如果用户查询是自然语言问句,语义泛化需求强,向量检索权重应该更高。
我一般会做一个查询分类器,简单规则就能覆盖大部分情况:查询里包含数字、大写字母组合、特殊符号的,偏向 BM25;查询是完整问句、包含“怎么”“为什么”“如何”这类词的,偏向向量。这个分类器不需要很复杂,正则加关键词表就能做到 80% 以上的准确率。
| 查询类型 | 示例 | 推荐策略 |
|---|---|---|
| 精确标识符 | ERR_4032、iPhone 15 Pro | BM25 优先 |
| 自然语言问句 | 怎么申请退款 | 向量优先 |
| 混合型 | iPhone 15 电池怎么保养 | 混合检索,RRF 融合 |
| 短关键词 | 退款流程 | 混合检索,BM25 权重略高 |
4. 重排模型:把召回结果真正用起来的关键一步
4.1 为什么召回之后还需要重排
召回阶段的目标是尽量不漏,所以会返回较多候选(比如 Top50 或 Top100)。但大模型上下文有限,不可能把 100 个片段全塞进去。这时候就需要重排模型,对候选做精细打分,挑出真正相关的 Top5 或 Top10。
召回和重排的区别,类似于海选和决赛。召回用轻量级方法快速筛出候选集,重排用更重的模型做精细判断。重排模型通常是 Cross-Encoder 结构,把查询和文档拼接后一起编码,能捕捉更细粒度的交互信息,精度比双塔式的 Embedding 高不少。代价是计算量大,没法对全库做,只能对召回候选做。
4.2 重排模型的引入时机与选型
重排不是必须的。如果召回质量已经很好,Top5 里基本都是相关结果,重排收益有限。但如果召回 Top20 里相关结果分散,重排能显著提升最终上下文的质量。
我的判断标准是:看召回 Top10 的准确率。如果 Top10 里相关结果少于 5 个,重排收益会很明显;如果 Top10 里相关结果超过 8 个,重排的边际收益就低了,可以考虑跳过以节省延迟。
重排模型选型上,开源方案里 BGE-Reranker 系列用得比较多,中文场景表现稳定。商业 API 里 Cohere Rerank 效果不错但成本要考虑。实际选型时,我会用业务数据做一个小规模评测,看重排后 Top3 的准确率提升幅度,以及单次重排的延迟。延迟超过 200ms 就要谨慎,因为这会直接叠加到用户等待时间上。
4.3 重排与 Embedding 的协同关系
重排模型和 Embedding 模型不是竞争关系,而是流水线上的两道工序。Embedding 负责从全库快速召回,重排负责对候选精细排序。优化 Embedding 提升的是召回上限,优化重排提升的是最终上下文质量。
有个常见误区是:Embedding 效果不好,就换个更大的重排模型来补救。这治标不治本。如果召回阶段就没把相关文档捞出来,重排再强也无能为力。正确的顺序是先把召回做扎实,再用重排做精排。
实操心得:重排模型的输入长度有限制,通常是 512 token。如果文档块切得太长,重排时会被截断,影响效果。所以切块策略要和重排模型的最大长度对齐,一般建议块大小控制在 256 到 512 token 之间。
5. 一套可落地的 RAG 检索优化实操路径
5.1 第一步:建立评测集,别凭感觉调优
优化之前必须先有评测集。没有评测集,所有调优都是盲调,今天觉得好了,明天换个查询又不行了。
评测集的构建方法是:从真实业务查询里采样 100 到 200 条,人工标注每条查询对应的相关文档。标注时不用追求完美,标出“直接相关”和“不相关”两档就够用。然后定义指标:召回率(相关文档有多少被召回)、MRR(第一个相关结果的排名倒数)、NDCG(考虑排序位置的综合指标)。
我一般用召回率和 MRR 作为主要指标。召回率看漏没漏,MRR 看排得准不准。这两个指标能覆盖大部分优化决策。
5.2 第二步:基线跑通,定位瓶颈在召回还是重排
先跑一个基线:纯向量检索,Top10 召回,不加重排。记录召回率和 MRR。然后加 BM25 做混合检索,再记录。最后加重排,再记录。
这样能清楚看到每个环节的增益。如果混合检索后召回率提升明显,说明 BM25 补上了精确匹配的短板。如果重排后 MRR 提升明显,说明召回候选里有相关结果但排序不对。如果两者提升都不大,问题可能在切块策略或 Embedding 模型本身。
5.3 第三步:切块策略的调整与验证
切块是 RAG 里最容易被忽视但影响很大的环节。块太大,语义混杂,检索精度下降;块太小,上下文不完整,大模型没法回答。
我的经验值是:中文场景下,块大小 300 到 500 字比较均衡。同时要加重叠窗口,一般重叠 50 到 100 字,避免关键信息被切在边界上。切块时优先按语义边界切(段落、标题、列表项),而不是固定字数硬切。
如果文档有层级结构(比如产品手册的章节),可以保留层级信息,在检索时把父级标题也拼进块内容里,增强语义完整性。这个技巧在实际项目里提升很明显,尤其是技术文档场景。
5.4 第四步:参数调优的计算逻辑
混合检索的 RRF 常数 k、重排的 TopN、召回路数,这些参数需要调,但不要瞎调。
RRF 的 k 值影响排名靠前文档的权重。k 越小,排名第一的文档优势越大;k 越大,排名差异被平滑。一般 k=60 是经验值,如果发现第一名和第二名分数差距太大,可以适当增大 k。
重排的 TopN 取决于大模型上下文窗口和延迟预算。如果上下文窗口是 4k token,每个块 500 token,最多放 8 个块,那重排 TopN 取 8 到 10 就够。取太多浪费重排算力,取太少可能漏掉相关结果。
召回路数方面,BM25 和向量各取 Top20 做融合,通常够用。如果知识库很大,可以各取 Top50,但要注意融合后的候选集大小和重排延迟。
| 参数 | 推荐值 | 调整依据 |
|---|---|---|
| 块大小 | 300-500 字 | 文档类型、重排模型最大长度 |
| 重叠窗口 | 50-100 字 | 避免边界信息丢失 |
| RRF k 值 | 60 | 排名分数平滑程度 |
| 召回路数 TopN | 20-50 | 知识库规模、延迟预算 |
| 重排 TopN | 5-10 | 大模型上下文窗口 |
5.5 第五步:上线后的持续监控与迭代
上线不是终点。要监控线上查询的召回情况,定期采样分析 bad case。我一般会做一个简单的日志系统,记录每次查询的召回结果、重排结果、用户反馈(如果有)。每周看一次 bad case,归类是召回问题、重排问题还是切块问题,然后针对性优化。
这个循环跑起来之后,RAG 系统的效果会持续提升,而不是上线即巅峰然后慢慢退化。
6. 常见问题与排查技巧实录
6.1 召回不准的典型排查路径
召回不准是最常见的问题。排查时按这个顺序走:先看查询本身有没有被正确编码,再看召回候选里有没有相关文档,最后看排序是否合理。
如果召回候选里根本没有相关文档,问题在召回阶段。检查 Embedding 模型是否适配领域、切块是否合理、BM25 是否该启用。如果召回候选里有相关文档但排名靠后,问题在排序阶段,加重排或者调整融合权重。
我遇到过一个案例:用户搜“设备离线怎么处理”,召回结果全是“设备上线流程”。查了半天发现是 Embedding 模型把“离线”和“上线”编码得太近,因为训练语料里这两个词经常一起出现。解决办法是在查询侧做同义词扩展,把“离线”扩展成“离线 断连 失联”,再检索,效果明显改善。
6.2 重排后效果反而变差的可能原因
重排模型不是万能的,有时候加了重排效果反而下降。常见原因有三个:重排模型和 Embedding 模型的领域不匹配、重排输入被截断、重排模型对某些查询类型不敏感。
领域不匹配的解决办法是换一个在业务领域数据上微调过的重排模型,或者用业务数据做少量微调。输入截断的解决办法是调整切块大小,确保块内容在重排模型最大长度内。查询类型不敏感的解决办法是分析哪些查询重排后变差,针对性调整。
6.3 知识库更新后的向量同步问题
知识库不是静态的,文档会增删改。每次更新都要同步向量库,否则检索结果会过时。同步策略有两种:全量重建和增量更新。
全量重建简单但慢,适合知识库规模小、更新频率低的场景。增量更新快但复杂,需要处理文档删除、修改时的向量删除和重新插入。我一般用增量更新,配合一个版本号机制,确保查询时用的是最新向量。
注意:向量库的删除操作在很多实现里是软删除,实际存储不会立即释放。如果频繁更新,要定期做 compaction,否则存储会膨胀。
6.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 | 解决思路 |
|---|---|---|---|
| 召回结果不相关 | Embedding 领域不适配 | 检查模型训练语料 | 换领域模型或微调 |
| 精确词搜不到 | 纯向量检索缺精确匹配 | 检查是否启用 BM25 | 加 BM25 混合检索 |
| 相关文档排名靠后 | 召回候选排序不佳 | 检查重排是否启用 | 加重排模型 |
| 重排后效果变差 | 重排模型领域不匹配 | 对比重排前后指标 | 换模型或微调 |
| 长文档检索效果差 | 切块过大或过小 | 检查块大小和重叠 | 调整切块策略 |
| 知识库更新后检索过时 | 向量未同步 | 检查更新流程 | 增量更新加版本控制 |
7. Agentic RAG 与 Ontology RAG:下一步的优化方向
7.1 Agentic RAG 解决的是什么问题
传统 RAG 是单轮检索:用户提问,检索一次,生成回答。但很多问题需要多步推理和多次检索。比如“对比 A 产品和 B 产品的退款政策差异”,需要先分别检索两个产品的退款政策,再对比。单轮检索很难一次召回所有需要的信息。
Agentic RAG 的思路是让模型自己决定什么时候检索、检索什么、检索几次。模型可以先检索 A 产品政策,再检索 B 产品政策,然后综合对比。这需要模型具备工具调用能力,能根据中间结果决定下一步动作。
这种模式适合复杂查询场景,但工程复杂度高,延迟也更高。我的建议是:如果业务查询以简单事实型为主,传统 RAG 加混合检索和重排已经够用;如果复杂对比、多跳推理类查询占比高,再考虑 Agentic RAG。
7.2 Ontology RAG 的价值与落地难度
Ontology RAG 是在检索时引入领域本体,用实体和关系来增强检索。比如医疗场景里,疾病、症状、药物之间有明确关系,Ontology RAG 可以利用这些关系做推理式检索,而不只是语义相似。
它的价值在于结构化知识增强,能解决纯文本检索难以处理的逻辑关系问题。但落地难度也大:需要先构建领域本体,这本身就是一个知识工程任务,成本高、周期长。我的判断是,只有在领域知识关系复杂、且业务价值足够高的场景才值得投入,比如医疗诊断、金融风控、工业故障排查。
7.3 本地知识库与轻量级方案的取舍
不是所有场景都需要重型 RAG 架构。如果是个人使用或小团队内部知识库,用 Ollama 加一个轻量级向量库就能跑起来,成本低、部署简单。LangChain4j 的 Easy RAG 这类方案也在降低门槛,适合快速验证。
但轻量级方案的局限也很明显:检索质量、并发能力、可维护性都不如生产级架构。我的建议是:验证阶段用轻量级方案快速跑通,确认业务价值后再逐步升级到混合检索加重排的生产级架构。不要一上来就上重型架构,也不要一直停留在 Demo 阶段。
8. 我个人的优化优先级建议
如果让我给一个优化顺序,我会这样排:先做评测集,再做混合检索,然后加重排,最后调切块和参数。
评测集是一切的基础,没有它所有优化都是盲目的。混合检索的投入产出比最高,BM25 加向量检索的组合能覆盖大部分查询类型,工程实现也不复杂。重排是第二优先级,能显著提升最终上下文质量,但要控制延迟。切块和参数调优是持续迭代的事情,不需要一次做到完美,上线后根据 bad case 逐步调整。
Embedding 模型本身的优化,我的看法是:除非领域适配问题非常突出,否则不要频繁换模型。换模型的成本高,收益往往不如优化检索架构来得直接。把精力放在混合检索、重排、切块这些环节,RAG 系统的整体效果提升会更明显。
最后分享一个小技巧:在检索结果拼进 Prompt 时,给每个片段加上来源标注和相关性分数,让大模型知道哪些片段更可信。这个细节在实际使用中能减少模型被低相关片段带偏的情况,尤其是当召回结果里混有噪声时,效果更明显。