最近在搭一个知识库问答系统,用的pgvector(也试了Milvus),文档大概5万条,切块后embedding存进去。一开始直接做相似度检索效果还行,但业务上需要按部门、时间范围过滤,所以我在查询时加了metadata filter(比如where department=‘HR’)。结果发现加了过滤后,top-k召回的相关度明显下降,有时候甚至返回一些看起来完全无关的内容。我确认过过滤条件是能正确命中的,向量本身也重新embedding过。想问问大家,是不是过滤和向量检索的顺序/机制上有什么坑?比如被过滤掉的高相似度向量会影响整体距离分布吗?还是说应该换一种索引方式(比如先过滤再检索,或者用HNSW的filtered search)?另外,这种情况是不是更适合用混合检索(BM25+向量)来兜底?求有经验的大佬指点一下,谢谢!
用向量数据库做RAG,为什么加了过滤条件后召回效果反而变差了?
全部回复
共 105 条HNSW索引在带过滤条件时确实容易出这问题,因为图遍历是先走邻居再过滤,被过滤掉的点等于把路径断了,召回自然掉。可以试试把过滤条件直接做成分区或者partial index,让向量检索只在目标子集里走。另外如果过滤后候选集本来就小,暴力扫描可能比近似索引更稳。Milvus那边我记得有partition key和filtered search的优化,可以翻下文档对比下。
pgvector的HNSW索引在带filter时确实容易出问题,它默认是先做近似向量搜索再过滤,如果过滤后剩下的候选不够,就会导致结果质量骤降。你可以在查询里调大ef_search或者用iterative index scan,让它在过滤后继续补候选。Milvus那边我记得有partition级别的过滤,把部门直接做成partition可能比纯metadata filter更稳。另外5万条数据不算大,实在不行可以试试先按条件筛子集再做暴力检索,效果会稳定很多。
这个坑我去年也踩过,当时用Milvus加部门过滤后,召回直接崩了,后来发现是HNSW索引在带过滤条件时,图遍历会被截断,导致本来该走到的邻居节点进不来。pgvector那边如果用的是ivfflat,情况也类似,它是先按聚类中心找最近的那几个list,过滤条件一加,可能某个list里符合条件的数据很少甚至没有,但查询还是只在那几个list里翻,结果自然就偏了。你说的“被过滤掉的高相似度向量影响距离分布”这个直觉其实挺准的,因为ANN索引本身是在全量空间里做近似搜索,过滤相当于在检索过程中动态缩小候选集,但索引结构并不知道你的过滤意图,它还是按原来的图或聚类路径走。比较靠谱的做法是确认你的向量库支不支持pre-filter,也就是先按metadata把候选集缩到符合条件的那批,再在这批里做向量比较,Milvus可以试试用partition或者把过滤字段做成标量索引配合表达式下推。另外五万条这个量级其实不算大,如果过滤后候选集经常只有几百上千条,暴力算精确距离可能比走ANN还快还准,值得试一下关掉索引直接算。还有个容易忽略的点是,如果过滤条件命中的文档本身在语义空间里就比较分散,那top-k相关度下降未必是检索的锅,可能是这批数据内部就不太像。
pgvector这情况我踩过,大概率是HNSW索引在过滤时的坑。它的图结构搜索会先按向量距离跳,filter只是后置剪枝,被过滤掉的近邻会让搜索路径断掉,返回的结果自然就飘了。可以试试开pgvector的iterative scan,或者干脆先按条件把候选集缩到小范围再走精确检索,5万条数据量其实扛得住。Milvus的话看看能不能用partition加filtered search,效果会稳不少。
pgvector的filter是后置的,先召回再过滤,过滤完可能就没几条了。试试先where再排序,或者用分区表。