Skip to content

Tonic Validate#

什么是 Tonic Validate#

Tonic Validate 是一个用于帮助开发检索增强生成(RAG)系统的工具,用于评估系统的性能。您可以使用 Tonic Validate 对 LlamaIndex 设置的性能进行一次性的抽查,甚至可以将其用于现有的 CI/CD 系统,如 Github Actions。Tonic Validate 由两部分组成:

  1. 开源 SDK
  2. Web UI

如果您愿意,可以仅使用 SDK 而不使用 Web UI。SDK 包括评估 RAG 系统所需的所有工具。Web UI 的目的是在 SDK 之上提供一个可视化结果的层。这使您可以更好地了解系统的性能,而不仅仅是查看原始数据。

如果您想使用 Web UI,可以前往这里注册免费账户。

如何使用 Tonic Validate#

设置 Tonic Validate#

您可以通过以下命令安装 Tonic Validate

pip install tonic-validate

要使用 Tonic Validate,您需要提供一个 OpenAI 密钥,因为评分计算使用了后端的 LLM。您可以通过将 OPENAI_API_KEY 环境变量设置为您的 OpenAI API 密钥来设置 OpenAI 密钥。

import os

os.environ["OPENAI_API_KEY"] = "put-your-openai-api-key-here"

如果您要上传结果到 UI,还请确保设置您在web UI账户设置期间收到的 Tonic Validate API 密钥。如果您尚未在 Web UI 上设置您的账户,可以在这里进行设置。一旦获得 API 密钥,您可以通过 TONIC_VALIDATE_API_KEY 环境变量进行设置。

import os

os.environ["TONIC_VALIDATE_API_KEY"] = "put-your-validate-api-key-here"

单个问题的使用示例#

对于这个示例,我们有一个问题的示例,其中包含一个参考正确答案,但与 LLM 响应答案不匹配。有两个检索到的上下文块,其中一个包含了正确答案。

question = "What makes Sam Altman a good founder?"
reference_answer = "He is smart and has a great force of will."
llm_answer = "He is a good founder because he is smart."
retrieved_context_list = [
    "Sam Altman is a good founder. He is very smart.",
    "What makes Sam Altman such a good founder is his great force of will.",
]

答案相似度分数是介于 0 到 5 之间的分数,用于评估 LLM 答案与参考答案的匹配程度。在这种情况下,它们并不完全匹配,因此答案相似度分数不是完美的 5。

answer_similarity_evaluator = AnswerSimilarityEvaluator()
score = await answer_similarity_evaluator.aevaluate(
    question,
    llm_answer,
    retrieved_context_list,
    reference_response=reference_answer,
)
print(score)
# >> EvaluationResult(query='What makes Sam Altman a good founder?', contexts=['Sam Altman is a good founder. He is very smart.', 'What makes Sam Altman such a good founder is his great force of will.'], response='He is a good founder because he is smart.', passing=None, feedback=None, score=4.0, pairwise_source=None, invalid_result=False, invalid_reason=None)

答案一致性分数介于 0.0 到 1.0 之间,用于衡量答案中是否包含未出现在检索到的上下文中的信息。在这种情况下,答案确实出现在检索到的上下文中,因此分数为 1。

answer_consistency_evaluator = AnswerConsistencyEvaluator()
score = await answer_consistency_evaluator.aevaluate(
    question, llm_answer, retrieved_context_list
)
print(score)
# >> EvaluationResult(query='What makes Sam Altman a good founder?', contexts=['Sam Altman is a good founder. He is very smart.', 'What makes Sam Altman such a good founder is his great force of will.'], response='He is a good founder because he is smart.', passing=None, feedback=None, score=1.0, pairwise_source=None, invalid_result=False, invalid_reason=None)

增强准确度衡量了答案中检索到的上下文的百分比。在这种情况下,一个检索到的上下文出现在答案中,因此该分数为 0.5。

augmentation_accuracy_evaluator = AugmentationAccuracyEvaluator()
score = await augmentation_accuracy_evaluator.aevaluate(
    question, llm_answer, retrieved_context_list
)
print(score)
# >> EvaluationResult(query='What makes Sam Altman a good founder?', contexts=['Sam Altman is a good founder. He is very smart.', 'What makes Sam Altman such a good founder is his great force of will.'], response='He is a good founder because he is smart.', passing=None, feedback=None, score=0.5, pairwise_source=None, invalid_result=False, invalid_reason=None)
数据增强精度衡量检索到的相关上下文是否出现在答案中。两个检索到的上下文都是相关的,但只有一个出现在答案中。因此,该得分为0.5。

augmentation_precision_evaluator = AugmentationPrecisionEvaluator()


score = await augmentation_precision_evaluator.aevaluate(
    question, llm_answer, retrieved_context_list
)
print(score)
# >> EvaluationResult(query='Sam Altman是一个好创始人的原因是什么?', contexts=['Sam Altman是一个好创始人。他非常聪明。', 'Sam Altman之所以成为一个好创始人,是因为他有很强的意志力。'], response='他是一个好创始人,因为他很聪明。', passing=None, feedback=None, score=0.5, pairwise_source=None, invalid_result=False, invalid_reason=None)

检索精度衡量检索到的上下文与回答问题的相关性百分比。在这种情况下,两个检索到的上下文都与回答问题相关,因此得分为1.0。

retrieval_precision_evaluator = RetrievalPrecisionEvaluator()


score = await retrieval_precision_evaluator.aevaluate(
    question, llm_answer, retrieved_context_list
)
print(score)
# >> EvaluationResult(query='Sam Altman是一个好创始人的原因是什么?', contexts=['Sam Altman是一个好创始人。他非常聪明。', 'Sam Altman之所以成为一个好创始人,是因为他有很强的意志力。'], response='他是一个好创始人,因为他很聪明。', passing=None, feedback=None, score=1.0, pairwise_source=None, invalid_result=False, invalid_reason=None)

TonicValidateEvaluator 可以一次计算所有 Tonic Validate 的指标。

tonic_validate_evaluator = TonicValidateEvaluator()


scores = await tonic_validate_evaluator.aevaluate(
    question,
    llm_answer,
    retrieved_context_list,
    reference_response=reference_answer,
)
print(scores.score_dict)
# >> {
#     'answer_consistency': 1.0,
#     'answer_similarity': 4.0,
#     'augmentation_accuracy': 0.5,
#     'augmentation_precision': 0.5,
#     'retrieval_precision': 1.0
# }

一次评估多个问题#

您还可以使用 TonicValidateEvaluator 一次评估多个查询和回答,并返回一个 tonic_validate Run 对象,可以将其记录到 Tonic Validate UI

为此,您将问题、LLM答案、检索到的上下文列表和参考答案放入列表中,并调用 evaluate_run。

questions = ["法国的首都是什么?", "西班牙的首都是什么?"]
reference_answers = ["巴黎", "马德里"]
llm_answer = ["巴黎", "马德里"]
retrieved_context_lists = [
    [
        "巴黎是法国的首都,也是最大的城市。",
        "巴黎,法国的首都,是一个重要的欧洲城市,也是艺术、时尚、美食和文化的全球中心。",
    ],
    [
        "马德里是西班牙的首都,也是最大的城市。",
        "马德里,西班牙的中央首都,是一个拥有优雅林荫大道和广阔整洁公园(如布埃纳雷蒂罗公园)的城市。",
    ],
]


tonic_validate_evaluator = TonicValidateEvaluator()


scores = await tonic_validate_evaluator.aevaluate_run(
    [questions], [llm_answers], [retrieved_context_lists], [reference_answers]
)
print(scores.run_data[0].scores)
# >> {
#     'answer_consistency': 1.0,
#     'answer_similarity': 3.0,
#     'augmentation_accuracy': 0.5,
#     'augmentation_precision': 0.5,
#     'retrieval_precision': 1.0
# }

将结果上传到 UI#

如果您想将分数上传到 UI,则可以使用 Tonic Validate API。在这样做之前,请确保已设置 TONIC_VALIDATE_API_KEY,如 设置 Tonic Validate 部分所述。您还需要确保在 Tonic Validate UI 中创建了一个项目,并复制了项目 ID。设置好 API 密钥和项目后,您可以初始化 Validate API 并上传结果。

validate_api = ValidateApi()
project_id = "your-project-id"
validate_api.upload_run(project_id, scores)

现在您可以在 Tonic Validate UI 中查看您的结果了!

Tonic Validate 图表

端到端示例#

这里将展示如何使用 Tonic Validate 端到端与 Llama Index。首先,让我们使用 Llama Index CLI 下载一个数据集,以便 Llama Index 在其上运行。

llamaindex-cli download-llamadataset EvaluatingLlmSurveyPaperDataset --download-dir ./data

现在,我们可以创建一个名为 llama.py 的 Python 文件,并将以下代码放入其中。

from llama_index.core import SimpleDirectoryReader
from llama_index.core import VectorStoreIndex


documents = SimpleDirectoryReader(input_dir="./data/source_files").load_data()
index = VectorStoreIndex.from_documents(documents=documents)
query_engine = index.as_query_engine()

这段代码主要是加载数据集文件,然后初始化 Llama Index。 Llama Index 的 CLI 还会下载一份问题和答案列表,你可以用它来测试他们的示例数据集。如果你想使用这些问题和答案,可以使用下面的代码。

from llama_index.core.llama_dataset import LabelledRagDataset

rag_dataset = LabelledRagDataset.from_json("./data/rag_dataset.json")


# 我们只打算做 10 个问题,因为运行整个数据集太耗时了
questions = [item.query for item in rag_dataset.examples][:10]
reference_answers = [item.reference_answer for item in rag_dataset.examples][
    :10
]

现在我们可以查询 Llama Index 的响应了。

llm_answers = []
retrieved_context_lists = []
for question in questions:
    response = query_engine.query(question)
    context_list = [x.text for x in response.source_nodes]
    retrieved_context_lists.append(context_list)
    llm_answers.append(response.response)

现在来计算分数,我们可以这样做

from tonic_validate.metrics import AnswerSimilarityMetric
from llama_index.evaluation.tonic_validate import TonicValidateEvaluator


tonic_validate_evaluator = TonicValidateEvaluator(
    metrics=[AnswerSimilarityMetric()], model_evaluator="gpt-4-1106-preview"
)

scores = tonic_validate_evaluator.evaluate_run(
    questions, retrieved_context_lists, reference_answers, llm_answers
)
print(scores.overall_scores)

如果你想将你的分数上传到 UI,你可以使用 Tonic Validate API。在这之前,请确保你已经设置了 TONIC_VALIDATE_API_KEY,就像 设置 Tonic Validate 部分描述的那样。你还需要确保你在 Tonic Validate UI 中创建了一个项目,并且已经复制了项目 ID。在设置了 API Key 和项目之后,你可以初始化 Validate API 并上传结果。

validate_api = ValidateApi()
project_id = "your-project-id"
validate_api.upload_run(project_id, run)

更多文档#

除了这里的文档之外,你还可以访问 Tonic Validate 的 Github 页面 了解更多关于如何与我们的 API 交互以上传结果的文档。