Query-Routing
2566 字约 9 分钟
domain/aiai/rag
2026-07-24
查询路由(Query Routing)是 Advanced RAG 中的预检索决策策略,根据用户 Query 的意图、复杂度和领域特征,动态选择最合适的检索管道和处理策略。其本质是在检索之前做一次"分诊"——不同问题走不同路径,避免一刀切的检索策略导致效果劣化。
一句话解释
根据用户查询的意图和复杂度,将其路由到最适合的检索策略(精准关键词、向量检索、混合检索、子查询分解等),让每种类型的问题都走最优处理路径。
核心问题
为什么需要 Query Routing?
- 单一策略无法覆盖所有 Query 类型:精确关键词查询和开放性语义查询的最优检索策略完全不同
- 资源效率:简单问题不需要复杂的多路召回 + Rerank 管道,直接精准匹配更快更省
- 多数据源场景:企业环境中往往有多个知识库(产品文档、FAQ、工单记录、代码库),不同 Query 应路由到不同数据源
- 成本优化:避免对每个 Query 都执行昂贵的 LLM 改写和多路检索
四种路由策略详解
1. 语义路由(Semantic Routing)
原理:将用户 Query 和预定义的路由类别分别 embedding,通过语义相似度匹配选择最佳路由。
工作流程:
- 预定义 N 个路由类别,每个类别有若干示例 Query
- 将路由类别的示例 Query 编码为向量,构建路由索引
- 用户 Query 到达后,编码为向量,计算与各类别的相似度
- 选择相似度最高的类别(或 Top-K 类别)作为路由目标
典型应用:
- 路由到不同 Prompt 模板(技术问题→技术回答 Prompt,闲聊→对话 Prompt)
- 路由到不同检索策略(事实性查询→精准检索,分析性查询→多路召回)
优势:无需 LLM 调用,延迟低(纯向量计算),适合高并发场景 局限:依赖预定义类别的覆盖度,对未见过的 Query 类型可能路由失败
2. 意图路由(Intent Routing)
原理:使用 LLM 或分类器对用户 Query 进行意图分类,根据意图类别选择处理策略。
与语义路由的区别:语义路由基于向量相似度(隐式匹配),意图路由基于显式的意图分类(可以是规则、分类器或 LLM 判断)。
意图分类体系示例:
| 意图类别 | 典型 Query | 路由策略 |
|---|---|---|
| 事实查询 | "Redis 的默认端口是多少" | 精准关键词检索 + 直接回答 |
| 操作指引 | "如何在 Linux 上安装 Redis" | 文档检索 + 步骤化回答 |
| 对比分析 | "Redis vs Memcached 的区别" | Query 分解 + 多路检索 |
| 故障排查 | "Redis 连接超时怎么办" | 相似问题匹配 + 知识库检索 |
| 闲聊/越界 | "今天天气怎么样" | 拒绝/转通用对话 |
实现方式:
- 轻量方案:关键词规则 + 正则匹配(延迟 <10ms,覆盖 60-70% 场景)
- 中等方案:fine-tuned 分类器(BERT 级别,延迟 ~50ms)
- 重量方案:LLM 意图识别(延迟 200-500ms,灵活度最高)
3. 复杂度路由(Complexity Routing)
原理:根据 Query 的复杂度动态调整检索和处理的深度。这是 Adaptive RAG 的核心机制。
复杂度分级:
| 复杂度级别 | 特征 | 处理策略 |
|---|---|---|
| L0 - 极简 | 单个关键词/术语 | 直接检索,无需改写 |
| L1 - 简单 | 单一明确问题 | 标准 RAG 流程 |
| L2 - 中等 | 需要多步推理或跨文档 | Query 分解 + 多路召回 + Rerank |
| L3 - 复杂 | 需要综合分析和推理 | 迭代检索 + Reflection + 多轮整合 |
Adaptive RAG 的决策逻辑:
Query 到达
├─ LLM 评估复杂度(或规则判断)
├─ L0: 直接关键词检索 → 生成回答(无需改写)
├─ L1: 标准 RAG → 单次检索 → 生成
├─ L2: Query 分解 → 多路检索 → Rerank → 生成
└─ L3: 迭代检索 → 中间验证 → 补充检索 → Reflection → 生成关键论文:Adaptive-RAG(Jeong et al., 2024)提出根据 Query 复杂度自适应选择检索策略,在简单 Query 上跳过检索直接回答,在复杂 Query 上使用多轮迭代检索,显著降低了计算成本同时提升了回答质量。
4. 数据源路由(Data Source Routing)
原理:根据 Query 的领域和主题,将检索请求路由到不同的知识库或数据源。
典型场景:
- 企业多部门知识库:技术问题→技术文档库,HR 问题→人事制度库,财务问题→财务规范库
- 多语言知识库:中文 Query→中文库,英文 Query→英文库
- 多模态数据源:文本问题→文档库,代码问题→代码库,数据问题→数据库
实现方式:
- 基于 Metadata 过滤:每个数据源有唯一标识,Query 分类后附加过滤条件
- 基于向量空间隔离:不同数据源使用不同的 embedding 索引,按路由选择查询哪个索引
- 基于 LangChain MultiRetriever:使用
MultiRetriever或RouterRetriever实现自动路由
路由决策树设计
一个实用的综合路由决策框架:
用户 Query 到达
│
├─ Step 1: 是否需要路由?
│ ├─ 单数据源 + 单策略 → 跳过路由,直接进入标准 RAG
│ └─ 多数据源或多策略 → 进入路由流程
│
├─ Step 2: 数据源路由(多知识库场景)
│ ├─ 语义路由:Query embedding vs 数据源描述 embedding
│ └─ 意图路由:LLM/分类器判断 Query 属于哪个领域
│
├─ Step 3: 策略路由(选定数据源后)
│ ├─ 复杂度判断:规则/LLM 评估 Query 复杂度
│ ├─ L0/L1 → 标准检索
│ ├─ L2 → Query 分解 + 多路召回
│ └─ L3 → 迭代检索 + 反思
│
└─ Step 4: 回退机制
├─ 路由置信度低 → 使用全策略并行检索 + Rerank
└─ 路由失败 → 回退到默认策略路由失败的回退机制
路由不是万能的,必须有健壮的失败处理:
- 置信度阈值:语义路由的相似度低于阈值时,不路由到单一策略,而是多策略并行
- 默认策略兜底:始终保留一个"全策略并行 + Rerank"的兜底路径
- 路由结果验证:检索后用 Rerank 分数验证路由是否合理,分数过低则触发重新路由
- 渐进式降级:复杂策略超时或失败时,自动降级到更简单的策略
与 Query Rewrite 的协同
路由和改写的执行顺序是一个关键设计决策:
| 顺序 | 逻辑 | 优势 | 劣势 |
|---|---|---|---|
| 先路由再改写 | 根据原始 Query 判断类型,然后在对应管道内做改写 | 路由判断更快(未改写的 Query 更"原始"),改写策略可以按路由结果定制 | 原始 Query 可能表述不规范,影响路由准确性 |
| 先改写再路由 | 先规范化 Query,再根据改写后的 Query 判断路由 | 路由基于更规范的 Query,准确性更高 | 增加一次 LLM 调用的延迟和成本 |
| 并行执行 | 路由和改写同时进行,改写结果用于验证路由 | 延迟最优 | 实现复杂度高 |
推荐实践:对于规则路由和语义路由,先路由再改写(延迟低);对于意图路由且 Query 表述可能不规范时,先做轻量规范化再路由。
在 Modular RAG 中的位置
Query Routing 位于 Modular RAG 架构的检索前处理阶段,是连接"用户输入"和"检索器"的决策层:
用户输入 → [Query Routing] → 选定检索策略 → [Retriever] → [Reranker] → [Generator]
↑
路由决策依据:
- Query 语义/意图
- Query 复杂度
- 数据源特征
- 历史路由效果在更复杂的 Agentic RAG 架构中,路由本身也可以由 Agent 动态决策,而非静态规则。
工程实现要点
LangChain 实现
# 语义路由示例
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
# 定义路由类别和示例
routes = {
"technical": ["如何配置...", "为什么报错...", "API 用法..."],
"conceptual": ["什么是...", "解释一下...", "...的原理"],
"operational": ["怎么部署...", "如何安装...", "运维步骤..."],
}
# 构建路由索引
embeddings = OpenAIEmbeddings()
route_texts = []
route_labels = []
for label, examples in routes.items():
route_texts.extend(examples)
route_labels.extend([label] * len(examples))
route_index = FAISS.from_texts(route_texts, embeddings)
# 路由决策
def route_query(query: str, threshold: float = 0.85) -> str:
results = route_index.similarity_search_with_score(query, k=1)
best_doc, score = results[0]
# 注意:score 的语义取决于 VectorStore 实现(distance vs similarity)
# FAISS 的 similarity_search_with_score 返回的是 L2 距离(越小越相似)
# 若使用 similarity_search_with_relevance_scores,返回值越大越相似
if score > threshold: # 对于距离型 score,应改为 score < threshold
return "default" # 回退到默认策略
return route_labels[route_texts.index(best_doc.page_content)]性能与成本考量
| 路由方式 | 延迟 | 成本 | 灵活性 | 推荐场景 |
|---|---|---|---|---|
| 规则路由 | <10ms | 零 | 低 | 高并发、Query 类型固定 |
| 语义路由 | ~50ms | 仅 embedding | 中 | 多数据源、多策略 |
| LLM 意图路由 | 200-500ms | LLM 调用 | 高 | 复杂场景、Query 多样 |
常见误区
- 所有场景都加路由:单数据源、单策略的简单 RAG 不需要路由,增加复杂度无收益
- 路由类别过细:类别太多会导致语义路由的相似度分散,反而降低准确性
- 忽视回退机制:路由失败时的兜底策略比路由本身更重要
- 路由后不验证:路由选择是否正确需要检索结果来验证,不能"路由完就结束"
- 用大模型做简单路由:规则和语义路由能解决的场景,不需要 LLM 意图分类
可继续补充的方向
- Adaptive RAG 的完整实现与评估
- 路由策略的 A/B 测试框架
- 多租户场景下的动态路由配置
- 路由与缓存策略的结合(高频路由结果缓存)