RAGの評価指標をまとめる
はじめに
「RAGを構築したけど、どう評価したらいいかわからない」
RAGを開発したことがある人なら、一度はこの壁にぶつかったことがあると思います。
「検索」と「生成」が絡むRAGでは、評価が複雑になっています。本記事では、そんなRAGの評価指標を一挙にまとめます。
対象読者
- RAGを開発・運用するエンジニア
- RAGの評価指標を知りたいデータサイエンティスト
読み終えたら、RAGで起きている問題は「検索」「生成」のどこかを切り分け、適切な評価指標を選べることを目指します。
参考文献
RAG評価のサーベイ論文とRAGASを参考にしています。
1. RAG評価の全体像
RAGは、「検索」「生成」の2要素から成立します。
評価では、各フェーズで「システム出力」と「真の答え」で評価が作られます。
また、評価軸に「内部(コンポーネント)」と「外部(システム全体)」があります。
複雑なので、表にまとめます。
RAG評価
├── 内部評価(コンポーネントレベル)
│ ├── 検索性能:Relevance, Accuracy, Comprehensiveness
│ └── 生成品質:Relevance, Faithfulness, Correctness
│
└── 外部評価(システムレベル)
├── 安全性:Robustness, Factuality, Adversarial Attacks, Privacy, Fairness, Transparency / Accountability
└── 効率性:Latency, Throughput, TokenUsage, Cost
内部評価(コンポーネント)
検索・生成といったコンポーネントレベルの性能です。いわゆる回答精度です。
検索コンポーネントの評価
| 比較関係 | 評価観点 | 説明 |
|---|---|---|
| 検索文書 ↔ クエリ | Relevance(適合性) | クエリに対して適切な文書を取れているか |
| 検索文書 ↔ 正解文書 | Accuracy(正確性) | 正解の文書を正しく取れているか |
| 検索文書 ↔ 検索文書 | Comprehensiveness(網羅性) | 必要な情報を網羅できているか |
生成コンポーネントの評価
| 比較関係 | 評価観点 | 説明 |
|---|---|---|
| 回答 ↔ クエリ | Relevance(適合性) | 質問にちゃんと答えているか |
| 回答 ↔ 検索文書 | Faithfulness(忠実性) | 検索文書に基づいているか(ハルシネーションしていないか) |
| 回答 ↔ 正解回答 | Correctness(正確性) | 正解と一致しているか |
外部評価(システム全体)
システム全体の実用性です。運用上の観点になります。
| 評価観点 | 説明 |
|---|---|
| Latency(レイテンシ) | 応答速度は十分か |
| Cost(コスト) | トークン消費量・API料金は許容範囲か |
| Robustness(堅牢性) | 無関係・誤情報が混じっても正しく回答できるか |
| Factuality(事実性) | ハルシネーションが発生していないか |
| Privacy(プライバシー) | 個人情報が漏洩していないか |
<補足>
評価指標は、従来型とLLMベースに分かれます。
| 種類 | 指標 | メリット | デメリット |
|---|---|---|---|
| 従来型(IR由来) | Recall, Precision, MRR, NDCG | 高速、解釈しやすい | 意味的類似性を捉えにくい |
| 従来型(NLG由来) | ROUGE, BLEU, BertScore | 高速、再現性が高い | 言い換えに弱い |
| LLMベース | LLM-as-a-Judge | 意味的評価ができる | コスト高、評価基準が難しい |
実務では、正解データの準備が難しいので、LLMベース指標がよく使われます。
2. 検索性能の評価
検索コンポーネントの評価は、3つの観点があります。
| 比較関係 | 評価観点 | 説明 |
|---|---|---|
| 検索文書 ↔ クエリ | Relevance(適合性) | クエリに対して適切な文書を取れているか |
| 検索文書 ↔ 正解文書 | Accuracy(正確性) | 正解の文書を正しく取れているか |
| 検索文書 ↔ 検索文書 | Comprehensiveness(網羅性) | 必要な情報を網羅できているか |
それぞれの指標を見てみます。
2.1 Relevance(適合性)
検索された文書が、クエリの情報に合致しているかを測定します。
Context Precision(RAGAS)
LLMを使って、検索文書がクエリに関連しているか評価をします。上位に関連文書があるほど高スコアです。
| クラス名 | 正解データ | 比較対象 |
|---|---|---|
ContextPrecision |
必要 | 正解回答(reference) |
ContextUtilization |
不要 | 生成回答(response) |
from ragas.metrics.collections import ContextPrecision, ContextUtilization
from ragas.llms import llm_factory
from openai import AsyncOpenAI
client = AsyncOpenAI()
llm = llm_factory("gpt-4o-mini", client=client)
# 正解回答がある場合(Referenceに入れる)
scorer = ContextPrecision(llm=llm)
result = await scorer.ascore(
user_input="エッフェル塔はどこにありますか?",
reference="エッフェル塔はパリにあります。",
retrieved_contexts=["エッフェル塔はパリにあります。", "ブランデンブルク門はベルリンにあります。"]
)
# 正解回答がない場合(Responseに生成AIの回答を入れる)
scorer = ContextUtilization(llm=llm)
result = await scorer.ascore(
user_input="エッフェル塔はどこにありますか?",
response="エッフェル塔はパリにあります。",
retrieved_contexts=["エッフェル塔はパリにあります。", "ブランデンブルク門はベルリンにあります。"]
)
※コードをよく見ると、引数名が違うだけで中のプロンプトは同じになっていそうです。
2.2 Accuracy(正確性)
正解の文書が定義されているときに、検索の正確さを測定します。
非ランク指標
検索結果の順位を考慮しません。
| 指標 | 計算式 | 説明 |
|---|---|---|
| Accuracy@K | 正解が1つでも含まれるか | 上位K件に正解があれば1、なければ0 |
| Precision@K | 正解文書数 / K | 上位K件のうち、正解がいくつあるか |
| Recall@K | 取得した正解文書数 / 全正解文書数 | 正解文書をどれだけ網羅できたか |
| F1 Score | Precisionとrecallの調和平均 | PrecisionとRecallのバランス |
ランク指標
検索結果の順位を考慮します。正解文書が上位であるほど高スコアです。
| 指標 | 説明 | 使いどころ |
|---|---|---|
| MRR | 最初の正解が何位にあるか | 正解が1つ |
| MAP | 各正解の順位を考慮した平均精度 | 正解が複数ある |
| NDCG@K | 関連度に段階があるときの順位評価 | 関連度が0/1でなく段階的である |
RAGASでの実装
RAGASでは、「正解回答」を使いLLMで評価します。
| クラス名 | 評価方法 | 説明 |
|---|---|---|
ContextRecall |
主張ベース | 正解回答の各主張が検索文書で裏付けられるか |
ContextEntityRecall |
エンティティベース | 正解回答のエンティティが検索文書に含まれるか |
LLMを使わない指標もありますが、今回は紹介しません。
ContextRecall
正解回答を「主張(claims)」に分解し、各主張が検索文書で裏付けられるかを評価します。
スコア = 検索文書で裏付けられた主張の数 / 正解回答の全主張数
import openai
from ragas.llms import llm_factory
from ragas.metrics.collections import ContextRecall
client = openai.AsyncOpenAI()
llm = llm_factory("gpt-4o-mini", client=client)
metric = ContextRecall(llm=llm)
result = await metric.ascore(
user_input="フランスの首都はどこですか?",
retrieved_contexts=["パリはフランスの首都です。"],
reference="パリはフランスの首都であり、最大の都市です。"
)
print(f"Context Recall: {result.value}")
ContextEntityRecall
正解回答と検索文書からエンティティ(固有名詞など)をLLMで抽出して評価します。
スコア = |正解のエンティティ ∩ 検索文書のエンティティ| / |正解のエンティティ|
import openai
from ragas.llms import llm_factory
from ragas.metrics.collections import ContextEntityRecall
client = openai.AsyncOpenAI()
llm = llm_factory("gpt-4o-mini", client=client)
metric = ContextEntityRecall(llm=llm)
result = await metric.ascore(
reference="パリはフランスの首都で、紀元前52年に建設されました。",
retrieved_contexts=["フランスの首都はパリです。", "この都市は古代に建設されました。"]
)
print(f"Entity Recall: {result.value}")
2.3 Comprehensiveness(網羅性)
検索結果が、多様な情報を網羅しているかを評価します。似た文書ばかりだと、情報源が偏り精度が悪くなります。
| 指標 | 説明 |
|---|---|
| Coverage | 正解文書のうち、検索できた文書の割合 |
| Textual Similarity | 検索文書間のコサイン類似度の逆数(低いほど多様) |
直接的に評価するメソッドや他ライブラリは見つけられませんでした。
RAGASでは、ContextEntityRecall で似た評価もできます。
多様性は見落とされがちです。
検索精度は高いのに回答の質が低いときは、似た文書ばかり取得していることが精度を悪くしている原因であることがあります。あまり考えずに、大量の文書をデータベースに入れるとこの現象に陥ります。
3. 生成品質の評価
生成コンポーネントの評価は、3つの観点から行います。
| 比較関係 | 評価観点 | 説明 |
|---|---|---|
| 回答 ↔ クエリ | Relevance(適合性) | 質問にちゃんと答えているか |
| 回答 ↔ 検索文書 | Faithfulness(忠実性) | 検索文書に基づいてるか(=ハルシネーションしてないか) |
| 回答 ↔ 正解回答 | Correctness(正確性) | 正解と一致しているか |
それぞれ指標を見ていきます。
3.1 Relevance(適合性)
生成回答が、クエリの意図に沿っているかを測定します。
AnswerRelevancy(RAGAS)
| クラス名 | 説明 | 正解データ |
|---|---|---|
AnswerRelevancy |
回答がクエリに対して適切かをLLMが判定 | 不要 |
AnswerRelevancyは、回答から質問を逆生成し、クエリとのcos類似度を算出します。
import openai
from ragas.llms import llm_factory
from ragas.metrics.collections import AnswerRelevancy
client = openai.AsyncOpenAI()
llm = llm_factory("gpt-4o-mini", client=client)
metric = AnswerRelevancy(llm=llm)
result = await metric.ascore(
user_input="フランスの首都はどこですか?",
response="パリはフランスの首都です。美しいエッフェル塔があります。",
retrieved_contexts=["パリはフランスの首都です。"]
)
print(f"Answer Relevancy: {result.value}")
3.2 Faithfulness(忠実性)
生成回答が、検索文書の内容を反映しているかを評価します。RAG評価で1番重要です。
<理由>
RAGは「事実に基づく生成」が嬉しいです。検索文書にない情報を生成(ハルシネーション)すると、RAGを使う意味がありません。
| 指標 | 概要 | 特徴 |
|---|---|---|
| FactScore | 生成文を事実に分解し、知識源で裏付けられるかを検証する | ハルシネーション検出に特化 |
| KPR | 検索文書のキーポイントが回答に含まれるかを測定する | 情報の活用度を評価 |
| SePer | LLMの内部確率分布を用いて意味的な正確性を測定 | LLM内部状態へのアクセスが必要 |
RAGASでの実装
RAGASのFaithfulnessは、FactScoreと同じく回答を主張に分解して評価を行います。
| クラス名 | 説明 | 正解データ |
|---|---|---|
Faithfulness |
回答の各主張が検索文書で裏付けられるか | 不要 |
スコア = 検索文書で裏付けられた主張の数 / 回答の全主張数
import openai
from ragas.llms import llm_factory
from ragas.metrics.collections import Faithfulness
client = openai.AsyncOpenAI()
llm = llm_factory("gpt-4o-mini", client=client)
metric = Faithfulness(llm=llm)
result = await metric.ascore(
user_input="フランスの首都はどこですか?",
response="パリはフランスの首都で、人口は200万人です。",
retrieved_contexts=["パリはフランスの首都です。"]
)
print(f"Faithfulness: {result.value}")
3.3 Correctness(正確性)
生成回答と正解回答(GroundTruth)の一致を評価します。
従来の指標
| 指標 | 説明 | 特徴 |
|---|---|---|
| Exact Match(EM) | 完全一致かどうか | 厳密だが、言い換えに弱い |
| F1 Score | トークンレベルの一致度 | 部分一致を評価できる |
| ROUGE | n-gramの重複率 | 要約評価に最適 |
| BLEU | n-gram精度 | 機械翻訳評価に由来 |
| BERTScore | 埋込みベースの類似度 | 言い換えに強い |
従来の指標は計算が高速で解釈しやすいです。しかし、意味は同じだが表現が異なる場合に、評価が大きく変わってしまいます。
RAGASでの実装
RAGASでは、LLMを用いた評価を行います。どれも正解回答が必要です。
| クラス名 | 説明 | 特徴 |
|---|---|---|
AnswerCorrectness |
正解回答との総合的な一致度 | 事実性と意味的類似度の加重平均 |
FactualCorrectness |
事実レベルでの正確性 | TP/FP/FNを用いたF1スコア |
SemanticSimilarity |
埋め込みベースの意味的類似度 | 言い換えに強い |
AnswerCorrectness
- 回答と正解回答を、主張に分解し、TP、 FP、FNに分類する。
- F1スコアを計算(Factuality Score)
- 埋め込みでコサイン類似度を計算(Similarity Score)
- 加重平均で最終スコアを算出する
最終スコア= 0.75 × Factuality + 0.25 × Similarity
import openai
from ragas.llms import llm_factory
from ragas.embeddings.base import embedding_factory
from ragas.metrics.collections import AnswerCorrectness
client = openai.AsyncOpenAI()
llm = llm_factory("gpt-4o-mini", client=client)
embeddings = embedding_factory("openai", model="text-embedding-ada-002", client=client, interface="modern")
metric = AnswerCorrectness(llm=llm, embeddings=embeddings)
result = await metric.ascore(
user_input="フランスの首都はどこですか?",
response="パリはフランスの首都で、多くの美術館があります。",
reference="パリはフランスの首都です。"
)
print(f"Answer Correctness: {result.value}")
# 出力例: Answer Correctness: 0.85
FactualCorrectness
- 回答を主張に分解し、各主張が正解回答で裏付けられるかをLLMで評価する
- モード(
precision、recall、f1)に応じてスコアを計算する
import openai
from ragas.llms import llm_factory
from ragas.metrics.collections import FactualCorrectness
client = openai.AsyncOpenAI()
llm = llm_factory("gpt-4o-mini", client=client)
metric = FactualCorrectness(llm=llm, mode="f1")
result = await metric.ascore(
response="アインシュタインは1879年にドイツで生まれました。",
reference="アルベルト・アインシュタインは1879年3月14日にドイツのウルムで生まれました。"
)
print(f"Factual Correctness: {result.value}")
# 出力例: Factual Correctness: 0.8
SemanticSimilarity
- 回答と正解回答を埋め込みベクトルに変換する
- cos類似度を計算する
BERTScoreとおなじです。Embeddingが必要ですが、LLMは不要です。
import openai
from ragas.embeddings.base import embedding_factory
from ragas.metrics.collections import SemanticSimilarity
client = openai.AsyncOpenAI()
embeddings = embedding_factory("openai", model="text-embedding-ada-002", client=client, interface="modern")
metric = SemanticSimilarity(embeddings=embeddings)
result = await metric.ascore(
reference="パリはフランスの首都です。",
response="フランスの首都はパリです。"
)
print(f"Semantic Similarity: {result.value}")
# 出力例: Semantic Similarity: 0.95
4. 外部評価(システムレベル)
2・3節では内部評価(コンポーネントレベル)を扱いました。システム全体を対象とした外部評価を整理します。
4.1 安全性(Safety)
ノイズや悪意ある入力に対しても安全なコンテンツを生成できるかを評価します。
堅牢性(Robustness)
誤情報やノイズを含む検索結果に対するシステムの挙動を評価します。
| 指標 | 概要 |
|---|---|
| Misleading Rate | 誤情報に騙された回答の割合 |
| Misrepresentation Ratio | 意味的に関連するが無関係な情報に影響された割合 |
| Uncertainty Ratio | 不確実な情報に対する応答の割合 |
| Resilience Rate | 検索前後で正確性を維持できた割合 |
| Boost Rate | 検索により誤答が正答に修正された割合 |
事実性(Factuality)
ノイズがある中で、正確な情報を生成できるかを評価します。
| 指標 | 概要 |
|---|---|
| Factual Accuracy | 誤解を招く文脈でのEM、F1、Accuracyなど |
| Hallucination Rate | 検索文書に裏付けのない情報を生成した割合 |
| Citation Precision | 引用した出典が正確である割合 |
| Citation Recall | 正しい出典を漏れなく引用できた割合 |
敵対的攻撃(Adversarial Attacks)
RAGの各コンポーネントへの攻撃耐性を評価します。
| 攻撃タイプ | 概要 | 評価指標 |
|---|---|---|
| PoisonedRAG | 知識ベースに悪意のあるテキストを注入 | Attack Success Rate (ASR) |
| HijackRAG | ランキングアルゴリズムを悪用して悪意のあるコンテンツを優先 | 攻撃の転移性 |
| Phantom Attack | トリガーで発動する悪意のある文書 | Retrieval Failure Rate (Ret-FR) |
| Jamming Attack | 応答拒否を強制する「ブロッカー」文書を挿入 | Oracle-based metrics |
プライバシー(Privacy)
検索DBやクエリからの情報漏洩リスクを評価します。
| 指標 | 概要 |
|---|---|
| PII Leakage Rate | 個人情報が出力に漏洩した割合 |
| Extraction Success Rate | 攻撃者が個人情報を抽出できた割合 |
| Membership Inference Attack Success | 特定データが知識ベースに含まれるか推測できた割合 |
公平性(Fairness)
検索文書からのバイアスを増やしてないかを評価します。
| 指標 | 概要 |
|---|---|
| Bias Metrics | 人口統計グループ間でのパフォーマンス格差 |
| Stereotype Detection | 有害なステレオタイプの出現頻度 |
| Counterfactual Fairness | センシティブな属性を変更した際の出力変化 |
透明性・説明責任(Transparency / Accountability)
回答の根拠を説明できるかを評価します。
| 指標 | 概要 |
|---|---|
| Explanation Quality | 説明の明確さ・完全性・有用性(人間評価) |
| Traceability | 出力を特定のソース文書に遡れるか |
| Citation Accuracy | 出典の引用精度(Precision / Recall) |
RAGASでの実装
RAGASではNoiseSensitivityで、ノイズに対する感度を測定できます。
| モード | 説明 |
|---|---|
relevant |
関連文書から誤った情報を取り込んでいないか |
irrelevant |
無関係な文書から誤った情報を取り込んでいないか |
import openai
from ragas.llms import llm_factory
from ragas.metrics.collections import NoiseSensitivity
client = openai.AsyncOpenAI()
llm = llm_factory("gpt-4o-mini", client=client)
# 関連文書に対するノイズ感度(デフォルト)
metric = NoiseSensitivity(llm=llm, mode="relevant")
result = await metric.ascore(
user_input="LIC(インド生命保険公社)は何で知られていますか?",
response="LICはインド最大の保険会社で、巨大な投資ポートフォリオで知られています。LICは国の金融安定に貢献しています。",
reference="LICはインド最大の保険会社で、1956年に保険業界の国有化により設立されました。大規模な投資ポートフォリオの管理で知られています。",
retrieved_contexts=[
"LICは1956年にインドの保険業界の国有化に伴い設立されました。",
"LICはインド最大の保険会社で、多くの契約者と巨額の投資を持っています。",
"インド最大の機関投資家として、LICは多額の資金を運用し、国の金融安定に貢献しています。",
"インド経済は、金融、技術、製造業などの分野のおかげで、世界で最も急成長している主要経済の一つです。" # ノイズ
]
)
print(f"Noise Sensitivity (relevant): {result.value}")
# 出力例: Noise Sensitivity (relevant): 0.333
# → 3つの主張のうち1つが正解に基づかない誤った主張
コードまでは触れませんが、他のライブラリで使える指標を紹介します。
DeepEvalの安全性指標
| 指標 | 概要 |
|---|---|
| BiasMetric | 出力に含まれるバイアスを検出 |
| ToxicityMetric | 出力の有害性を検出 |
| HallucinationMetric | 検索文書に基づかない情報の生成を検出 |
| PII Leakage | LLM出力にPIIやプライバシーデータがないかを検出 |
TruLensの安全性指標

OpenAI Moderation API
| カテゴリ | 概要 |
|---|---|
| harassment | ハラスメント表現 |
| hate | ヘイトスピーチ(人種・性別・宗教等に基づく) |
| illicit | 違法行為の助長 |
| self-harm | 自傷行為の促進 |
| sexual | 性的コンテンツ |
| violence | 暴力的なコンテンツ |
4.2 効率性(Efficiency)
RAGは精度だけでなく、計算効率も大事です。
| 指標 | 概要 |
|---|---|
| Latency(応答時間) | クエリから応答までのエンドツーエンドの処理時間 |
| Throughput(スループット) | 単位時間あたりのクエリ処理数(QPS) |
| Token Consumption(トークン消費量) | LLM推論時に消費されるトークン数 |
4.2.1 コスト評価(Cost Evaluation)
RAGのコスト評価は重要です。総コストは以下のカテゴリに分類されます。
| コストカテゴリ | 概要 |
|---|---|
| Infrastructure Costs(インフラコスト) | 埋め込み生成、ベクトルDBの維持、LLM推論のローカル計算リソース |
| Token-based Expenses(トークンベースの費用) | 入出力トークン使用量に基づくAPI料金 |
| Storage Costs(ストレージコスト) | コーパスサイズに応じてスケールするベクトルDBのホスティング費用 |
| Operational Overhead(運用オーバーヘッド) | 人件費、システムメンテナンス、ナリッジベースの定期更新 |
| Development Costs(開発コスト) | 初期実装、カスタマイズの費用 |
4.2.2 経済効率性(Economic efficiency)
RAGを本番運用するとき、パフォーマンスとコストのバランスを把握することは重要です。
| 指標 | 概要 |
|---|---|
| Cost-Effectiveness Ratio(費用対効果比) | コスト単位あたりのパフォーマンス |
| Retrieval Precision ROI(検索精度ROI) | 検索精度の向上による経済的リターン |
| User-Controllable Cost-Accuracy Tradeoffs | 検索コストと精度のトレードオフ |
| Comparative Cost Analysis(比較コスト分析) | 異なるRAG実装間の相対的なコスト効率 |
4.2.3 オブザーバビリティツールの活用
効率性・コストの測定には、LangSmithやAWS AgentCoreなどのオブザーバビリティツールでトレースを確認してください。これらのツールにより、Latency・トークン消費・コストを可視化し、ボトルネックの特定ができます。
まとめ
本記事では、RAGの評価手法を体系的にまとめました。
実務では、以下を意識して評価をして下さい。
- まず検索・生成をRAGASで評価する
- 安全性と効率性をシステム全体で評価する
- 継続的なモニタリングして改善を行う
RAGASなどのツールは、自分でパイプラインに組み込む必要があります。
本記事で紹介した指標を参考に、ユースケースに応じた評価基盤を構築してみてください。
Discussion