教程:评估 RAG 管道
最后更新:2025 年 8 月 25 日
- 级别:中级
- 完成时间:15 分钟
- 使用的组件:
InMemoryDocumentStore,InMemoryEmbeddingRetriever,ChatPromptBuilder,OpenAIChatGenerator,DocumentMRREvaluator,FaithfulnessEvaluator,SASEvaluator - 先决条件: 您必须拥有来自 OpenAI 活跃账户的 API 密钥,因为本教程使用的是 OpenAI 的 gpt-4o-mini 模型:https://platform.openai.com/api-keys
- 目标: 完成本教程后,您将学会如何使用 Haystack 提供的模型基础评估和统计指标来评估您的 RAG 管道。您还将了解 Haystack 集成了哪些其他评估框架。
概述
在本教程中,您将学习如何评估 Haystack 管道,特别是检索增强生成(RAG)管道。
- 您将首先构建一个基于 PubMed 数据的医学问题回答管道。
- 您将构建一个评估管道,该管道利用文档 MRR 和答案忠实度等指标。
- 您将运行您的 RAG 管道,并使用您的评估管道来评估输出。
Haystack 提供了多种评估器,它们可以执行两种类型的评估:
在本教程中,我们将使用这些评估技术中的一些来评估一个旨在回答 PubMed 数据问题的 RAG 管道。
🧑🍳 除了 Haystack 自有的评估指标外,您还可以集成多个评估框架。请参阅下面的集成和示例 👇
评估 RAG 流水线
RAG 管道最终由至少 2 个步骤组成
- 检索
- 生成
要评估一个完整的 RAG 管道,我们必须单独评估每个步骤,并评估整体。虽然在某些情况下,检索可以使用一些需要标签的统计指标进行评估,但对于生成步骤执行相同的操作并不容易。相反,我们通常依赖基于模型的指标来评估生成步骤,其中 LLM 被用作“评估器”。
📺 代码演示
准备 Colab 环境
安装 Haystack
使用 pip 安装 Haystack 和 datasets
%%bash
pip install haystack-ai
pip install "datasets>=2.6.1"
pip install "sentence-transformers>=4.1.0"
创建要评估的 RAG 管道
要评估 RAG 管道,我们需要一个 RAG 管道作为起点。因此,我们将首先创建一个问答管道。
💡 有关创建检索增强生成管道的完整教程,请参阅创建您的第一个带检索增强的 QA 管道教程。
在本教程中,我们将使用一个标记的 PubMed 数据集,其中包含问题、上下文和答案。这样,我们就可以将上下文用作文档,并且我们还拥有一些评估指标所需的标记数据。
首先,让我们获取准备好的数据集并提取 all_documents、all_questions 和 all_ground_truth_answers。
ℹ️ 数据集相当大,在本示例中我们使用了前 1000 行,但您可以根据需要增加此数量。
from datasets import load_dataset
from haystack import Document
dataset = load_dataset("vblagoje/PubMedQA_instruction", split="train")
dataset = dataset.select(range(1000))
all_documents = [Document(content=doc["context"]) for doc in dataset]
all_questions = [doc["instruction"] for doc in dataset]
all_ground_truth_answers = [doc["response"] for doc in dataset]
接下来,让我们构建一个简单的索引管道,并将 documents 写入 DocumentStore。这里,我们使用的是 InMemoryDocumentStore。
InMemoryDocumentStore是最容易入门的 DocumentStore。它不需要外部依赖,并且是小型项目和调试的不错选择。但它在大规模文档集合上的扩展性不太好,因此不适合生产系统。要了解 Haystack 支持的各种外部数据库,请参阅 DocumentStore 集成。
from typing import List
from haystack import Pipeline
from haystack.components.embedders import SentenceTransformersDocumentEmbedder
from haystack.components.writers import DocumentWriter
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.document_stores.types import DuplicatePolicy
document_store = InMemoryDocumentStore()
document_embedder = SentenceTransformersDocumentEmbedder(model="sentence-transformers/all-MiniLM-L6-v2")
document_writer = DocumentWriter(document_store=document_store, policy=DuplicatePolicy.SKIP)
indexing = Pipeline()
indexing.add_component(instance=document_embedder, name="document_embedder")
indexing.add_component(instance=document_writer, name="document_writer")
indexing.connect("document_embedder.documents", "document_writer.documents")
indexing.run({"document_embedder": {"documents": all_documents}})
现在我们的数据已准备就绪,我们可以创建一个简单的 RAG 管道。
在此示例中,我们将使用
-
InMemoryEmbeddingRetriever,它将获取与查询相关的文档。 -
OpenAIChatGenerator来生成查询的答案。您可以在管道中将OpenAIChatGenerator替换为另一个ChatGenerator。在此处查看生成器的完整列表:这里。
import os
from getpass import getpass
from haystack.components.builders import AnswerBuilder, ChatPromptBuilder
from haystack.dataclasses import ChatMessage
from haystack.components.embedders import SentenceTransformersTextEmbedder
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.retrievers.in_memory import InMemoryEmbeddingRetriever
if "OPENAI_API_KEY" not in os.environ:
os.environ["OPENAI_API_KEY"] = getpass("Enter OpenAI API key:")
template = [
ChatMessage.from_user(
"""
You have to answer the following question based on the given context information only.
Context:
{% for document in documents %}
{{ document.content }}
{% endfor %}
Question: {{question}}
Answer:
"""
)
]
rag_pipeline = Pipeline()
rag_pipeline.add_component(
"query_embedder", SentenceTransformersTextEmbedder(model="sentence-transformers/all-MiniLM-L6-v2")
)
rag_pipeline.add_component("retriever", InMemoryEmbeddingRetriever(document_store, top_k=3))
rag_pipeline.add_component("prompt_builder", ChatPromptBuilder(template=template))
rag_pipeline.add_component("generator", OpenAIChatGenerator(model="gpt-4o-mini"))
rag_pipeline.add_component("answer_builder", AnswerBuilder())
rag_pipeline.connect("query_embedder", "retriever.query_embedding")
rag_pipeline.connect("retriever", "prompt_builder.documents")
rag_pipeline.connect("prompt_builder.prompt", "generator.messages")
rag_pipeline.connect("generator.replies", "answer_builder.replies")
rag_pipeline.connect("retriever", "answer_builder.documents")
提问
在提问时,请使用管道的 run() 方法。确保将问题提供给所有需要它的组件。在这种情况下,这些是 query_embedder、prompt_builder 和 answer_builder。
question = "Do high levels of procalcitonin in the early phase after pediatric liver transplantation indicate poor postoperative outcome?"
response = rag_pipeline.run(
{
"query_embedder": {"text": question},
"prompt_builder": {"question": question},
"answer_builder": {"query": question},
}
)
print(response["answer_builder"]["answers"][0].data)
评估管道
在本教程中,让我们使用以下指标来评估管道:
- 文档平均倒数排名 (Document Mean Reciprocal Rank):使用真实标签评估检索到的文档。它会检查真实文档在检索到的文档列表中的排名。
- 语义答案相似度 (Semantic Answer Similarity):使用真实标签评估预测的答案。它使用经过微调的语言模型来检查预测答案与真实答案的语义相似度。
- 忠实度 (Faithfulness):使用 LLM 来评估是否可以从提供的上下文中推断出生成的答案。不需要真实标签。
首先,让我们实际运行 RAG 管道并处理一组问题,并确保我们拥有这些问题的真实标签(答案和文档)。让我们从 25 个随机问题和标签开始👇
📝 一些说明:
- 有关可用指标的完整列表,请查看Haystack 评估器。
- 在我们的数据集中,对于每个示例问题,我们有 1 个真实文档作为标签。但是,在某些情况下,可能会提供多个真实文档作为标签。您会注意到,这就是为什么我们为每个问题提供一个
ground_truth_documents列表。
import random
questions, ground_truth_answers, ground_truth_docs = zip(
*random.sample(list(zip(all_questions, all_ground_truth_answers, all_documents)), 25)
)
接下来,让我们运行管道,并确保跟踪管道返回的答案以及它检索到的文档。
rag_answers = []
retrieved_docs = []
for question in list(questions):
response = rag_pipeline.run(
{
"query_embedder": {"text": question},
"prompt_builder": {"question": question},
"answer_builder": {"query": question},
}
)
print(f"Question: {question}")
print("Answer from pipeline:")
print(response["answer_builder"]["answers"][0].data)
print("\n-----------------------------------\n")
rag_answers.append(response["answer_builder"]["answers"][0].data)
retrieved_docs.append(response["answer_builder"]["answers"][0].documents)
虽然每个评估器都是可以在 Haystack 中单独运行的组件,但也可以将它们添加到管道中。这样,我们就可以构建一个包含所有所需评估指标的 eval_pipeline 来评估我们的管道。
from haystack.components.evaluators.document_mrr import DocumentMRREvaluator
from haystack.components.evaluators.faithfulness import FaithfulnessEvaluator
from haystack.components.evaluators.sas_evaluator import SASEvaluator
eval_pipeline = Pipeline()
eval_pipeline.add_component("doc_mrr_evaluator", DocumentMRREvaluator())
eval_pipeline.add_component("faithfulness", FaithfulnessEvaluator())
eval_pipeline.add_component("sas_evaluator", SASEvaluator(model="sentence-transformers/all-MiniLM-L6-v2"))
results = eval_pipeline.run(
{
"doc_mrr_evaluator": {
"ground_truth_documents": list([d] for d in ground_truth_docs),
"retrieved_documents": retrieved_docs,
},
"faithfulness": {
"questions": list(questions),
"contexts": list([d.content] for d in ground_truth_docs),
"predicted_answers": rag_answers,
},
"sas_evaluator": {"predicted_answers": rag_answers, "ground_truth_answers": list(ground_truth_answers)},
}
)
构建评估报告
运行评估管道后,我们还可以创建完整的评估报告。Haystack 提供了 EvaluationRunResult,我们可以使用它来显示 aggregated_report 👇
from haystack.evaluation.eval_run_result import EvaluationRunResult
inputs = {
"question": list(questions),
"contexts": list([d.content] for d in ground_truth_docs),
"answer": list(ground_truth_answers),
"predicted_answer": rag_answers,
}
evaluation_result = EvaluationRunResult(run_name="pubmed_rag_pipeline", inputs=inputs, results=results)
evaluation_result.aggregated_report()
额外:您还可以查看包含数据集中每个样本分数(scores)的详细报告,我们将选择输出格式为 DataFrame。
results_df = evaluation_result.detailed_report(output_format="df")
results_df
将我们的评估结果作为 DataFrame 非常有用。例如,在下面,我们可以使用 pandas DataFrame 过滤结果,以获取语义答案相似度(sas_evaluator)排名前 3 的以及排名最低的 3 个结果👇
import pandas as pd
top_3 = results_df.nlargest(3, "sas_evaluator")
bottom_3 = results_df.nsmallest(3, "sas_evaluator")
pd.concat([top_3, bottom_3])
下一步
🎉 恭喜!您已经学会了如何使用基于模型的评估框架来评估 RAG 管道,并且无需任何标记工作。
如果您喜欢这个教程,您可能还会喜欢
要及时了解最新的 Haystack 开发动态,您可以订阅我们的新闻通讯。感谢您的阅读!
