后过滤召回塌陷: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(优雅降级)


用户体验从:

没找到。

变成:

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

相关推荐
Javatutouhouduan2 分钟前
Java小白如何快速玩转Redis?
java·数据库·redis·分布式锁·java面试·后端开发·java程序员
摇滚侠1 小时前
DSL 学习 ElasticSearch 主要就是学习 DSL 查询语言
学习·elasticsearch·jenkins
Lyyaoo.2 小时前
Redisson
数据库·缓存
网络工程小王2 小时前
【LCEL 链式调用详解】调用篇-2
java·服务器·前端·数据库·人工智能
道法自然,人法天3 小时前
PostgreSQL安装与初始化教程(二进制压缩包)
数据库·postgresql
yzs873 小时前
从Hydra到storage_engine:PostgreSQL列存引擎的性能跃迁与技术进化
数据库·postgresql
红云梦3 小时前
官方 Anthropic Postgres MCP Server 存在 SQL 注入漏洞 -- SafeDB 是如何做到 4 层防御的
数据库·sql
TDengine (老段)3 小时前
红有软件重构智能油田时序数据底座,支撑生产实时感知与设备预测性维护
大数据·数据库·人工智能·重构·时序数据库·tdengine
用户9747138121403 小时前
Elasticsearch 文档版本控制实验手册
elasticsearch
倒霉蛋小马3 小时前
【Redis】什么是缓存击穿?
数据库·redis·缓存