KnowFlow ②:ES 混合检索(KNN 向量 + BM25 Rescore)面试深挖
源码口径更新(2026-09-29):本文保留学习笔记与原理示例。项目实际实现、个人增量和测试边界请先看项目总览;示例数字不代表可复核的项目实测。
KnowFlow ②:ES 混合检索(KNN 向量 + BM25 Rescore)
项目: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```
3.2 当前实现是 BM25 加权 Rescore
HybridSearchService 在一个 Elasticsearch 请求中设置顶层 knn 和文本 query,Elasticsearch 将两路候选按 OR 合并。随后 rescore 在窗口内按 queryWeight=0.2 保留原始分数,再以 rescoreQueryWeight=1.0 加入 BM25 match 分数。这里没有独立的两份排名,也没有按 1/(k+rank) 计算,所以不是 RRF。
顶层文本查询中的权限过滤不会自动约束顶层 KNN。当前源码给两路分别使用同一权限过滤条件;3 项请求结构回归测试已通过;真实 Elasticsearch 端到端隔离测试仍待补充。topK × 30 是候选窗口设置,不代表有准确率或召回率提升的实测结论。
四、权限过滤的多租户设计(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:当前 Rescore 与 RRF 有什么区别?
答:当前实现把原始查询分数乘以 0.2,再加上 BM25 重排分数;RRF 只看两份结果各自的名次。项目没有实现 RRF,也没有成套检索评测,不能声称 RRF 带来效果提升。
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 的 KNN 与文本查询召回候选,再以 BM25 Rescore 调整排序;权限过滤同时加在两路查询上。尚无可靠的离线评测数据可量化准确率提升。
面试时被问到,按这个顺序讲:
- 先说痛点:纯向量检索在企业场景不行,专有名词向量模型没见过,召回不准;
- 说方案:KNN 向量召回(负责语义相似)+ BM25 关键词匹配(负责精确匹配),ES 合并两路候选,再用 BM25 Rescore 加权重排;
- 说细节:KNN 的 k 放大 30 倍再重排,防止近似搜索的误差;权限过滤放 filter 上下文利用缓存;
- 说降级:Embedding 服务挂了自动降级到纯 BM25,系统不挂。
© 2026 KnowFlow 面试手册 · 转载请注明出处