コンテンツにスキップ

RAG 2.0 日本語検索 評価ガイド(詳細版)

Version: 1.0
Date: 2026-05-20
Status: Production-Ready
Document ID: RAG-JP-EVAL-GUIDE-001


目次

  1. 概要
  2. クイックスタート
  3. 詳細な使用方法
  4. 評価基準と改善指針
  5. テストセットのカスタマイズ
  6. ダッシュボード化(オプション)
  7. トラブルシューティング
  8. ベストプラクティス

概要

このドキュメントは、EvoSpikeNet RAGシステムの日本語検索精度を評価するためのフレームワークについて説明しています。

本ガイドは以下を対象としています: - 実装者: Phase 1-5 の検索精度を測定 - 運用者: 継続的な品質監視とアラート対応 - 研究者: 検索アルゴリズムの改善検証

評価の全体フロー

┌─────────────────────────────────────────────────────────┐
│           テストケースセットアップ                       │
│   (variation + entity cases, ground truth)              │
└────────────────────┬────────────────────────────────────┘
                     │
        ┌────────────┴────────────┐
        │                         │
   ┌────▼──────────┐     ┌────────▼─────┐
   │  表記揺れ     │     │  固有名詞     │
   │  評価         │     │  評価         │
   │  (MRR)        │     │  (Recall)     │
   └────┬──────────┘     └────────┬─────┘
        │                         │
        └────────────┬────────────┘
                     │
        ┌────────────▼────────────┐
        │   メトリクス集計        │
        │ (Mean, Std, Status)     │
        └────────────┬────────────┘
                     │
        ┌────────────▼────────────┐
        │  レポート生成&改善     │
        │  提案                   │
        └─────────────────────────┘

クイックスタート

1. 依存関係のインストール

cd /Volumes/HD-PCGU3-A/Products/EvoSpikeNet-Core/rag-system

# 基本パッケージ
pip install pandas numpy pyyaml

# RAG関連(既にインストール済みの可能性)
pip install sudachi sudachipy_dict_small
pip install elasticsearch pymilvus
pip install transformers tner

2. 評価の実行

# 全体評価(推奨)
python backend/evaluate_robustness.py

# 表記揺れのみ
python backend/evaluate_robustness.py --variation-only

# 固有名詞のみ
python backend/evaluate_robustness.py --entity-only

# デバッグモード(詳細ログ出力)
python backend/evaluate_robustness.py --verbose

3. 結果の確認

評価後、以下のファイルが生成されます:

backend/
├── variation_results.csv      # 表記揺れ評価結果(行=ケース、列=指標)
├── entity_results.csv         # 固有名詞評価結果(行=ケース、列=指標)
└── evaluation_report.txt      # サマリーレポート(人が読みやすい形式)

結果ファイルの例:

# variation_results.csv
case_id,canonical_form,found_rate,mrr_mean,mrr_std,coverage_rate
var_001,盆休み,1.0,0.85,0.12,100%
var_002,サーバー,0.67,0.58,0.18,67%
# entity_results.csv
case_id,entity,entity_type,recall,precision,f1_score,top_rank
ent_001,EV-2024-001,PROJECT_ID,1.0,1.0,1.0,1
ent_002,田中太郎,PERSON,0.9,0.8,0.85,2

詳細な使用方法

A. 表記揺れ(Variation)の評価

A.1 目的と概念

表記揺れ(表記ゆれ)とは:

同じ概念を複数の表記で表現すること。日本語特有の課題です。

例1: 「盆休み」「お盆休み」「盆期間」
     → これらが全て同じドキュメントを見つけるべき

例2: 「サーバー」「サーバ」「サーバーコンピューター」
     → 長音符ゆれ + 複合語の違い

例3: 「会議」「ミーティング」「カンファレンス」
     → 言い換え(同義語)

A.2 評価メトリクス

メトリクス 説明 計算式 目標値
MRR (Mean Reciprocal Rank) 正解ドキュメントが見つかった順位の逆数 1/rank または 0 > 0.7
found_rate 複数の表記ゆれの中で、正解が見つかった割合 found_count / variations_count > 0.8
MRR_std MRRの標準偏差(ばらつき) std([MRR1, MRR2, ...]) < 0.15
coverage_rate テストケース全体での成功率 success_count / total_cases > 85%

MRR(Mean Reciprocal Rank)の解釈:

MRR値    │ 順位  │ 評価       │ コメント
─────────┼───────┼───────────┼─────────────────
1.0      │ 1位   │ 最高 ✓    │ 最初の結果が正解
0.5      │ 2位   │ 良好 ✓    │ 正解が2位
0.33     │ 3位   │ 中程度 △  │ 正解が3位だが検出
0.0      │ -     │ 失敗 ✗    │ 正解が見つからない

A.3 テストケース例

variation:
  - case_id: "var_001"
    canonical_form: "盆休み"                # 標準形
    variations:                            # テストする表記揺れ
      - "盆休み"                            # オリジナル
      - "お盆休み"                          # 接頭辞ゆれ
      - "盆期間"                            # 言い換え
      - "盆"                               # 短縮形
    ground_truth_doc_id: "DOC_001"        # 正解ドキュメント
    variation_type: "prefix"              # ゆれの分類

  - case_id: "var_002"
    canonical_form: "サーバー"
    variations:
      - "サーバー"
      - "サーバ"
      - "server"
    ground_truth_doc_id: "DOC_002"
    variation_type: "long_vowel"

A.4 実行例

from backend.evaluate_robustness import RAGRobustnessEvaluator
from backend.rag_milvus import EvoRAG

# RAG システムを初期化
rag = EvoRAG(llm_type="huggingface")

# 評価器を作成
evaluator = RAGRobustnessEvaluator(rag)

# テストケースを読み込み
import yaml
with open("backend/test_cases.yaml") as f:
    cases = yaml.safe_load(f)

variation_cases = cases["variation"]

# 表記揺れ評価を実行
results_df = evaluator.variation_eval.evaluate_variation_set(variation_cases)

# 結果の一部を表示
print(results_df[["case_id", "canonical_form", "found_rate", "mrr_mean"]].head(10))

# CSV に保存
results_df.to_csv("variation_results.csv", index=False)

A.5 結果の解釈

Good パターン(期待通りの動作):

case_id  canonical_form  found_rate  mrr_mean  evaluation
────────────────────────────────────────────────────────
var_001  盆休み          1.0         0.85      ✓ 優秀
var_003  会議            0.9         0.75      ✓ 良好
var_005  スケーリング    0.67        0.58      △ 改善中

Problem パターン(問題検出):

case_id  canonical_form  found_rate  mrr_mean  原因推定
────────────────────────────────────────────────────────
var_002  カンファレンス  0.0         0.00      ✗ 同義語未対応
var_004  パフォーマンス  0.25        0.33      ✗ 正規化不完全

原因分析のフローチャート:

MRR が低い?
    ├─ YES
    │  ├─ found_rate が低い(0.5未満)
    │  │  └─ 原因: トークナイズ・正規化が不十分
    │  │     └─ 対策: Sudachi 設定確認 / 正規化ルール追加
    │  │
    │  └─ found_rate は高い(0.8以上)だが MRR 低い
    │     └─ 原因: ランキングが悪い(正解が下位に出現)
    │        └─ 対策: ベクター検索の重み増加 / Query Expansion
    │
    └─ NO: 良好。他ケースを確認

B. 固有名詞(Entity)の評価

B.1 目的と概念

固有表現(Named Entity)とは:

特定の意味を持つ名詞:人名、地名、企業名、製品名、プロジェクトID など。

例1: プロジェクトID「EV-2024-001」
     → クエリに含まれたら、完全一致で検索すべき

例2: 人名「田中太郎」
     → 別の「田中花子」と混同されないべき

例3: 製品名「EvoSpikeNet Pro」
     → 単なる「Pro」だけではなく、製品全体を指す

B.2 評価メトリクス

メトリクス 説明 計算式 目標値
Recall 正解ドキュメント中、実際に見つかった割合 found_count / ground_truth_count > 0.8
Precision 検索結果中、正解の割合 found_count / result_count > 0.8
F1 Score Recall と Precision の調和平均 2(PR)/(P+R) > 0.75
top_rank_gt 最初の正解ドキュメントがランクインした位置 1-10 (1位ならば1) < 5

Recall vs Precision:

────────────────────────────────────────────────

Recall が高い、Precision が低い:
│ 検索結果: DOC_A ✓, DOC_B ✗, DOC_C ✓, DOC_D ✗
│ Recall = 2/2 = 100%  (正解を全部見つけた)
│ Precision = 2/4 = 50% (ノイズが混在)
│ 対策: Entity フィルタリングを強化
│
├─ 例: Entity Boost を低くしすぎた場合
│      → 正解は出ているが、ノイズも増加

────────────────────────────────────────────────

Recall が低い、Precision が高い:
│ 検索結果: DOC_A ✓
│ Recall = 1/2 = 50%  (正解の一部を見落とし)
│ Precision = 1/1 = 100% (出た結果は全て正解)
│ 対策: NER・Boost を強化
│
├─ 例: Entity フィルタリングが強すぎた場合
│      → 正解は限定的だが精度は高い

────────────────────────────────────────────────

理想: Recall 90%, Precision 90%, F1 90%

B.3 テストケース例

entity:
  - case_id: "ent_001"
    query: "プロジェクトEV-2024-001の進捗報告を確認したい"
    entity: "EV-2024-001"
    entity_type: "PROJECT_ID"
    ground_truth_doc_ids:
      - "DOC_A"
      - "DOC_B"
    should_not_match:
      - "DOC_C"      # EV-2024-002 (異なるプロジェクト)
      - "DOC_D"      # EV-2023-001 (異なるプロジェクト)

  - case_id: "ent_002"
    query: "田中太郎の評価表"
    entity: "田中太郎"
    entity_type: "PERSON"
    ground_truth_doc_ids:
      - "DOC_E"
      - "DOC_F"
    should_not_match:
      - "DOC_G"      # 田中花子 (別人)
      - "DOC_H"      # 太郎 (名前だけ)

  - case_id: "ent_003"
    query: "EvoSpikeNet Pro のスペック表"
    entity: "EvoSpikeNet Pro"
    entity_type: "PRODUCT"
    ground_truth_doc_ids:
      - "DOC_I"
    should_not_match:
      - "DOC_J"      # EvoSpikeNet Core (別製品)
      - "DOC_K"      # "Pro" だけの文書 (誤マッチ)

B.4 実行例

entity_cases = cases["entity"]

# 固有名詞評価を実行
results_df = evaluator.entity_eval.evaluate_entity_set(entity_cases)

# エンティティタイプ別に集計
summary = results_df.groupby("entity_type")[["recall", "precision", "f1_score"]].mean()

print("Entity Type Summary:")
print(summary)
# 出力例:
#             recall  precision  f1_score
# entity_type                           
# PROJECT_ID     0.95       0.92      0.93  ✓
# PERSON         0.88       0.85      0.86  ✓
# PRODUCT        0.72       0.68      0.70  △ 要改善

# CSV に保存
results_df.to_csv("entity_results.csv", index=False)

B.5 結果の解釈

Good パターン:

entity          entity_type recall precision f1_score  top_rank evaluation
──────────────────────────────────────────────────────────────────────────
EV-2024-001     PROJECT_ID  1.0    1.0       1.0       1       ✓ 最高
田中太郎        PERSON      0.9    0.85      0.87      2       ✓ 良好
EvoSpikeNet Pro PRODUCT     0.85   0.80      0.82      3       ✓ 良好

Problem パターン:

entity          entity_type recall precision f1_score  top_rank 原因
──────────────────────────────────────────────────────────────────────────
SPIKE-00123     PROJECT_ID  0.0    0.0       0.0       -        ✗ 辞書なし
AIシステム部    ORG         0.4    0.3       0.35      15       ✗ NER精度低

評価基準と改善指針

対応表1: 表記揺れ(Variation)の問題と対策

MRR が低い場合(< 0.5)

症状:

検索クエリ: "お盆休みの規定"
期待: DOC_001 が1~3位に出現
実際: DOC_001 が見つからない(MRR = 0.0)

原因の特定:

  1. トークナイズ/正規化の不完全
問題例:
  Kuromoji: "お盆休み" → ["お", "盆", "休み"]
           (接頭辞「お」が分割されている)

  Sudachi:  "お盆休み" → ["盆", "休み"]
           (接頭辞を正規化できた) ✓

改善策(優先度: 高):

# Sudachi をインストール
pip install --upgrade sudachi sudachipy_dict_small

# Elasticsearch のアナライザ設定を更新
curl -X PUT "localhost:9200/rag_kb_index_v2/_settings" \
  -H "Content-Type: application/json" \
  -d '{
    "index": {
      "analysis": {
        "tokenizer": {
          "sudachi_tokenizer": {
            "type": "sudachi_tokenizer",
            "mode": "search",
            "resources_path": "/path/to/sudachi/resources"
          }
        },
        "analyzer": {
          "ja_sudachi_normalized": {
            "tokenizer": "sudachi_tokenizer",
            "char_filter": [
              "unicode_normalize",
              "mapping"
            ]
          }
        }
      }
    }
  }'
  1. 長音符ゆれ(ー問題)
問題例:
  "サーバー" と "サーバ" が別物として扱われる

原因: Unicode 正規化が不完全

改善策(優先度: 中):

# elasticsearch_settings.json の char_filter に追加
"char_filter": {
  "mapping": {
    "mappings": [
      "サーバー => サーバ",
      "データベース => DB",
      "コンピューター => コンピュータ"
    ]
  }
}
  1. ベクター検索の重みが低い

改善策(優先度: 中):

# RRF パラメータを調整
final_score = 0.3 * bm25_score + 0.7 * vector_score  # vector 重み増加

# または Query Expansion を有効化
ENABLE_QUERY_EXPANSION = True

対応表2: 固有名詞(Entity)の問題と対策

Recall が低い場合(< 0.7)

症状:

検索クエリ: "プロジェクトEV-2024-001の進捗"
期待: DOC_A, DOC_B が検出される
実際: DOC_A のみ検出(Recall = 1/2 = 50%)

原因1: NER(固有表現認識)層がない

# 現在: キーワード検索のみ
results = elasticsearch_client.search("EV-2024-001")
# 問題: "EV-2024-001" が Kuromoji で ["EV", "2024", "001"] に分割

# 改善: NER 層を追加
from transformers import pipeline
ner_pipeline = pipeline("ner", model="tner/roberta-large-japanese-char-luw-ner")
entities = ner_pipeline("プロジェクトEV-2024-001の進捗")
# OUTPUT: [{"entity_group": "PROJECT_ID", "word": "EV-2024-001", ...}]

改善策(優先度: 高):

# インストール
pip install tner transformers

# Python コード
class EntityRecognizer:
    def __init__(self):
        self.ner = pipeline("ner", model="tner/roberta-large-japanese-char-luw-ner")

    def extract_and_boost(self, query: str) -> Dict:
        entities = self.ner(query)

        # Elasticsearch query に entity boost を追加
        es_query = {
            "bool": {
                "should": [
                    {
                        "term": {
                            "entity_project": {
                                "value": "EV-2024-001",
                                "boost": 5.0  # ← HIGH PRIORITY
                            }
                        }
                    }
                ]
            }
        }
        return es_query

原因2: Entity Boost が弱い or 機能していない

# 確認方法
# Elasticsearch マッピングを確認
curl -X GET "localhost:9200/rag_kb_index_v2/_mapping?pretty"

# entity_project フィールドが keyword 型か確認
{
  "properties": {
    "entity_project": {
      "type": "keyword",  # ✓ 完全一致用
      "index": true
    }
  }
}

# boost が有効か確認(クエリ時に score を表示)
curl -X POST "localhost:9200/rag_kb_index_v2/_search?pretty" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {
      "bool": {
        "should": [
          {"term": {"entity_project": {"value": "EV-2024-001", "boost": 5.0}}}
        ]
      }
    },
    "explain": true  # ← スコア計算の詳細を表示
  }'

改善策(優先度: 高):

# boost 係数を増加
curl -X PUT "localhost:9200/rag_kb_index_v2/_mapping" \
  -H "Content-Type: application/json" \
  -d '{
    "properties": {
      "entity_project": {
        "type": "keyword",
        "boost": 5.0  # ← INCREASE from 2.0
      },
      "entity_product": {
        "type": "keyword",
        "boost": 4.0
      },
      "entity_person": {
        "type": "keyword",
        "boost": 3.0
      }
    }
  }'

テストセットのカスタマイズ

自社の用語を追加する

ステップ1: test_cases.yaml を編集

variation:
  # 既存ケース...

  # 新規ケース(カスタム)
  - case_id: "var_custom_001"
    canonical_form: "貴社の用語1"
    variations:
      - "バリエーション1"
      - "バリエーション2"
      - "バリエーション3"
    ground_truth_doc_id: "DOC_CUSTOM_001"
    variation_type: "custom"
    description: "カスタム用語の表記ゆれテスト"

entity:
  # 既存ケース...

  # 新規ケース(カスタム)
  - case_id: "ent_custom_001"
    query: "貴社の固有名詞を含むクエリ"
    entity: "固有名詞"
    entity_type: "CUSTOM_TYPE"
    ground_truth_doc_ids:
      - "DOC_CUSTOM_A"
      - "DOC_CUSTOM_B"
    should_not_match:
      - "DOC_CUSTOM_NEGATIVE"
    description: "カスタム固有名詞のテスト"

ステップ2: テストドキュメントを追加

from backend.rag_milvus import add_user_text

# Milvus にテストドキュメントを追加
add_user_text(
    collection=rag.collection,
    text="貴社の用語1に関する詳細な説明文...",
    source="DOC_CUSTOM_001",
    metadata={"type": "custom"}
)

add_user_text(
    collection=rag.collection,
    text="固有名詞を含む別の文書...",
    source="DOC_CUSTOM_A",
    metadata={"type": "custom"}
)

ステップ3: 評価を実行

import yaml

with open("backend/test_cases.yaml") as f:
    cases = yaml.safe_load(f)

# 全体評価(カスタムケース含む)
results = evaluator.run_full_evaluation(
    cases["variation"],
    cases["entity"]
)

print("カスタムケース評価結果:")
print(results[results["case_id"].str.contains("custom")])

ダッシュボード化(オプション)

Streamlit を使用した可視化

インストール

pip install streamlit matplotlib plotly

app.py を作成

# app.py
import streamlit as st
import pandas as pd
import matplotlib.pyplot as plt
import plotly.express as px

from backend.evaluate_robustness import RAGRobustnessEvaluator
from backend.rag_milvus import EvoRAG

st.set_page_config(
    page_title="RAG 2.0 Evaluation Dashboard",
    layout="wide",
    initial_sidebar_state="expanded"
)

st.title("🔍 RAG 2.0 日本語検索精度ダッシュボード")

# サイドバーでパラメータを設定
st.sidebar.header("設定")
evaluation_type = st.sidebar.radio(
    "評価タイプを選択",
    ["表記揺れ", "固有名詞", "全体"]
)

if st.button("🚀 評価を実行"):
    st.spinner("評価中...(約1-2分)")

    rag = EvoRAG(llm_type="huggingface")
    evaluator = RAGRobustnessEvaluator(rag)

    import yaml
    with open("backend/test_cases.yaml") as f:
        cases = yaml.safe_load(f)

    # 評価実行
    if evaluation_type == "表記揺れ":
        results = evaluator.variation_eval.evaluate_variation_set(cases["variation"])
    elif evaluation_type == "固有名詞":
        results = evaluator.entity_eval.evaluate_entity_set(cases["entity"])
    else:
        var_results = evaluator.variation_eval.evaluate_variation_set(cases["variation"])
        ent_results = evaluator.entity_eval.evaluate_entity_set(cases["entity"])
        results = pd.concat([var_results, ent_results], ignore_index=True)

    # メトリクス表示
    st.subheader("📊 主要メトリクス")
    col1, col2, col3, col4 = st.columns(4)

    with col1:
        if evaluation_type in ["表記揺れ", "全体"]:
            var_mrr = results[results["mrr_mean"].notna()]["mrr_mean"].mean()
            st.metric("表記揺れ MRR", f"{var_mrr:.1%}", "目標: 70%")

    with col2:
        if evaluation_type in ["固有名詞", "全体"]:
            ent_recall = results[results["recall"].notna()]["recall"].mean()
            st.metric("固有名詞 Recall", f"{ent_recall:.1%}", "目標: 80%")

    with col3:
        st.metric("テストケース数", len(results))

    with col4:
        success_rate = (results.shape[0] if results.shape[0] > 0 else 0) / max(len(cases.get("variation", [])), len(cases.get("entity", []))) * 100
        st.metric("成功率", f"{success_rate:.0f}%")

    # 詳細テーブル表示
    st.subheader("📋 詳細結果")
    st.dataframe(results, use_container_width=True)

    # グラフ表示
    if evaluation_type == "表記揺れ":
        fig = px.bar(
            results,
            x="case_id",
            y="mrr_mean",
            color="mrr_mean",
            title="表記揺れ - MRR スコア",
            color_continuous_scale="RdYlGn",
            range_color=[0, 1]
        )
        st.plotly_chart(fig, use_container_width=True)

    elif evaluation_type == "固有名詞":
        fig = px.scatter(
            results,
            x="recall",
            y="precision",
            color="f1_score",
            size="f1_score",
            hover_data=["entity", "entity_type"],
            title="固有名詞 - Recall vs Precision",
            color_continuous_scale="Viridis"
        )
        st.plotly_chart(fig, use_container_width=True)

st.sidebar.info("""
### 📚 ドキュメント
- [RAG 2.0 仕様書](RAG_JAPANESE_SpecV2.md)
- [評価ガイド](RAG_JAPANESE_EVALUATE_GUIDE.md)
- [ロードマップ](RAG_REMAINING_FUNCTIONALITY.md)
""")

実行方法

streamlit run app.py

# ブラウザで http://localhost:8501 を開く

ダッシュボード画面:

┌─────────────────────────────────────────────────────┐
│                RAG 2.0 Evaluation                   │
├─────┬──────────┬──────────┬──────────┬─────────────┤
│MRR  │ Recall   │ Precision│ F1 Score │ Success %   │
│72%  │ 84%      │ 86%      │ 85%      │ 92%         │
├─────────────────────────────────────────────────────┤
│ [📊表 でタイプ別統計] [📈グラフ] [💾エクスポート]  │
└─────────────────────────────────────────────────────┘

トラブルシューティング

Q1: 評価が遅い(> 5分)

A: インデックスが最適化されていない可能性があります。

# Milvus インデックス最適化
from pymilvus import Collection

collection = Collection("rag_kb")
collection.create_index(
    field_name="embedding",
    index_params={
        "metric_type": "L2",
        "index_type": "IVF_FLAT",
        "params": {"nlist": 128}
    }
)

# Elasticsearch インデックス最適化
from elasticsearch import Elasticsearch

es = Elasticsearch(["localhost:9200"])
es.indices.optimize(index="rag_kb_index_v2", force_merge=True)

Q2: NER モデルが見つからない

A: 日本語対応の NER モデルをインストール

pip install tner

# モデルの自動ダウンロード
python -c "from transformers import pipeline; \
           ner = pipeline('ner', model='tner/roberta-large-japanese-char-luw-ner')"

Q3: Sudachi が見つからない / エラーが出る

A: Sudachi と辞書をインストール

pip install sudachi sudachipy-dict-small

# 辞書ファイルの場所を確認
python -c "import sudachipy; print(sudachipy.__file__)"

Q4: Elasticsearch の接続エラー

A: Elasticsearch が起動しているか確認

# 起動確認
curl -X GET "localhost:9200/_cluster/health?pretty"

# 起動していない場合(Docker)
docker run -p 9200:9200 -p 9300:9300 \
  -e "discovery.type=single-node" \
  docker.elastic.co/elasticsearch/elasticsearch:8.6.2

ベストプラクティス

1. 週次評価の習慣化

Cron ジョブ設定:

# ~/.crontab に追加(毎週月曜 9:00 に実行)
0 9 * * 1 cd /path/to/rag-system && \
           python backend/evaluate_robustness.py >> logs/eval_$(date +%Y%m%d).log 2>&1

Slack 通知スクリプト:

# scripts/evaluate_and_notify.py
import json
import requests
from datetime import datetime

def notify_slack(channel, message):
    webhook_url = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
    payload = {"channel": channel, "text": message}
    requests.post(webhook_url, json=payload)

# 評価実行
results = evaluator.run_full_evaluation(...)
var_mrr = results["variation"]["mrr_mean"].mean()
ent_recall = results["entity"]["recall"].mean()

# Slack 通知
message = f"""
🔍 RAG 2.0 Weekly Evaluation - {datetime.now().strftime('%Y-%m-%d')}
────────────────────────
表記揺れ MRR: {var_mrr:.1%} (目標: 70%)
固有名詞 Recall: {ent_recall:.1%} (目標: 80%)
"""
notify_slack("#rag-monitoring", message)

2. ユーザーフィードバックの収集

# RAG API にフィードバック機能を追加
from fastapi import FastAPI
from datetime import datetime

app = FastAPI()

@app.post("/api/v2/rag/feedback")
def record_feedback(
    query: str,
    doc_id: str,
    rating: int,
    user_id: str
):
    """
    ユーザーの満足度フィードバックを記録

    rating: 1 (not relevant) ~ 5 (highly relevant)
    """
    feedback = {
        "query": query,
        "doc_id": doc_id,
        "rating": rating,
        "user_id": user_id,
        "timestamp": datetime.utcnow().isoformat()
    }

    # DB に記録
    db.feedbacks.insert_one(feedback)

    # 定期的に集計して改善案を検討
    if db.feedbacks.count_documents({"rating": {"$lte": 2}}) > 100:
        send_alert("低評価が多い。改善検討が必要")

    return {"status": "success"}

3. 定期的な改善サイクル

月次改善プロセス:

週1 (月)   → 評価実行 → 結果集計
         ↓
週2 (月)   → 低評価ケース分析
         ↓
週3 (月)   → 改善案実装 (Sudachi/NER/Query Expansion)
         ↓
週4 (月)   → リグレッションテスト
         ↓
月末 (金)  → 本番デプロイ
         ↓
翌月 (月)  → 評価実行(ループ)

関連リソース

公式ドキュメント

論文・参考資料


Document Version: 1.0
Last Updated: 2026-05-20
Approval Status: Ready for Production
Owner: RAG Evaluation Team

For updates or questions, contact: rag-team@evospikenet.com