先看现象。同时从向量库、关键词索引和热门问题三路召回,合并后前十条里有一半是同一段内容的不同切片。
把日志和配置对完,能解释这个现象的基本就这几处。不同来源返回同一文档的不同片段。另一种情况是只按文本内容去重,忽略了文档 id。
不容易想到的是,融合时按分数排序,重复项把位置占满。
还有一种情况,召回数量没有按路分配配额。
上来先按文档 id 去重后保留最高分
docs = {d['doc_id']: d for d in sorted(all_hits, key=lambda x: -x['score'])}
每路限制召回数量,避免单路垄断
python -c "print({k: len(v[:10]) for k,v in hits.items()})"
接着用 RRF 做跨路融合
final = sorted(docs.values(), key=lambda d: -d['rrf'])[:10]
剩下的就是抽样检查最终列表的相关性
python -c "print([d['title'] for d in final])")
怎么算真的好了,看的是同一批现象。最终列表内容多样,重复项消失,前几条覆盖了不同角度的相关信息。
同一文档的不同切片各自计分时会互相抬轿,先按文档聚合再排序,结果通常比按片段排序更稳定。报错文案容易误导,真正的原因往往在相邻那一层的日志里。