游乐游手机版
首页/AI热点日报/热点详情

Self-RAG实战:自我反思提升内容生成质量

类型:热点整理2026-06-26
Self-RAG让大模型在生成文本时自主判断是否需要检索,并评估检索结果的相关性与支持度,同时对其实用性打分。内置“内部质检员”机制,可有效过滤不相关信息,提升内容生成的准确性和完整性。

在之前关于RAG系统的系列文章中,我们系统梳理了从索引、查询转换、路由、查询构建到检索召回各个环节的优化路径。有了这些基础,搭建一个“能跑”的Naive RAG已经不成问题。但真正把RAG推向生产环境,大家往往会发现,最终生成环节才是真正的“试金石”。

输出格式乱掉,比如让生成表格却输出了纯文本;回答不完整,甚至在对比类问题上答非所问;更头疼的是,偶尔还会冒出一些“整治不正确”或不符合社会偏好的内容——这些问题,几乎是每个RAG实践者都会遇到的“坎”。

这篇文章,我们就聚焦在生成环节(下图右下角的紫色区域),介绍一种非常高效、且经得起推敲的优化方案:Self-RAG。

RAG实战篇:Self-RAG,通过自我反思大幅提升内容生成质量

Self-RAG的概念

想象一下你正在写一篇论文,写到一半突然对某个事实不太确定,你大概率会停下来,上网查证,再继续写。写完后,你可能还会回头审视某个段落,觉得逻辑不顺或证据不足,于是删掉重写。Self-RAG干的事,本质上就是把这个“写论文”的过程,内化到语言模型的生成逻辑里。

它的核心思想其实很朴素:让大模型在生成文本时,自己就能“停下来”问自己——我需要更多信息吗?我生成的内容足够好吗?相当于给模型配了一个随时盯场的“内部质检员”,一边写一边自我审核。

实现这个机制的关键,在于几组被称为“反思标记”的控制信号。它们为LLM指明了思考方向,具体包括下面四个标记:

  • 检索需求标记(Retrieve):决定当前是否需要从外部数据源拉信息。选项包括“是”、“否”,还有一个“继续使用证据”——意思是之前查到的信息还能接着用,先不用动。
  • 相关性标记(ISREL):判断检索到的信息跟当前输入是否相关。二分法,“相关”或“不相关”。
  • 支持度标记(ISSUP):评估检索到的信息是否支撑了模型的生成内容。分三个等级:完全支持、部分支持、无支持或矛盾。
  • 实用性标记(ISUSE):用1到5分(最常用五级标准)打分,评价生成内容的整体效用。

上面的流程图可以直观展示整个流程。为了更清晰,我们来看一段伪代码,它把执行过程讲得明明白白:

输入:接收输入提示(x)和之前生成的文本(y

从这段伪代码可以很清晰地看到,四大类标记是如何一关一关地介入检索与生成的。它不改变模型本身,而是在模型输出前后加了一道“自我检查”的工序。

Self-RAG的代码实现

下面进入工程环节。在参考Self-RAG原始思路的基础上,我们做了一个“轻量级”的实现——并没有完全照搬论文里的全部设计。比如,我们默认所有情况下都需要检索,因此没有实现检索需求评分器;实用性评分也没有输出1~5的分数,而是简单输出了yes/no。大家如果感兴趣,完全可以自行扩展,写出更复杂的评分器。

检索相关性评分器

第一个评分器的作用是:判断检索回来的文档跟用户问题是否相关。目标是“抓大放小”,不用太严格,关键在于过滤掉那些明显检索错误的垃圾信息。

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI

# Data model
class GradeDocuments(BaseModel):
    """Binary score for relevance check on retrieved documents."""
    binary_score: str = Field(
        description="Documents are relevant to the question, 'yes' or 'no'"
    )

# LLM with function call
llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
structured_llm_grader = llm.with_structured_output(GradeDocuments)

# Prompt
system = """You are a grader assessing relevance of a retrieved document to a user question. \n 
It does not need to be a stringent test. The goal is to filter out erroneous retrievals. \n
If the document contains keyword(s) or semantic meaning related to the user question, grade it as relevant. \n
Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question."""

grade_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system),
        ("human", "Retrieved document: \n\n {document} \n\n User question: {question}"),
    ]
)

retrieval_grader = grade_prompt | structured_llm_grader
question = "agent memory"
docs = retriever.get_relevant_documents(question)
doc_txt = docs[1].page_content
print(retrieval_grader.invoke({"question": question, "document": doc_txt}))

生成相关性评分器

第二个评分器主要负责“查幻觉”——看模型生成的内容,是不是真的有被检索到的事实做支撑。如果生成的内容跟检索回来的事实对不上,那就需要标记出来,不能让它蒙混过关。

### Hallucination Grader

# Data model
class GradeHallucinations(BaseModel):
    """Binary score for hallucination present in generation answer."""
    binary_score: str = Field(
        description="Answer is grounded in the facts, 'yes' or 'no'"
    )

# LLM with function call
llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
structured_llm_grader = llm.with_structured_output(GradeHallucinations)

# Prompt
system = """You are a grader assessing whether an LLM generation is grounded in / supported by a set of retrieved facts. \n 
 Give a binary score 'yes' or 'no'. 'Yes' means that the answer is grounded in / supported by the set of facts."""

hallucination_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system),
        ("human", "Set of facts: \n\n {documents} \n\n LLM generation: {generation}"),
    ]
)

hallucination_grader = hallucination_prompt | structured_llm_grader
hallucination_grader.invoke({"documents": docs, "generation": generation})

回答质量评分器

最后一个评分器负责“终审”——看最终的回答到底有没有解决用户的问题。这个评分器输出的结果,直接决定了这个答案是否可以对外输出。

### Answer Grader

# Data model
class GradeAnswer(BaseModel):
    """Binary score to assess answer addresses question."""
    binary_score: str = Field(
        description="Answer addresses the question, 'yes' or 'no'"
    )

# LLM with function call
llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
structured_llm_grader = llm.with_structured_output(GradeAnswer)

# Prompt
system = """You are a grader assessing whether an answer addresses / resolves a question \n 
 Give a binary score 'yes' or 'no'. Yes' means that the answer resolves the question."""

answer_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system),
        ("human", "User question: \n\n {question} \n\n LLM generation: {generation}"),
    ]
)

answer_grader = answer_prompt | structured_llm_grader
answer_grader.invoke({"question": question, "generation": generation})

篇幅有限,这里只展示了最关键的三个评分器的核心代码。其余的实现逻辑,比如如何串联这些评分器、如何处理排序和重试,这里就不一一贴出了。

总结

通过这篇文章,我们完整走通了一个Self-RAG的轻量级Demo。核心就是四个评分器——检索需求评分器(本文中未实现)、检索相关性评分器、生成相关性评分器和回答质量评分器。它们共同构成了一个“生成-反思-再生成”的闭环,让RAG的输出不再是“黑盒”式的单次结果,而是经过自我审视的可靠内容。

到这一步,一个相对完整的RAG系统其实已经搭建完毕。但放眼整个RAG生态,还有两座大山注定绕不过去:一是将RAG与知识图谱结合,系统性提升推理和总结能力;二是让RAG与Agent融合,真正嵌入到实际业务流程里去。这些,也都是下一阶段值得深耕的方向。

来源:https://www.53ai.com/news/RAG/2024101402457.html

相关热点

继续查看同栏目近期热点。

延伸阅读

补充最近整理过的热点入口。