RAG测试集生成
在RAG应用中,当用户通过您的应用程序与一组文档交互时,系统可能会遇到不同类型的查询模式。让我们首先了解RAG应用中可能遇到的不同查询类型。
RAG中的查询类型
graph TD
A[Queries] --> B[Single-Hop Query]
A --> C[Multi-Hop Query]
B --> D1[Specific Query]
B --> E1[Abstract Query]
C --> F1[Specific Query]
C --> G1[Abstract Query]
单跳查询
单跳查询是一种直接的问题,只需要从单一文档或来源检索信息即可提供相关答案。它仅需一步就能得出答案。
示例(具体查询):
- "阿尔伯特·爱因斯坦在哪一年发表了相对论?"
这是一个具体的、基于事实的问题,只需从包含该信息的文档中检索一次即可回答。
示例(抽象查询):
- "爱因斯坦的理论如何改变了我们对时间和空间的理解?"
虽然这个查询仍然涉及单一概念(相对论理论),但它需要从原始材料中获得更抽象或解释性的说明。
多跳查询
多跳查询涉及多步推理,需要从两个或多个来源获取信息。系统必须从不同文档中检索信息并串联线索,才能生成准确答案。
示例(具体查询):
- "哪位科学家影响了爱因斯坦的相对论研究,他们提出了什么理论?"
这要求系统从可能两个不同的来源检索关于影响爱因斯坦的科学家及其具体理论的信息。
示例(抽象查询):
- "自爱因斯坦最初发表以来,关于相对论的科学理论是如何演变的?"
这个抽象查询需要跨时间和不同来源检索多条信息,以形成关于该理论演变的广泛解释性回应。
RAG中的具体查询与抽象查询
-
特定查询: 专注于清晰、基于事实的检索。RAG的目标是从一个或多个文档中检索高度相关的信息,直接回答具体问题。
-
抽象查询:需要更广泛、更具解释性的回答。在RAG中,抽象查询会挑战检索系统从包含更高层次推理、解释或观点的文档中提取信息,而非简单的事实。
在单跳和多跳场景中,具体查询与抽象查询的区别通过决定关注点是精确性(具体)还是综合更广泛的概念(抽象),从而塑造了检索和生成过程。
不同类型的查询需要合成不同的上下文。为了解决这个问题,Ragas采用基于知识图谱的方法来生成测试集。
知识图谱创建
鉴于我们需要从给定文档集中生成不同类型的查询,我们面临的主要挑战是确定正确的文档块或文档集,以使LLM能够创建查询。为解决这一问题,Ragas采用基于知识图谱的测试集生成方法。

知识图谱是通过以下组件创建的:
文档分割器
文档会被分块形成层级节点。分块可以通过使用不同的分割器来完成。例如,对于财务文档,可以使用按章节(如利润表、资产负债表、现金流量表等)分割文档的分割器。您可以编写自己的自定义分割器,根据与您领域相关的章节来分割文档。
示例
从 ragas.testset.graph 导入 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[Node: 4f6b94] -.-> |Properties| A1[page_content]
B[Node: 952361] -.-> |Properties| 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': ['Einstein'],
'MISC': ['theory of relativity',
'space',
'time',
"observer's frame of reference"]})
让我们将提取的信息添加到节点中。
_ = [node.properties.update({key:val}) for (key,val), node in zip(output, sample_nodes)]
sample_nodes[0].properties
输出:
{'page_content': "爱因斯坦的相对论彻底改变了我们对空间和时间的理解。它提出了时间并非绝对的概念,而是会随着观察者的参考系而变化。",
'entities': {'ORG': [],
'LOC': [],
'PER': ['Einstein'],
'MISC': ['theory of relativity',
'space',
'time',
"observer's frame of reference"]}}
graph TD
A[Node: 4f6b94] -.-> |Properties| A1[page_content]
A -.-> |Properties| A2[entities]
B[Node: 952361] -.-> |Properties| B1[page_content]
B -.-> |Properties| B2[entities]
关系构建器
提取的信息用于建立节点之间的关系。例如,在金融文档的情况下,可以根据节点中存在的实体来建立节点之间的关系。 您可以编写自己的自定义关系构建器,根据与您领域相关的信息来建立节点之间的关系。
示例
from ragas.testset.graph import KnowledgeGraph
from ragas.testset.transforms.relationship_builders.traditional import JaccardSimilarityBuilder
kg = KnowledgeGraph(nodes=sample_nodes)
rel_builder = JaccardSimilarityBuilder(property_name="entities", key_name="PER", new_property_name="entity_jaccard_similarity")
relationships = await rel_builder.transform(kg)
relationships
[Relationship(Node(id: 4f6b94) <-> Node(id: 952361), type: jaccard_similarity, properties: ['entity_jaccard_similarity'])]
graph TD
A[Node: 4f6b94] -.-> |Properties| A1[page_content]
A -.-> |Properties| A2[entities]
B[Node: 952361] -.-> |Properties| B1[page_content]
B -.-> |Properties| B2[entities]
A ===|entity_jaccard_similarity| B
现在让我们了解如何使用上述组件通过transform
构建知识图谱,这将使您的工作更轻松。
转换器
用于构建知识图谱的所有组件可以组合成一个单一的transform
,该转换可以应用于知识图谱以构建知识图谱。转换由一系列按顺序应用于知识图谱的组件组成。它还可以处理组件的并行处理。apply_transforms
方法用于将转换应用于知识图谱。
示例
让我们使用上述组件通过transform
构建上述知识图谱。
from ragas.testset.transforms import apply_transforms
transforms = [
extractor,
rel_builder
]
apply_transforms(kg,transforms)
要并行应用部分组件,您可以将它们包装在Parallel
类中。
from ragas.testset.transforms import KeyphraseExtractor, NERExtractor
from ragas.testset.transforms import apply_transforms, Parallel
tranforms = [
Parallel(
KeyphraseExtractor(),
NERExtractor()
),
rel_builder
]
apply_transforms(kg,transforms)
知识图谱创建完成后,可以通过遍历图谱生成不同类型的查询。例如,要生成查询"比较公司X和公司Y从2020财年到2023财年的收入增长情况",可以遍历图谱找到包含公司X和公司Y在2020至2023财年收入增长信息的节点。
场景生成
现在我们有了可用于生成各类查询正确上下文的知识图谱。当用户群体与RAG系统交互时,他们可能会根据角色身份(如高级工程师、初级工程师等)、查询长度(简短、冗长等)和查询风格(正式、非正式等)以不同方式构建查询。为了生成覆盖所有这些场景的查询,Ragas采用基于场景的测试集生成方法。
测试集生成中的每个Scenario
都是以下参数的组合。
- 节点:用于生成查询的节点
- 查询长度:期望查询的长度,可以是短、中或长等。
- 查询风格:查询的样式,可以是网页搜索、聊天等。
- 角色 : 用户的角色设定,可以是高级工程师、初级工程师等(即将推出)

查询合成器
QuerySynthesizer
负责为单一查询类型生成不同的场景。generate_scenarios
方法用于为单一查询类型生成场景。generate_sample
方法用于为单个场景生成查询和参考答案。让我们通过一个示例来理解这一点。
示例
在前面的示例中,我们创建了一个知识图谱,其中包含两个基于实体相似性相互关联的节点。现在假设您的知识图谱中有20对这样的节点,它们都基于实体相似性相互关联。
假设你的目标是创建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):
"""
logic to query nodes with entity
logic describing how to combine nodes,styles,length,persona to form n scenarios
"""
return scenarios
async def _generate_sample(
self, scenario, callbacks
):
"""
logic on how to use tranform each scenario to EvalSample (Query,Context,Reference)
you may create singleturn or multiturn sample
"""
return SingleTurnSample(user_input=query, reference_contexs=contexts, reference=reference)