语言模型在回答问题时,并不总是需要先查外部知识库。传统的检索增强生成(RAG)通常把“先检索、后生成”作为固定流程,无论问题是“1+1等于几”还是“最新版本的Kubernetes有哪些破坏性变更”,都会先执行一次向量检索。主动检索(Active Retrieval)要改变的就是这一点:模型在生成过程中持续评估自己的知识状态,只有当内部知识不足以产生可靠答案时,才触发检索,把外部文档作为补充证据拿进来。这样一来,简单问题可以直接作答,复杂问题可以多次检索,中间的决策完全由模型根据上下文和置信度动态完成。

一、固定检索为什么变成瓶颈
固定 RAG 流水线的核心假设是:每个问题都需要外部知识。这个假设在开放域问答、知识密集型任务里有一定道理,但落到真实产品中会暴露两个问题。第一,很多日常问题根本不需要检索。用户问“Python 怎么读取 CSV 文件”“用 JavaScript 把字符串转成小写”,模型本身就可以稳定回答,强制检索反而可能从文档库里找出过时示例、无关代码片段,干扰生成结果。第二,一些复杂问题需要多步推理和多次查找,单轮检索往往只能覆盖第一层信息,后续推理过程中暴露出的新信息需求没有被满足。
更隐蔽的问题是上下文噪声。向量检索返回的 top-k 片段并不保证与当前生成状态相关。比如用户问“Kubernetes 1.32 的 API 弃用对现有集群有什么影响”,检索器可能返回大量关于 Kubernetes 历史版本或通用概念的文档。生成器为了利用这些材料,容易写出偏离用户问题的答案。主动检索不是简单地在模型外面加一个开关,而是把检索决策放进生成循环里:模型每生成一个片段或一个 token,都可以判断当前是否有足够把握,是否需要新的证据。
从成本角度看,固定检索还意味着每次请求都要承担向量搜索、重排和上下文拼接的开销。对于高并发的对话系统,这种开销会直接转化为延迟和资源消耗。主动检索的目标是把检索次数降到最低,同时保证复杂场景下仍有足够的证据补给,这正是自适应 RAG 的核心思想。
二、模型靠什么信号判断该不该检索
主动检索的实现方式并不唯一,不同方法在决策信号和模型改造深度上有所区别。一类经典方案是训练模型输出特殊标记。Self-RAG 在词表中加入 <Retrieve> 和 <NoRetrieve> 这样的 token,让模型在生成过程中先决定是否需要取回外部文档。如果模型输出 <Retrieve>,系统就调用检索器,把结果拼入上下文;如果输出 <NoRetrieve>,则继续生成答案。这种方法需要额外的训练数据和批评模型来提供监督信号,但决策与生成共享同一个模型,端到端比较自然。
另一类方法是基于概率的触发机制,不需要重新训练生成模型。FLARE 就是一个代表:它观察模型在生成下一个 token 时的概率分布,如果某个位置的最大概率低于预设阈值,就认为模型对这个位置不确定,应该引入外部信息。此时系统用已经生成的文本作为查询,去检索相关文档,再继续生成。这种方案实现成本低,适合快速迭代,但阈值对效果影响很大,需要在精确率和召回率之间做权衡。
还有一类轻量路由方案,在生成器之前增加一个小型分类器或评分网络,根据问题本身和对话历史判断是否需要检索。比如问题里包含“最新”“今年”“实时汇率”等时间敏感或事实类信号时,分类器更倾向触发检索;而“写一首诗”“解释一下闭包”这类通用知识问题则直接进入生成器。这种方案简单可控,容易在工程上落地,但决策粒度较粗,无法覆盖生成中途突然不确定的情况。
工具调用形式的主动检索也值得关注。模型被训练成可以输出结构化请求,例如调用 search(query) 或 lookup(keyword),系统执行后把结果作为上下文继续喂给模型。这种模式在 Agent 系统中非常常见,优点是模型可以显式决定检索内容、检索时机甚至检索来源,但需要函数调用框架配合,并且要防止模型频繁发起低质量查询。
三、一个最小化的主动检索生成循环
要理解主动检索的工作方式,可以把它抽象成一个带条件的生成循环。核心组件包括一个生成器、一个检索器,以及一个决策器。决策器可以是概率阈值、分类器或模型输出的特殊标记。生成器每生成一个 token 或一小段文本,决策器就检查一次状态,判断是否触发检索。如果触发,系统用当前生成内容或原始问题构造查询,拿到文档后拼接到输入中,再继续生成。
下面这段伪代码展示了基于概率阈值的主动检索流程。它没有依赖特殊标记,只需要一个标准自回归语言模型。当模型对下一个 token 的最高预测概率过低时,就认为出现了犹豫,随即发起检索并用新文档扩充上下文。
def generate_with_active_retrieval(prompt, max_new_tokens=128, threshold=0.7):
input_ids = tokenizer.encode(prompt)
generated = []
for _ in range(max_new_tokens):
logits = model(input_ids).logits[-1]
probs = softmax(logits)
top_prob = probs.max().item()
# 模型把握不足时,先检索再继续
if top_prob < threshold:
query = tokenizer.decode(input_ids[-64:])
docs = retriever.search(query, top_k=3)
input_ids = input_ids + tokenizer.encode(format_docs(docs))
continue
next_id = sample_from(probs)
generated.append(next_id)
input_ids.append(next_id)
if next_id == eos_token_id:
break
return tokenizer.decode(generated)
这个流程虽然简单,但已经能体现主动检索的关键特性:检索次数不再由外部流程预先决定,而是由模型在生成过程中的不确定程度决定。在实际系统中,还需要设置防止无限检索的机制,比如最大检索次数、连续检索后的冷却窗口,以及对检索结果做去重和截断。否则模型在某个生僻问题上可能不断触发检索,导致延迟失控。
工程实现上,另一个实用技巧是把触发粒度从 token 级放宽到句子级或段落级。因为 token 级概率波动很大,容易产生高频繁的无效检索。可以等模型生成完一个自然句,再根据整个句子的平均置信度决定是否检索。查询也可以结合原始问题和已生成句子共同构造,比单纯用最后几十个 token 更稳定。
四、训练数据、评估指标与调优建议
如果希望模型显式输出 <Retrieve> 或 <NoRetrieve> 标记,就需要构造对应的训练数据。常用做法是先从问答数据集中找出答案需要外部知识的样本,再让一个较强的模型或批评模型判断:在生成该样本答案时,是否应该在开头或某个中间位置加入检索。训练时把检索标记作为模型需要预测的目标之一,并用批评信号指导哪些位置检索能带来增益。Self-RAG 进一步把批评模型输出的相关性、支持度和有用性等信号用于训练生成模型,使模型不仅知道何时检索,还能评估检索内容是否真的有用。
评估主动检索系统不能只看最终答案准确率。一个典型场景里,A 系统检索触发率 30%,答案准确率 80%;B 系统触发率 80%,答案准确率 82%。如果只比较准确率,两者差异很小,但 B 系统的延迟和成本可能高出一倍以上。因此需要同时记录平均检索次数、检索触发率、文档利用率、平均延迟和最终效果。对于事实密集型任务,可以设置双重策略:简单闲聊和通用编码问题直接关闭检索;事实类、实时类、多跳推理问题强制开启检索或降低触发阈值。
阈值类方法尤其需要在线评估。因为离散 token 的概率分布会受采样策略、上下文长度、甚至提示词风格影响,很难找到一个放之四海皆准的固定数值。可以在开发集上扫描阈值,观察不同区间下的准确率和召回率,再结合线上流量做小流量对照。若条件允许,用分类器单独建模“需要检索”的概率,比直接复用生成概率更可控,因为分类器可以专门学习复杂问题、事实冲突、低置信度等模式。
主动检索并不意味着完全抛弃固定 RAG。很多生产系统会采用混合策略:入口处用规则或分类器决定是否走检索增强;生成过程中用置信度判断是否需要补充检索;每次检索结果进入上下文前先做相关性过滤,避免把模型带偏。这样既保留了固定流程的稳定性,又获得了动态决策带来的成本和准确性收益。
模型决定何时检索外部信息的核心价值,在于把检索从外部固定步骤变成模型自身能力的一部分。无论采用特殊标记、概率阈值还是工具调用,目标都是让系统在需要证据时及时获取,在不需要时保持简洁。理解这个机制,对设计高效、可控的 RAG 应用会有很大帮助。