Skip to content

RAG测试集生成

在RAG应用中,当用户通过您的应用程序与一组文档交互时,用户可能会提出不同类型的查询。这些查询在RAG系统中可以大致分为两种类型:

RAG中的两种基本查询类型

graph TD
    A[查询] -->  B[具体查询]
    A -->  C[抽象查询]

在任何RAG应用中,当最终用户与系统交互时,查询可以大致分为两种类型:

  • 具体查询

    • 通过参考单一上下文可以直接回答的查询
    • “报告FY2020中X的值是多少?”
  • 抽象查询

    • 只能通过参考多个文档来回答的查询
    • “公司X从FY2020到FY2023的收入趋势是什么?”

合成具体查询相对容易,因为它只需要一个上下文来生成查询。然而,抽象查询需要多个上下文来生成查询。现在,基本问题是,如何选择正确的文档块集来生成抽象查询。不同类型的抽象查询需要不同类型的上下文。例如,

  • 比较特定领域中两个实体的抽象查询需要包含实体信息的上下文。
    • “比较公司X和公司Y从FY2020到FY2023的收入增长”
  • 关于在不同上下文中讨论的主题的抽象查询需要包含主题信息的上下文。
    • “公司增加收入的不同策略是什么?”

为了解决这个问题,Ragas使用基于知识图谱的方法来生成测试集。

知识图谱创建

鉴于我们希望从给定的文档集中制造不同类型的查询,我们的主要挑战是识别正确的文档块集或文档,以使LLMs能够创建查询。为了解决这个问题,Ragas使用基于知识图谱的方法来生成测试集。

知识图谱创建
知识图谱创建

知识图谱是通过使用以下组件创建的:

文档分割器

文档被分割成层次节点。分割可以通过使用不同的分割器来完成。例如,在财务文档的情况下,分割可以通过使用基于收入表、资产负债表、现金流量表等部分的分割器来完成。您可以编写自己的自定义分割器,根据与您的领域相关的部分来分割文档。

示例

from ragas.testset.graph import Node

sample_nodes = [Node(
    properties={"page_content": "爱因斯坦的相对论理论彻底改变了我们对空间和时间的理解。它引入了时间不是绝对的概念,而是可以根据观察者的参考系而改变的。"}
),Node(
    properties={"page_content": "当物体以接近光速的速度移动时,时间膨胀会发生,导致时间相对于静止观察者变慢。这种现象是爱因斯坦狭义相对论的关键预测。"}
)]
sample_nodes
输出:
[Node(id: 4f6b94, type: , properties: ['page_content']),
 Node(id: 952361, type: , properties: ['page_content'])]

graph TD
    A[节点: 4f6b94] -.-> |属性| A1[page_content]

    B[节点: 952361] -.-> |属性| B1[page_content]

提取器

不同的提取器用于从每个节点中提取信息,这些信息可以用于建立节点之间的关系。例如,在财务文档的情况下,可以使用的提取器包括实体提取器,用于提取公司名称等实体,关键短语提取器,用于提取每个节点中的重要关键短语等。您可以编写自己的自定义提取器,以提取与您的领域相关的信息。

提取器可以是基于LLM的,继承自LLMBasedExtractor,或者是基于规则的,继承自Extractor

示例

假设我们有一个来自知识图谱的示例节点。我们可以使用NERExtractor从节点中提取命名实体。

from ragas.testset.transforms.extractors import NERExtractor

extractor = NERExtractor()
output = [await extractor.extract(node) for node in sample_nodes]
output[0]
返回一个包含提取器类型和提取信息的元组。

('entities',
 {'ORG': [],
  'LOC': [],
  'PER': ['爱因斯坦'],
  'MISC': ['相对论理论',
   '空间',
   '时间',
   "观察者的参考系"]})

让我们将提取的信息添加到节点中。

_ = [node.properties.update({key:val}) for (key,val), node in zip(output, sample_nodes)]
sample_nodes[0].properties

输出:

{'page_content': "爱因斯坦的相对论彻底改变了我们对空间和时间的理解。它引入了时间的概念,即时间并非绝对,而是可以根据观察者的参考系发生变化。",
 'entities': {'ORG': [],
  'LOC': [],
  'PER': ['爱因斯坦'],
  'MISC': ['相对论',
   '空间',
   '时间',
   "观察者的参考系"]}}
graph TD
    A[节点: 4f6b94] -.-> |属性| A1[页面内容]
    A -.-> |属性| A2[实体]

    B[节点: 952361] -.-> |属性| B1[页面内容]
    B -.-> |属性| B2[实体]

想象一下,你的目标是创建50个不同的查询,每个查询都是关于比较两个实体的抽象问题。我们首先需要查询知识图谱(KG),以获取基于实体相似性相互关联的节点对。然后,我们必须为每一对节点生成场景,直到我们得到50个不同的场景。这个逻辑在generate_scenarios方法中实现。

from dataclasses import dataclass
from ragas.testset.synthesizers.base_query import QuerySynthesizer

@dataclass
class EntityQuerySynthesizer(QuerySynthesizer):

    async def _generate_scenarios( self, n, knowledge_graph, callbacks):
        """
        逻辑用于查询具有实体的节点
        逻辑描述如何组合节点、风格、长度、角色以形成n个场景
        """

        return scenarios

    async def _generate_sample(
        self, scenario, callbacks
    ):

        """
        逻辑描述如何将每个场景转换为EvalSample(查询、上下文、参考)
        你可以创建单轮或多轮样本
        """

        return SingleTurnSample(user_input=query, reference_contexs=contexts, reference=reference)