AnySearch 实时结果与本地向量库混合检索时,权重不是拍脑袋定的常数,而是需要在一组固定查询上反复对比才能确定。实时搜索分数和向量相似度来自不同打分逻辑,不归一化就相加,结果没有可比性。下面这套流程可以重复执行:先建评测集,再归一化,然后搜权重,最后固化。
混合检索权重没有普适默认值。适合当前业务的权重,只能通过带标注测试集上的对比试验得到。实际做法是:从真实日志抽 50~100 条查询,人工标注理想来源;用 min-max 或 z-score 把两类分数统一到同一区间;用网格搜索遍历权重组合,在评测集上计算召回率和 MRR;把表现最好的权重写入配置,并在 CI 中定期回归。若测试集太小或标注不一致,权重会偏向个别查询,需要先扩充标注再调权。
准备一组带标注的测试查询
权重调优的第一步不是写代码,而是先确定“什么结果算对”。从业务日志中筛选 50~100 条真实查询,覆盖高频词、长尾词、包含型号或地点的实体类查询,然后人工标注每条查询的最佳来源。
标注结果建议分成三类:该查询只应该由实时结果返回、只应该由向量库返回、需要两边内容合并才能完整回答。这个标注集合就是后续所有比较的基准。如果连标准答案都没有,网格搜索跑出来的权重只是对历史排序结果的拟合,不能说明它对新查询也有效。
对两类结果分别打分并归一化
拿到测试查询后,先让 AnySearch 实时结果和本地向量库各自返回归一化前的分数,把分数分布画出来。常见问题是实时分数范围是 0~100,向量相似度是 0~1,直接加权会让数值大的一方主导最终排序。建议先用 min-max 或 z-score 把两类结果映射到同一区间,再检查归一化后的分布是否有离群点。
import numpy as np
def minmax(scores):
lo, hi = min(scores), max(scores)
return [(s - lo) / (hi - lo) if hi > lo else 0.0 for s in scores]
def zscore(scores):
mu, sigma = np.mean(scores), np.std(scores)
return [(s - mu) / sigma if sigma > 0 else 0.0 for s in scores]归一化时要注意:min-max 容易被单个离群点拉低其他分数,z-score 对离群点相对稳一些。建议先分别画出两类分数的直方图,确认大多数样本落在什么区间,再决定用哪种方式。
实现加权融合并搜索权重
归一化之后,定义一个加权融合函数。权重 w_real 和 w_vec 分别代表实时结果和向量库结果的重要性,通常要求两者之和为 1。下面的脚本遍历 0 到 1 的网格,在标注测试集上计算每个权重组合的 MRR,并记录当前最优配置。
import numpy as np
def hybrid_score(real_score, vec_score, w_real, w_vec):
return w_real * real_score + w_vec * vec_score
def evaluate_weights(eval_set, w_real, w_vec):
# 每个查询得到混合排序后,计算 MRR 或 Recall@10
mrr_total = 0.0
for query, gold in eval_set:
ranked = run_hybrid(query, w_real, w_vec)
mrr_total += mrr_at_k(ranked, gold, k=10)
return mrr_total / len(eval_set)
best = None
best_mrr = -1.0
for w_real in np.arange(0, 1.01, 0.1):
w_vec = round(1.0 - w_real, 2)
mrr = evaluate_weights(eval_set, w_real, w_vec)
if mrr > best_mrr:
best_mrr = mrr
best = (w_real, w_vec)
print(best, best_mrr)这段脚本会输出一个权重组合,但不要直接照搬。网格步长建议先用 0.1,找到大致的优势区域后,再在附近按 0.05 或 0.02 加密。如果不同查询组的最佳权重差异很大,说明测试集本身可以分类,需要先按查询类型切分再调。
在评测集上比较融合前后效果
找到最优权重后,还要确认混合检索真的比单一来源好。在同一组标注查询上,分别跑“仅实时结果”“仅本地向量库”“混合检索(最优权重)”三种模式,计算召回率、MRR 等指标,并输出对比表。
| 方案 | Recall@10 | MRR |
|---|---|---|
| 仅实时结果 | 由评测脚本输出 | 由评测脚本输出 |
| 仅本地向量库 | 由评测脚本输出 | 由评测脚本输出 |
| 混合检索(最优权重) | 由评测脚本输出 | 由评测脚本输出 |
如果对比结果显示混合检索没有明显优势,先不要继续调权重,而是检查两类结果的重叠度:同一个查询下,实时结果和向量库返回的文档是否大量重合?人工标注是否一致?答案来源本来就相同,融合权重不会创造新信息。
将最优权重写入配置并加入回归测试
权重一旦确定,就把它写进独立的配置文件,不要在融合脚本里硬编码。建议至少保存三个值:w_real、w_vec、归一化方式。配置示例:
[hybrid]
w_real = 0.6
w_vec = 0.4
norm = zscore把配置文件和评测脚本一起提交到代码库,然后在 CI 中增加一个回归任务:定期用同一组标注查询跑一遍混合检索,计算 MRR 或 Recall@10,并对比上次结果。如果分数下降超过可接受范围,触发人工检查。这里说的定期应该绑定到实际发布节奏或数据更新频率,具体周期由团队根据评测集大小和构建时长决定。
当业务日志出现新查询模式,或向量库的 embedding 模型更换后,重新运行网格搜索,不要沿用旧权重。