生成指標:忠実性と回答の関連性
RAGASを使って、生成された回答が取得したコンテキストに忠実か、またハルシネーションを起こさずにユーザーの質問へ実際に答えているかを測定します。
「生成指標:忠実性と回答の関連性」はCoddyKit上の無料AI Engineering Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Engineering Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Engineering Academyコースには全4レッスンが含まれています。
生成段階を測定する
検索システムが完璧なチャンクを見つけた場合でも、生成段階で失敗することがあります。LLMが検索されたコンテキストを無視してパラメトリックメモリから回答したり、コンテキストの内容を誤って解釈したり、尋ねられた内容とは少し異なる質問に答えたりする可能性があります。生成指標は、検索とは別にこれらの失敗を数値化するため、それぞれの問題を特定して修正できます。生成における主な指標は、忠実度と回答の関連性です。
忠実度:定義
忠実度は、生成された回答のすべての主張を、検索されたコンテキストに直接たどれるかどうかを測定します。忠実な回答は、コンテキストに含まれていない情報を導入しません。忠実度は主張単位で測定します。回答を個々の原子的な文に分解し、それぞれがコンテキストによって裏付けられているか確認します。忠実度スコアは、裏付けられている主張の割合です。
# Faithfulness = supported_claims / total_claims
example_answer = (
'Employees receive 15 vacation days per year. '
'Remote work is allowed on Wednesdays and Fridays. '
'The CEO is John Smith.'
)
example_context = (
'15 vacation days are granted annually. '
'Remote work is permitted on Wednesdays and Fridays.'
)
# Claim 1: 15 vacation days — SUPPORTED
# Claim 2: Remote work Wed+Fri — SUPPORTED
# Claim 3: CEO is John Smith — NOT IN CONTEXT (hallucinated)
# Faithfulness = 2/3 = 0.67LLM-as-judgeによる忠実度の実装
大規模に忠実度を測定する最も実用的な方法は、強力なLLMを判定役として使うことです。判定用LLMに回答を個々の主張へ分解させ、各主張をコンテキストと照合させます。判定用LLMは、SUPPORTEDまたはUNSUPPORTEDのラベルが付いた主張の一覧を返すため、SUPPORTEDの割合を計算します。この方法なら、1回の評価に数セントのコストで、1時間あたり数千件の評価に対応できます。
import json
def evaluate_faithfulness(answer, context, llm_client):
prompt = (
'Task: Evaluate the faithfulness of an answer against a context.\n\n'
f'Context:\n{context}\n\n'
f'Answer:\n{answer}\n\n'
'Steps:\n'
'1. Break the answer into individual atomic claims.\n'
'2. For each claim, check if it is supported by the context.\n'
'3. Return JSON: {"claims": [{"text": "...", "supported": true/false}], "score": 0.0-1.0}'
)
response = llm_client.chat.completions.create(
model='gpt-4o',
response_format={'type': 'json_object'},
messages=[{'role': 'user', 'content': prompt}]
)
return json.loads(response.choices[0].message.content)回答の関連性:定義
回答の関連性は、生成された回答が実際にユーザーの質問に答えているかどうかを測定します。非常に忠実な回答でも、要点を外すことがあります。たとえば、ユーザーが「パスワードをリセットするにはどうすればよいですか?」と尋ねたのに対し、回答がパスワードのリセット手順には触れず、会社の一般的なセキュリティポリシーを詳しく説明する場合です。回答の関連性は忠実性とは別の指標です。つまり、コンテキスト内の内容だけを述べていて忠実であっても、質問に答えていなければ関連性はありません。
回答の関連性を測定する
RAGASは、巧妙な逆生成手法を使って回答の関連性を測定します。まず評価用LLMが、生成された回答によって答えられる架空の質問を複数生成し、次に埋め込みのコサイン類似度を使って、それらの生成された質問が元の質問とどの程度似ているかを測定します。生成された架空の質問と元の質問の類似度が高いほど、回答の関連性が高いことを示します。
def evaluate_answer_relevance(question, answer, llm_client, embed_fn):
# Generate hypothetical questions for this answer
prompt = (
f'Given this answer: "{answer}"\n\n'
'Generate 3 questions that this answer would be a good response to.\n'
'Return as JSON: {"questions": ["...", "...", "..."]}'
)
response = llm_client.chat.completions.create(
model='gpt-4o-mini',
response_format={'type': 'json_object'},
messages=[{'role': 'user', 'content': prompt}]
)
hyp_questions = json.loads(response.choices[0].message.content)['questions']
# Measure similarity to original question
orig_embedding = embed_fn(question)
hyp_embeddings = [embed_fn(q) for q in hyp_questions]
similarities = [cosine_similarity(orig_embedding, e) for e in hyp_embeddings]
return sum(similarities) / len(similarities)コンテキスト再現率:十分に取得できているか
コンテキスト再現率は、取得したコンテキストに質問へ正しく答えるための十分な情報が含まれているかどうかを測定します。正解データの回答を文ごとに確認し、各文を取得したチャンクのいずれかに帰属できるかを調べます。コンテキスト再現率が高い場合、検索器が必要な情報をすべて見つけています。低い場合は、取得したチャンクに重要な情報が欠けているため、生成が完璧でもLLMは正しく回答できません。
def evaluate_context_recall(ground_truth_answer, retrieved_contexts, llm_client):
prompt = (
f'Ground truth answer:\n{ground_truth_answer}\n\n'
f'Retrieved context:\n{",".join(retrieved_contexts)}\n\n'
'For each sentence in the ground truth answer, determine if it '
'can be attributed to the retrieved context.\n'
'Return JSON: {"sentences": [{"text": "...", "in_context": true/false}], "recall": 0.0-1.0}'
)
response = llm_client.chat.completions.create(
model='gpt-4o',
response_format={'type': 'json_object'},
messages=[{'role': 'user', 'content': prompt}]
)
return json.loads(response.choices[0].message.content)コンテキスト適合率:取得したチャンクは関連しているか
コンテキスト適合率は、取得したチャンクが質問への回答に実際に役立つかどうかを測定します。コンテキスト適合率が高い場合、取得したチャンクは質問に密接に関連しています。低い場合は、取得したチャンクの多くが無関係なノイズとなり、LLMが読み取って破棄しなければならないため、混乱するリスクが高まります。コンテキスト適合率は、生成された回答で実際に使用または参照された取得チャンクを確認して測定します。
def evaluate_context_precision(question, answer, retrieved_contexts, llm_client):
precision_scores = []
for i, context in enumerate(retrieved_contexts, 1):
prompt = (
f'Question: {question}\n\n'
f'Context chunk {i}: {context}\n\n'
f'Answer: {answer}\n\n'
'Was this context chunk useful in generating the answer? '
'Return JSON: {"useful": true/false, "reason": "..."}'
)
response = llm_client.chat.completions.create(
model='gpt-4o-mini',
response_format={'type': 'json_object'},
messages=[{'role': 'user', 'content': prompt}]
)
result = json.loads(response.choices[0].message.content)
precision_scores.append(1.0 if result['useful'] else 0.0)
return sum(precision_scores) / len(precision_scores)すべての指標を一度にRAGASで使用する
RAGASライブラリは、コンテキスト適合率、コンテキスト再現率、忠実性、回答の関連性というRAGの主要4指標すべてを、単一のフレームワークで実装しています。評価用LLMの呼び出しも内部で処理します。質問、生成された回答、取得したコンテキスト、正解データの回答を含むデータセットを渡すと、包括的なスコアレポートを受け取れます。RAGASは非同期評価にも対応しているため、数百件の例を並列に評価できます。
from ragas import evaluate
from ragas.metrics import (
faithfulness, answer_relevancy,
context_precision, context_recall
)
from datasets import Dataset
# Build evaluation dataset
eval_samples = [
{
'question': item['question'],
'answer': item['generated_answer'],
'contexts': item['retrieved_texts'],
'ground_truth': item['expected_answer']
}
for item in test_results
]
eval_dataset = Dataset.from_list(eval_samples)
results = evaluate(eval_dataset,
metrics=[faithfulness, answer_relevancy, context_precision, context_recall]
)
results.to_pandas().to_csv('eval_results.csv', index=False)指標の組み合わせによる診断
指標を組み合わせると、システムの具体的な問題が明らかになります。忠実性が低い + コンテキスト適合率が高い → LLMがコンテキストを無視してハルシネーションを起こしています(プロンプトを修正します)。コンテキスト再現率が低い + 忠実性が高い → 検索器が重要なチャンクを見落としていますが、LLMは見つかった内容を忠実に報告しています(チャンク分割または埋め込みを修正します)。回答の関連性が低い + 忠実性が高い → 取得したチャンクは接線的には関連しており、LLMもそれらを忠実に説明していますが、質問には答えていません(検索結果のフィルタリングまたはクエリの書き換えを改善します)。
# Diagnostic matrix
diagnostics = [
{
'condition': 'Low faithfulness + High context precision',
'cause': 'LLM ignoring context, answering from parametric memory',
'fix': 'Strengthen system prompt: "Answer ONLY from the provided context"'
},
{
'condition': 'Low context recall + High faithfulness',
'cause': 'Retriever missing relevant chunks',
'fix': 'Improve chunking strategy, embedding model, or increase top-k'
},
{
'condition': 'Low answer relevance + High context recall',
'cause': 'Retrieved context is off-topic; LLM is answering wrong question',
'fix': 'Add query rewriting or improve metadata filters'
}
]キャリブレーションのための人手評価
LLM-as-judgeによる指標は人間の判断と相関しますが、完全に一致するわけではありません。キャリブレーションを行うには、3人の人間評価者に、無作為に抽出した50件の出力について、忠実性と回答の関連性を1~5の尺度で採点してもらいます。LLMの評価と人間による平均評価の一致度を計算します。LLMの評価が人間より体系的に高すぎたり低すぎたりする場合は、補正係数を適用します。キャリブレーション済みの自動評価指標により、スコアの改善が実際の品質向上を反映していると確信できます。
from scipy.stats import spearmanr
def calibrate_judge_vs_humans(samples):
'''samples: [{"llm_score": 0.9, "human_score": 4.2}, ...]'''
llm_scores = [s['llm_score'] for s in samples]
human_scores = [s['human_score'] / 5.0 for s in samples] # normalize to 0-1
correlation, pvalue = spearmanr(llm_scores, human_scores)
print(f'LLM-Human Spearman correlation: {correlation:.3f} (p={pvalue:.4f})')
mean_llm = sum(llm_scores) / len(llm_scores)
mean_human = sum(human_scores) / len(human_scores)
bias = mean_llm - mean_human
print(f'LLM bias vs humans: {bias:+.3f}')
return correlation, bias本番環境向けの指標目標を設定する
RAGシステムを本番環境にデプロイする前に、システムが満たすべき指標の最低しきい値を定義します。本番環境でよく使われる目標は、忠実性 > 0.90(回答内の主張のうちハルシネーションが10%未満)、回答の関連性 > 0.80(80%以上の回答が実際に質問に答えている)、コンテキスト適合率 > 0.60(取得したチャンクの大半が関連している)、コンテキスト再現率 > 0.75(重要な事実の大半が取得したコンテキストに含まれている)です。これらのしきい値を満たさないシステムは、さらなる改善なしにデプロイすべきではありません。
PRODUCTION_THRESHOLDS = {
'faithfulness': 0.90,
'answer_relevancy': 0.80,
'context_precision': 0.60,
'context_recall': 0.75
}
def check_production_readiness(metrics):
ready = True
print('Production readiness check:')
for metric, threshold in PRODUCTION_THRESHOLDS.items():
score = metrics.get(metric, 0)
status = 'PASS' if score >= threshold else 'FAIL'
print(f' {metric}: {score:.2f} (>= {threshold}) -> {status}')
if status == 'FAIL':
ready = False
print(f'Overall: {"READY" if ready else "NOT READY"}')
return ready理解度チェック
このレッスンで学んだAI Engineeringの概念を確認しましょう。
レッスンのまとめ
このレッスンでは、回答の各文が取得したコンテキストによって裏付けられているかを主張単位で確認する指標としての忠実性、回答が実際の質問に答えているかを測定する指標としての回答の関連性、生成の観点から検索品質を測定するコンテキスト再現率と適合率、そして複数の指標を自動評価するRAGASライブラリについて学びました。次は、これらすべての指標を、変更のたびに実行できる自動評価ハーネスにまとめます。
AI チューターと学ぶ Python — 無料
ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。
- コース
- 30
- レッスン
- 120
よくある質問
「生成指標:忠実性と回答の関連性」レッスンは無料ですか?
はい。「生成指標:忠実性と回答の関連性」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Engineering Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Engineering Academyコースには全4レッスンが含まれています。
「生成指標:忠実性と回答の関連性」で何を学びますか?
RAGASを使って、生成された回答が取得したコンテキストに忠実か、またハルシネーションを起こさずにユーザーの質問へ実際に答えているかを測定します。 ブラウザで直接実行するハンズオンコードでAI Engineering Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
AI Engineering Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのAI Engineering Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「生成指標:忠実性と回答の関連性」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このAI Engineering Academyレッスンでコードを書いて実行できますか?
はい。すべてのAI Engineering Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- RAGで評価が重要な理由
- 検索指標:Hit Rate、MRR、NDCG
- 生成指標:忠実性と回答の関連性
- 自動評価ハーネスの構築