Advanced RAG 进阶:Self-RAG、CRAG、Adaptive RAG 对比 Advanced RAG 进阶:Self-RAG、CRAG、Adaptive RAG 对比一、从 Naive RAG 到 Advanced RAG2023 年以来,检索增强生成(Retrieval-Augmented Generation, RAG)已经成为大模型落地的标配方案。它的核心思路简单:把用户问题先拿去检索相关文档,再把文档作为上下文注入 LLM,让模型基于准确的私有知识回答,从而缓解幻觉问题。然而,做过 RAG 工程的同学很快就会发现——Naive RAG 有三大硬伤:检索噪声:向量检索召回的文档不一定都相关,无关文档反而会把模型带偏无反馈机制:一次检索定终身,检索质量差就直接翻车,没有补救机会一刀切策略:简单问题(“什么是 Transformer”)和复杂问题(“对比 Self-RAG 和 CRAG 在多跳推理上的差异”)用同一套检索流程这些问题驱动了Advanced RAG的诞生。本文将深入剖析三种主流进阶方案——Self-RAG、CRAG、Adaptive RAG——覆盖原理、代码实现和实际落地的坑。二、Self-RAG:让模型学会"自我反思"2.1 核心思想Self-RAG(Self-Reflective RAG)的 insight 非常朴素:让 LLM 在生成过程中自己判断什么时候该检索、检索结果是否相关、生成内容是否可靠。它通过特殊的反思标记(reflection tokens)实现这一能力。这些标记是在训练阶段学到的,分为两类:标记类型取值含义Retrieve[Retrieve]/[NoRetrieve]是否需要检索ISREL[Relevant]/[Irrelevant]检索文档是否相关ISSUP[FullySupported]/[PartiallySupported]/[NoSupport]生成内容是否有据可查ISUSE[5]~[1]生成结果的实用性评分2.2 工作流程Self-RAG 的推理循环可以概括为四个步骤:用户问题 ↓ Step 1: 判定是否需要检索(predict [Retrieve] / [NoRetrieve]) ↓ (若需检索) Step 2: 检索 Top-K 文档片段,逐段评估 ISREL ↓ Step 3: 基于相关片段并行生成多个候选回答 ↓ Step 4: 对每个候选评估 ISSUP + ISUSE,选出最优 ↓ 最终输出这里的关键设计是Step 3 的并行生成 + Step 4 的反思择优。相比传统 RAG 的"检索→拼接→一次生成"的串行模式,Self-RAG 给了模型自我纠错的空间。2.3 实战实现下面用 LangChain + HuggingFace 演示 Self-RAG 的核心逻辑(简化版,去掉了实际训练部分,聚焦推理流程):importtorchfromtransformersimportAutoTokenizer,AutoModelForCausalLMfromtypingimportList,TupleclassSelfRAGPipeline:""" Self-RAG 推理管线(简化实现) 实际 Self-RAG 需要微调过的模型,这里展示流程框架 """REFLECTION_TOKENS={"retrieve":"RETRIEVE","no_retrieve":"NO_RETRIEVE","relevant":"RELEVANT","irrelevant":"IRRELEVANT","fully_supported":"FULLY_SUPPORTED","partially_supported":"PARTIALLY_SUPPORTED","no_support":"NO_SUPPORT",}def__init__(self,model_name:str,retriever,threshold:float=0.5):self.retriever=retriever self.threshold=threshold# 实际应加载 Self-RAG 微调模型,这里用通用模型演示defshould_retrieve(self,query:str)-bool:""" Step 1: 判断是否需要检索 实际 Self-RAG 用模型预测特殊 token,这里简化 """# 简单启发式:包含特定领域术语则检索domain_keywords=["RAG","transformer","向量","embedding","检索","微调","LLM","大模型"]returnany(kw.lower()inquery.lower()forkwindomain_keywords)defevaluate_relevance(self,query:str,documents:List[str])-List[Tuple[str,bool]]:""" Step 2: 评估每个文档片段的相关性 (ISREL) 返回 (doc, is_relevant) 的列表 """results=[]fordocindocuments:# 实际用模型预测 RELEVANT 或 IRRELEVANT# 这里用简单的 token overlap 模拟query_tokens=set(query.lower().split())doc_tokens=set(doc.lower().split())overlap=len(query_tokensdoc_tokens)/len(query_tokens)ifquery_tokenselse0results.append((doc,overlap0.3))returnresultsdefgenerate_with_reflection(self,query:str,documents:List[str],num_candidates:int=3)-Tuple[str,dict]:""" Step 3 4: 基于相关文档生成多个候选并择优 (ISSUP + ISUSE) """# 过滤相关文档relevant_docs=[docfordoc,is_relinself.evaluate_relevance(query,documents)ifis_rel]ifnotrelevant_docs:return"抱歉,未找到相关信息。",{"source":"no_relevant_docs"}# 并行生成多个候选(简化:用不同文档组合)