LESSON

出力品質評価

「エージェントは動いた。だが、動くことと信頼できることは別だ。」

田中VPoEが品質管理の重要性を説く。

「営業チームがこのエージェントの出力を信じてアクションを起こす。もし出力が不正確だったら、顧客との関係が壊れる。品質評価の仕組みが必要だ。」

出力品質の3つの軸

エージェント出力の品質評価:
1. 正確性(Accuracy)    → 予測値と分析が事実に基づいているか
2. 有用性(Usefulness)  → ビジネスアクションにつながるか
3. 一貫性(Consistency) → 同じ入力に同じ出力を返すか

正確性の評価

MLモデルの精度

from sklearn.metrics import roc_auc_score, precision_recall_curve
import numpy as np

def evaluate_model_accuracy(model, X_test, y_test):
    """モデルの精度を多角的に評価"""
    y_proba = model.predict_proba(X_test)[:, 1]
    y_pred = model.predict(X_test)

    metrics = {
        "AUC-ROC": roc_auc_score(y_test, y_proba),
        "Precision@50%Recall": None,
        "Recall@50%Precision": None,
    }

    # Precision-Recall曲線から特定の閾値での性能を計算
    precision, recall, thresholds = precision_recall_curve(y_test, y_proba)

    # Recall 50%時のPrecision
    idx = np.argmin(np.abs(recall - 0.5))
    metrics["Precision@50%Recall"] = precision[idx]

    # Precision 50%時のRecall
    idx = np.argmin(np.abs(precision - 0.5))
    metrics["Recall@50%Precision"] = recall[idx]

    return metrics

metrics = evaluate_model_accuracy(model, X_test, y_test)
for k, v in metrics.items():
    print(f"  {k}: {v:.4f}")

SHAP分析の妥当性検証

def validate_shap_explanations(model, X_test, y_test, n_samples=100):
    """SHAP分析の妥当性を検証"""
    import shap
    explainer = shap.TreeExplainer(model)

    # 一貫性チェック: SHAP値の合計 ≈ 予測値 - ベースライン
    errors = []
    for i in range(min(n_samples, len(X_test))):
        sample = X_test.iloc[i:i+1]
        sv = explainer.shap_values(sample)
        if isinstance(sv, list):
            shap_sum = sv[1][0].sum()
            base = explainer.expected_value[1]
        else:
            shap_sum = sv[0].sum()
            base = explainer.expected_value

        pred = model.predict_proba(sample)[0][1]
        # ロジスティック変換を考慮した近似チェック
        error = abs(pred - (base + shap_sum))
        errors.append(error)

    print(f"SHAP一貫性チェック:")
    print(f"  平均誤差: {np.mean(errors):.6f}")
    print(f"  最大誤差: {np.max(errors):.6f}")
    print(f"  判定: {'OK' if np.mean(errors) < 0.01 else 'NG'}")

有用性の評価

施策提案の評価フレームワーク

def evaluate_action_usefulness(actions, ground_truth_actions=None):
    """施策提案の有用性を評価"""
    criteria = {
        "具体性": {
            "score": 0,
            "check": lambda a: len(a.get("action", "")) > 20,
            "description": "施策が具体的で実行可能か",
        },
        "根拠の明確さ": {
            "score": 0,
            "check": lambda a: "trigger" in a or "rationale" in a,
            "description": "なぜその施策を提案するのか根拠があるか",
        },
        "優先度の明示": {
            "score": 0,
            "check": lambda a: "priority" in a,
            "description": "優先度が付いているか",
        },
    }

    for action in actions:
        for criterion_name, criterion in criteria.items():
            if criterion["check"](action):
                criterion["score"] += 1

    total = len(actions) if actions else 1
    results = {k: v["score"] / total for k, v in criteria.items()}
    return results

LLM応答の評価

def evaluate_response_quality(response: str) -> dict:
    """LLM応答の品質を評価"""
    checks = {
        "リスクレベルの明示": any(
            level in response for level in ["HIGH", "MEDIUM", "LOW", "高", "中", "低"]
        ),
        "確率値の含有": any(
            char.isdigit() for char in response
        ),
        "要因の説明": any(
            keyword in response for keyword in ["要因", "理由", "原因", "なぜ"]
        ),
        "施策の提案": any(
            keyword in response for keyword in ["提案", "施策", "対策", "推奨", "アクション"]
        ),
        "適切な長さ": 100 < len(response) < 2000,
    }

    score = sum(checks.values()) / len(checks)
    return {"checks": checks, "overall_score": score}

一貫性の評価

def evaluate_consistency(app, query: str, n_runs: int = 5) -> dict:
    """同一入力に対する出力の一貫性を評価"""
    results = []
    probabilities = []

    for _ in range(n_runs):
        result = app.invoke({"query": query})
        results.append(result.get("response", ""))
        if result.get("probability"):
            probabilities.append(result["probability"])

    consistency = {
        "probability_std": np.std(probabilities) if probabilities else None,
        "risk_level_consistent": len(set(
            r.get("risk_level") for r in results if isinstance(r, dict)
        )) <= 1,
        "response_similarity": None,  # 応答テキストの類似度
    }

    # 予測値の一貫性(MLモデル部分は完全に決定的であるべき)
    if consistency["probability_std"] is not None:
        consistency["prediction_deterministic"] = consistency["probability_std"] < 0.001

    return consistency

品質ダッシュボード

def quality_dashboard(model, app, X_test, y_test):
    """品質の全体像を表示"""
    print("=" * 50)
    print("品質ダッシュボード")
    print("=" * 50)

    # 1. モデル精度
    print("\n[1] モデル精度")
    metrics = evaluate_model_accuracy(model, X_test, y_test)
    for k, v in metrics.items():
        status = "OK" if v and v > 0.7 else "要改善"
        print(f"  {k}: {v:.4f} [{status}]")

    # 2. SHAP妥当性
    print("\n[2] SHAP分析の妥当性")
    validate_shap_explanations(model, X_test, y_test)

    # 3. 応答品質(サンプル)
    print("\n[3] 応答品質")
    sample_result = app.invoke({"query": "顧客 7590-VHVEG の離反リスクは?"})
    quality = evaluate_response_quality(sample_result.get("response", ""))
    for k, v in quality["checks"].items():
        print(f"  {k}: {'OK' if v else 'NG'}")
    print(f"  総合スコア: {quality['overall_score']:.0%}")

まとめ

項目ポイント
正確性AUC-ROC + SHAP一貫性チェック
有用性施策の具体性・根拠・優先度
一貫性同一入力への出力の安定性
自動評価チェック関数で定期的に検証
ダッシュボード品質の全体像を一覧表示

チェックリスト

  • MLモデルの精度を多角的に評価できる
  • SHAP分析の妥当性を検証できる
  • 施策提案の有用性を評価できる
  • LLM応答の品質チェックを実装できる
  • 出力の一貫性を検証できる

次のステップへ

品質評価の仕組みができた。次はHuman-in-the-Loop設計で、人間のフィードバックを取り込む仕組みを構築しよう。

推定読了時間: 30分