Tonic Validate
Tonic Validate
Tonic Validate는 RAG 시스템의 성능을 평가하기 위한 도구예요. 답변 유사도, 답변 일관성, 증강 정확도, 검색 정밀도 같은 메트릭을 LlamaIndex와 함께 쓰는 방법을 알아봅시다.
출처: 문서
본문
Tonic Validate란?
Tonic Validate는 검색 증강 생성(RAG) 시스템을 개발하는 사람들이 시스템 성능을 평가하기 위해 사용하는 도구입니다. LlamaIndex 설정 성능을 일회성으로 점검(spot check)하거나 Github Actions 같은 기존 CI/CD 시스템 안에서 사용할 수 있습니다. Tonic Validate에는 두 가지 부분이 있습니다.
- 오픈소스 SDK
- Web UI
원한다면 Web UI 없이 SDK만 사용할 수 있습니다. 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 키를 설정하면 됩니다.
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.",
]
답변 유사도(Answer Similarity) 점수는 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)
답변 일관성(Answer Consistency) 점수는 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)
증강 정확도(Augmentation Accuracy)는 답변에 포함된 검색 컨텍스트의 비율을 측정합니다. 이 경우 검색된 컨텍스트 중 하나가 답변에 들어 있으므로 점수는 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)
증강 정밀도(Augmentation Precision)는 관련 있는 검색 컨텍스트가 답변에 들어가는지 측정합니다. 검색된 컨텍스트 둘 다 관련이 있지만 답변에는 하나만 들어갔습니다. 그 때문에 점수는 0.5입니다.
augmentation_precision_evaluator = AugmentationPrecisionEvaluator()
score = await augmentation_precision_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)
검색 정밀도(Retrieval Precision)는 질문에 답하는 데 관련 있는 검색 컨텍스트의 비율을 측정합니다. 이 경우 검색된 컨텍스트 둘 다 질문에 답하는 데 관련이 있으므로 점수는 1.0입니다.
retrieval_precision_evaluator = RetrievalPrecisionEvaluator()
score = await retrieval_precision_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)
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 UI에 기록할 수 있는 tonic_validate Run 객체를 반환받을 수 있습니다.
이렇게 하려면 질문, LLM 답변, 검색 컨텍스트 목록, 참조 정답을 리스트로 넣고 evaluate_run을 호출합니다.
questions = ["What is the capital of France?", "What is the capital of Spain?"]
reference_answers = ["Paris", "Madrid"]
llm_answer = ["Paris", "Madrid"]
retrieved_context_lists = [
[
"Paris is the capital and most populous city of France.",
"Paris, France's capital, is a major European city and a global center for art, fashion, gastronomy and culture.",
],
[
"Madrid is the capital and largest city of Spain.",
"Madrid, Spain's central capital, is a city of elegant boulevards and expansive, manicured parks such as the Buen Retiro.",
],
]
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를 사용할 수 있습니다. 그전에 Setting Up Tonic Validate 섹션에서 설명한 대로 TONIC_VALIDATE_API_KEY가 설정되어 있는지 확인하세요. 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에서 결과를 볼 수 있습니다!

End to End 예제
여기서는 LlamaIndex와 함께 Tonic Validate를 End to End로 사용하는 방법을 보여드리겠습니다. 먼저 LlamaIndex CLI를 사용해 LlamaIndex로 실행할 데이터셋을 다운로드합니다.
터미널 창
llamaindex-cli download-llamadataset EvaluatingLlmSurveyPaperDataset --download-dir ./data
이제 llama.py라는 파이썬 파일을 만들고 다음 코드를 넣습니다.
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()
이 코드는 기본적으로 데이터셋 파일을 로드한 뒤 LlamaIndex를 초기화합니다.
LlamaIndex CLI는 예제 데이터셋에서 테스트에 사용할 수 있는 질문과 답변 목록도 다운로드합니다. 이 질문과 답변을 사용하려면 아래 코드를 사용할 수 있습니다.
from llama_index.core.llama_dataset import LabelledRagDataset
rag_dataset = LabelledRagDataset.from_json("./data/rag_dataset.json")
# We are only going to do 10 questions as running through the full data set takes too long
questions = [item.query for item in rag_dataset.examples][:10]
reference_answers = [item.reference_answer for item in rag_dataset.examples][
:10
]
이제 LlamaIndex에서 응답을 쿼리할 수 있습니다.
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를 사용할 수 있습니다. 그전에 Setting Up Tonic Validate 섹션에서 설명한 대로 TONIC_VALIDATE_API_KEY가 설정되어 있는지 확인하세요. Tonic Validate UI에서 프로젝트를 만들고 프로젝트 ID를 복사해 두는 것도 필요합니다. API 키와 프로젝트가 준비되면 Validate API를 초기화하고 결과를 업로드할 수 있습니다.
validate_api = ValidateApi()
project_id = "your-project-id"
validate_api.upload_run(project_id, run)
더 많은 문서
여기 문서 외에도 Tonic Validate의 Github 페이지에서 결과 업로드를 위한 API 사용법에 대한 더 많은 문서를 확인할 수 있습니다.