传统 RAG 的一个隐藏问题传统 RAG Pipeline 有一个从不质疑的假设:所有问题都需要检索。用户问"RAG 系统怎么评估"——检索。用户问"1 + 1 等于几"——也检索。用户问"帮我写一个求最大公约数的函数"——还是检索。后两个问题完全不需要外部知识,强行检索不仅浪费资源,还可能把无关文档塞进上下文,干扰 LLM 的判断。2023 年 Asai 等人提出的Self-RAG,用一套"反思机制"解决了这个问题:模型在生成过程中会输出特殊的反思 token,自主决定何时检索、检索到的内容是否相关、最终答案是否有据可查。Self-RAG 的四个反思 token原始论文里,Self-RAG 训练了一个能输出四种特殊 token 的模型:Token含义可能的值[Retrieve]是否需要检索?yes/no/continue[IsRel]检索到的文档与问题相关吗?relevant/irrelevant[IsSup]生成的内容有文档支撑吗?fully supported/partially supported/no support[IsUse]这个回答对用户有用吗?1~5分这四个 token 贯穿整个生成过程,让模型在不同阶段做出自适应决策,而不是盲目地"总是检索,总是使用"。工程实现上,不需要专门训练带这些特殊 token 的模型——用普通 LLM + Prompt 模拟这四个判断节点,已经能取得不错的效果。用 LangGraph 实现 Self-RAG整体流程用户问题 ↓ [decide] 需要检索吗? ├─ yes → [retrieve] 向量检索 top-4 │ ↓ │ [filter] 逐篇判断相关性,过滤无关文档 │ ↓ │ [rag_generate] 基于相关文档生成答案 │ ↓ └─ no → [direct_generate] 直接生成答案 ↓ [support_check] 回答有文档支撑吗? ↓ 输出最终答案State 设计LangGraph 的核心是 State——在节点之间流转的状态对象:classSelfRAGState(TypedDict):question:strneed_retrieve:str# "yes" | "no"retrieved_docs:list[Document]relevant_docs:list[Document]answer:strsupport_verdict:str# "supported" | "unsupported"token_count:intpath:list[str]# 记录执行路径关键节点实现节点1:检索决策(decide)RETRIEVE_DECISION_PROMPT=ChatPromptTemplate.from_messages([("system","你是一个 RAG 系统的路由决策器。判断以下问题是否需要检索外部知识库。\n\n""需要检索:涉及具体技术细节、参数、推荐选型等事实性内容\n""不需要检索:简单常识、数学计算、逻辑推理、闲聊问候\n\n""只输出 yes 或 no,不要解释。"),("human","问题:{question}"),