LLM Agent 之 RAG 的反思:放弃了压缩还是智能么?
引言:RAG 对模型智能的限制
反思来源于对 RAG(Retrieval-Augmented Generation)下模型回答的直观感受。最初我们被 ChatGPT 的能力所震惊,并不是它能背诵知识,而是模型在知识压缩后表现出的'涌现能力'。更具体到 RAG 所属的问答领域,是模型能够精准地基于上文从压缩的参数中召回并整合相应的知识,甚至进行知识外推的能力。通俗点说,它有可能生成我在任何地方都检索不到的答案!
但 RAG 当前的多数使用方法,采用只让模型基于检索到的内容进行回答的方案,其实限制了模型自身对知识压缩形成的智能。大模型似乎变成了文本抽取和总结润色的工具。体验上大模型直接回答的效果就像是学霸答题文采四溢,而 RAG 有时倒像是学渣开卷考试,答得小心翼翼一有不慎还会抄错答案。
既要保证事实性,又要保留模型智能,则需要最大化地使用模型已经内化压缩到参数中的信息,只在需要使用外部知识增强的时候再进行工具调用。以下将详细介绍几种主流方案。
策略概览
- Detection(前置判断):通过前置判断,决策模型何时需要使用外挂。在模型可以自行回答的时候,使用模型回答;当模型不能回答的时候走 RAG 检索生成。
- Realtime Data:需要获取动态世界的信息,部分场景可以通过意图进行决策,相对好解决。
- Incorrect or Incomplete:模型不知道,或者模型推理幻觉,如何知道模型可能不知道是更难解决的问题。
- Calibration(后置处理):通过后置处理,让模型先生成,再使用召回内容对模型回答进行修正、校准和事实性检查。
- 混合方案:高置信度判断模型可以自行完成直接回答,中置信度先生成再校验,低置信度直接走 RAG 检索生成,或者通过意图和场景进行决策。
前置判断:Detection
检测模型回答存在幻觉可以通过检索外部知识进行校验,不过考虑生成式模型覆盖问题的广泛性,Self-Contradictory 论文中评估 ChatGPT 生成的回答中 38.5% 的内容无法通过 Wiki 等外部知识进行校验。
因此这里我们先介绍一种完全基于模型自身,不依赖外部知识的幻觉判断方案——自我矛盾。后介绍一种模型直接拒绝回答的方案,和 RLHF 里面的事实性原则类似,这里是基于 SFT 的模型自我拒绝方案。
自我矛盾(Self-Contradiction)
第一种发现模型幻觉的方案是基于模型多次回答的不一致性来判断模型是否在胡说八道。相似的概念在 Self-Consistency CoT 时就提到过,该论文是使用多路 COT 推理来投票出一个最合理的推理路径,从而提高思考的准确率。这里只不过改变了使用的形式,通过模型多次回答的不一致来判断模型是否出现了幻觉。
单模型推理
主要参考论文包括 SELF-CHECKGPT: Zero-Resource Black-Box Hallucination Detection for Generative Large Language Models 和 SELF-CONTRADICTORY HALLUCINATIONS OF LLMS: EVALUATION, DETECTION AND MITIGATION。
对于如何度量模型随机生成的多个回答之间的不一致性,Self-Check 尝试了包括 Bert 相似度计算在内的 5 种方法,其中效果最好的两种分别是传统 NLI(Natural Language Inference)和基于大模型 Prompt 的 NLI。从推理性价比上传统 NLI 有优势,效果上 LLM 更好。
传统 NLI 推理任务,是给定前提(premise)判断假设(hypothesis)是否成立或者矛盾。这里论文就是使用 MNLI 数据训练的 DeBERTa-v3-Large 来判断模型生成的回答 r(hypothesis),是否和其他 N 个采样生成的回答 (premise) 相矛盾。论文分别尝试了句子级的判断和整个回答粒度的判断,句子级别的效果显著更好。
而基于大模型 Prompt,同样是 NLI 任务的思路,只不过改成了自然语言指令。Context 等同于以上的 Sn,sentence 就是 Ri,大模型推理返回的 Yes/NO 会被转化成 0/1,并计算均值。
多模型问答
另一种思路是通过多模型对话的方式来进行。DeepMind 的 LM vs LM: Detecting Factual Errors via Cross Examination 采用了模型 B 多次反复提问模型 A 的方式来生成多个回答。类似的方式也用于问卷中问题的设计,出题人会用不同的方式把一个问题问好几遍,如果每次回答都不一样,说明做题人对类似问题的回答是不确定的。
第一步模型 A 先生成回答 (claim)。第二步模型 B 会针对 claim,从多个角度生成提问并让模型 A 再次进行回答。第三步模型 B 会基于 A 的原始回答,和对多个问题的回答来判断原始回答的正确性。以上 B 提问 A 回答的步骤,如果 B 判断需要进行补充提问的话,可能会重复多次。
这里涉及到的三个任务都是通过大模型指令来进行的,三个任务分别是:模型 B 基于 A 的 claim 进行提问,模型 B 判断是否继续提问,模型 B 基于 A 的所有回答判断 claim 是否正确。
相比上面 SELF-CHECK 随机解码生成多个答案的方案,从多角度进行提问,个人感觉更有针对性,但两种方法都会有遗漏和误伤。推理成本上 SELF-CHECK 更低,LM vs LM 更高。
自我拒绝(Self-Rejection)
除了通过不一致性判断模型出现幻觉,另一种更干脆直接的方案,是让模型在碰到自己不确定的问题时,直接选择拒绝回答,和 RLHF 中的事实性原则是一个思路。但我对这类方案最大的疑惑是拒识能力的泛化性。究竟模型是学到了对于自身 parametric knowledge 置信度较低、混淆度较高的问题进行拒绝回答,还是模型背下来了对某些知识和上文语义空间进行拒绝回答。
所以这里我们绕过这个问题,聊一种中间策略。R-Tuning 提出指令微调可能放大了模型的回答幻觉。因为指令微调的数据集中所有问题都有答案,微调任务就是负责教会模型各种任务范式,以及在不同的任务中如何召回预训练中学习的知识并回答问题。但我们忽略了 SFT 中很多任务涉及到的知识在模型预训练中可能是没接触过的,但我们依旧选择让模型去进行回答。这种预训练和指令微调间的不一致性,可能会进一步放大模型幻觉。
R-Tuning 给出的解决方案是在构建指令微调数据集时,加入模型是否对该答案表示肯定的描述,这样允许模型拒绝自己不确定的问题。分成 2 个步骤:
- 找到模型不确定的问题,论文尝试了两种方案:
- R-Tuning:模型回答和标注答案不一致,适用于有标准答案的 QA 问题。
- R-Tuning-U:模型回答自我矛盾,这里论文计算模型回答包含的所有答案的熵值。
- 构建允许模型拒绝的指令数据集,论文也尝试了以下两种 prompt 指令模板:
- R-Tuning:'Q:{Question},A:{Answer}.{Prompt}.',其中 prompt 是 Are you sure you accurately answered the question based on your internal knowledge。对于上面模型确定的问题加上 I am sure,不确定的问题加上 I am not sure。
- R-Tuning-R:对于确定给的问题使用"Q:{Question},A:{Answer}",对于不确定的问题用 I am not sure 的各种相似表达来直接替换 Answer。
然后使用以上加入模型不确定性表达的数据集进行指令微调即可。在我们的使用场景中 R-Tuning-R 这种直接拒绝的方案更加合适,毕竟我倾向于指令微调的核心并不是知识注入,而是任务对齐,所以模型只要学习到对于自己不确定的问题选择拒绝回答即可。在论文验证的 MMLU 等数据集上这种拒绝微调方案有一定的领域外的泛化效果,不过这些数据集和我们的使用场景相差很大,具体效果要等测试后才知道了。
后置处理:Calibration
除了前置判断,Calibration 方案侧重于让模型先生成,再使用召回内容对模型回答进行修正校准和事实性检查。这通常涉及以下步骤:
- 生成草稿:模型基于内部知识首先生成一个初步回答。
- 证据检索:根据生成的关键实体或论点,检索相关文档片段。
- 一致性校验:将检索到的内容与生成内容进行比对,标记冲突点。
- 重写与引用:模型根据校验结果重写回答,并强制要求引用来源。
这种方法的优势在于保留了模型的创造性,同时利用外部知识纠正事实错误。实现时可以使用 Chain-of-Thought 引导模型先列出观点,再逐一验证。
混合策略与最佳实践
在实际工程中,单一方案往往难以兼顾效率与准确性。建议采用混合策略:
- 高置信度场景:如常识性问题、通用逻辑推理,直接调用模型,减少延迟和 Token 消耗。
- 中置信度场景:先生成回答,并行检索关键信息,通过轻量级校验器判断是否需要修正。
- 低置信度场景:如专业医疗、法律、实时新闻,直接触发 RAG 流程,强制模型基于检索内容回答。
此外,还可以引入人类反馈机制(RLHF),在系统上线初期收集用户纠错数据,持续优化 Detection 和 Calibration 模块的阈值。
代码示例:基础幻觉检测逻辑
以下是一个简化的 Python 伪代码示例,展示如何使用多轮采样检测一致性:
import random
from typing import List, Dict
def generate_response(model, prompt, n_samples=5):
responses = []
for _ in range(n_samples):
response = model.generate(prompt, temperature=0.7)
responses.append(response)
return responses
def check_consistency(responses: List[str]) -> bool:
"""
简单的一致性检查:比较不同回答的核心语义
实际应用中应结合 NLI 模型或 Embedding 相似度
"""
if len(responses) < 2:
return True
avg_similarity = calculate_avg_similarity(responses)
return avg_similarity > 0.8
def detect_hallucination(model, query: str) -> Dict:
responses = generate_response(model, query)
is_consistent = check_consistency(responses)
if is_consistent:
return {"status": "safe", "answer": responses[0]}
else:
return {"status": "hallucination_risk", : responses}
结论
RAG 技术的核心挑战在于如何在引入外部知识的同时,不牺牲大模型本身的参数化智能。单纯的检索增强容易导致模型退化为提取器,而纯生成模式又面临幻觉风险。
通过前置的 Detection(自我矛盾、自我拒绝)和后置的 Calibration(证据校验、重写),我们可以构建一个更加鲁棒的 Agent 系统。未来的方向在于更细粒度的置信度估计,以及端到端的联合优化,使得模型能够自适应地决定何时信任内部知识,何时调用外部工具。开发者应根据具体业务场景,灵活组合上述方案,在成本、延迟和准确性之间找到最佳平衡点。