两段式重排#
向量召回用双塔模型对查询与文档分别编码再算相似度,对“短文档片段 vs 长查询”只是粗匹配。重排阶段对召回的每一条候选与查询做联合打分,重新排序后取最终结果。NLQL 在召回阶段按 rerank_factor 倍数过取候选,再用 Reranker 精排。
本例用 FakeEmbedder 与 FakeReranker 离线演示。
import nlql
from nlql.rerank import FakeReranker
DOCS = [
# 包含全部查询词但很长,双塔相似度被稀释
("agent memory planning tool retrieval vector index query model system", "full"),
("banana bread recipe with flour and sugar", "noise"),
("agent", "partial"),
]
QUERY = 'SELECT SENTENCE LET rel = SIMILARITY(content, "agent memory planning tool") ORDER BY rel DESC LIMIT 3'
不加重排器#
engine = nlql.Engine(nlql.embed.FakeEmbedder(), reranker=None, rerank_factor=10)
for text, doc_id in DOCS:
engine.add_text(text, id=doc_id)
print("== without reranking ==")
for unit in engine.search(QUERY):
print(f" ({unit.doc_id:8}) rel={unit.scores.get('rel', 0.0):+.3f}")
full 文档虽然覆盖了所有查询词,但因为句子长,相似度被平均稀释,可能排不到前面。
加重排器#
engine = nlql.Engine(nlql.embed.FakeEmbedder(), reranker=FakeReranker(), rerank_factor=10)
for text, doc_id in DOCS:
engine.add_text(text, id=doc_id)
print("== with FakeReranker ==")
for unit in engine.search(QUERY):
rerank = unit.scores.get("rerank")
tail = f" rerank={rerank:.2f}" if rerank is not None else ""
print(f" ({unit.doc_id:8}) rel={unit.scores.get('rel', 0.0):+.3f}{tail}")
Reranker 协议要求 rerank(query, units) -> units:接收查询文本与召回候选列表,返回按新分数排序的列表。结果中 unit.scores["rerank"] 即重排后的分数。召回阶段先取 limit × rerank_factor 条候选,精排后只返回前 limit 条。
rerank_factor#
rerank_factor 控制过取倍数:最终需要的 limit 乘以这个倍数得到召回数量。倍数越大召回越全、精度越依赖重排器,但也越慢。常用值在 5 到 20 之间。
生产用 CrossEncoder#
FakeReranker 仅用于演示。生产中替换为真实重排器:
from nlql.rerank import CrossEncoderReranker
engine = nlql.Engine(
nlql.embed.OpenAIEmbedder(),
reranker=CrossEncoderReranker(model="cross-encoder/ms-marco-MiniLM-L-6-v2"),
rerank_factor=5,
)
CrossEncoderReranker 基于 sentence-transformers,需安装 pip install "python-nlql[local]"。
按查询覆盖重排文本
engine.search(q, rerank_query="自定义文本") 可覆盖重排阶段使用的查询文本,默认取主 SIMILARITY 查询。也可在单次调用上传入不同的 reranker 实例。
EXPLAIN 中可见重排
engine.explain(q) 在存在重排器时会附上重排器类名,便于确认配置生效。