LabHub

ブログ

RAGチャットボット評価実践:オフライン/オンライン品質測定からプロダクションガードレールまで

한국어English日本語

RAG チャットボット評価の実践:オフライン/オンライン品質測定からプロダクションガードレールまで

「精度の平均」では RAG の品質はわからない

RAG チャットボットの評価で最もよくある誤りは、単一の指標(例:全体正答率 82%)だけを見て配備を決めてしまうことだ。この数字の裏には、次のようなリスクが隠れている。

本記事では、RAG チャットボットの品質を Retrieval、Grounding、Answer、Safety という四つの独立した次元で測定し、オフラインベンチマークからオンラインモニタリングまでの評価パイプライン全体を設計する。

RAG 評価の四つの次元

次元測定対象主要指標失敗時の症状
Retrieval検索段階が関連文書を見つけたかRecall@K, MRR, nDCG見当違いなトピックの回答
Grounding回答が検索された文書に根拠を持つかFaithfulness, Citation Precisionハルシネーション、根拠なき主張
Answer最終回答がユーザーの質問に合うかAnswer Relevance, Correctness質問と無関係な回答
Safety回答が安全で規定を遵守しているかToxicity Rate, PII Leak Rate有害コンテンツ、個人情報漏洩

オフライン評価: Golden Dataset の構築と自動測定

Golden Dataset の構造

"""
RAG 評価用の Golden Dataset。
各テストケースは質問、正解、正解根拠となる文書、期待カテゴリを含む。
"""
from dataclasses import dataclass, field
from typing import List, Optional

@dataclass
class GoldenTestCase:
    """単一の評価ケース"""
    question_id: str
    question: str
    expected_answer: str
    relevant_doc_ids: List[str]     # 正解を含む文書 ID のリスト
    category: str                    # faq, policy, product, troubleshooting
    difficulty: str                  # easy, medium, hard
    expected_citations: List[str]    # 回答で引用されるべき文書 ID
    negative_assertions: List[str] = field(default_factory=list)  # 回答に含まれてはいけない内容

# 評価セットの例
GOLDEN_DATASET = [
    GoldenTestCase(
        question_id="faq_001",
        question="返金処理には何日かかりますか?",
        expected_answer="返金はリクエスト日から営業日基準で 3-5 日以内に処理されます。",
        relevant_doc_ids=["doc_refund_policy_v3", "doc_faq_payment"],
        category="faq",
        difficulty="easy",
        expected_citations=["doc_refund_policy_v3"],
        negative_assertions=["即時返金", "当日処理"],
    ),
    GoldenTestCase(
        question_id="policy_002",
        question="海外配送時の関税は誰が負担しますか?",
        expected_answer="海外配送時の関税および付加価値税は受取人の負担です。",
        relevant_doc_ids=["doc_shipping_international", "doc_customs_guide"],
        category="policy",
        difficulty="medium",
        expected_citations=["doc_shipping_international"],
        negative_assertions=["関税免除", "送料無料"],
    ),
    GoldenTestCase(
        question_id="trouble_003",
        question="アプリで決済エラー P4021 が発生します。どう解決しますか?",
        expected_answer="P4021 エラーはカード会社の認証失敗により発生します。カード会社のアプリでオンライン決済を有効化してから再試行してください。",
        relevant_doc_ids=["doc_error_codes", "doc_payment_troubleshoot"],
        category="troubleshooting",
        difficulty="hard",
        expected_citations=["doc_error_codes"],
        negative_assertions=["カスタマーセンターに問い合わせ", "不明なエラー"],
    ),
]

Retrieval 品質の測定

"""
検索段階の品質を測定するモジュール。
Recall@K、MRR(Mean Reciprocal Rank)、nDCG を計算する。
"""
from typing import List, Dict
import numpy as np

def recall_at_k(
    retrieved_doc_ids: List[str],
    relevant_doc_ids: List[str],
    k: int = 5,
) -> float:
    """
    上位 K 件の検索結果に関連文書が含まれる割合。
    例: relevant_docs = ["A", "B"], retrieved = ["C", "A", "D", "B", "E"]
    recall@5 = 2/2 = 1.0
    """
    retrieved_set = set(retrieved_doc_ids[:k])
    relevant_set = set(relevant_doc_ids)
    if not relevant_set:
        return 1.0  # 関連文書がなければ recall は無意味
    return len(retrieved_set & relevant_set) / len(relevant_set)

def mean_reciprocal_rank(
    retrieved_doc_ids: List[str],
    relevant_doc_ids: List[str],
) -> float:
    """
    最初の関連文書の逆数順位。
    例: relevant = ["B"], retrieved = ["A", "B", "C"] -> MRR = 1/2 = 0.5
    """
    relevant_set = set(relevant_doc_ids)
    for i, doc_id in enumerate(retrieved_doc_ids):
        if doc_id in relevant_set:
            return 1.0 / (i + 1)
    return 0.0

def evaluate_retrieval(
    test_cases: List[dict],
    retriever,
    k_values: List[int] = [3, 5, 10],
) -> Dict[str, float]:
    """
    テストセット全体に対して retrieval 品質を測定する。
    """
    results = {f"recall@{k}": [] for k in k_values}
    results["mrr"] = []

    for case in test_cases:
        retrieved = retriever.search(
            query=case["question"],
            top_k=max(k_values),
        )
        retrieved_ids = [doc.id for doc in retrieved]

        for k in k_values:
            r = recall_at_k(retrieved_ids, case["relevant_doc_ids"], k)
            results[f"recall@{k}"].append(r)

        mrr = mean_reciprocal_rank(retrieved_ids, case["relevant_doc_ids"])
        results["mrr"].append(mrr)

    return {
        metric: round(float(np.mean(values)), 4)
        for metric, values in results.items()
    }

LLM-as-a-Judge: Grounding と Answer 品質の測定

LLM を判定者として用い、回答の faithfulness(検索文書に基づく忠実度)と relevance(質問との関連性)を測定する。

"""
LLM-as-a-Judge パターンで RAG 回答の Grounding と Relevance を評価する。
OpenAI GPT-4o または Claude 3.5 Sonnet を判定者として使う。
"""
from dataclasses import dataclass
from typing import List, Optional
import json

FAITHFULNESS_PROMPT = """あなたは RAG チャットボット回答の忠実度を評価する専門の判定者です。

与えられる情報:
- ユーザーの質問: {question}
- 検索された文書群: {retrieved_contexts}
- チャットボットの回答: {answer}

評価基準:
1. 回答のすべての主張(claim)が検索された文書に根拠を持つか?
2. 文書にない内容を追加したり作り上げたりしていないか?
3. 文書の内容を歪曲していないか?

必ず以下の JSON 形式で応答してください:
{{
  "faithfulness_score": <0.0-1.0>,
  "claims": [
    {{
      "claim": "<回答から抽出した主張>",
      "supported": <true/false>,
      "evidence": "<根拠となる文書内容または 'no evidence found'>"
    }}
  ],
  "unsupported_claims_count": <int>,
  "hallucination_detected": <true/false>
}}"""

RELEVANCE_PROMPT = """あなたは RAG チャットボット回答の関連性を評価する専門の判定者です。

与えられる情報:
- ユーザーの質問: {question}
- チャットボットの回答: {answer}

評価基準:
1. 回答が質問に直接答えているか?
2. 不要な情報が過度に含まれていないか?
3. 中核的な情報が欠落していないか?

必ず以下の JSON 形式で応答してください:
{{
  "relevance_score": <0.0-1.0>,
  "addresses_question": <true/false>,
  "missing_information": "<欠落している中核情報または 'none'>",
  "unnecessary_information": "<不要な情報または 'none'>"
}}"""

@dataclass
class JudgmentResult:
    question_id: str
    faithfulness_score: float
    relevance_score: float
    hallucination_detected: bool
    unsupported_claims: int
    missing_info: str
    raw_judgment: dict

async def evaluate_with_llm_judge(
    question: str,
    answer: str,
    retrieved_contexts: List[str],
    judge_client,  # OpenAI または Anthropic のクライアント
    question_id: str = "",
) -> JudgmentResult:
    """LLM 判定者で faithfulness と relevance を評価する。"""

    # Faithfulness の評価
    faith_prompt = FAITHFULNESS_PROMPT.format(
        question=question,
        retrieved_contexts="\n---\n".join(retrieved_contexts),
        answer=answer,
    )
    faith_response = await judge_client.chat.completions.create(
        model="gpt-4o-2024-11-20",
        messages=[{"role": "user", "content": faith_prompt}],
        response_format={"type": "json_object"},
        temperature=0.0,  # 判定の一貫性のため temperature 0
    )
    faith_result = json.loads(faith_response.choices[0].message.content)

    # Relevance の評価
    rel_prompt = RELEVANCE_PROMPT.format(question=question, answer=answer)
    rel_response = await judge_client.chat.completions.create(
        model="gpt-4o-2024-11-20",
        messages=[{"role": "user", "content": rel_prompt}],
        response_format={"type": "json_object"},
        temperature=0.0,
    )
    rel_result = json.loads(rel_response.choices[0].message.content)

    return JudgmentResult(
        question_id=question_id,
        faithfulness_score=faith_result.get("faithfulness_score", 0.0),
        relevance_score=rel_result.get("relevance_score", 0.0),
        hallucination_detected=faith_result.get("hallucination_detected", False),
        unsupported_claims=faith_result.get("unsupported_claims_count", 0),
        missing_info=rel_result.get("missing_information", "unknown"),
        raw_judgment={"faithfulness": faith_result, "relevance": rel_result},
    )

オンライン評価: プロダクション品質のモニタリング

オフライン評価を通過したモデルがプロダクションに配備された後も、実トラフィックに対する品質を継続的にモニタリングする必要がある。

リアルタイム品質指標の収集

"""
プロダクション RAG チャットボットのリアルタイム品質指標を収集するミドルウェア。
すべてのリクエスト-レスポンス対に対して軽量な品質シグナルを収集する。
"""
import time
import hashlib
from dataclasses import dataclass, field
from typing import List, Optional, Dict
from prometheus_client import Histogram, Counter, Gauge

# Prometheus メトリクスの定義
RESPONSE_LATENCY = Histogram(
    "rag_response_latency_seconds",
    "RAG 応答の総所要時間",
    ["pipeline_stage"],  # retrieval, generation, total
    buckets=[0.1, 0.25, 0.5, 1.0, 2.0, 5.0, 10.0],
)
RETRIEVAL_EMPTY = Counter(
    "rag_retrieval_empty_total",
    "検索結果が 0 件のリクエスト数",
)
CITATION_RATE = Gauge(
    "rag_citation_rate",
    "直近 100 件のうち引用を含む応答の割合",
)
THUMBS_DOWN = Counter(
    "rag_thumbs_down_total",
    "ユーザーの否定的フィードバック数",
    ["category"],
)

@dataclass
class QualitySignals:
    """1 件のリクエスト-レスポンスから収集する軽量な品質シグナル"""
    request_id: str
    retrieval_count: int              # 検索された文書数
    retrieval_latency_ms: float
    top_similarity_score: float       # 最上位検索結果の類似度スコア
    generation_latency_ms: float
    response_length_chars: int
    has_citation: bool                # 応答に出典の引用が含まれているか
    language_detected: str            # 応答の言語
    contains_hedging: bool            # 「おそらく」「〜かもしれません」のような不確実表現の有無

def collect_quality_signals(
    request_id: str,
    query: str,
    retrieved_docs: list,
    response: str,
    retrieval_time: float,
    generation_time: float,
) -> QualitySignals:
    """リクエスト-レスポンス対から品質シグナルを抽出する。"""
    top_score = max((doc.score for doc in retrieved_docs), default=0.0)
    has_citation = any(
        marker in response
        for marker in ["[出典:", "[参考:", "[文書", "出典:"]
    )
    hedging_phrases = ["おそらく", "〜かもしれません", "正確でない可能性", "確認が必要です"]
    contains_hedging = any(phrase in response for phrase in hedging_phrases)

    signals = QualitySignals(
        request_id=request_id,
        retrieval_count=len(retrieved_docs),
        retrieval_latency_ms=retrieval_time * 1000,
        top_similarity_score=top_score,
        generation_latency_ms=generation_time * 1000,
        response_length_chars=len(response),
        has_citation=has_citation,
        language_detected="ko" if any('\uac00' <= c <= '\ud7a3' for c in response) else "en",
        contains_hedging=contains_hedging,
    )

    # Prometheus メトリクスの記録
    RESPONSE_LATENCY.labels(pipeline_stage="retrieval").observe(retrieval_time)
    RESPONSE_LATENCY.labels(pipeline_stage="generation").observe(generation_time)
    RESPONSE_LATENCY.labels(pipeline_stage="total").observe(retrieval_time + generation_time)

    if signals.retrieval_count == 0:
        RETRIEVAL_EMPTY.inc()

    return signals

品質アラート閾値の設定

# prometheus-rules.yaml
groups:
  - name: rag_quality_alerts
    interval: 30s
    rules:
      # 検索の空結果比率が 10% を超えたら警告
      - alert: RAGRetrievalEmptyRateHigh
        expr: |
          rate(rag_retrieval_empty_total[10m]) /
          rate(rag_response_latency_seconds_count{pipeline_stage="total"}[10m]) > 0.10
        for: 5m
        labels:
          severity: warning
          team: chatbot
        annotations:
          summary: 'RAG 検索の空結果比率が 10% を超えています'
          description: |
            現在の空結果比率: {{ $value | humanizePercentage }}
            インデックスの状態、埋め込みモデル、クエリ前処理を確認してください。

      # 引用を含む比率が 70% を下回ったら警告
      - alert: RAGCitationRateLow
        expr: rag_citation_rate < 0.70
        for: 10m
        labels:
          severity: warning
          team: chatbot
        annotations:
          summary: 'RAG の引用を含む比率が 70% を下回っています'
          description: |
            現在の引用比率: {{ $value | humanizePercentage }}
            LLM プロンプトの引用指示または検索品質を確認してください。

      # 応答遅延が急増したら警告
      - alert: RAGResponseLatencyHigh
        expr: |
          histogram_quantile(0.95,
            rate(rag_response_latency_seconds_bucket{pipeline_stage="total"}[5m])
          ) > 5.0
        for: 3m
        labels:
          severity: critical
          team: chatbot
        annotations:
          summary: 'RAG 応答の p95 遅延が 5 秒を超えています'

      # ユーザーの否定的フィードバックの急増
      - alert: RAGNegativeFeedbackSpike
        expr: |
          rate(rag_thumbs_down_total[30m]) > 2 * rate(rag_thumbs_down_total[24h] offset 1d)
        for: 15m
        labels:
          severity: warning
          team: chatbot
        annotations:
          summary: 'ユーザーの否定的フィードバックが前日比 2 倍以上に増加しました'

回帰防止パイプライン: CI で品質ゲートをかける

RAG パイプラインの構成要素(プロンプト、検索設定、LLM モデル)を変更するたびに、品質の回帰を自動的に検知しなければならない。

"""
RAG 品質の回帰テスト。
Golden dataset に対して 4 つの次元の品質を測定し、
以前のバージョンに対する回帰の有無を判定する。
"""
import json
import sys
from pathlib import Path
from typing import Dict

# 品質のベースライン (以前の配備バージョンの成績)
BASELINE_SCORES = {
    "recall@5": 0.88,
    "mrr": 0.75,
    "faithfulness": 0.91,
    "relevance": 0.87,
    "safety_pass_rate": 1.00,
    "citation_rate": 0.82,
}

# 許容可能な下落幅 (絶対値)
REGRESSION_TOLERANCE = {
    "recall@5": 0.03,       # 3% の下落まで許容
    "mrr": 0.03,
    "faithfulness": 0.02,   # faithfulness は厳格に
    "relevance": 0.03,
    "safety_pass_rate": 0.0,  # safety は回帰を許さない
    "citation_rate": 0.05,
}

def check_regression(current_scores: Dict[str, float]) -> dict:
    """
    現在のスコアをベースラインと比較し、回帰の有無を判定する。
    Returns: {"passed": bool, "regressions": [...], "improvements": [...]}
    """
    regressions = []
    improvements = []

    for metric, baseline in BASELINE_SCORES.items():
        current = current_scores.get(metric, 0.0)
        tolerance = REGRESSION_TOLERANCE.get(metric, 0.0)
        delta = current - baseline

        if delta < -tolerance:
            regressions.append({
                "metric": metric,
                "baseline": baseline,
                "current": current,
                "delta": round(delta, 4),
                "tolerance": tolerance,
            })
        elif delta > 0.01:  # 1% 以上の改善
            improvements.append({
                "metric": metric,
                "baseline": baseline,
                "current": current,
                "delta": round(delta, 4),
            })

    passed = len(regressions) == 0
    return {
        "passed": passed,
        "regressions": regressions,
        "improvements": improvements,
        "summary": (
            f"PASSED: {len(improvements)} improvements, 0 regressions"
            if passed
            else f"FAILED: {len(regressions)} regressions detected"
        ),
    }

if __name__ == "__main__":
    # CI で実行: python eval/regression_check.py results.json
    results_path = sys.argv[1] if len(sys.argv) > 1 else "eval_results.json"
    with open(results_path) as f:
        current_scores = json.load(f)

    result = check_regression(current_scores)
    print(json.dumps(result, indent=2, ensure_ascii=False))

    if not result["passed"]:
        print("\n=== REGRESSION DETAILS ===")
        for reg in result["regressions"]:
            print(f"  {reg['metric']}: {reg['baseline']:.4f} -> {reg['current']:.4f} "
                  f"(delta: {reg['delta']:.4f}, tolerance: {reg['tolerance']:.4f})")
        sys.exit(1)

CI ワークフロー全体の構成

# .github/workflows/rag-eval.yml
name: RAG Quality Evaluation
on:
  push:
    paths:
      - 'src/rag/**'
      - 'prompts/**'
      - 'config/retrieval/**'
  pull_request:
    paths:
      - 'src/rag/**'
      - 'prompts/**'

jobs:
  offline-eval:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: pip install -r requirements-eval.txt

      - name: Run retrieval evaluation
        run: |
          python eval/retrieval_eval.py \
            --dataset eval/golden_dataset.json \
            --k-values 3,5,10 \
            --output eval_results_retrieval.json

      - name: Run LLM judge evaluation
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: |
          python eval/llm_judge_eval.py \
            --dataset eval/golden_dataset.json \
            --judge-model gpt-4o-2024-11-20 \
            --output eval_results_judge.json

      - name: Run safety evaluation
        run: |
          python eval/safety_eval.py \
            --dataset eval/golden_dataset.json \
            --output eval_results_safety.json

      - name: Merge results and check regression
        run: |
          python eval/merge_results.py \
            eval_results_retrieval.json \
            eval_results_judge.json \
            eval_results_safety.json \
            --output eval_results.json

          python eval/regression_check.py eval_results.json

      - name: Upload evaluation report
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: eval-report
          path: eval_results*.json

RAGAS フレームワークの活用

RAGAS(Retrieval Augmented Generation Assessment)は RAG 評価のためのオープンソースフレームワークだ。上で自前実装した指標を RAGAS で置き換えたり補完したりできる。

"""
RAGAS フレームワークを活用した RAG 評価の例。
pip install ragas==0.2.6
"""
from ragas import evaluate
from ragas.metrics import (
    faithfulness,
    answer_relevancy,
    context_precision,
    context_recall,
)
from datasets import Dataset

# 評価データの準備 (RAGAS 形式)
eval_data = {
    "question": [
        "返金処理には何日かかりますか?",
        "海外配送時の関税は誰が負担しますか?",
    ],
    "answer": [
        "返金は営業日基準で 3-5 日以内に処理されます。",
        "海外配送時の関税と付加価値税は受取人が負担します。",
    ],
    "contexts": [
        ["返金リクエスト日から営業日基準で 3-5 日以内に返金が処理されます。週末および祝日は営業日に含まれません。"],
        ["海外配送時に発生する関税および付加価値税は受取人の負担です。関税額は輸入国の税率によって変わります。"],
    ],
    "ground_truth": [
        "返金はリクエスト日から営業日基準で 3-5 日以内に処理されます。",
        "海外配送時の関税および付加価値税は受取人の負担です。",
    ],
}

dataset = Dataset.from_dict(eval_data)

# RAGAS 評価の実行
results = evaluate(
    dataset=dataset,
    metrics=[
        faithfulness,        # 回答がコンテキストに忠実か
        answer_relevancy,    # 回答が質問に関連しているか
        context_precision,   # 検索されたコンテキストが正確か
        context_recall,      # 関連コンテキストを漏れなく検索できたか
    ],
)

print(results)
# {'faithfulness': 0.95, 'answer_relevancy': 0.92,
#  'context_precision': 0.88, 'context_recall': 0.90}

障害シナリオ別の対応

シナリオ 1: Faithfulness スコアの急落

症状: オフライン評価で faithfulness が 0.91 から 0.72 に低下
     ユーザーからの報告: 「根拠のない回答が増えた」

点検の順序:
  1. LLM モデルのバージョン変更の有無を確認
     -> gpt-4o-2024-08-06 から gpt-4o-2024-11-20 に更新されていた
  2. システムプロンプトの grounding 指示を確認
     -> モデル更新後にプロンプトのチューニングがされていない

解決:
  1. システムプロンプトに「必ず検索された文書に基づいて回答してください。
     文書にない内容は『該当する情報が見つかりません』と回答してください。」を追加して強化
  2. モデル更新前に必ずオフライン評価を実施するルールを策定
  3. モデル別のプロンプトバージョン管理 (model_version -> prompt_version のマッピングテーブル)

シナリオ 2: 検索の空結果率の急増

症状: rag_retrieval_empty_total カウンタが平常時比で 5 倍に増加
     エラーログ: なし (検索自体は成功するが結果が 0)

原因: 埋め込みモデル更新後、新しいクエリ埋め込みと既存文書の埋め込みの
     ベクトル空間がずれた (cosine similarity が全般的に低下)

解決:
  1. 直ちに以前の埋め込みモデルへロールバック
  2. 新しい埋め込みモデル適用時は全文書の再埋め込みが必須
  3. 埋め込みモデル変更手順に「再インデックス完了の確認」ステップを追加

シナリオ 3: LLM-as-a-Judge の評価コスト急増

症状: 月間 LLM Judge コストが $3,000 から $12,000 に増加
原因: テストセットが 50 件から 500 件に増え、PR ごとに実行していた

解決:
  1. Golden dataset を core(50) + extended(450) に分離
  2. PR では core のみ実行し、main へのマージ時に extended を含める
  3. LLM Judge のキャッシュ: 同一入力に対する判定結果を 30 日間キャッシュ
  4. 低コストモデル(gpt-4o-mini)で一次フィルタリングし、失敗ケースのみ gpt-4o で再判定
クイズ

Q1. RAG 評価を Retrieval、Grounding、Answer、Safety の四次元に分ける理由は?

||各次元で発生する問題の原因と解決方法が異なるからである。Retrieval の失敗はインデックス/埋め込みの問題であり、Grounding の失敗は LLM プロンプトの問題である。これを統合指標で見ると原因分析ができなくなる。||

Q2. LLM-as-a-Judge で temperature を 0 に設定する理由は?

||判定の再現性(reproducibility)を高めるためである。temperature が高いと同じ入力に対して異なる判定結果が出ることがあり、CI テストの安定性が下がる。||

Q3. オンラインモニタリングで「検索の空結果率」を別指標として追跡する理由は?

||検索結果が 0 件だと LLM が自前の知識で回答し、ハルシネーションのリスクが急増するからである。この指標が上昇したら、インデックス品質、埋め込みモデル、クエリ前処理に問題があるという強いシグナルだ。||

Q4. Safety 指標の回帰許容範囲を 0% に設定する理由は?

||PII の漏洩や有害コンテンツの生成は、一件であっても法的リスクとブランド毀損を招きうるからである。精度は小幅な低下を許容できるが、安全性は絶対基準である。||

Q5. RAGAS の context_precision と context_recall の違いは?

||context_precision は「検索された文書のうち実際に関連がある割合」であり、context_recall は「関連文書のうち検索できた割合」である。Precision が低いと不要な情報がプロンプトを汚染し、recall が低いと回答に必要な情報が欠ける。||

Q6. 回帰防止パイプラインで絶対基準と相対基準の両方を使うべき理由は?

||絶対基準(例: faithfulness > 0.85)だけだと、既存の 0.95 から 0.86 へ急落しても通過してしまう。相対基準(以前との delta)だけだと、もともとスコアが低いサービスで問題になる。二つの基準を組み合わせてこそ品質を多角的に守れる。||

Q7. Golden dataset に negative_assertions を含める理由は?

||正しい回答に近いが誤っている内容(例: 「即時返金」)が含まれるのを検知するためである。正解との類似度だけでは、こうした微妙な誤りを捉えられない。||

参考資料

コメント

まだコメントはありません。

ログインするとコメントできます