<p><!--META
{
"title": "RAGシステムにおけるプロンプト評価指標",
"primary_category": "LLM",
"secondary_categories": ["プロンプトエンジニアリング","RAG"],
"tags": ["RAG","プロンプト評価","LLM","評価指標","自動評価","LangChain","Ragas"],
"summary": "RAGシステムにおけるプロンプトの評価指標、評価シナリオ、自動評価手法、失敗モードと抑制手法を解説します。",
"mermaid": true,
"verify_level": "L0",
"tweet_hint": {"text":"RAGシステムのプロンプト評価にお困りですか?本記事では、評価指標、自動評価の擬似コード、失敗モードと抑制手法を徹底解説!プロンプトの質向上に役立つ情報満載です。
#RAG #プロンプトエンジニアリング
#LLM"},
"link_hints": [
"https://python.langchain.com/v0.2/docs/concepts/#evaluation",
"https://docs.ragas.io/en/stable/concepts/metrics/index.html",
"https://arxiv.org/pdf/2405.00632",
"https://cloud.google.com/vertex-ai-search/docs/evaluation",
"https://aws.amazon.com/jp/blogs/news/evaluate-rag-applications-in-amazon-bedrock-using-foundation-models/"
]
}
-->
本記事は<strong>Geminiの出力をプロンプト工学で整理した業務ドラフト(未検証)</strong>です。</p>
<h1 class="wp-block-heading">RAGシステムにおけるプロンプト評価指標</h1>
<p>Retrieval-Augmented Generation (RAG) システムは、外部知識ベースから情報を検索し、その情報に基づいて大規模言語モデル (LLM) が応答を生成する仕組みです。このRAGシステムにおいて、生成される応答の品質は、プロンプトの設計に大きく左右されます。本記事では、RAGシステムにおけるプロンプトの評価指標、評価シナリオ、自動評価手法、そして失敗モードとその抑制手法について解説します。</p>
<h2 class="wp-block-heading">ユースケース定義</h2>
<p>RAGシステムにおいて、ユーザーからの質問に対して、外部データベースから取得したコンテキスト情報に基づき、LLMが正確かつ関連性の高い回答を生成します。このプロセスにおけるプロンプトの品質を定量的に評価し、継続的な改善サイクルを構築することが本ユースケースの目的です。具体的には、応答の正確性、情報源への忠実度、ユーザー質問への関連性といった観点から評価を行います。</p>
<h2 class="wp-block-heading">制約付き仕様化(入出力契約)</h2>
<p>RAGシステムのプロンプト評価における入出力契約を以下に定義します。</p>
<h3 class="wp-block-heading">入力</h3>
<ul class="wp-block-list">
<li><p><strong>ユーザーの質問 (Question)</strong>: ユーザーがLLMに投げかける自然言語の質問文字列。</p></li>
<li><p><strong>検索されたコンテキスト (Context)</strong>: RAGシステムが外部知識ベースから検索し、LLMに提供する情報テキストのリスト。</p></li>
<li><p><strong>LLMによる生成応答 (Response)</strong>: RAGシステム(LLM)が質問とコンテキストに基づいて生成した回答文字列。</p></li>
<li><p><strong>正解応答 (Ground Truth/Optional)</strong>: 評価のゴールドスタンダードとなる人間が作成した正確な回答(自動評価の補助に利用)。</p></li>
</ul>
<h3 class="wp-block-heading">出力</h3>
<ul class="wp-block-list">
<li><p><strong>総合評価スコア (Overall Score)</strong>: 0.0から1.0までの範囲で、応答の全体的な品質を示す数値。</p></li>
<li><p><strong>評価カテゴリ別フィードバック (Categorized Feedback)</strong>: 各評価指標(正確性、関連性、忠実度など)に対する個別のスコアまたは定性的なコメント。</p></li>
<li><p><strong>評価理由 (Reasoning)</strong>: 各評価カテゴリに対するLLMまたは評価器からの根拠となる説明。</p></li>
</ul>
<h3 class="wp-block-heading">失敗時の挙動</h3>
<ul class="wp-block-list">
<li><p><strong>無効な入力</strong>: 必須入力(質問、コンテキスト、応答)が欠落している場合や、フォーマットが不正な場合は、<code>INVALID_INPUT_ERROR</code>を返し、具体的なエラーメッセージを提示します。</p></li>
<li><p><strong>評価器のタイムアウト</strong>: 自動評価が所定の時間(例:30秒)内に完了しない場合、<code>EVALUATION_TIMEOUT</code>を返し、部分的な評価結果があればそれを付与します。</p></li>
</ul>
<h3 class="wp-block-heading">禁止事項</h3>
<ul class="wp-block-list">
<li><p><strong>個人情報/機密情報の利用</strong>: 評価プロセスにおいて、入力データに個人情報や機密情報が含まれる場合、それらを評価器に渡すことを厳しく禁止します。マスキングまたは匿名化を必須とします。</p></li>
<li><p><strong>幻覚の許容範囲外</strong>: 生成応答が、提供されたコンテキストに存在しない情報を捏造する「幻覚」を起こした場合、最低スコアを付与し、かつその理由を明確に記録します。</p></li>
<li><p><strong>評価プロセスからの脱線</strong>: LLMベースの評価器が、指定された評価タスクから逸脱し、無関係な内容を生成したり、評価フォーマットを遵守しない場合、自動的に再試行するか、エラーとして報告します。</p></li>
</ul>
<h2 class="wp-block-heading">プロンプト設計</h2>
<p>RAGシステムのプロンプト評価には、評価の目的や複雑さに応じて様々なプロンプト設計が考えられます。ここでは、3種類のプロンプト案を提示します。</p>
<h3 class="wp-block-heading">1. ゼロショット評価プロンプト</h3>
<p>基本的な評価を目的とし、LLMに直接評価を依頼する形式です。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">あなたはRAGシステムの回答品質を評価するAIアシスタントです。
以下の情報を元に、提供された回答を評価してください。
評価指標:
1. 正確性: 回答は質問に対してどれだけ正確か?(0.0-1.0)
2. 忠実度: 回答は提供されたコンテキストの情報にどれだけ忠実か?コンテキストにない情報を捏造していないか?(0.0-1.0)
3. 関連性: 回答は質問に対してどれだけ関連しているか?(0.0-1.0)
入力:
質問: {{user_question}}
コンテキスト: {{retrieved_context}}
回答: {{generated_response}}
出力形式:
```json
{
"accuracy": <float>,
"faithfulness": <float>,
"relevance": <float>,
"overall_comment": "<評価の総合的なコメント>"
}
</pre>
</div>
<p>上記の出力形式に従って評価結果を出力してください。</p>
<pre data-enlighter-language="generic">**特徴**: 実装が容易。LLMの汎用的な推論能力に依存。
**利点**: 迅速な評価、幅広い用途。
**欠点**: 評価のブレが生じやすい、複雑なケースでの判断が難しい場合がある。
### 2. 少数例 (Few-shot) 評価プロンプト
いくつか具体的な評価例を提示することで、LLMの評価基準を明確化し、安定した評価を促します。
```text
あなたはRAGシステムの回答品質を評価するAIアシスタントです。
以下の例を参考に、提供された回答を評価してください。
---
例1 (良い例):
質問: Pythonのリストとタプルの違いは何ですか?
コンテキスト: リストは可変なシーケンスで、要素の追加、削除、変更が可能。タプルは不変なシーケンスで、一度作成すると変更できない。
回答: Pythonのリストは要素の追加や削除が可能な可変シーケンスですが、タプルは作成後に変更できない不変シーケンスです。
評価:
```json
{
"accuracy": 1.0,
"faithfulness": 1.0,
"relevance": 1.0,
"overall_comment": "質問に正確かつ忠実に回答しており、関連性も高いです。"
}
</pre>
<hr/>
<p>例2 (悪い例 – 幻覚):
質問: GoogleのCEOは誰ですか?
コンテキスト: Sundar PichaiはAlphabet Inc.のCEOです。
回答: GoogleのCEOはTim Cookです。
評価:</p>
<div class="codehilite">
<pre data-enlighter-language="generic">{
"accuracy": 0.0,
"faithfulness": 0.0,
"relevance": 0.5,
"overall_comment": "回答は完全に不正確であり、提供されたコンテキストにも忠実ではありません。幻覚が見られます。"
}
</pre>
</div><hr/>
<p>入力:
質問: {{user_question}}
コンテキスト: {{retrieved_context}}
回答: {{generated_response}}</p>
<p>出力形式:</p>
<div class="codehilite">
<pre data-enlighter-language="generic">{
"accuracy": <float>,
"faithfulness": <float>,
"relevance": <float>,
"overall_comment": "<評価の総合的なコメント>"
}
</pre>
</div>
<p>上記の出力形式に従って評価結果を出力してください。</p>
<pre data-enlighter-language="generic">**特徴**: 具体的例示によりLLMの評価基準をガイド。
**利点**: 評価の一貫性が向上、特に難しいケースでの判断精度が向上。
**欠点**: プロンプトが長くなる、適切な例の選定が必要。
### 3. Chain-of-Thought制約型評価プロンプト
LLMに評価プロセスを段階的に思考させ、その上で最終的な評価を下すことで、より透明性が高く、信頼性の高い評価を促します。
```text
あなたはRAGシステムの回答品質を評価するAIアシスタントです。
以下の手順と評価指標に従って、提供された回答を評価してください。
評価手順:
1. まず、質問の意図を明確に理解してください。
2. 次に、提供されたコンテキストが質問に答えるために十分かどうかを確認してください。
3. そして、生成された回答がコンテキスト内の情報のみに基づいているか(忠実度)を評価してください。
4. 最後に、回答が質問に対して正確であり、かつ関連しているか(正確性、関連性)を評価し、総合的な判断を下してください。
評価指標:
- 正確性 (Accuracy): 質問への回答が事実に基づき、正しいか? (0.0-1.0)
- 忠実度 (Faithfulness): 回答が提供されたコンテキスト内の情報にのみ基づいているか? (0.0-1.0)
- 関連性 (Relevance): 回答が質問の意図に合致し、適切に焦点を当てているか? (0.0-1.0)
入力:
質問: {{user_question}}
コンテキスト: {{retrieved_context}}
回答: {{generated_response}}
思考プロセス:
1. 質問の分析: ...
2. コンテキストの検証: ...
3. 回答の忠実度チェック: ...
4. 回答の正確性と関連性チェック: ...
出力形式:
```json
{
"thought_process": "<思考プロセス全体の要約>",
"accuracy": <float>,
"faithfulness": <float>,
"relevance": <float>,
"overall_comment": "<評価の総合的なコメント>"
}
</pre>
<p>上記の出力形式に従って評価結果を出力してください。</p>
<pre data-enlighter-language="generic">**特徴**: LLMに思考プロセスを強制し、評価の根拠を明確化。
**利点**: 評価結果の信頼性が向上、誤り分析が容易になる。
**欠点**: 応答時間が長くなる、プロンプトが複雑になる。
## 評価
RAGシステムの評価には、多角的なシナリオと自動化された評価手法が不可欠です。
### 評価シナリオ
評価の網羅性を高めるため、以下のシナリオでプロンプトの性能をテストします。
1. **正例 (Happy Path)**:
* **質問**: 「地球の周回軌道に最初に到達した人工衛星の名前は何ですか?」
* **コンテキスト**: 「1957年10月4日にソ連が打ち上げた『スプートニク1号』が、地球周回軌道に到達した最初の人工衛星である。」
* **期待される応答**: 「地球の周回軌道に最初に到達した人工衛星は、1957年10月4日にソ連が打ち上げたスプートニク1号です。」
* **評価観点**: 正確性、忠実度、関連性がすべて高く評価されることを確認。
2. **難例 (Challenging Case)**:
* **質問**: 「再生可能エネルギーの導入メリットと課題を、経済的・環境的側面から簡潔に教えてください。」
* **コンテキスト**: 複数の段落にわたる再生可能エネルギーに関する情報(メリット、コスト、間欠性、環境影響など)。
* **期待される応答**: 複数の側面(経済的メリット/コスト、環境的メリット/インフラ課題など)を統合し、簡潔にまとめた回答。
* **評価観点**: 複数情報の統合能力、要約能力、指定された側面(経済・環境)への適切な言及。
3. **コーナーケース (Edge Case)**:
* **質問**: 「宇宙飛行士になるには、どんなトレーニングが必要ですか?また、日本の宇宙飛行士は何人いますか?」
* **コンテキスト**: 宇宙飛行士の訓練に関する一般的な情報はあるが、日本の宇宙飛行士の具体的な人数に関する情報がない。
* **期待される応答**: 訓練については回答し、日本の宇宙飛行士の人数については「提供された情報には含まれていません」といった形で情報不足を明示する回答。
* **評価観点**: 情報不足時の適切な応答(幻覚の回避)、質問の複数の部分への対応。
### 自動評価の擬似コード
RAGシステムの自動評価には、RagasやLangChain Evaluationのようなフレームワークが有用です。ここでは、LLMを評価器として利用する擬似コードを示します。
```python
# 評価指標の定義
class EvaluationMetrics:
ACCURACY = "accuracy" # 正確性
FAITHFULNESS = "faithfulness" # 忠実度 (幻覚の有無)
RELEVANCE = "relevance" # 関連性
COHERENCE = "coherence" # 一貫性
# LLM評価器をシミュレートする関数
def evaluate_rag_response_llm(question: str, context: str, response: str, prompt_template: str) -> dict:
"""
LLMを評価器として使用し、RAGの応答を評価する。
Args:
question (str): ユーザーの質問。
context (str): 検索されたコンテキスト。
response (str): LLMが生成した回答。
prompt_template (str): 評価に使用するプロンプトテンプレート。
Returns:
dict: 各評価指標のスコアとコメント。
"""
# プロンプトテンプレートに情報を埋め込む
filled_prompt = prompt_template.replace("{{user_question}}", question)
filled_prompt = filled_prompt.replace("{{retrieved_context}}", context)
filled_prompt = filled_prompt.replace("{{generated_response}}", response)
# ここで実際のLLM API呼び出しを行う(例: Gemini API)
# response_from_llm_evaluator = call_llm_api(filled_prompt)
# 擬似的な応答を生成
if "宇宙飛行士になるには" in question and "日本の宇宙飛行士" in question and "提供された情報には含まれていません" in response:
llm_eval_output = """
```json
{
"thought_process": "質問の二つの部分を識別。一つは訓練、もう一つは人数。コンテキストには訓練に関する情報はあるが人数はない。回答は訓練に答え、人数については情報がないことを適切に伝えているため、幻覚を回避している。",
"accuracy": 0.9,
"faithfulness": 1.0,
"relevance": 1.0,
"overall_comment": "提供された情報に基づいて正確に回答し、情報がない部分についても適切に言及しています。"
}
</pre>
<pre data-enlighter-language="generic"> """
elif "Tim Cook" in response and "GoogleのCEO" in question:
llm_eval_output = """
<div class="codehilite">
<pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">"thought_process"</span><span class="p">:</span><span class="w"> </span><span class="s2">"質問はGoogleのCEOについて。コンテキストはAlphabet Inc.のCEOがSundar Pichaiだと示している。回答はTim Cookと述べており、コンテキストに反し、事実も異なるため、幻覚であり不正確。"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"accuracy"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"faithfulness"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"relevance"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.5</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"overall_comment"</span><span class="p">:</span><span class="w"> </span><span class="s2">"回答は完全に不正確であり、提供されたコンテキストにも忠実ではありません。幻覚が見られます。"</span>
<span class="p">}</span>
</code></pre>
</div>
"""
else:
# 一般的な良い回答のケース
llm_eval_output = """
<div class="codehilite">
<pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">"thought_process"</span><span class="p">:</span><span class="w"> </span><span class="s2">"質問の意図を正確に捉え、コンテキスト内の情報に忠実に、かつ関連性の高い回答を生成している。"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"accuracy"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"faithfulness"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"relevance"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"overall_comment"</span><span class="p">:</span><span class="w"> </span><span class="s2">"質問に正確かつ忠実に回答しており、関連性も高いです。"</span>
<span class="p">}</span>
</code></pre>
</div>
"""
import json
import re
# LLMの応答からJSONを抽出
match = re.search(r"```json\n(.*?)```", llm_eval_output, re.DOTALL)
if match:
try:
return json.loads(match.group(1))
except json.JSONDecodeError:
print("Error: Failed to parse LLM evaluation output.")
return {"error": "JSON_PARSE_ERROR"}
return {"error": "NO_JSON_FOUND"}
</pre>
<h1 class="wp-block-heading">採点ルーブリック例(簡易的な正規表現ベースのチェックも補助的に利用可能)</h1>
<p>def check_keyword_presence(response: str, keywords: list) -> float:
“”” 回答に特定のキーワードが含まれているかチェックする。”””
score = 0.0
for keyword in keywords:
if keyword.lower() in response.lower():
score += 1.0
return score / len(keywords) if keywords else 0.0</p>
<h1 class="wp-block-heading">メイン評価ループ (Big-O: O(N*M) – Nはテストケース数、MはLLM評価器の推論コスト)</h1>
<h1 class="wp-block-heading">メモリ条件: LLM評価器が状態を保持しない限り、各評価は独立。</h1>
<h1 class="wp-block-heading">評価器の入力サイズはプロンプト+コンテキスト+応答に比例。</h1>
<p>def run_evaluation_suite(test_cases: list, prompt_template: str):
results = []
for i, case in enumerate(test_cases):
print(f”— Evaluating Test Case {i+1} —“)
evaluation_result = evaluate_rag_response_llm(
question=case[“question”],
context=case[“context”],
response=case[“response”],
prompt_template=prompt_template
)</p>
<pre data-enlighter-language="generic"> # 例: 正規表現で特定キーワードの有無をチェックする補助的な評価
# if "スプートニク1号" in case["response"]:
# evaluation_result["keyword_check"] = 1.0
# else:
# evaluation_result["keyword_check"] = 0.0
results.append(evaluation_result)
print(f"Result: {evaluation_result}")
return results
</pre>
<h1 class="wp-block-heading">例示用のテストケース(上記シナリオを簡略化)</h1>
<p>test_cases_example = [
{
“question”: “地球の周回軌道に最初に到達した人工衛星の名前は何ですか?”,
“context”: “1957年10月4日にソ連が打ち上げた『スプートニク1号』が、地球周回軌道に到達した最初の人工衛星である。”,
“response”: “地球の周回軌道に最初に到達した人工衛星は、1957年10月4日にソ連が打ち上げたスプートニク1号です。”
},
{
“question”: “GoogleのCEOは誰ですか?”,
“context”: “Sundar PichaiはAlphabet Inc.のCEOです。”,
“response”: “GoogleのCEOはTim Cookです。”
},
{
“question”: “宇宙飛行士になるには、どんなトレーニングが必要ですか?また、日本の宇宙飛行士は何人いますか?”,
“context”: “宇宙飛行士の訓練には、身体的フィットネス、科学的知識、サバイバル訓練などが含まれます。”,
“response”: “宇宙飛行士になるには、身体的フィットネス、科学的知識、サバイバル訓練が必要です。日本の宇宙飛行士の人数については、提供された情報には含まれていません。”
}
]</p>
<h1 class="wp-block-heading">評価の実行</h1>
<h1 class="wp-block-heading">evaluation_results = run_evaluation_suite(test_cases_example, zero_shot_prompt_template)</h1>
<h1 class="wp-block-heading">print(“\nFinal Evaluation Results:”)</h1>
<h1 class="wp-block-heading">for res in evaluation_results:</h1>
<h1 class="wp-block-heading">print(res)</h1>
<pre data-enlighter-language="generic">
## 誤り分析と抑制手法
RAGシステムにおいて発生しうる主な失敗モードとその抑制手法を以下に示します。
### 失敗モード
1. **幻覚 (Hallucination)**: LLMが提供されたコンテキストにない情報を捏造したり、誤った事実を提示したりする。
2. **様式崩れ (Format Deviation)**: LLMが指示された出力フォーマット(例:JSON、箇条書き)を遵守しない。
3. **脱線 (Off-topic/Irrelevance)**: LLMが質問の意図から外れた回答を生成したり、関連性の低い情報を含めたりする。
4. **禁止事項への抵触**: 特定のキーワードの使用禁止、個人情報の出力禁止などの制約を破る。
### 抑制手法
1. **System指示の強化**:
* **幻覚抑制**: プロンプトに「提供されたコンテキスト外の情報は絶対に含めないでください」「コンテキストに情報がない場合は『情報がありません』と明示してください」といった強い指示を含める。
* **様式崩れ抑制**: 「出力は厳密にJSON形式でなければなりません」「JSONスキーマに沿ってください」など、フォーマットに関する具体的な制約を明記する。
2. **検証ステップの導入**:
* **幻覚/禁止事項**: LLMの出力後に、キーワードマッチ、正規表現、または別のLLMを用いた事実確認ステップを導入。生成された回答がコンテキストに忠実か、禁止語句を含んでいないかをチェックする。
* **様式崩れ**: 生成されたJSONが出力スキーマに準拠しているか、バリデーターで確認する。
3. **リトライ戦略**:
* **様式崩れ**: 検証ステップでフォーマットエラーが検出された場合、エラーメッセージとともに元のプロンプトに「フォーマットが不正です。正しいJSON形式で再度出力してください」という指示を加えてLLMに再試行させる。
* **脱線**: 評価スコアが低すぎたり、主要な評価指標が基準値を下回った場合、追加の指示(例:「もっと簡潔に」「質問のA点に焦点を当てて」)を与えて再生成を試みる。
4. **少数例 (Few-shot) プロンプティング**: 良い例と悪い例の両方をプロンプトに含めることで、LLMに期待される挙動と避けるべき挙動を明示的に示す。
## 改良と再評価のループ
評価を通じて特定された課題は、プロンプトの改良に直接フィードバックされます。この「プロンプト→モデル→評価→改良」のループを回すことが、RAGシステムの性能向上には不可欠です。
```mermaid
graph TD
A["プロンプト設計"] --> |生成指示| B(LLM)
B --> |回答生成| C{"RAGシステムの回答"}
C --> |質問とコンテキストと共に| D["評価システム"]
D --> |評価スコア/フィードバック| E{"評価結果"}
E --> |課題分析| F["プロンプト改良"]
F --> |新しいプロンプト| A
</pre>
<ol class="wp-block-list">
<li><p><strong>プロンプト設計</strong>: 初期プロンプトを作成または既存プロンプトを調整。</p></li>
<li><p><strong>LLMによる回答生成</strong>: RAGシステムがプロンプト、質問、コンテキストに基づいて回答を生成。</p></li>
<li><p><strong>評価システム</strong>: 生成された回答を、定義された指標とシナリオ(人間または自動評価器)で評価。2024年7月29日現在、LangChain EvaluationやRagasといったフレームワークが自動評価を支援します。</p></li>
<li><p><strong>評価結果</strong>: 評価スコア、定性的なフィードバック、失敗モードの特定が行われます。</p></li>
<li><p><strong>プロンプト改良</strong>: 評価結果に基づき、プロンプトの指示の具体化、制約の追加、few-shot例の修正、Chain-of-Thoughtステップの調整などを行います。</p></li>
<li><p><strong>再評価</strong>: 改良されたプロンプトを用いて、再度RAGシステムを評価し、性能が向上したかを確認します。このループを繰り返すことで、プロンプトの最適化を図ります。</p></li>
</ol>
<h2 class="wp-block-heading">まとめ</h2>
<p>RAGシステムにおけるプロンプト評価は、システム全体の品質を保証し、継続的に改善するための重要なプロセスです。評価指標の明確な定義、多様なシナリオに基づく評価、そして自動評価手法の導入により、効率的かつ客観的な評価が可能となります。また、幻覚や様式崩れといった失敗モードを特定し、システム指示の強化、検証ステップ、リトライ戦略を組み合わせた抑制手法を適用することで、堅牢なRAGシステムを構築できます。この評価→改良のフィードバックループを継続的に回すことで、より信頼性の高いRAGアプリケーションへと進化させることが期待されます。</p>
本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
RAGシステムにおけるプロンプト評価指標
Retrieval-Augmented Generation (RAG) システムは、外部知識ベースから情報を検索し、その情報に基づいて大規模言語モデル (LLM) が応答を生成する仕組みです。このRAGシステムにおいて、生成される応答の品質は、プロンプトの設計に大きく左右されます。本記事では、RAGシステムにおけるプロンプトの評価指標、評価シナリオ、自動評価手法、そして失敗モードとその抑制手法について解説します。
ユースケース定義
RAGシステムにおいて、ユーザーからの質問に対して、外部データベースから取得したコンテキスト情報に基づき、LLMが正確かつ関連性の高い回答を生成します。このプロセスにおけるプロンプトの品質を定量的に評価し、継続的な改善サイクルを構築することが本ユースケースの目的です。具体的には、応答の正確性、情報源への忠実度、ユーザー質問への関連性といった観点から評価を行います。
制約付き仕様化(入出力契約)
RAGシステムのプロンプト評価における入出力契約を以下に定義します。
入力
ユーザーの質問 (Question): ユーザーがLLMに投げかける自然言語の質問文字列。
検索されたコンテキスト (Context): RAGシステムが外部知識ベースから検索し、LLMに提供する情報テキストのリスト。
LLMによる生成応答 (Response): RAGシステム(LLM)が質問とコンテキストに基づいて生成した回答文字列。
正解応答 (Ground Truth/Optional): 評価のゴールドスタンダードとなる人間が作成した正確な回答(自動評価の補助に利用)。
出力
総合評価スコア (Overall Score): 0.0から1.0までの範囲で、応答の全体的な品質を示す数値。
評価カテゴリ別フィードバック (Categorized Feedback): 各評価指標(正確性、関連性、忠実度など)に対する個別のスコアまたは定性的なコメント。
評価理由 (Reasoning): 各評価カテゴリに対するLLMまたは評価器からの根拠となる説明。
失敗時の挙動
禁止事項
個人情報/機密情報の利用: 評価プロセスにおいて、入力データに個人情報や機密情報が含まれる場合、それらを評価器に渡すことを厳しく禁止します。マスキングまたは匿名化を必須とします。
幻覚の許容範囲外: 生成応答が、提供されたコンテキストに存在しない情報を捏造する「幻覚」を起こした場合、最低スコアを付与し、かつその理由を明確に記録します。
評価プロセスからの脱線: LLMベースの評価器が、指定された評価タスクから逸脱し、無関係な内容を生成したり、評価フォーマットを遵守しない場合、自動的に再試行するか、エラーとして報告します。
プロンプト設計
RAGシステムのプロンプト評価には、評価の目的や複雑さに応じて様々なプロンプト設計が考えられます。ここでは、3種類のプロンプト案を提示します。
1. ゼロショット評価プロンプト
基本的な評価を目的とし、LLMに直接評価を依頼する形式です。
あなたはRAGシステムの回答品質を評価するAIアシスタントです。
以下の情報を元に、提供された回答を評価してください。
評価指標:
1. 正確性: 回答は質問に対してどれだけ正確か?(0.0-1.0)
2. 忠実度: 回答は提供されたコンテキストの情報にどれだけ忠実か?コンテキストにない情報を捏造していないか?(0.0-1.0)
3. 関連性: 回答は質問に対してどれだけ関連しているか?(0.0-1.0)
入力:
質問: {{user_question}}
コンテキスト: {{retrieved_context}}
回答: {{generated_response}}
出力形式:
```json
{
"accuracy": <float>,
"faithfulness": <float>,
"relevance": <float>,
"overall_comment": "<評価の総合的なコメント>"
}
上記の出力形式に従って評価結果を出力してください。
**特徴**: 実装が容易。LLMの汎用的な推論能力に依存。
**利点**: 迅速な評価、幅広い用途。
**欠点**: 評価のブレが生じやすい、複雑なケースでの判断が難しい場合がある。
### 2. 少数例 (Few-shot) 評価プロンプト
いくつか具体的な評価例を提示することで、LLMの評価基準を明確化し、安定した評価を促します。
```text
あなたはRAGシステムの回答品質を評価するAIアシスタントです。
以下の例を参考に、提供された回答を評価してください。
---
例1 (良い例):
質問: Pythonのリストとタプルの違いは何ですか?
コンテキスト: リストは可変なシーケンスで、要素の追加、削除、変更が可能。タプルは不変なシーケンスで、一度作成すると変更できない。
回答: Pythonのリストは要素の追加や削除が可能な可変シーケンスですが、タプルは作成後に変更できない不変シーケンスです。
評価:
```json
{
"accuracy": 1.0,
"faithfulness": 1.0,
"relevance": 1.0,
"overall_comment": "質問に正確かつ忠実に回答しており、関連性も高いです。"
}
例2 (悪い例 – 幻覚):
質問: GoogleのCEOは誰ですか?
コンテキスト: Sundar PichaiはAlphabet Inc.のCEOです。
回答: GoogleのCEOはTim Cookです。
評価:
{
"accuracy": 0.0,
"faithfulness": 0.0,
"relevance": 0.5,
"overall_comment": "回答は完全に不正確であり、提供されたコンテキストにも忠実ではありません。幻覚が見られます。"
}
入力:
質問: {{user_question}}
コンテキスト: {{retrieved_context}}
回答: {{generated_response}}
出力形式:
{
"accuracy": <float>,
"faithfulness": <float>,
"relevance": <float>,
"overall_comment": "<評価の総合的なコメント>"
}
上記の出力形式に従って評価結果を出力してください。
**特徴**: 具体的例示によりLLMの評価基準をガイド。
**利点**: 評価の一貫性が向上、特に難しいケースでの判断精度が向上。
**欠点**: プロンプトが長くなる、適切な例の選定が必要。
### 3. Chain-of-Thought制約型評価プロンプト
LLMに評価プロセスを段階的に思考させ、その上で最終的な評価を下すことで、より透明性が高く、信頼性の高い評価を促します。
```text
あなたはRAGシステムの回答品質を評価するAIアシスタントです。
以下の手順と評価指標に従って、提供された回答を評価してください。
評価手順:
1. まず、質問の意図を明確に理解してください。
2. 次に、提供されたコンテキストが質問に答えるために十分かどうかを確認してください。
3. そして、生成された回答がコンテキスト内の情報のみに基づいているか(忠実度)を評価してください。
4. 最後に、回答が質問に対して正確であり、かつ関連しているか(正確性、関連性)を評価し、総合的な判断を下してください。
評価指標:
- 正確性 (Accuracy): 質問への回答が事実に基づき、正しいか? (0.0-1.0)
- 忠実度 (Faithfulness): 回答が提供されたコンテキスト内の情報にのみ基づいているか? (0.0-1.0)
- 関連性 (Relevance): 回答が質問の意図に合致し、適切に焦点を当てているか? (0.0-1.0)
入力:
質問: {{user_question}}
コンテキスト: {{retrieved_context}}
回答: {{generated_response}}
思考プロセス:
1. 質問の分析: ...
2. コンテキストの検証: ...
3. 回答の忠実度チェック: ...
4. 回答の正確性と関連性チェック: ...
出力形式:
```json
{
"thought_process": "<思考プロセス全体の要約>",
"accuracy": <float>,
"faithfulness": <float>,
"relevance": <float>,
"overall_comment": "<評価の総合的なコメント>"
}
上記の出力形式に従って評価結果を出力してください。
**特徴**: LLMに思考プロセスを強制し、評価の根拠を明確化。
**利点**: 評価結果の信頼性が向上、誤り分析が容易になる。
**欠点**: 応答時間が長くなる、プロンプトが複雑になる。
## 評価
RAGシステムの評価には、多角的なシナリオと自動化された評価手法が不可欠です。
### 評価シナリオ
評価の網羅性を高めるため、以下のシナリオでプロンプトの性能をテストします。
1. **正例 (Happy Path)**:
* **質問**: 「地球の周回軌道に最初に到達した人工衛星の名前は何ですか?」
* **コンテキスト**: 「1957年10月4日にソ連が打ち上げた『スプートニク1号』が、地球周回軌道に到達した最初の人工衛星である。」
* **期待される応答**: 「地球の周回軌道に最初に到達した人工衛星は、1957年10月4日にソ連が打ち上げたスプートニク1号です。」
* **評価観点**: 正確性、忠実度、関連性がすべて高く評価されることを確認。
2. **難例 (Challenging Case)**:
* **質問**: 「再生可能エネルギーの導入メリットと課題を、経済的・環境的側面から簡潔に教えてください。」
* **コンテキスト**: 複数の段落にわたる再生可能エネルギーに関する情報(メリット、コスト、間欠性、環境影響など)。
* **期待される応答**: 複数の側面(経済的メリット/コスト、環境的メリット/インフラ課題など)を統合し、簡潔にまとめた回答。
* **評価観点**: 複数情報の統合能力、要約能力、指定された側面(経済・環境)への適切な言及。
3. **コーナーケース (Edge Case)**:
* **質問**: 「宇宙飛行士になるには、どんなトレーニングが必要ですか?また、日本の宇宙飛行士は何人いますか?」
* **コンテキスト**: 宇宙飛行士の訓練に関する一般的な情報はあるが、日本の宇宙飛行士の具体的な人数に関する情報がない。
* **期待される応答**: 訓練については回答し、日本の宇宙飛行士の人数については「提供された情報には含まれていません」といった形で情報不足を明示する回答。
* **評価観点**: 情報不足時の適切な応答(幻覚の回避)、質問の複数の部分への対応。
### 自動評価の擬似コード
RAGシステムの自動評価には、RagasやLangChain Evaluationのようなフレームワークが有用です。ここでは、LLMを評価器として利用する擬似コードを示します。
```python
# 評価指標の定義
class EvaluationMetrics:
ACCURACY = "accuracy" # 正確性
FAITHFULNESS = "faithfulness" # 忠実度 (幻覚の有無)
RELEVANCE = "relevance" # 関連性
COHERENCE = "coherence" # 一貫性
# LLM評価器をシミュレートする関数
def evaluate_rag_response_llm(question: str, context: str, response: str, prompt_template: str) -> dict:
"""
LLMを評価器として使用し、RAGの応答を評価する。
Args:
question (str): ユーザーの質問。
context (str): 検索されたコンテキスト。
response (str): LLMが生成した回答。
prompt_template (str): 評価に使用するプロンプトテンプレート。
Returns:
dict: 各評価指標のスコアとコメント。
"""
# プロンプトテンプレートに情報を埋め込む
filled_prompt = prompt_template.replace("{{user_question}}", question)
filled_prompt = filled_prompt.replace("{{retrieved_context}}", context)
filled_prompt = filled_prompt.replace("{{generated_response}}", response)
# ここで実際のLLM API呼び出しを行う(例: Gemini API)
# response_from_llm_evaluator = call_llm_api(filled_prompt)
# 擬似的な応答を生成
if "宇宙飛行士になるには" in question and "日本の宇宙飛行士" in question and "提供された情報には含まれていません" in response:
llm_eval_output = """
```json
{
"thought_process": "質問の二つの部分を識別。一つは訓練、もう一つは人数。コンテキストには訓練に関する情報はあるが人数はない。回答は訓練に答え、人数については情報がないことを適切に伝えているため、幻覚を回避している。",
"accuracy": 0.9,
"faithfulness": 1.0,
"relevance": 1.0,
"overall_comment": "提供された情報に基づいて正確に回答し、情報がない部分についても適切に言及しています。"
}
"""
elif "Tim Cook" in response and "GoogleのCEO" in question:
llm_eval_output = """
<div class="codehilite">
<pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">"thought_process"</span><span class="p">:</span><span class="w"> </span><span class="s2">"質問はGoogleのCEOについて。コンテキストはAlphabet Inc.のCEOがSundar Pichaiだと示している。回答はTim Cookと述べており、コンテキストに反し、事実も異なるため、幻覚であり不正確。"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"accuracy"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"faithfulness"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"relevance"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.5</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"overall_comment"</span><span class="p">:</span><span class="w"> </span><span class="s2">"回答は完全に不正確であり、提供されたコンテキストにも忠実ではありません。幻覚が見られます。"</span>
<span class="p">}</span>
</code></pre>
</div>
"""
else:
# 一般的な良い回答のケース
llm_eval_output = """
<div class="codehilite">
<pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">"thought_process"</span><span class="p">:</span><span class="w"> </span><span class="s2">"質問の意図を正確に捉え、コンテキスト内の情報に忠実に、かつ関連性の高い回答を生成している。"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"accuracy"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"faithfulness"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"relevance"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"overall_comment"</span><span class="p">:</span><span class="w"> </span><span class="s2">"質問に正確かつ忠実に回答しており、関連性も高いです。"</span>
<span class="p">}</span>
</code></pre>
</div>
"""
import json
import re
# LLMの応答からJSONを抽出
match = re.search(r"```json\n(.*?)```", llm_eval_output, re.DOTALL)
if match:
try:
return json.loads(match.group(1))
except json.JSONDecodeError:
print("Error: Failed to parse LLM evaluation output.")
return {"error": "JSON_PARSE_ERROR"}
return {"error": "NO_JSON_FOUND"}
採点ルーブリック例(簡易的な正規表現ベースのチェックも補助的に利用可能)
def check_keyword_presence(response: str, keywords: list) -> float:
“”” 回答に特定のキーワードが含まれているかチェックする。”””
score = 0.0
for keyword in keywords:
if keyword.lower() in response.lower():
score += 1.0
return score / len(keywords) if keywords else 0.0
メイン評価ループ (Big-O: O(N*M) – Nはテストケース数、MはLLM評価器の推論コスト)
メモリ条件: LLM評価器が状態を保持しない限り、各評価は独立。
評価器の入力サイズはプロンプト+コンテキスト+応答に比例。
def run_evaluation_suite(test_cases: list, prompt_template: str):
results = []
for i, case in enumerate(test_cases):
print(f”— Evaluating Test Case {i+1} —“)
evaluation_result = evaluate_rag_response_llm(
question=case[“question”],
context=case[“context”],
response=case[“response”],
prompt_template=prompt_template
)
# 例: 正規表現で特定キーワードの有無をチェックする補助的な評価
# if "スプートニク1号" in case["response"]:
# evaluation_result["keyword_check"] = 1.0
# else:
# evaluation_result["keyword_check"] = 0.0
results.append(evaluation_result)
print(f"Result: {evaluation_result}")
return results
例示用のテストケース(上記シナリオを簡略化)
test_cases_example = [
{
“question”: “地球の周回軌道に最初に到達した人工衛星の名前は何ですか?”,
“context”: “1957年10月4日にソ連が打ち上げた『スプートニク1号』が、地球周回軌道に到達した最初の人工衛星である。”,
“response”: “地球の周回軌道に最初に到達した人工衛星は、1957年10月4日にソ連が打ち上げたスプートニク1号です。”
},
{
“question”: “GoogleのCEOは誰ですか?”,
“context”: “Sundar PichaiはAlphabet Inc.のCEOです。”,
“response”: “GoogleのCEOはTim Cookです。”
},
{
“question”: “宇宙飛行士になるには、どんなトレーニングが必要ですか?また、日本の宇宙飛行士は何人いますか?”,
“context”: “宇宙飛行士の訓練には、身体的フィットネス、科学的知識、サバイバル訓練などが含まれます。”,
“response”: “宇宙飛行士になるには、身体的フィットネス、科学的知識、サバイバル訓練が必要です。日本の宇宙飛行士の人数については、提供された情報には含まれていません。”
}
]
評価の実行
evaluation_results = run_evaluation_suite(test_cases_example, zero_shot_prompt_template)
print(“\nFinal Evaluation Results:”)
for res in evaluation_results:
print(res)
## 誤り分析と抑制手法
RAGシステムにおいて発生しうる主な失敗モードとその抑制手法を以下に示します。
### 失敗モード
1. **幻覚 (Hallucination)**: LLMが提供されたコンテキストにない情報を捏造したり、誤った事実を提示したりする。
2. **様式崩れ (Format Deviation)**: LLMが指示された出力フォーマット(例:JSON、箇条書き)を遵守しない。
3. **脱線 (Off-topic/Irrelevance)**: LLMが質問の意図から外れた回答を生成したり、関連性の低い情報を含めたりする。
4. **禁止事項への抵触**: 特定のキーワードの使用禁止、個人情報の出力禁止などの制約を破る。
### 抑制手法
1. **System指示の強化**:
* **幻覚抑制**: プロンプトに「提供されたコンテキスト外の情報は絶対に含めないでください」「コンテキストに情報がない場合は『情報がありません』と明示してください」といった強い指示を含める。
* **様式崩れ抑制**: 「出力は厳密にJSON形式でなければなりません」「JSONスキーマに沿ってください」など、フォーマットに関する具体的な制約を明記する。
2. **検証ステップの導入**:
* **幻覚/禁止事項**: LLMの出力後に、キーワードマッチ、正規表現、または別のLLMを用いた事実確認ステップを導入。生成された回答がコンテキストに忠実か、禁止語句を含んでいないかをチェックする。
* **様式崩れ**: 生成されたJSONが出力スキーマに準拠しているか、バリデーターで確認する。
3. **リトライ戦略**:
* **様式崩れ**: 検証ステップでフォーマットエラーが検出された場合、エラーメッセージとともに元のプロンプトに「フォーマットが不正です。正しいJSON形式で再度出力してください」という指示を加えてLLMに再試行させる。
* **脱線**: 評価スコアが低すぎたり、主要な評価指標が基準値を下回った場合、追加の指示(例:「もっと簡潔に」「質問のA点に焦点を当てて」)を与えて再生成を試みる。
4. **少数例 (Few-shot) プロンプティング**: 良い例と悪い例の両方をプロンプトに含めることで、LLMに期待される挙動と避けるべき挙動を明示的に示す。
## 改良と再評価のループ
評価を通じて特定された課題は、プロンプトの改良に直接フィードバックされます。この「プロンプト→モデル→評価→改良」のループを回すことが、RAGシステムの性能向上には不可欠です。
```mermaid
graph TD
A["プロンプト設計"] --> |生成指示| B(LLM)
B --> |回答生成| C{"RAGシステムの回答"}
C --> |質問とコンテキストと共に| D["評価システム"]
D --> |評価スコア/フィードバック| E{"評価結果"}
E --> |課題分析| F["プロンプト改良"]
F --> |新しいプロンプト| A
プロンプト設計: 初期プロンプトを作成または既存プロンプトを調整。
LLMによる回答生成: RAGシステムがプロンプト、質問、コンテキストに基づいて回答を生成。
評価システム: 生成された回答を、定義された指標とシナリオ(人間または自動評価器)で評価。2024年7月29日現在、LangChain EvaluationやRagasといったフレームワークが自動評価を支援します。
評価結果: 評価スコア、定性的なフィードバック、失敗モードの特定が行われます。
プロンプト改良: 評価結果に基づき、プロンプトの指示の具体化、制約の追加、few-shot例の修正、Chain-of-Thoughtステップの調整などを行います。
再評価: 改良されたプロンプトを用いて、再度RAGシステムを評価し、性能が向上したかを確認します。このループを繰り返すことで、プロンプトの最適化を図ります。
まとめ
RAGシステムにおけるプロンプト評価は、システム全体の品質を保証し、継続的に改善するための重要なプロセスです。評価指標の明確な定義、多様なシナリオに基づく評価、そして自動評価手法の導入により、効率的かつ客観的な評価が可能となります。また、幻覚や様式崩れといった失敗モードを特定し、システム指示の強化、検証ステップ、リトライ戦略を組み合わせた抑制手法を適用することで、堅牢なRAGシステムを構築できます。この評価→改良のフィードバックループを継続的に回すことで、より信頼性の高いRAGアプリケーションへと進化させることが期待されます。
コメント