方法学库和语言教程库已经分开建索引。用户有时问「GAPDH 内参」,有时问「列表推导式」。把两个库的块混在一次 top-k 里,生成会被无关段落带偏。两条路:
- 先选路:LLM 读各路
description,只打一条(或几条)Retriever——RouterRetriever - 条条跑再合并:多路都召回,用 RRF 合成名单——跨索引的
QueryFusionRetriever
前者省嵌入与噪声,后者不怕选错路、但费用和噪声都更高。
段末注释:RouterRetriever 按选择器挑
RetrieverTool;跨索引融合 与「同一语料上向量+BM25」都叫 Fusion,但选路条件不同。

1. 一句话定位
| 维度 | 内容 |
|---|---|
| 角色 | 知识层的多路调度:分流或融合,输出仍是 list[NodeWithScore] |
| 输入 → 输出 | query →(选路或全跑)→ 合并后的 NodeWithScore |
| 典型调用入口 | RouterRetriever(selector, retriever_tools)、QueryFusionRetriever([r1, r2], num_queries=1) |
| 与 LangChain / LangGraph | 近邻是路由链 / EnsembleRetriever;有环的重试选路仍用 LangGraph 条件边 |
出现背景:单索引 as_retriever 假设「全世界只有一个抽屉」。多集合、多模态、事实问 vs 综述问,需要显式调度,而不是把 description 写进生成 prompt 碰运气。
2. 前置依赖与环境
1 | pip install -U llama-index-core llama-index-llms-ollama llama-index-embeddings-ollama |
- Router 的 selector 要 LLM;Fusion 在
num_queries=1时不要 LLM - 本地模型用
LLMSingleSelector比强依赖 function calling 的 Pydantic selector 更稳 await router.aretrieve(q)与同步参数一致
3. 实现逻辑
Router(选路)
1 | 1. 每个子索引 as_retriever → RetrieverTool.from_defaults(description=...) |
Fusion(全跑)
1 | 1. 准备多路 Retriever(不同 index 或不同算法) |
字段级变形(Router):
1 | query = "GAPDH 内参是什么" |
4. 原理说明
主轴是:Router 把「选哪路」交给 LLM;Fusion 把「选哪路」变成「全都要,事后按名次加权」。
1 | 1. RetrieverTool 把 retriever + name + description 编成选择器可见的选项 |
RetrieverTool.from_defaults(retriever, name=..., description=...) 出现在步骤 1。description 是给选择器的,不是给最终用户的。必须写清何时用 / 何时不用。
LLMSingleSelector.from_defaults(llm=...) 出现在步骤 2。一次选一个。多路并行选 LLMMultiSelector。
RouterRetriever 出现在步骤 3。输入输出与普通 Retriever 相同,可再塞进 QueryEngine。
QueryFusionRetriever 出现在步骤 5。跨不同语料库时要自问:是否真的该全跑。同一语料向量+BM25 的 Fusion 是混合召回,不是分流。
不要用 LangGraph 再画一个「选 Retriever」子图,除非选路失败要重试、要 HITL、要 checkpoint。
5. 最小可运行示例
1 | pip install -U llama-index-core llama-index-llms-ollama llama-index-embeddings-ollama |
1 | from llama_index.core import Document, Settings, VectorStoreIndex |
6. 重要配置参数
| 参数(API 名) | 类型 / 默认值 | 功能说明 | 作用与影响 | 参考起点 / 常用范围 | 配置指导 |
|---|---|---|---|---|---|
RetrieverTool.description |
str,必填语义 | 选择器用来判断「这条路是否该走」 | 两路 description 雷同则乱跳 | 含「何时用 / 何时不用」各一句 | 比 name 更重要 |
selector |
LLMSingle / Multi | 一次选 1 路还是多路 | Multi 接近弱融合,费用更高 | 互斥库用 Single | 本地模型先 Single |
select_multi |
bool,部分 from_defaults | 是否允许多 tool | True 时噪声近似 Fusion | 默认 False | 与 Fusion 二选一 |
num_queries |
int,Fusion 默认 4 | 额外生成几条 query | Router 场景通常不需要改写 | 1 | 改写另做,不要叠在选路上 |
similarity_top_k |
int | Fusion 最终截断条数 | 路数 × 每路 k 很大时必须截 | 与单路 k 同量级 | 先看各路再定 |
temperature |
LLM,建议 0 | 选择器采样 | >0 时同一问可能换路 | 0 | 选路必须稳 |
7. 适用 / 不适用
| 维度 | 适用 | 不适用 |
|---|---|---|
| 任务形态 | 库主题互斥、问句类型可分 | 同一库里语义+货号——用向量+BM25 Fusion,不要 Router |
| 集成约束 | 能接受一次小 LLM 选路 | 选路必须零 LLM——规则 if/else 或 Fusion |
| 工程阶段 | 2~5 个抽屉 | 几十个工具还要失败重试——LangGraph 条件边 + 显式 state |
8. 易踩坑
- description 写成广告语(「非常有用」):选择器无法互斥。
- 用 Fusion 替代 Router 处理互斥库:第二库头名稳定污染。
- PydanticSingleSelector + 不支持 tool 的本地模型:解析失败,retrieve 空或乱。改
LLMSingleSelector。 - Router 里
num_queries=4的 Fusion 当子路:一次用户问题变成选路 LLM + 多路改写,账算不清。
小结
- 互斥库 → Router;同一问题必须多路证据 → Fusion。
- Tool description 是选路契约;temperature=0。
- 跨索引 Fusion 会抬进「另一路的头名」,这是特性不是 bug。
- 选路失败要重试或审批时,把 Router 的结果当 LangGraph 一个节点的输出,不要在 Retriever 里造环。