RAGシステムの評価指標と実践

Tech

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

RAGシステムの評価指標と実践

Retrieval Augmented Generation (RAG) システムは、大規模言語モデル (LLM) の知識を外部情報源で補強し、より正確で最新の回答を生成するために広く利用されています。本記事では、RAGシステムの効果的な評価指標と実践的なアプローチについて、プロンプト設計から評価、誤り分析、改良のサイクルを専門的に解説します。

1. ユースケース定義

本稿では、企業内のFAQドキュメントや製品マニュアルから関連情報を検索し、ユーザーの質問に対して正確で信頼性の高い回答を生成するRAGシステムを想定します。 目的は以下の通りです。

  • 正確性: 回答が提供されたコンテキスト内の事実と矛盾しないこと。

  • 関連性: 回答がユーザーの質問に直接的かつ適切であること。

  • 完全性: 質問の意図する主要な情報がすべて回答に含まれること。

  • 安全性: 不適切または有害なコンテンツを生成しないこと。

2. 制約付き仕様化(入出力契約)

RAGシステムのプロンプト設計と評価を進める上で、入力と出力の契約を明確に定義します。

入力契約

  • フォーマット: JSON形式で入力されます。

    {
      "query": "ユーザーの質問文",
      "context": ["関連ドキュメントのチャンク1", "関連ドキュメントのチャンク2", ...]
    }
    
  • 失敗時の挙動:

    • 無効なJSON、queryまたはcontextが空の場合、モデルは{"error": "Invalid input format or missing data."}を返します。

    • contextが特定の文字数(例: 2000トークン)を超過する場合、超過分は切り捨てられるか、エラーを返します。

  • 禁止事項:

    • プロンプトインジェクションを意図した文字列の埋め込み。

    • モデルが直接アクセスすべきでない機密情報や個人情報の入力。

出力契約

  • フォーマット: JSON形式で出力されます。

    {
      "answer": "生成された回答文",
      "sources": ["参照したドキュメントのチャンクIDまたはタイトル1", "参照したドキュメントのチャンクIDまたはタイトル2", ...]
    }
    
  • 失敗時の挙動:

    • コンテキストに基づいて回答を生成できない場合、"answer": "提供された情報から回答を生成できませんでした。"を返します。

    • 指定されたフォーマットに従えない場合、モデルはエラーメッセージを返すか、最も近い形式で出力します。

  • 禁止事項:

    • 提供されたcontextにない情報を捏造すること(幻覚)。

    • 不適切、差別的、または有害なコンテンツの生成。

    • ユーザーの個人情報や機密情報の漏洩。

3. プロンプト設計

効果的なRAGシステムを実現するためには、プロンプトの設計が重要です。ここでは、3種類のプロンプト案を提示します。

3.1. ゼロショットプロンプト

最もシンプルで、基本的な指示のみを与えます。

あなたは専門家アシスタントです。以下の質問と提供された情報(コンテキスト)に基づいて、正確かつ簡潔に回答してください。コンテキストにない情報は絶対に含めないでください。回答と参照した情報のリストをJSON形式で出力してください。

質問: {{query}}
コンテキスト:
{{context}}

出力:

3.2. 少数例プロンプト(Few-shot Prompt)

いくつかの入出力例を示し、モデルに期待する挙動を学習させます。

あなたは専門家アシスタントです。以下の質問と提供された情報(コンテキスト)に基づいて、正確かつ簡潔に回答してください。コンテキストにない情報は絶対に含めないでください。回答と参照した情報のリストをJSON形式で出力してください。

---
例1:
質問: AIモデルの訓練には何が必要ですか?
コンテキスト:
["AIモデルの訓練には大量のデータと計算資源が必要です。データはモデルがパターンを学習するために不可欠であり、計算資源(GPUなど)は訓練プロセスを高速化します。", "データ品質はモデル性能に直結します。"]
出力:
```json
{
  "answer": "AIモデルの訓練には、大量のデータと計算資源(GPUなど)が必要です。データはモデルがパターンを学習するために不可欠であり、計算資源は訓練プロセスを高速化します。",
  "sources": ["関連ドキュメントのチャンク1", "関連ドキュメントのチャンク2"]
}

例2: 質問: RAGシステムとは何ですか? コンテキスト: [“RAG(Retrieval Augmented Generation)システムは、大規模言語モデル(LLM)が外部の知識ベースから情報を検索し、その情報に基づいて回答を生成するフレームワークです。”, “これにより、LLMの幻覚を減らし、より正確で最新の回答を提供できます。”] 出力:

{
  "answer": "RAGシステムとは、大規模言語モデルが外部の知識ベースから関連情報を検索し、その情報を用いて回答を生成するフレームワークです。これにより、LLMの幻覚を減らし、より正確で最新の情報に基づいた回答が可能になります。",
  "sources": ["関連ドキュメントのチャンク1", "関連ドキュメントのチャンク2"]
}

質問: {{query}} コンテキスト: {{context}}

出力:

### 3.3. Chain-of-Thought制約型プロンプト

モデルに思考プロセスを段階的に実行させ、特定の形式で最終回答を導き出すように制約をかけます。これにより、複雑な質問に対する回答品質を高めます。

```text
あなたはプロンプト設計と評価の専門家です。以下の指示に従って質問に回答してください。

### 指示


1.  **質問分析**: ユーザーの質問の意図と主要なキーワードを特定してください。

2.  **コンテキスト評価**: 提供されたコンテキストが質問にどの程度関連しているか、また回答に必要な情報が全て含まれているか評価してください。

3.  **情報抽出と統合**: コンテキストから質問に直接関連する情報を抽出し、それらを論理的に統合してください。コンテキストにない情報は一切使用しないでください。

4.  **回答生成**: 抽出・統合した情報に基づき、明確で簡潔な回答を生成してください。

5.  **ソース特定**: 回答を生成する際に参照したコンテキストのチャンクIDまたはタイトルを特定してください。

### 入出力フォーマット

最終的な出力はJSON形式で、`thought_process`(各ステップの思考)と`final_answer`(最終回答とソース)を含めてください。

質問: {{query}}
コンテキスト:
{{context}}

出力:
```json
{
  "thought_process": {
    "step1_question_analysis": "...",
    "step2_context_evaluation": "...",
    "step3_information_extraction_and_integration": "..."
  },
  "final_answer": {
    "answer": "...",
    "sources": ["...", "..."]
  }
}
## 4. 評価

RAGシステムの評価は、設定したユースケースと出力契約に基づいて行われます。

### 4.1. 評価シナリオ


-   **正例**: シンプルで直接的な質問。コンテキスト内に明確な回答があるケース。

    -   例: 「RAGの目的は何ですか?」

-   **難例**:

    -   **複数情報源の統合**: 複数のコンテキストチャンクから情報を組み合わせて回答する必要がある質問。

    -   **曖昧な質問**: ユーザーの意図が不明瞭で、複数の解釈が可能な質問。

    -   **否定的な質問**: 「~ではないものはどれか?」といった形式の質問。

-   **コーナーケース**:

    -   **コンテキスト不足**: 質問に対する回答がコンテキストに全く含まれていないケース。

    -   **矛盾する情報**: コンテキスト内に互いに矛盾する情報が含まれているケース。

    -   **最新情報の欠如**: ユーザーが求めている情報が最新であるにも関わらず、コンテキストが古いケース。

### 4.2. 自動評価の擬似コード

自動評価は、手動評価の負担を軽減し、継続的なシステム改善を可能にします。

```python
import json
import re
from sklearn.metrics.pairwise import cosine_similarity
from sentence_transformers import SentenceTransformer

# 評価指標の定義

def evaluate_rag_output(expected_output: dict, actual_output: dict, context: list) -> dict:
    """
    RAGシステムの出力を評価する関数。
    :param expected_output: 期待されるJSON出力 ({'answer': '...', 'sources': [...]})
    :param actual_output: 実際のJSON出力 ({'answer': '...', 'sources': [...]})
    :param context: 評価に使用された元のコンテキストリスト
    :return: 評価結果を含む辞書
    """
    scores = {
        "factual_accuracy": 0,  # 事実の正確性
        "relevance": 0,         # 質問への関連性
        "completeness": 0,      # 回答の完全性
        "context_utilization": 0, # コンテキスト利用率
        "hallucination_rate": 0,  # 幻覚率 (1で幻覚あり、0で幻覚なし)
        "format_adherence": 0   # フォーマット遵守度
    }

    # 1. フォーマット遵守度の評価

    try:
        if isinstance(actual_output, dict) and "answer" in actual_output and "sources" in actual_output and isinstance(actual_output["sources"], list):
            scores["format_adherence"] = 1
    except (json.JSONDecodeError, TypeError):
        pass # JSONフォーマットでない場合は0のまま

    if scores["format_adherence"] == 0:
        return scores # フォーマットが不正な場合は以降の評価は困難

    actual_answer = actual_output.get("answer", "")
    expected_answer = expected_output.get("answer", "")
    actual_sources = actual_output.get("sources", [])
    expected_sources = expected_output.get("sources", [])

    # 2. 事実の正確性 (キーワードマッチと文の類似度)


    # 簡易版: 期待される回答の主要キーワードが実際回答に含まれているか

    expected_keywords = set(re.findall(r'\b\w+\b', expected_answer.lower()))
    actual_keywords = set(re.findall(r'\b\w+\b', actual_answer.lower()))
    if expected_keywords:
        scores["factual_accuracy"] = len(expected_keywords.intersection(actual_keywords)) / len(expected_keywords)

    # 3. 関連性 (埋め込みベクトル類似度)


    # モデルのロードは一度だけ行う (パフォーマンスのため)


    # model = SentenceTransformer('paraphrase-MiniLM-L6-v2') # 計算量: O(N*D) N=文数 D=埋め込み次元


    # expected_embedding = model.encode([expected_answer])


    # actual_embedding = model.encode([actual_answer])


    # scores["relevance"] = cosine_similarity(expected_embedding, actual_embedding)[0][0] # 計算量: O(D)

    # 4. 完全性 (期待される回答の情報がどの程度含まれているか)


    # 事実の正確性と類似するが、より多くの情報を含んでいるかに焦点を当てる


    # ここでは簡易的に、期待される回答の文字数に対する実際回答の文字数の比率で判断する

    if expected_answer:
        scores["completeness"] = min(len(actual_answer) / len(expected_answer), 1.0) # 最大1.0

    # 5. コンテキスト利用率と幻覚率


    # 幻覚チェック: 実際回答に含まれる情報がコンテキストに存在しない場合


    # 簡易版: 実際回答の各文がコンテキスト内のいずれかの文と高類似度を持つか

    is_hallucinating = False
    if actual_answer:

        # 実際回答の各文をコンテキスト全体で検索


        # この実装は簡易的なものであり、より高度なNLP技術が必要 (NLIなど)


        # 例えば、実際のRAGシステムでは、モデルが参照したソースと回答の整合性をチェックする

        relevant_context_chunks = [c for c in context if any(src in c for src in actual_sources)]
        if not relevant_context_chunks:

            # 参照ソースがコンテキストにない場合は高確率で幻覚

            is_hallucinating = True
        else:

            # 回答の各部分が実際に参照されたコンテキスト部分から導出可能か


            # このステップは非常に複雑で、現状の自動評価では完全には解決できないことが多い


            # NLI (Natural Language Inference) モデルなどが必要

            pass # ここでは詳細な幻覚チェックは省略

        # 簡易的な幻覚チェックとして、期待されるソースが実際に利用されたかを確認


        # 本来は回答内容がコンテキスト外の情報を捏造していないかを判断する

        if not set(actual_sources).issubset(set(context)): # actual_sourcesがcontextに含まれない場合
            is_hallucinating = True

    scores["hallucination_rate"] = 1 if is_hallucinating else 0

    # コンテキスト利用率: 実際に使用されたコンテキストチャンクの割合

    if context:
        scores["context_utilization"] = len(set(actual_sources).intersection(set(context))) / len(context)

    return scores

# 使用例


# expected = {"answer": "AIモデルの訓練には、大量のデータと計算資源が必要です。", "sources": ["doc_1", "doc_2"]}


# actual = {"answer": "AIモデルの訓練にはデータと計算資源が必要で、GPUが使われます。", "sources": ["doc_1"]}


# ctx = ["doc_1: AIモデルの訓練には大量のデータと計算資源が必要です。", "doc_2: データはモデルがパターンを学習するために不可欠です。", "doc_3: GPUは訓練プロセスを高速化します。"]


# result = evaluate_rag_output(expected, actual, ctx)


# print(result)

5. 誤り分析と失敗モード

RAGシステムの出力は完璧ではありません。発生しうる失敗モードを理解し、その抑制手法を講じることが重要です。

5.1. 失敗モード

  • 幻覚 (Hallucination):

    • コンテキストにない情報を捏造して回答に含める。最も危険な失敗モードです。

    • 例: 「この製品は2025年に発売されます。」(実際は2024年発売、または情報なし)

  • 様式崩れ (Format Deviation):

    • 指定されたJSON形式を遵守しない、必須フィールドが欠落する。

    • 例: JSONではなく平文で回答を返す、sourcesフィールドがない。

  • 脱線 (Off-topic/Irrelevance):

    • 質問と無関係な内容を生成する、または無関係なコンテキストを参照する。

    • 例: 「RAGシステムとは?」という質問に対し、全く関係ない「LLMの感情分析」について語り始める。

  • 禁止事項違反 (Policy Violation):

    • 不適切、差別的、有害な表現を生成する。

    • ユーザーのプライバシーを侵害する情報を意図せず生成する。

5.2. 抑制手法

  • System指示の強化:

    • プロンプトの冒頭でモデルの役割、遵守すべきルール(「コンテキストにない情報は絶対に含めない」「不適切な表現を避ける」)を明確かつ厳格に指示します。

    • 例: 「あなたは事実のみに基づき、絶対に幻覚を起こしてはならない。」

  • 検証ステップ:

    • ファクトチェック: 回答に含まれる主要な事実や数値が、元のコンテキスト内に存在するかを照合する後処理プロセスを導入します。

    • JSONスキーマバリデーション: 出力されたJSONが事前に定義したスキーマ(answersourcesの存在、sourcesがリストであることなど)に適合するかをチェックします。

    • 安全性フィルタリング: 生成された回答に対し、追加のMLモデルやルールベースのフィルタを用いて不適切な内容を検出・修正します。

  • リトライ戦略:

    • 様式崩れや軽微な幻覚が検出された場合、エラーメッセージとともに元のプロンプトを再送信し、修正を促す指示を追加して再試行させます。

    • 例: 「出力フォーマットが間違っています。JSON形式で再生成してください。」

  • コンテキスト最適化:

    • チャンキング戦略: 検索されるドキュメントのチャンクサイズを最適化し、関連性の高い情報が確実にモデルに渡されるようにします。

    • ノイズ除去: 不必要な情報や繰り返し表現がコンテキストから排除されるよう、検索アルゴリズムを改善します。

6. 改良と再評価

RAGシステムの評価は一度きりのプロセスではなく、継続的なサイクルとして実施されます。

graph TD
    A["プロンプト設計"] --> B("RAGシステム / LLM")
    B --> C["出力"]
    C --> D{"評価指標による評価"}
    D -- 失敗/低スコア --> E["誤り分析"]
    E --> F["プロンプト改良 / コンテキスト最適化 / モデルチューニング"]
    F --> A
    D -- 成功/高スコア --> G("本番デプロイ")
    G --> A

図1: RAGシステム評価・改良のループ

  1. プロンプト設計: 上述のゼロショット、少数例、Chain-of-Thoughtなどの手法を用いてプロンプトを設計します。

  2. RAGシステム/LLM: 設計されたプロンプトとコンテキストを用いて、LLMが回答を生成します。

  3. 出力: LLMから生成された回答と参照ソースのJSON出力。

  4. 評価指標による評価: 定義した評価指標(正確性、関連性、完全性、幻覚率など)に基づいて、生成された出力を自動または手動で評価します。

  5. 誤り分析: 評価結果が期待値を下回った場合、どの失敗モード(幻覚、様式崩れ、脱線など)が発生したかを詳細に分析します。特に、難例やコーナーケースで失敗したパターンに注目します。

  6. 改良:

    • プロンプト改良: 誤り分析の結果に基づき、プロンプトの指示をより明確にする、少数例を調整する、CoTステップを追加するなど、プロンプト自体を改善します。

    • コンテキスト最適化: 検索アルゴリズムやドキュメントのチャンキング戦略を見直し、より関連性の高いコンテキストが提供されるようにします。

    • モデルチューニング: 必要に応じて、特定のタスクに特化したファインチューニングや、より適切な基盤モデルの選定を検討します。

  7. 再評価: 改良されたシステムに対して再度評価を実行し、改善効果を定量的に測定します。このサイクルを繰り返すことで、システムの性能を継続的に向上させます。

まとめ

RAGシステムの評価と実践は、単一の技術的課題ではなく、プロンプト工学、データエンジニアリング、そして継続的な改善の文化を統合する多面的なアプローチを必要とします。本記事で示した評価指標、プロンプト設計、誤り分析、そして改良のループを実践することで、企業はより信頼性が高く、ユーザーの期待に応えるRAGシステムを構築し、運用できるでしょう。特に、2024年7月29日時点でのLLM技術の進化は目覚ましく、継続的な評価と適応が成功の鍵となります。

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

タイトルとURLをコピーしました