KnowFlow ②:ES 混合检索(KNN 向量 + BM25 关键词 + RRF 重排序)面试深挖
KnowFlow ②:ES 混合检索(KNN 向量 + BM25 关键词 + RRF 重排序)
项目:KnowFlow 企业级 RAG 知识库系统 | 整理时间:2026-06-07 | 适用于后端实习/校招面试
一、先搞懂:为什么单纯的关键词搜索不够用?
1.1 关键词搜索的死穴
假设你公司有个技术文档库,里面有一句话:
“MySQL 主从复制延迟过高的排查思路”
用户搜索:“MySQL 同步慢怎么查?”
BM25(关键词搜索)的结果:
1 | |
这就是语义鸿沟——用户说的词和文档里的词不一样,但意思是一样的,传统搜索找不到。
1.2 向量搜索怎么解决这个问题?
把每个文本块都变成一个 2048 维的向量(可以理解为一组 2048 个数字),意思相近的文本,它们的向量在空间里距离就很近。
1 | |
1.3 那为什么还要 BM25?(面试必问)
向量搜索也有死角:
| 场景 | 向量搜索 | BM25 关键词搜索 |
|---|---|---|
| “MySQL 8.0 新特性”(精确关键词) | 可能把”PostgreSQL 14 新特性”也召回了,不够精确 | ✅ 精准匹配”MySQL 8.0”,准确率极高 |
| “怎么配置主从复制”(语义相似) | ✅ 能理解语义,召回全面 | ❌ 必须出现”配置””主从复制”这些词才行 |
| 企业专有名词(”XX 系统””YY 平台”) | 训练数据里没有这些词,向量质量差 | ✅ 精确匹配,不依赖训练数据 |
结论:两个都要,取长补短。
二、KnowFlow 的混合检索架构(核心设计)
2.1 整体流程图
1 | |
2.2 ES 索引结构(面试可能问)
1 | |
💡 为什么
textContent和vector存在同一个索引里?
答:这样 KNN 召回和 BM25 重排序可以在一次查询里完成,不用先向量搜一次、再关键词搜一次、再合并结果,省一次网络往返,延迟更低。
三、源码深度解析:HybridSearchService.java
3.1 KNN 向量召回 + 权限过滤
1 | |
3.2 RRF(倒数秩融合)是什么鬼?
先说说为什么需要 RRF:
KNN 给每个结果打一个分数(向量相似度,范围 0~1),BM25 也给每个结果打一个分数(关键词匹配度,范围不确定)。这两个分数的量级不一样,不能直接相加。
RRF 的思路非常巧妙:不看具体分数,只看排名。
1 | |
举个例子:
1 | |
ES 里不需要自己实现 RRF,用 rescore 就能组合 KNN 分数和 BM25 分数:
1 | |
💡 面试追问:”queryWeight 和 rescoreQueryWeight 为什么要这样设?”
答:向量召回负责”找相关的”,BM25 负责”确认关键词匹配程度”。企业专有词汇在向量空间里可能训练不充分,所以 BM25 权重设高一点(1.0),向量分数作为辅助信号(0.2),这样能保证精确匹配不被语义相似但关键词不匹配的结果挤掉。
四、权限过滤的多租户设计(KnowFlow 的核心业务)
4.1 三种权限级别
1 | |
4.2 组织标签的层级关系(源码解析)
1 | |
ES 查询里怎么用:
1 | |
4.3 为什么权限过滤要放在 filter 里而不是 must 里?
答:filter 上下文不计算相关度分数,只做 true/false 判断,可以利用 ES 的缓存机制加速。权限过滤和条件过滤不需要参与分数计算,所以用 filter 而不是 must,性能更好。
五、Embedding 失败时的降级策略(系统的鲁棒性)
5.1 为什么向量生成会失败?
1 | |
5.2 降级方案:纯 BM25 搜索
1 | |
💡 面试亮点:这是一个很实际的工程设计。大模型依赖的服务不一定 100% 可靠,系统要有降级能力。纯 BM25 虽然召回质量低一些,但至少能用,总比给用户报 500 错误好。
六、ES 复合索引设计(面试八股文)
6.1 为什么要建复合索引?
1 | |
ES 里的注意事项:
| 字段 | 类型 | 能不能用于 term 过滤 |
原因 |
|---|---|---|---|
userId |
keyword |
✅ | keyword 类型是精确值,可用于 term 查询 |
orgTag |
keyword |
✅ | 同上 |
public |
boolean |
✅ | boolean 类型可精确匹配 |
textContent |
text |
❌ | text 类型会被分词,不能用 term 精确匹配 |
💡 面试追问:”如果要支持
textContent的关键词精确过滤怎么办?”
答:用multi-field,给textContent同时建 text 类型和 keyword 类型:"fields": {"keyword": {"type": "keyword"}},然后过滤用textContent.keyword。
七、面试八股文高频题
Q1:ES 的 KNN 搜索底层是什么算法?
答:ES 8.x 的 dense_vector 字段使用 HNSW(Hierarchical Navigable Small World) 算法,是一种基于图的近似最近邻搜索算法。查询复杂度约 O(log N),比暴力搜索 O(N) 快很多。牺牲少量精度(recall 约 95~99%)换取大幅速度提升。
Q2:为什么 KNN 的 k 要设成 topK × 30?
答:KNN 是近似搜索,会有一些误召回或漏召回。把召回窗口放大(比如要 5 个最终结果,先召回 150 个),然后在这 150 个里面用 BM25 精确重排序,这样最终Top-K 的质量更高。这叫 “召回+重排”两阶段检索,是搜索系统的标准做法。
Q3:RRF 和直接加权求和(score_kNN + score_BM25)比,好在哪?
答:直接加权求和要求两个分数在相同的数值范围内(比如都是 0~1),但 KNN 距离分数和 BM25 分数的分布完全不同,直接相加需要手动做归一化,很麻烦而且容易调不好。RRF 基于排名,不需要归一化,更鲁棒,这也是 TREC 比赛里证明过的方法。
Q4:如果文档库里有 1 亿条数据,KNN 搜索会变慢吗?
答:HNSW 的查询复杂度是 O(log N),1 亿条也只会访问图中很少的节点,速度影响不大。但索引会占用较多内存(向量要全部加载到内存里才能快)。解决思路:如果向量维度高(2048 维),可以考虑用 PQ(Product Quantization)压缩,或者升级到 ES 的 int8_hnsw 量化索引,内存占用降到 1/4。
Q5:多租户场景下,怎么防止 A 租户通过精心构造的查询”撞库”到 B 租户的数据?
答:三层防护:
- 应用层:所有搜索接口先从 JWT Token 里取出 userId 和 orgTags,作为参数传入 ES 查询的 filter 里,用户无法伪造;
- ES 层:用
filter做权限过滤,不匹配的文档直接不返回,不是靠分数压低,而是根本不出现在结果集里; - 组织标签层级:即使 A 用户知道 B 用户的文档 ID,只要 orgTag 不匹配,就搜不到。
八、简历上怎么讲这句话(可直接用)
原句参考:”为解决单一语义检索在企业专有词汇场景下召回率不足的痛点,设计基于 Elasticsearch 的混合检索架构:结合大模型 Embedding(2048 维)实现 KNN 向量召回,配合 BM25 关键词匹配,最终通过 RRF(倒数秩融合)算法对多路召回进行重排序,显著提升私有知识库的检索准确度。”
面试时被问到,按这个顺序讲:
- 先说痛点:纯向量检索在企业场景不行,专有名词向量模型没见过,召回不准;
- 说方案:KNN 向量召回(负责语义相似)+ BM25 关键词匹配(负责精确匹配),两路结果用 RRF 融合;
- 说细节:KNN 的 k 放大 30 倍再重排,防止近似搜索的误差;权限过滤放 filter 上下文利用缓存;
- 说降级:Embedding 服务挂了自动降级到纯 BM25,系统不挂。
© 2026 KnowFlow 面试手册 · 转载请注明出处