使用 LangChain 时,如何实现多路召回结果的动态权重分配?
- 标签:大模型
🏛 业务架构师
多路召回的动态权重分配,我在 LangChain 里的实现思路与业务考量:
实现路径:
- 多路并行召回:向量检索、BM25 关键词检索、知识图谱/结构化查询各自返回 Top-N,LCEL 里用
RunnableParallel并发执行; - 融合层:先用 RRF(倒数排名融合)做基础合并,消除分数量纲差异;
- 动态加权:权重怎么"动"起来有三档——
- 规则驱动:query 带精确标识(单号、型号)时关键词路权重调高,口语化长句向量路权重调高——按 query 特征路由,简单可靠,首选;
- 模型驱动:让 LLM 先判断 query 类型再输出权重,或训练一个小分类器做 query 意图分类;
- 学习驱动:用用户点击/点赞数据在线调权(简易 bandit 思路),效果最好但要数据量支撑。
业务提醒:动态权重是"有数据后再做"的优化。冷启动阶段用固定权重+RRF 融合已经够用;等积累了点击日志和 badcase 样本,再让权重真正"学会"分配。我见过太多团队一上来就做学习排序,最后卡在没有训练数据——顺序错了,全盘皆输。
🏗 系统架构师
多路召回的动态权重分配,我先给实现方案再讲架构判断:
实现三阶梯:
- 静态融合(起步):RRF(Reciprocal Rank Fusion)——按各路排名取倒数求和,免调参、对分数尺度不敏感,是默认方案;加权版对归一化后的分数线性组合(向量 0.6 + BM25 0.4);
- 规则动态权重:按 query 特征切换权重——含型号/错误码的精确词 query 提高关键词权重;长句自然语言提高向量权重;有明确元数据(时间、部门)加重过滤路。实现上是一个轻量分类器或关键词规则,延迟可忽略;
- 学习排序(LTR):离线标注"query→正确文档",把各路召回分数、rank、query 特征喂 LightGBM/小模型训练融合权重,在线推理 <10ms。数据积累够了这是效果上限最高的方案。
架构判断:动态权重的收益要先验证——多数系统 RRF 已经拿到 80% 的收益,LTR 是最后 20%。上线策略:RRF 打底 → 离线对比动态权重方案的 recall@K 提升 → 显著才上。同时保留单路降级:某路检索超时(P99 > 100ms)自动降权重为 0,避免慢路拖垮整体延迟。
💻 全栈开发专家
多路召回的权重分配,我按复杂度递进三种做法:
1. RRF 融合(默认首选,无需调权):只看排名不看分数,score = Σ 1/(k + rank_i),k 常取 60。天然规避「向量余弦和 BM25 分数量纲不可比」的问题:
python
def rrf(results_list, k=60, top_n=20):
scores = defaultdict(float)
for results in results_list:
for rank, doc in enumerate(results, 1):
scores[doc.id] += 1.0 / (k + rank)
return sorted(scores, key=scores.get, reverse=True)[:top_n]2. 归一化加权:各路分数 min-max 或 z-score 归一化后加权求和,权重按 query 类型动态定——由一个轻量分类器先判断意图:精确匹配型(型号/单号)BM25 权重 0.7;语义模糊型向量权重 0.7;再叠加时间/来源的 boost。
3. 模型学习权重(LTR):攒够点击/正确性反馈后训练轻量排序模型(LambdaMART),把各路召回分、文档质量分、时效分全做特征。这是终态但要数据量支撑。
工程提醒:先用 RRF 上线拿到基线,别一开始就搞动态权重(调不动还解释不了);所有权重进配置中心可热调;评估用 interleaving 或离线 recall@k 对比,防止「感觉更好」。我们生产上 90% 的场景 RRF + 少量业务 boost 规则就够了。