《Advanced RAG》-10-Corrective Retrieval Augmented Generation (CRAG)

摘要

CRAG 设计了一个轻量级检索评估器,用于评估针对特定查询检索到的文档的整体质量,并使用网络搜索作为改进检索结果的辅助工具。CRAG 可与基于 RAG 的各种方法无缝集成,并提供了一个插件式的解决方案。

CRAG 的主要思想是引入一个检索评估器,用于评估检索文档与查询之间的关系,并根据评估结果分为三种情况:正确、不正确和模棱两可。

  • 对于正确的情况,使用知识细化算法重写检索文档;
  • 对于不正确的情况,使用网络搜索引擎检索外部知识;
  • 对于模棱两可的情况,使用知识细化算法和搜索引擎进行处理。

文章要点

  1. CRAG 是一种改进传统 RAG 方法的技术,可以避免引入不相关信息,提高模型获取准确知识的能力。
  2. CRAG 设计了一个轻量级检索评估器,用于评估检索文档的质量,并使用网络搜索作为辅助工具。
  3. CRAG 可与基于 RAG 的各种方法无缝集成,并提供了一个插件式的解决方案。
  4. CRAG 的主要思想是引入一个检索评估器,用于评估检索文档与查询之间的关系,并根据评估结果分为三种情况:正确、不正确和模棱两可。
  5. CRAG 的代码已开源,并提供了 Langchain 和 LlamaIndex 的实现。

本文从一个常见的场景开始:参加开卷考试。

通常,我们有三种策略:

  • 方法 1:快速回答熟悉的题目。对于不熟悉的题目,可参考参考书。快速找到相关章节,在头脑中进行整理和归纳,然后将答案写在试卷上。
  • 方法 2:针对每个题目,参考书目。找出相关章节,在脑海中进行总结,然后将答案写在试卷上。
  • 方法 3:针对每个主题,查阅本书并确定相关章节。在形成观点之前,将收集到的信息分为三类:正确、不正确和模糊。分别处理每一类信息。然后,根据这些处理过的信息,在头脑中进行梳理和总结。在试卷上写下你的答案。

方法 1 涉及Self-RAG,而方法 2 则是传统的 RAG 流程。

最后,本文将介绍方法 3,即修正检索增强生成(CRAG)。

CRAG 的动机

在这里插入图片描述

图 1 显示,大多数传统的 RAG 方法并不考虑文档与问题的相关性,而只是简单地合并检索到的文档。这可能会引入不相关的信息,从而阻碍模型获取准确的知识,并可能误导模型,导致幻觉问题。

此外,大多数传统的 RAG 方法将整个检索文档作为输入。然而,在这些检索文档中,有很大一部分文本往往不是生成所必需的,也不应该同样参与到 RAG 中。

CRAG 的主要理念

CRAG 设计了一个轻量级检索评估器,用于评估针对特定查询检索到的文档的整体质量。它还将网络搜索作为改进检索结果的辅助工具。

CRAG 即插即用,可与基于 RAG 的各种方法无缝集成。整体架构如图 2 所示。

在这里插入图片描述

如图 2 所示,CRAG 通过引入检索评估器来评估检索文档与查询之间的关系,从而增强了传统的 RAG。

有 3 种可能的判断结果。

  1. 如果它是正确的,这意味着检索到的文档包含查询所需的必要内容,然后采用知识细化算法重写检索到的文档
  2. 如果检索到的文档不正确,这就意味着查询和检索到的文档不相关。因此,我们不能将文档发送给 LLM。在 CRAG 中,我们使用网络搜索引擎来检索外部知识。
  3. 对于模棱两可的情况,这意味着检索到的文件可能很接近,但不足以提供答案。在这种情况下,就需要通过网络搜索来获取更多信息。因此,需要同时使用知识提炼算法和搜索引擎。

最后,经过处理的信息被转发到 LLM,以生成响应。图 3 正式描述了这一过程。

在这里插入图片描述

请注意,网络搜索并不直接使用用户的输入查询进行搜索。相反,它会构建一个提示,并以少量样本的方式呈现给GPT-3.5 Turbo,以获取搜索的查询。

在对该方法有了总体了解之后,让我们逐一讨论 CRAG 的两个关键组成部分:检索评价器和知识完善算法。

检索评估(Retrieval Evaluator)

如图 4 所示,检索评价器对后续程序的结果有重大影响,是决定整个系统性能的关键。

在这里插入图片描述

CRAG 采用轻量级的 T5-large 模型作为检索评估器,并对其进行微调。值得注意的是,在大型语言模型时代,T5-large 也被认为是轻量级的。

对于每个查询,通常会检索到十个文档。然后,将查询与每个文档单独连接起来,作为预测文档相关性的输入。在微调过程中,为正样本赋予 1 的标签,为负样本赋予 -1 的标签。在推理过程中,评价器会给每个文档分配一个相关性分数,范围从 -1 到 1 不等。

这些分数将根据阈值分为三个等级。显然,这种分类需要两个阈值。在 CRAG 中,阈值的设置可能会根据实验数据的不同而有所变化:

触发三项行动之一的两个置信度阈值是根据经验设定的。具体来说,

  • 在 PopQA 中,它们被设定为(0.59, -0.99);
  • 在 PubQA 和 ArcChallenge 中,它们被设定为(0.5, -0.91);
  • 在 Biography 中,它们被设定为(0.95, -0.91)。

知识提炼算法

对于检索到的相关文档,CRAG 设计了一种分解–再分解的知识提取方法,以进一步提取最关键的知识语句,如图 4 所示。

首先,采用启发式规则将每篇文档分解成细粒度的知识条,以获得细粒度的结果。如果检索到的文档仅由一两个句子组成,则将其视为一个独立单元。否则,文档会被分割成更小的单元,通常由几个句子组成,具体取决于总长度。每个单元都应包含一条独立的信息。

接下来,检索评价器会计算每个知识条的相关性得分。相关性得分低的知识条带会被过滤掉。然后将剩余的相关知识条重新组合,形成内部知识。

代码解释

CRAG 是开源的,Langchain 和 LlamaIndex 都提供了自己的实现。我们将以 LlamaIndex 的实现 作为解释的参考。

环境配置

(base) Florian:~ Florian$ conda create -n crag python=3.11
(base) Florian:~ Florian$ conda activate crag
(crag) Florian:~ Florian$ pip install llama-index llama-index-tools-tavily-research
(crag) Florian:~ Florian$ mkdir "YOUR_DOWNLOAD_DIR"

安装完成后,LlamaIndex 和 Tavily 的相应版本如下:

(crag) Florian:~ Florian$ pip list | grep llama
llama-index                             0.10.29
llama-index-agent-openai                0.2.2
llama-index-cli                         0.1.11
llama-index-core                        0.10.29
llama-index-embeddings-openai           0.1.7
llama-index-indices-managed-llama-cloud 0.1.5
llama-index-legacy                      0.9.48
llama-index-llms-openai                 0.1.15
llama-index-multi-modal-llms-openai     0.1.5
llama-index-packs-corrective-rag        0.1.1
llama-index-program-openai              0.1.5
llama-index-question-gen-openai         0.1.3
llama-index-readers-file                0.1.19
llama-index-readers-llama-parse         0.1.4
llama-index-tools-tavily-research       0.1.3
llama-parse                             0.4.1
llamaindex-py-client                    0.1.18

(crag) Florian:~ Florian$ pip list | grep tavily
llama-index-tools-tavily-research       0.1.3

测试代码

测试代码如下。第一次执行需要下载 CorrectiveRAGPack。

import os
os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"

from llama_index.core import Document


# Option: Download CorrectiveRAGPack
# The first execution requires the download of CorrectiveRAGPack
# Subsequent executions can comment this out.
from llama_index.core.llama_pack import download_llama_pack
CorrectiveRAGPack = download_llama_pack(
    "CorrectiveRAGPack", "YOUR_DOWNLOAD_DIR"
)


# Create testing documents
documents = [
    Document(
        text="A group of penguins, known as a 'waddle' on land, shuffled across the Antarctic ice, their tuxedo-like plumage standing out against the snow."
    ),
    Document(
        text="Emperor penguins, the tallest of all penguin species, can dive deeper than any other bird, reaching depths of over 500 meters."
    ),
    Document(
        text="Penguins' black and white coloring is a form of camouflage called countershading; from above, their black back blends with the ocean depths, and from below, their white belly matches the bright surface."
    ),
    Document(
        text="Despite their upright stance, penguins are birds that cannot fly; their wings have evolved into flippers, making them expert swimmers."
    ),
    Document(
        text="The fastest species, the Gentoo penguin, can swim up to 36 kilometers per hour, using their flippers and streamlined bodies to slice through the water."
    ),
    Document(
        text="Penguins are social birds; many species form large colonies for breeding, which can number in the tens of thousands."
    ),
    Document(
        text="Intriguingly, penguins have excellent hearing and rely on distinct calls to identify their mates and chicks amidst the noisy colonies."
    ),
    Document(
        text="The smallest penguin species, the Little Blue Penguin, stands just about 40 cm tall and is found along the coastlines of southern Australia and New Zealand."
    ),
    Document(
        text="During the breeding season, male Emperor penguins endure the harsh Antarctic winter for months, fasting and incubating their eggs, while females hunt at sea."
    ),
    Document(
        text="Penguins consume a variety of seafood; their diet mainly consists of fish, squid, and krill, which they catch on their diving expeditions."
    ),
]


from llama_index.packs.corrective_rag import CorrectiveRAGPack
corrective_rag = CorrectiveRAGPack(documents, "YOUR_TAVILYAI_API_KEY")


# From here, you can use the pack, or inspect and modify the pack in ./corrective_rag_pack.
# The run() function contains around logic behind Corrective Retrieval Augmented Generation - CRAG paper.
query = "How tall is the smallest penguins?"
print('-' * 100)
print("The response of the query " + query + " is:")
response = corrective_rag.run(query, similarity_top_k=2)
print(response)

其中 YOUR_TAVILYAI_API_KEY 可通过tavilyai网站申请。

测试代码产生了以下结果(大部分调试信息已被删除):

(crag) Florian:~ Florian$ python /Users/Florian/Documents/crag.py
--------------------------------------------------------------------------------------
The response of the query How tall is the smallest penguins? is:
--------------------------------------------------------------------------------------
The smallest penguins are about 40 cm (16 inches) tall.

理解测试代码的关键在于 corrective_rag.run() 的实现,让我们深入了解一下。

类 CorrectiveRAGPack 的构造函数

首先,我们来看看构造函数,其源代码如下:

class CorrectiveRAGPack(BaseLlamaPack):
    def __init__(self, documents: List[Document], tavily_ai_apikey: str) -> None:
        """Init params."""
        llm = OpenAI(model="gpt-4")
        self.relevancy_pipeline = QueryPipeline(
            chain=[DEFAULT_RELEVANCY_PROMPT_TEMPLATE, llm]
        )
        self.transform_query_pipeline = QueryPipeline(
            chain=[DEFAULT_TRANSFORM_QUERY_TEMPLATE, llm]
        )

        self.llm = llm
        self.index = VectorStoreIndex.from_documents(documents)
        self.tavily_tool = TavilyToolSpec(api_key=tavily_ai_apikey)

请注意,默认设置为 gpt-4。如果没有权限使用 gpt-4,可以手动将其切换为 gpt-3.5-turbo。

类 CorrectiveRAGPack:: run()

函数 run() 的源代码如下:

class CorrectiveRAGPack(BaseLlamaPack):
    ...
    ...
    def run(self, query_str: str, **kwargs: Any) -> Any:
        """Run the pipeline."""
        # Retrieve nodes based on the input query string.
        retrieved_nodes = self.retrieve_nodes(query_str, **kwargs)

        # Evaluate the relevancy of each retrieved document in relation to the query string.
        relevancy_results = self.evaluate_relevancy(retrieved_nodes, query_str)
        # Extract texts from documents that are deemed relevant based on the evaluation.
        relevant_text = self.extract_relevant_texts(retrieved_nodes, relevancy_results)

        # Initialize search_text variable to handle cases where it might not get defined.
        search_text = ""

        # If any document is found irrelevant, transform the query string for better search results.
        if "no" in relevancy_results:
            transformed_query_str = self.transform_query_pipeline.run(
                query_str=query_str
            ).message.content
            # Conduct a search with the transformed query string and collect the results.
            search_text = self.search_with_transformed_query(transformed_query_str)

        # Compile the final result. If there's additional search text from the transformed query,
        # it's included; otherwise, only the relevant text from the initial retrieval is returned.
        if search_text:
            return self.get_result(relevant_text, search_text, query_str)
        else:
            return self.get_result(relevant_text, "", query_str)

上述代码与标准 CRAG 流程有三个主要区别:

  1. 无需判断或处理模棱两可的文件。
  2. 它不使用训练有素的 T5 大模型,而是利用 LLM 来评估检索到的信息。
  3. 跳过了知识提炼的过程。

尽管存在这些差异,但 LlamaIndex 提供了另一种思维方式(langchain 也是如此)。

使用 LLM 评估检索到的信息

代码如下

class CorrectiveRAGPack(BaseLlamaPack):
    ...
    ...
    def evaluate_relevancy(
        self, retrieved_nodes: List[Document], query_str: str
    ) -> List[str]:
        """Evaluate relevancy of retrieved documents with the query."""
        relevancy_results = []
        for node in retrieved_nodes:
            relevancy = self.relevancy_pipeline.run(
                context_str=node.text, query_str=query_str
            )
            relevancy_results.append(relevancy.message.content.lower().strip())
        return relevancy_results

调用 LLM 的提示如下所示:

DEFAULT_RELEVANCY_PROMPT_TEMPLATE = PromptTemplate(
    template="""As a grader, your task is to evaluate the relevance of a document retrieved in response to a user's question.

    Retrieved Document:
    -------------------
    {context_str}

    User Question:
    --------------
    {query_str}

    Evaluation Criteria:
    - Consider whether the document contains keywords or topics related to the user's question.
    - The evaluation should not be overly stringent; the primary objective is to identify and filter out clearly irrelevant retrievals.

    Decision:
    - Assign a binary score to indicate the document's relevance.
    - Use 'yes' if the document is relevant to the question, or 'no' if it is not.

    Please provide your binary score ('yes' or 'no') below to indicate the document's relevance to the user question."""
)

CRAG 论文显示,ChatGPT 在识别检索相关性方面的性能不如 T5-Large 高。

此外,在实际项目中,我们当然可以使用原始的知识提炼算法。你可以在这里找到相关代码。

重写搜索查询

如前所述,网络搜索并不直接使用用户的输入查询。取而代之的是,它构建了一个提示,并将其呈现给 GPT-3.5 Turbo,使用少量的方法来获取搜索查询。提示如下

DEFAULT_TRANSFORM_QUERY_TEMPLATE = PromptTemplate(
    template="""Your task is to refine a query to ensure it is highly effective for retrieving relevant search results. \n
    Analyze the given input to grasp the core semantic intent or meaning. \n
    Original Query:
    \n ------- \n
    {query_str}
    \n ------- \n
    Your goal is to rephrase or enhance this query to improve its search performance. Ensure the revised query is concise and directly aligned with the intended search objective. \n
    Respond with the optimized query only:"""
)

我对 CRAG 的见解和想法

CRAG 与 Self-RAG的区别

  1. 从流程的角度来看,self-RAG 可以使用 LLM 直接提供响应而无需检索,而 CRAG 则必须在添加评估层之前进行检索。
  2. 从结构上看,self-RAG 比 CRAG 更复杂,它需要更复杂的训练过程,并且在生成阶段需要生成和评估多个标签,这不可避免地增加了推理成本。因此,CRAG 比 selfRAG 更轻便。
  3. 从性能上看,如图 5 所示,在大多数情况下,CRAG 通常优于Self-RAG。

在这里插入图片描述

改进检索评估器

检索评估器可以看作是一个评分分类模型。该模型用于确定查询和文档的相关性,类似于 RAG 中的重新排序模型

这种相关性判断模型可以通过整合更多符合现实世界场景的特征来加以改进。例如,科学论文问题解答 RAG 包含许多专业术语,而旅游领域的 RAG 则往往包含更多口语化的用户查询。

通过在检索评估器的训练数据中添加场景特征,它可以更好地评估检索文档的相关性。如图 6 所示,还可以加入用户意图和编辑距离等其他特征:

在这里插入图片描述

此外,考虑到 T5-Large 所取得的成果,轻量级模型似乎也能取得良好的成果。这为 CRAG 在小规模团队或公司中的应用带来了希望。

检索评价器的分数和阈值

如前所述,不同类型数据的阈值是不同的。此外,我们还可以发现,模棱两可和不正确的阈值基本上都在-0.9 左右,这表明大部分检索到的知识都与查询相关。完全摒弃这些检索到的知识而完全依赖网络搜索可能并不可取。

在实际应用中,我们需要根据实际问题和需求进行调整。

结论

本文从一个直观的例子入手,概述了 CRAG 的基本流程,并辅以代码解释。其中还包括我的见解和想法。

总之,CRAG 作为一个即插即用的插件,可以显著提高 RAG 的性能。它为改进 RAG 提供了一个轻量级解决方案。

本文为翻译,原文地址:https://ai.gopubby.com/advanced-rag-10-corrective-retrieval-augmented-generation-crag-3f5a140796f9

  • 20
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

静愚 AGI

你的善意终将流回自己,加油!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值