后过滤召回塌陷:Redis 先召回 → ES 再过滤,如果全部被过滤掉怎么办?

真正做过企业级 RAG / 向量检索的人一定会遇到双引擎架构的核心难点:

Redis 先召回 → ES 再过滤,如果全部被过滤掉怎么办?

这叫:

Post-filter Recall Collapse(后过滤召回塌陷)

也是 Redis + ES 架构必须解决的问题。

下面我按 问题本质 → 为什么会发生 → 解决方案→兜底策略 讲清楚。


一、问题本质(发生了什么)

流程:

复制代码
RedisSearch KNN Top50
↓
ES 权限过滤
↓
0 条结果 ❌

原因不是 Redis 搜错。

而是:

复制代码
Top50 ≠ Top50 可访问文档

换句话说:

👉 语义最近 ≠ 用户有权限


举个真实例子

系统里:

文档 相似度 部门
A 0.99 CEO
B 0.98 CEO
C 0.97 CEO
... ... ...
Z 0.80 Finance

用户是 Finance。

Redis 返回:

复制代码
Top50 = 全是 CEO 文档

ES 一过滤:

复制代码

二、为什么这是 ANN 系统天然问题

ANN(HNSW)目标只有一个:

复制代码
最相似

不知道权限

所以:

权限分布越不均匀,问题越严重。

企业知识库里非常常见:

  • 高层文档语义集中
  • 普通员工权限少

三、工业界标准解法(⭐重点)

解决思路只有一个核心:

不要一次只召回固定 TopK

而是:

复制代码
动态扩大召回范围

方案 1⭐:Oversampling + 回填机制 ✅

这是最常见方案。


Step 1:Redis 多召回

不要:

复制代码
Top50

而是:

复制代码
Top200 / Top500

因为你知道会被过滤。


Step 2:ES 过滤

假设:

复制代码
200 → 只剩 8 条

Step 3:检测数量

如果:

复制代码
result < target_k

触发:

复制代码
二次召回

Step 4:扩大搜索范围

例如:

复制代码
第一次 ef_runtime = 100
第二次 ef_runtime = 300
TopK = 500

再过滤一次。

在 HNSW 算法中,从 ef_runtime = 100 增加到 300,查询耗时通常不会线性增加 3 倍,但会表现出明显的延迟增长。根据在 RedisSearch 环境下的实测经验,增加比例通常在 1.5倍 到 2.5倍 之间。
TopK 仅决定返回数量,而 ef_runtime 决定 HNSW 的搜索深度。后过滤召回塌陷的根因是搜索空间不足,因此扩大 ef_runtime 比单纯增加 TopK 更有效。


伪代码

java 复制代码
k = 50
recall = 200

while(true){
   candidates = redis.knn(recall)
   results = es.filter(candidates)

   if(results.size >= k)
       break

   recall *= 2
}

✅ 优点:

  • 简单
  • 稳定
  • 工业标准

方案 2⭐:权限感知 Oversampling

观察:

不同用户过滤比例不同。

例如:

用户 过滤率
admin 5%
普通员工 80%
外包 95%

于是:

动态计算:

复制代码
recall_k = target_k / (1 - filter_ratio)

例:

复制代码
目标50
预计过滤80%

→ 50 / 0.2 = 250

Redis 直接召回 250。

必须同时保证:ef_runtime ≥ recall_k


方案 3⭐:权限预分桶(工业优化)

核心思想:

不要让 Redis 完全不知道权限。

但又不能破坏 HNSW。

怎么办?


做"软隔离"索引

例如:

Redis 建多个索引:

复制代码
doc_idx_public
doc_idx_finance
doc_idx_hr

查询时:

复制代码
finance 用户 → 优先搜 finance index

必要时 fallback 全局索引。


好处:

✅ 不破坏图结构

✅ 召回稳定

✅ ES过滤压力下降


方案 4⭐:Filter-aware ANN(权限感知向量空间)

在 embedding 中加入权限特征:(1.向量拼接;2.训练模型)

复制代码
embedding = semantic + access embedding

通过将权限信息编码进向量空间,使可访问文档在语义空间中天然更接近查询,从而减少后过滤带来的召回浪费,本质是将"过滤问题"转化为空间几何问题。

让:

复制代码
无权限文档在向量空间更远

等于:

ANN 自然避开。

但实现复杂,有向量污染风险,权限维度过强,会导致语义被破坏,搜索质量下降。


四、Safety Fallback(安全兜底策略)

Fallback 1:降低 keyword 约束(放宽条件)

原查询

复制代码
部门=财务
AND
关键词=报销流程2024新版

可能太严格:

复制代码
0结果

fallback 做什么?

逐步放松条件:

复制代码
部门=财务
AND
关键词=报销流程

再不行:

复制代码
关键词=报销

本质:

复制代码
Precision ↓
Recall ↑

先保证有结果。


✅ 类似 Google:

你搜错字时:

"Showing results for ..."


Fallback 2:返回"权限允许的次优语义"

这是向量检索里的核心兜底。


情况:

复制代码
最相似文档 → 无权限

过滤后:

复制代码

fallback 做法:

扩大语义范围:

复制代码
Top50 → Top300 → Top1000

只要:

复制代码
语义还算相关 + 有权限

就返回。


例子:

用户问:

复制代码
差旅报销标准

最相关文档是总部制度(无权限)。

fallback 返回:

复制代码
部门差旅制度(相似但次优)

目标:

复制代码
有用 > 完美

Fallback 3(RAG ⭐)

不是只靠检索。

而是改变 LLM回答策略


当系统检测:

复制代码
高置信文档不存在

不要让 AI 硬答。

而是生成:

复制代码
未找到完全匹配内容,
但以下相关制度可能有帮助:

然后引用次相关文档。


这叫:Graceful Degradation(优雅降级)


用户体验从:

没找到。

变成:

没完全匹配,但这些可能相关。

相关推荐
Trouvaille ~5 分钟前
【MySQL篇】内外连接:多表关联的完整指南
android·数据库·mysql·面试·后端开发·dql·内外连接
KKKlucifer8 分钟前
三权分立 + AI 审计:解析国内堡垒机的合规与智能双引擎
大数据·数据库·人工智能
空太Jun11 分钟前
Redis 5大核心数据类型与持久化实战
数据库·redis·缓存
Java面试题总结31 分钟前
Spring Boot 包扫描新姿势:AutoScan vs @Import vs @ComponentScan 深度对比
java·数据库·spring boot
Elasticsearch40 分钟前
多大才算太大?Elasticsearch 容量规划最佳实践
elasticsearch
人工干智能1 小时前
科普:pandas 中的类 SQL语句:transaction.groupby(“card_id“)[‘purchase_day‘].diff()
数据库·sql·pandas
梦想与想象-广州大智汇1 小时前
MySQL 同步数据到 ClickHouse 方案对比分析
数据库·mysql·clickhouse
雨墨✘1 小时前
如何解决SQL多表查询数据重复问题_使用DISTINCT与JOIN优化
jvm·数据库·python
却话巴山夜雨时i1 小时前
互联网大厂Java面试:从Spring Boot到Kafka的业务场景深度剖析
spring boot·redis·spring cloud·微服务·kafka·prometheus·java面试
u0107475461 小时前
JavaScript 递归调用栈深度解析与层级遍历陷阱详解
jvm·数据库·python