# RAG效果不好怎么调?6张图讲透相似度·意图识别·召回排序·ReRank·混合检索(附代码)
📢 本文是「108张AI知识卡片·大模型通关手册」系列第 6 篇。上一篇把 RAG 核心链路(切→嵌→存→检→答)从 0 串通了,但"跑通"和"好用"之间隔着一条鸿沟——检索不准、排序不对、相似度选错、意图没识别……这篇专门讲"调优":6 个旋钮,每个都能让你的 RAG 效果提一个档次。每张卡都配了能动手感受的方式,看完你就知道自己的 RAG 该拧哪个旋钮。
目录
TL;DR 太长不看
一、相似度计算:选错"尺子",检索结果差30%
二、意图识别:用户说的话≠用户想要的东西
三、召回:第一道关卡,宁多勿漏
四、ReRank重排序:召回只是粗筛,精排才定胜负
五、排序:不止ReRank,排序的完整逻辑链路
六、混合检索:关键词+语义双剑合璧
七、一张图串起RAG调优链路
八、代码:加上ReRank和混合检索的RAG
写到最后
系列导航 & 持续更新
TL;DR 太长不看
⚡30 秒版:先记这 7 条,细节往下翻。
- 🔴相似度计算:余弦看方向、欧氏看距离、点积看方向+长度——选错"尺子",Top-K 排序直接变脸。
- 🟠意图识别:用户说"退货"可能是问政策、问流程、问时效——先分桶再检索,精准度翻倍。
- 🟡召回:第一道关卡,宽宽地捞,宁多勿漏,K 太小漏答案、K 太大炸噪声。
- 🟢ReRank:召回只是粗筛,交叉编码器把问题和文档拼在一起打分,精排才定胜负。
- 🔵排序:不止 ReRank,还叠加新鲜度、权威性、交互特征——“像不像"之外还要看"该不该”。
- 🟣混合检索:关键词擅长精确匹配、语义擅长同义匹配,双剑合璧互补短板。
- 🎁调优口诀:意图识别精准分流 → 相似度夯实底座 → 召回宽捞 → 排序精筛 → ReRank 定榜 → 混合检索兜底。
一、相似度计算:选错"尺子",检索结果差30%
同样的向量、同样的 Top-K,换个相似度公式,检索结果能差 30%。你以为是 Embedding 模型的锅,其实是你选错了"尺子"。
相似度计算解决的核心问题是:两个向量"像不像",用什么数学标准来量?三把尺子摆在你面前——余弦相似度(看方向)、欧氏距离(看直线距离)、点积(方向+长度)。选哪把,直接决定 Top-K 里谁排第一。
它到底在干嘛(机制层):三种公式的数学本质不同。余弦相似度只看两个向量夹角的 cos 值,不管向量多长,归一化后范围 [−1, 1],语义检索最常用——它回答"方向像不像"。欧氏距离算两个点之间的直线距离,对向量长度敏感,长度差异大的向量即使方向一致也可能排得远——它回答"位置近不近"。点积= 余弦 × 模长,兼顾方向和大小,某些场景下比纯余弦好——它回答"方向像不像且力道够不够"。
你能感受到什么(体感层):归一化后的向量,余弦和点积等价。但没归一化时,点积会让长向量(信息量大的长文档)天然得分高——有时这是你想要的(长文档更全面),有时不是(一条短但精准的答案被压下去)。欧氏距离则对向量模长极其敏感,两个方向完全一致但长度不同的向量,欧氏距离可能很远,余弦却判定"一模一样"。
| 公式 | 算什么 | 对长度敏感 | 典型场景 |
|---|---|---|---|
| 余弦相似度 | 夹角 cos | 否(归一化后) | 语义检索默认选择 |
| 欧氏距离 | 直线距离 | 是 | 聚类、空间分布分析 |
| 点积 | cos × 模长 | 是 | 向量已归一化时等价余弦;未归一化时偏好长向量 |
🎛️ 动手感受:同一批向量换3把尺子
操作:同一批文档向量 + 同一个查询,分别用余弦/欧氏/点积算 Top-5,看排序差异。
你会看到:
- 余弦:短文档和长文档公平竞争,纯看"意思像不像"。
- 点积:长文档排名集体上升——因为它"力道大"。
- 欧氏距离:向量模长差异大的文档排名剧烈抖动。
- 变化说明了什么:不是模型不准,是你选的尺子不对——换一把,结果就变了脸。
🤔 想一想
如果余弦相似度最通用,为什么有些框架默认用点积?什么场景下点积反而比余弦好?提示:当你希望"信息量大的文档天然排前面"时,点积的"偏心"反而是优势。
🔗 顺着他想:相似度只管"像不像",不管"对不对"——两段意思相近但事实矛盾的文档,相似度都很高。那"相关性"和"正确性"之间的鸿沟,靠什么填?
二、意图识别:用户说的话≠用户想要的东西
你问 RAG"退货",它给你捞了 5 段退货条款——可你其实想问的是"退货流程怎么走"。检索没毛病,是你没告诉它你到底想问什么。
意图识别解决的核心问题:用户说的话和用户想要的东西,经常不是一回事。它把用户输入分到不同的"意图桶"里,每个桶对应不同的检索策略——问政策走关键词精确匹配,问流程走语义向量检索,闲聊直接 LLM 生成不检索。
它到底在干嘛(机制层):意图识别本质上是个分类器,把用户输入映射到预定义的意图类别。常见做法分三档:关键词规则匹配("退货政策"命中"政策"关键词→走政策桶,简单粗暴但覆盖不全)、小模型分类(BERT 微调,准确率高但要标注数据)、大模型零样本分类(直接让 LLM 判断意图,零标注但延迟高)。分类结果决定后续检索策略——FAQ 类走关键词精确匹配、知识类走语义向量检索、闲聊类直接生成不检索、多跳推理类需要拆子问题分步检索。
你能感受到什么(体感层):同一个"退货","退货政策是什么"→检索政策文档,"怎么申请退货"→检索流程文档,"退货要多久"→检索时效文档。不分类,三段全召回,模型被信息淹没,回答又长又乱。分了类,每类只召回最精准的 3-5 条,回答干净利落。
🎛️ 动手感受:开/关意图识别对比
操作:输入同一个模糊问题(如"退货"),开/关意图识别,看检索结果和最终回答的区别。
你会看到:
- 不开意图识别:检索出政策、流程、时效三类文档混在一起,回答东一榔头西一棒。
- 开了意图识别:系统先判断你问的是"流程",只检索流程文档,回答精准。
- 变化说明了什么:同样的问题、同样的向量库,“先分桶再检索"比"一股脑全搜"精准得多——意图识别是检索的"导航仪”。
🤔 想一想
如果用户的问题横跨两个意图呢?“退货政策和退货流程有什么区别”——这种混合意图,分类器怎么处理?强制选一个桶会丢信息,选两个桶又可能召回太多。多意图场景,是意图识别的天花板。
🔗 顺着他想:意图识别做错了比不做更糟——把"怎么申请退货"误判为"退货政策",检索方向全错,比"不分桶全搜"还惨。那什么时候该信任分类器、什么时候该兜底?这引出了"置信度阈值"的概念。
三、召回:第一道关卡,宁多勿漏
RAG 检索不准?80% 的人第一反应是换 Embedding 模型——但真正的锅可能在召回策略。Top-K 设 5 和设 50,结果天差地别。
召回解决的核心问题:从海量文档里宽宽地捞一批候选,宁多勿漏。它不追求精准,追求的是"别把答案漏掉"——精准是排序环节的事。
它到底在干嘛(机制层):召回策略决定"捞多少、怎么捞"。向量召回(ANN 近似最近邻)是主流,按相似度取 Top-K。K 太小→漏答案(正确文档排在第 8 位,你只取 Top-5 就丢了);K 太大→噪声多、排序压力大(取 Top-200,其中 190 条是噪音,排序模型要在垃圾堆里找金子)。实际生产中 K 通常设 20-100,再交给排序环节精筛。更进阶的做法是多路召回:向量召回 + 关键词召回 + 知识图谱召回,三路互补,每路捞一批,合并后交给排序——单路漏掉的,另一路补上。
你能感受到什么(体感层):问"退货政策",K=5 可能只召回 3 条相关政策(另外 2 条是噪音);K=50 能召回全部 8 条相关政策,但也混进 42 条无关的。排序环节要从 50 条里挑出那 8 条——比从 5 条里猜要靠谱得多。这就是"宽捞"的哲学:宁可多捞再筛,不要漏了再也找不到。
🎛️ 动手感受:K=5 vs K=20 vs K=50
操作:同一个问题,K 分别设 5/20/50,看召回的"命中率"和"噪音率"变化。
你会看到:
- K=5:命中 3 条政策,但漏了 5 条——回答不完整。
- K=20:命中 7 条政策,噪音 13 条——排序能筛掉大部分噪音。
- K=50:命中全部 8 条政策,但噪音 42 条——排序压力骤增,可能被噪音干扰。
- 变化说明了什么:K 不是越大越好,存在一个"甜蜜区间"——命中够全、噪音可控。这个区间要靠你的数据实测,没有万能值。
🤔 想一想
K 越大越好?那为什么不直接 K=10000?K 增大的代价不只是排序压力——每多召回一条,排序模型的推理成本就多一份,延迟也多一截。在"不漏"和"不慢"之间,你的业务能容忍多少延迟?
🔗 顺着他想:多路召回听起来完美,但三路结果怎么合并?向量召回的 Top-5 和关键词召回的 Top-5 重叠了怎么办?这引出了"融合"(Fusion)的概念——倒排融合(RRF)是其中最经典的方案。
四、ReRank重排序:召回只是粗筛,精排才定胜负
召回捞了 50 条,相似度最高的那条真的是最相关的吗?不一定——双塔式 Embedding 各自算向量再比距离,问题和文档从没"见过"彼此。ReRank 就是让它们"面对面聊一次"。
ReRank 解决的核心问题:双塔式检索只看了"各自的长相",没看"彼此的交互"。交叉编码器把问题和文档拼在一起过一遍模型,直接打相关性分——精度碾压,但速度慢,所以只能用在召回后的小范围精排。
它到底在干嘛(机制层):双塔模型(Bi-Encoder)是 Embedding 检索的标配——问题和文档各自编码成向量,再算相似度。优点是快(向量可以预计算),缺点是问题和文档之间没有任何交互,"苹果"在问题里指水果还是手机,文档端完全不知道。交叉编码器(Cross-Encoder)把[CLS] 问题 [SEP] 文档 [SEP]拼成一条序列,喂进 Transformer 做完整注意力,直接输出一个相关性分数。因为问题和文档的每个 token 都能互相 attend,精度远高于双塔——但每对 (问题, 文档) 都要过一遍完整推理,不能预计算,所以只能对召回的几十条做精排,不能对全库做。
你能感受到什么(体感层):问"苹果最新的芯片",双塔检索可能把"苹果公司的历史"排第一(因为"苹果"这个词的向量权重太大),而 ReRank 会把"M3 芯片参数"提上来——因为它看到了"苹果"和"芯片"在同一个问题里的交互关系,知道这里"苹果"指公司不是水果。
| 双塔(Bi-Encoder) | 交叉编码器(Cross-Encoder) | |
|---|---|---|
| 速度 | 快(向量预计算) | 慢(每对都要推理) |
| 精度 | 中等 | 高 |
| 适用阶段 | 召回(全库扫描) | 精排(召回后小范围) |
| 交互 | 无(各自编码) | 完整注意力 |
🎛️ 动手感受:双塔 vs 交叉编码器排序
操作:同一批召回的 20 条文档,分别用双塔相似度排序 vs Cross-Encoder 重排序,看 Top-5 差异。
你会看到:
- 双塔排序:字面关键词匹配的文档排前面,但可能"苹果"指水果的那条排了第一。
- Cross-Encoder 排序:结合问题上下文,"苹果指公司+芯片"的文档被提上来。
- 变化说明了什么:ReRank 不是"更准的检索",是"更懂上下文的判断"——它让问题和文档真正"对话"了一次。
🤔 想一想
Cross-Encoder 这么准,为什么不直接用它做全库检索?因为全库 100 万条文档,每条都和问题拼一起过一遍 Transformer——延迟从毫秒级变成分钟级。精度和速度的博弈,永远是工程的核心矛盾。
🔗 顺着他想:有没有一种方案,比双塔准、比 Cross-Encoder 快?"晚期交互"模型(如 ColBERT)就是折中——每个 token 都保留向量,检索时做细粒度交互,速度接近双塔、精度接近 Cross-Encoder。
五、排序:不止ReRank,排序的完整逻辑链路
召回捞了 50 条,ReRank 精排到 10 条,模型只能吃 5 条——哪 5 条?排序决定生死。可大多数人只关心召回和 ReRank,排序环节草草了事。
排序解决的核心问题:“像不像"不等于"该不该”。相似度最高的文档,未必是最该喂给模型的——它可能过时了、来源不权威、或者和用户当前场景不匹配。排序要综合多种信号,做出"该不该"的判断。
它到底在干嘛(机制层):排序是一个多层信号叠加的过程。第一层:相似度分数(余弦/点积),纯看"像不像"。第二层:ReRank 分数(Cross-Encoder),看"交互相关性"。第三层:业务信号——文档新鲜度(时间衰减,2024 年的文档比 2020 年的权重高)、文档权威性(官网文档权重高于社区帖子)、查询-文档交互特征(文档是否包含问题中的实体词)。最终分数 = α×相似度 + β×ReRank 分 + γ×新鲜度 + δ×权威性,权重靠实验调。排序做完,取 Top-N(通常 3-5 条)喂给模型。
你能感受到什么(体感层):问"2024 年退货政策",纯相似度排序可能把 2020 年的旧政策排第一(文字最像),但加上时间衰减后,2024 年的新政策会被提上来。排序的威力在于:它不是只看"像不像",还看"该不该"——一个过时的答案,再像也不是你要的。
🎛️ 动手感受:纯相似度排序 vs 多信号排序
操作:同一批召回结果,分别用纯相似度排序 vs 加入时间衰减+权威性权重的排序,看 Top-5 差异。
你会看到:
- 纯相似度:2020 年旧政策排第一,因为文字和问题最像。
- 多信号排序:2024 年新政策排第一,旧政策被时间衰减压下去。
- 变化说明了什么:排序不是"排个序号"的事,是"综合判断该不该"的事——多加一个信号维度,结果就可能翻盘。
🤔 想一想
权重 α、β、γ、δ 怎么设?没有万能值——你的数据如果偏时效性(新闻类),γ 要大;偏权威性(法规类),δ 要大。这些权重靠什么调?靠 A/B 测试和人工评估,不是靠"拍脑袋"。
🔗 顺着他想:排序做完,喂给模型的 Top-5 顺序重要吗?模型是按顺序读的——排第一的文档注意力最高,排第五的可能被"挤掉"。所以排序不只是"选谁",还是"谁先谁后"。
六、混合检索:关键词+语义双剑合璧
纯向量检索搜"订单号 A12345"搜不到,纯关键词检索搜"怎么退款"搜不到同义的"如何退钱"——各有盲区。混合检索就是让它们组队:你搜不到的我来,我搜不到的你来。
混合检索解决的核心问题:向量检索擅长语义匹配但怕精确匹配,关键词检索擅长精确匹配但不懂同义。两路召回、合并排序,互补短板。
它到底在干嘛(机制层):混合检索同时跑两路召回——向量路(语义检索,ANN 索引)和关键词路(BM25/TF-IDF,倒排索引)。两路各出 Top-K,合并后统一排序。合并策略最经典的是倒排融合(Reciprocal Rank Fusion, RRF):每条文档的 RRF 分数 = Σ 1/(k+rank_i),k 通常取 60。RRF 不依赖绝对分数(向量相似度和 BM25 分数量纲不同,不能直接比),只依赖排名——"两路都排得靠前"的文档天然得高分。更精细的做法是:两路召回后,合并集再过一遍 Cross-Encoder 做最终精排。
你能感受到什么(体感层):问"订单 A12345 的物流状态"——关键词路精确命中"订单号 A12345"这条记录,向量路可能完全搜不到(因为 A12345 没有语义信息);问"怎么退钱"——向量路搜到"退款流程"文档(同义匹配),关键词路搜不到("退钱"和"退款"字面不同)。两路合并,两种问题都能答。
| 向量检索 | 关键词检索 | 混合检索 | |
|---|---|---|---|
| 同义匹配 | ✅ 强 | ❌ 弱 | ✅ |
| 精确匹配 | ❌ 弱 | ✅ 强 | ✅ |
| 速度 | 快(ANN) | 快(倒排) | 两路并行,略慢 |
| 复杂度 | 低 | 低 | 中(需合并策略) |
🎛️ 动手感受:纯向量 vs 纯关键词 vs 混合
操作:准备两类问题——“怎么退钱”(语义型)和"订单号 A12345"(精确型),分别用三路检索看结果。
你会看到:
- 纯向量:"怎么退钱"命中"退款流程"✅,"A12345"搜不到❌。
- 纯关键词:"A12345"精确命中✅,"怎么退钱"搜不到"退款"❌。
- 混合:两类问题都命中✅。
- 变化说明了什么:单路检索总有盲区,混合检索用"冗余"换"覆盖"——多跑一路,多一份保障。
🤔 想一想
混合检索一定比单路好吗?如果你的数据全是自然语言问答、几乎没有精确匹配需求(如客服对话),关键词路可能全是噪音——多跑一路不仅没增益,还增加了合并的复杂度和延迟。所以"要不要混合"取决于你的数据特征,不是无脑加。
🔗 顺着他想:两路召回后,合并策略 RRF 只看排名不看分数——这是优点(量纲无关)还是缺点(丢了分数信息)?有没有"既看排名又看分数"的合并方案?
七、一张图串起RAG调优链路
6 个调优旋钮不是散落的,它们串成一条从"用户输入"到"喂给模型"的完整调优链路:
用户提问 │ ① 意图识别 ← 分桶:FAQ/知识/闲聊/多跳,决定检索策略 │ ② 混合召回 ← 向量路(ANN) + 关键词路(BM25),宽捞宁多勿漏 │ ③ 融合(RRF) ← 两路合并,按排名融合 │ ④ 相似度计算 ← 底座:余弦/欧氏/点积,选错尺子全盘偏 │ ⑤ 排序(多信号) ← 相似度 + ReRank + 新鲜度 + 权威性 │ ⑥ ReRank精排 ← Cross-Encoder 交叉编码,定最终榜单 │ Top-N → 拼进 Prompt → 大模型生成调优口诀:
- 意图识别精准分流——别让"问流程"的人看"政策文档"。
- 相似度夯实底座——选错尺子,后面全白搭。
- 召回宽捞——宁多勿漏,漏了再也找不到。
- 排序精筛——“像不像"之外还要看"该不该”。
- ReRank定榜——让问题和文档面对面聊一次,最终排名它说了算。
- 混合检索兜底——单路总有盲区,双路互补才安心。
照这 6 个旋钮在脑子里走一遍,你就知道自己的 RAG “锅出在哪一步”——而不是只会换框架碰运气。
八、代码:加上ReRank和混合检索的RAG
上一篇跑了 20 行最小 RAG,这篇加两个关键升级:混合检索(BM25 + 向量)和ReRank(Cross-Encoder 精排)。
fromopenaiimportOpenAIimportnumpyasnpfromrank_bm25importBM25Okapi client=OpenAI(api_key="你的KEY",base_url="https://api.openai.com/v1")defembed(text):r=client.embeddings.create(input=text,model="text-embedding-3-small")returnnp.array(r.data[0].embedding)docs=["RAG用检索增强生成突破知识时效","向量数据库为高维相似度搜索而生","Chunk切分粒度直接决定检索质量","订单号A12345的物流状态已更新","ReRank用交叉编码器做精排提升精度","混合检索结合关键词和语义双路召回"]vecs=[embed(d)fordindocs]# ① 向量路:余弦相似度 Top-Kq="怎么让检索更精准"qv=embed(q)cos_sims=[qv @ v/(np.linalg.norm(qv)*np.linalg.norm(v))forvinvecs]vec_ranking=np.argsort(cos_sims)[::-1][:4]# ② 关键词路:BM25 Top-Ktokenized=[d.split()fordindocs]bm25=BM25Okapi(tokenized)kw_scores=bm25.get_scores(q.split())kw_ranking=np.argsort(kw_scores)[::-1][:4]# ③ RRF融合(k=60)k=60rrf_scores={}forrank,idxinenumerate(vec_ranking):rrf_scores[idx]=rrf_scores.get(idx,0)+1/(k+rank+1)forrank,idxinenumerate(kw_ranking):rrf_scores[idx]=rrf_scores.get(idx,0)+1/(k+rank+1)merged=sorted(rrf_scores,key=rrf_scores.get,reverse=True)[:3]# ④ ReRank精排(用LLM模拟Cross-Encoder打分)rerank_prompt="给以下(问题,文档)对的相关性打1-10分,只输出数字。\n"foridxinmerged:rerank_prompt+=f"问题:{q}| 文档:{docs[idx]}\n"scores=client.chat.completions.create(model="gpt-4o-mini",messages=[{"role":"user","content":rerank_prompt}]).choices[0].message.content# ⑤ 取Top1喂给模型生成top_idx=merged[0]ans=client.chat.completions.create(model="gpt-4o-mini",messages=[{"role":"system","content":"只根据参考资料回答"},{"role":"user","content":f"参考资料:{docs[top_idx]}\n问题:{q}"}]).choices[0].message.contentprint(ans)对比上一篇的 20 行,多了 BM25 关键词路、RRF 融合、和 LLM 模拟的 ReRank——这就是"从能用到好用"的距离。要上生产,把 LLM 模拟 ReRank 换成真正的 Cross-Encoder(如bge-reranker-v2-m3),4 行代码搞定。
写到最后
这篇把 RAG 从"跑通"推到了"调优"——相似度选错尺子、意图没识别、召回 K 设错、排序只看相似度、没上 ReRank、没用混合检索,6 个常见坑一次讲完。但 RAG 的天花板还不止于此——CRAG(纠错式 RAG)、Self-RAG(自反思 RAG)、句子滑动窗口检索、自动合并检索……这些高阶玩法在下一篇"高阶篇"里拆开讲。
写这种图文长文挺费劲——每个调优旋钮要不要讲透、动手场景能不能真感受到、那个"想一想"是不是真能卡住人,每一处都得磨。所以如果你读下来觉得真有用、不想下次又翻半天找不到:
- 👍点个赞,让我知道这种"类比+机制+体感+动手+灵魂提问"的写法值得继续做下去;
- ⭐收藏起来,这条 RAG 调优链路是后面所有检索优化文章的地基,回来翻的概率比你想的高;
- 💬关注一下,下一篇"RAG 高阶篇:CRAG·Self-RAG·滑动窗口·自动合并"我会尽快更上,关注了就不会错过。
如果这篇哪里没讲清楚、或者你想看的概念系列里还没排上,评论区直接说,我会一条条回,也按大家最想看的优先排期。
系列导航 & 持续更新
📚系列第 6 篇|上一篇:给AI装外挂知识库·RAG入门6张图把核心链路一次讲透 |下一篇预告:RAG还在用基础版?CRAG·Self-RAG·滑动窗口·自动合并·索引·融合全讲透
