site logo

Marico's space

自适应 RAG:设计运行时选择正确策略的检索管道

AI技术与应用 2026-08-21 11:32:25 7

你的 RAG 系统正在用同一种方式处理所有查询。

不是那种灾难性的错误。是一种固定的、程序化的错误。

不管来的是什么问题——简单的知识点查询、复杂的多跳推理任务,还是模型早就知道答案根本不需要检索的情况——它都检索同样数量的文档,用同样的策略。

这种"一刀切"的方案是当前生产环境 RAG 系统中最主要的成本浪费和质量损失的来源。而且这不是模型的问题,是架构的问题。

自适应 RAG 的解决思路很简单:在真正开始检索之前,先问一个问题——这是什么类型的问题,它实际上需要什么样的检索策略?

这篇文章聊聊怎么设计一个能在运行时正确判断检索策略的管道。

1. 固定策略的问题

一个生产级别的 RAG 系统收到的查询需求差异巨大。

"公司是什么时候成立的?" 根本不需要检索。模型从训练数据里就知道答案,去检索公司历史文档只会增加延迟、成本和上下文噪音,对答案质量没有任何提升。

"我们当前的退款政策是什么?" 只需要单步检索。精准地查一遍政策文档库就能找到相关内容。搞多跳检索完全是杀鸡用牛刀。

"哪些供应商受到了 Q3 物流中断的影响,这和大型客户报告的延迟发货有什么关联?" 就需要跨多个数据源的多跳检索,而且每轮检索之间还需要中间推理步骤。

一个固定策略的 RAG 管道对这三类问题用同一种方式处理。对简单查询要么过度检索——白花冤枉钱增加延迟,质量没提升——要么对复杂查询检索不足——返回的上下文不完整,导致幻觉或答案残缺。

研究已经把这个事情说得很清楚了。Adaptive-RAG(NAACL 2024,Jeong 等人发表)证明了把查询路由到"最便宜但够用"的检索策略——不用检索、单步检索或多步检索——能够在大幅降低成本的同时达到全程多跳 baseline 的效果。大多数已部署系统对所有查询都采用同一套范式。如果能根据每个查询路由到最适合的范式,消耗的 token 量能降好几个数量级,同时精度不损失。

Retriever Portfolios 这篇论文(arXiv:2605.31176,2026年5月发表)把这个问题的精确度说清楚了:没有哪个单一检索器能对所有查询都是最优的,固定单一检索策略会在不同信息需求场景下丢失大量本可获得的性能。整个业界已经从"哪种检索策略最好?"转向了"对当前这个特定查询,哪种策略最好?"

2. 自适应 RAG 到底是什么

自适应 RAG 是一种检索架构:它的检索策略——用什么方法、什么深度、什么规模进行检索——是在运行时根据输入查询的特征来决定的,而不是在系统设计时预先固定。

它不是某个特定的算法。它是一种架构模式,包含三个设计决策:

决策1:要不要检索。 这个查询到底要不要触发检索,还是模型用内部知识就能回答?

决策2:用哪种策略。 如果需要检索,用稠密向量搜索、稀疏关键词搜索、混合搜索、图遍历,还是迭代多跳?

决策3:检索多少。 应该返回多少文档或块?对于聚焦的事实查询和综合分析任务,答案差异巨大。

自适应 RAG 在运行时回答这三个问题。架构由两个阶段组成,在标准检索管道之前执行:一个是复杂度分类器,对查询进行特征刻画;另一个是策略路由器,把分类结果映射到具体的检索配置。

路由阶段的输出不是一组检索到的文档。它是一个给检索子系统的指令:执行这个特定策略,用这些参数,查询这些数据源。

3. 查询复杂度分类体系

实现自适应 RAG 的第一步是定义系统必须区分的复杂度类别。研究社区已经收敛到一个既足够实用能落地、又足够精确能指导有意义路由决策的分类体系。

A类:不需要检索。 查询可以仅凭模型的内部知识回答,不需要外部知识支撑。简单的事实查询、众所周知的定义、在训练数据中已经有充分表示的稳定历史事件。把这类查询路由到完整 RAG 管道是浪费算力,引入的上下文噪音对模型已有的知识没有任何补充。

B类:单步检索。 需要一次精准检索的查询。政策查询、产品规格、操作文档、某个特定语料库内的定义。答案存在于一篇或少数几篇文档中,一次精准的检索就能找到。

C类:多步检索。 需要顺序检索,中间结果决定后续查询方向。"哪些客户受到了由基础设施变更引发的服务中断影响?" 需要先找到基础设施变更,再找到它引发的中断,再找到受影响的客户。每一步的输出决定下一步的查询。

D类:聚合查询。 需要在大量文档中进行综合分析,没有清晰的检索链——比如"这季度客户反馈中有哪些反复出现的主题?" 答案不在任何单篇文档里,而是从整个语料库的分析中涌现出来的。

E类:混合查询。 需要多种检索模态——结构化数据和 unstructured 文本、图关联实体和向量相似内容。策略必须同时发散到多种模态并综合结果。

针对你的具体领域把这个分类体系调准确,比实现任何特定的路由算法都重要。一个基于通用研究假设的五类分类体系,不如一个根据你实际查询分布校准的三类分类体系效果好。

4. 运行时路由器:策略选择如何工作

路由器是自适应 RAG 的核心。它接收输入查询,输出路由决策——这个查询属于哪个复杂度类别,应该执行哪种检索策略。

三种路由方法已经在研究中得到验证:

训练好的分类器路由。 一个轻量级分类器——Adaptive-RAG 原论文用的是 T5-Large,RAGRouter-Bench 研究(2026年4月发表)中用更小的模型——训练来从查询文本预测复杂度类别。训练需要标注好的查询样例,每个样例标注正确的复杂度类别。分类器很小很快很便宜——在管道中增加不到 100 毫秒,但做出的路由决策能节省几秒钟的不必要检索工作。

RAGRouter-Bench 研究发现,更轻量的分类器——基于句向量的逻辑回归、小型 transformer 分类器——在路由准确率上能媲美更大的 T5 分类器,同时部署更快更便宜。路由决策本身不需要大模型。

免训练的自适应门控。 TARG(TARG: Retrieval as a Decision,arXiv:2511.09803,2026年4月更新)引入了一种免训练方法,用模型自身的置信度信号来决定是否需要检索。如果模型在某个查询上的输出概率分布很自信——只有少数 token 有高概率——说明模型很可能已经从内部知识知道答案,不需要检索。如果分布很发散,就触发检索。

TARG 的关键发现:在涵盖简答、多跳和长文本任务的五个 QA benchmark 上,它始终能匹配或超越"始终检索"方法的 exact match 和 F1,同时大幅降低检索频率。不需要训练数据,不需要标注复杂度类别,直接用模型自身的置信度作为路由信号。

基于嵌入的相似度路由。 对于有成熟查询历史的系统,路由可以由与历史已分类查询的相似度来驱动。一个新查询被嵌入后,与已知复杂度类别的查询库进行比对。如果库中存在足够相似的查询且有已知分类,新查询就继承该分类。这种方法随时间累积价值——查询库越大,路由准确率越高。

The Router Implementation

from enum import Enum
from dataclasses import dataclass
from sentence_transformers import SentenceTransformer
import numpy as np class ComplexityClass(Enum): NO_RETRIEVAL = "no_retrieval" SINGLE_STEP = "single_step" MULTI_STEP = "multi_step" AGGREGATION = "aggregation" HYBRID = "hybrid" @dataclass
class RoutingDecision: complexity_class: ComplexityClass retrieval_strategy: str k_documents: int data_sources: list[str] confidence: float class AdaptiveRouter: def __init__(self, classifier_model: str, threshold: float = 0.75): self.encoder = SentenceTransformer(classifier_model) self.threshold = threshold def route(self, query: str) -> RoutingDecision: complexity = self._classify_complexity(query) return self._map_to_strategy(query, complexity) def _classify_complexity(self, query: str) -> ComplexityClass: multi_hop_signals = [ "which", "how does", "why did", "what caused", "relationship between", "impact of", "correlation" ] aggregation_signals = [ "themes", "patterns", "summarize all", "across all", "common", "recurring", "overall" ] simple_signals = [ "what is", "define", "when was", "who is" ] query_lower = query.lower() if any(s in query_lower for s in aggregation_signals): return ComplexityClass.AGGREGATION if any(s in query_lower for s in multi_hop_signals): return ComplexityClass.MULTI_STEP if any(s in query_lower for s in simple_signals): if self._model_likely_knows(query): return ComplexityClass.NO_RETRIEVAL return ComplexityClass.SINGLE_STEP return ComplexityClass.SINGLE_STEP def _model_likely_knows(self, query: str) -> bool: # In production: call model with low max_tokens,
 # measure output entropy as confidence signal (TARG approach)
 return False def _map_to_strategy( self, query: str, complexity: ComplexityClass ) -> RoutingDecision: strategy_map = { ComplexityClass.NO_RETRIEVAL: RoutingDecision( complexity_class=complexity, retrieval_strategy="parametric", k_documents=0, data_sources=[], confidence=0.9 ), ComplexityClass.SINGLE_STEP: RoutingDecision( complexity_class=complexity, retrieval_strategy="hybrid_search", k_documents=5, data_sources=["primary_vector_store"], confidence=0.85 ), ComplexityClass.MULTI_STEP: RoutingDecision( complexity_class=complexity, retrieval_strategy="iterative_multihop", k_documents=3, data_sources=["primary_vector_store", "graph_db"], confidence=0.80 ), ComplexityClass.AGGREGATION: RoutingDecision( complexity_class=complexity, retrieval_strategy="global_search", k_documents=20, data_sources=["primary_vector_store"], confidence=0.75 ), } return strategy_map.get(complexity, strategy_map[ComplexityClass.SINGLE_STEP])

5. 策略菜单:六种检索模式

路由器产生路由决策后,检索子系统执行相应策略。六种模式覆盖了企业级检索需求的完整空间。

模式1:参数化(无检索)。 查询直接发给 LLM,不进行任何检索增强。仅限于 A 类查询——模型内部知识足够且可靠。成本:嵌入和检索成本完全消除。

模式2:单步稠密检索。 对主向量存储执行一次向量相似度搜索。标准 RAG 管道。适用于语义内容清晰的 B 类查询。成本:一次嵌入调用,一次 ANN 搜索。

模式3:单步混合检索。 一次检索同时执行稠密向量搜索和稀疏 BM25 关键词搜索,通过 Reciprocal Rank Fusion 融合。在典型企业语料库上比纯稠密检索召回率提升 15% 到 30%。适用于同时包含语义意图和特定术语的查询。

模式4:迭代多跳检索。 多轮检索,每轮根据前一轮结果来决定。查询被分解为子查询,每个子查询执行一轮检索,结果决定下一个子查询,如此循环直到检索链满足或达到最大迭代限制。适用于需要顺序推理文档链的 C 类查询。

模式5:全局聚合搜索。 在整个语料库中广泛检索以支持综合任务。可以使用 GraphRAG 社区摘要、文档聚类或大 k 值向量检索配合激进重排。适用于 D 类聚合查询——答案从整个语料库的全局模式分析中涌现,而非来自特定文档检索。

模式6:联邦多源检索。 同时查询多种不同类型的数据源——向量存储、知识图谱、SQL 数据库、文档仓库——通过合并步骤综合结果。适用于需要多种模态信息的 E 类混合查询。

6. Adaptive-k:动态决定检索数量

即使在单一检索策略内部,检索文档的数量——k 值——也应该随查询而变化,而不是固定不变。

DynamicRAG(Sun 等人,2025年引入)自适应地为每个查询同时决定排序和检索文档数量。其核心组件是一个用强化学习训练的动态重排器,以 LLM 生成响应的质量作为奖励信号。重排器通过观察哪些 k 值产生了最好的下游答案,来学习为每个查询选择最优 k。

基于聚类的自适应检索(CAR,arXiv:2511.14769,2025年10月发表)采用了另一种思路。CAR 不训练重排器,而是分析查询-文档相似度距离的聚类模式来确定相似度分布中的自然断点。断点以上的文档被纳入;以下的被排除。k 值由相似度分布的结构决定,而不是由固定参数决定。

CAR 的直觉是正确的,而且很重要:对于聚焦的、具体的查询,相似度分布在少数高度相关的文档之后有急剧下降。对于宽泛的、模糊的查询,分布会在很多文档上缓慢衰减。分布的形状告诉你这个查询需要多少文档。

Adaptive-k 论文(Taguchi 等人实现)把这一洞察简化成了更实用的版本:先检索一个大的候选集,然后从 top 得分开始,当相似度得分下降超过定义好的阈值时就截断。这个基于阈值的截断无需训练即可实现,而且能提供大部分学习式 adaptive-k 选择的好处。

def adaptive_k_retrieval( query_embedding: list[float], vector_store, max_candidates: int = 50, similarity_drop_threshold: float = 0.15
) -> list[dict]: candidates = vector_store.similarity_search_with_score( query_embedding, k=max_candidates ) if not candidates: return [] top_score = candidates[0][1] cutoff_score = top_score - similarity_drop_threshold selected = [ doc for doc, score in candidates if score >= cutoff_score ] return selected

7. 检索专家混合:自适应融合

当多个检索策略在同一个查询上运行——稠密向量搜索、稀疏 BM25、图遍历——它们的结果必须融合成单一的排序列表。

标准的 Reciprocal Rank Fusion 使用固定权重。每种检索方法对融合排序的贡献是相等的,不管哪种对当前查询更合适。这在平均情况下能工作,但在某种检索方法明显更优的查询上会