Human-in-the-Loop設計
「AIの判断を100%信頼するのは危険だ。特に顧客対応では。」
田中VPoEが過去のインシデントを振り返る。
「以前、別のプロジェクトでAIの判断をそのまま実行して顧客を怒らせたことがある。Human-in-the-Loop、つまり人間のチェックと承認を組み込むことが不可欠だ。」
Human-in-the-Loopとは
HITL(Human-in-the-Loop):
AIの意思決定プロセスに人間の判断を組み込む設計パターン
3つのレベル:
1. Human-on-the-Loop → AIが実行、人間が監視
2. Human-in-the-Loop → AIが提案、人間が承認してから実行
3. Human-over-the-Loop → 人間が方針決定、AIが補助
承認フローの設計
リスクレベル別の承認フロー
| リスクレベル | アクション例 | 承認フロー |
|---|---|---|
| LOW | 分析レポートの閲覧 | 承認不要(自動実行) |
| MEDIUM | クーポンの送付 | マネージャー承認 |
| HIGH | 契約変更の提案 | シニアマネージャー承認 |
| CRITICAL | 大口顧客への特別対応 | 部門長承認 |
from enum import Enum
from typing import Optional
from dataclasses import dataclass
from datetime import datetime
class ApprovalStatus(Enum):
PENDING = "pending"
APPROVED = "approved"
REJECTED = "rejected"
AUTO_APPROVED = "auto_approved"
@dataclass
class ApprovalRequest:
request_id: str
customer_id: str
risk_level: str
proposed_action: str
rationale: str
churn_probability: float
approver: Optional[str] = None
status: ApprovalStatus = ApprovalStatus.PENDING
created_at: datetime = None
decided_at: datetime = None
feedback: Optional[str] = None
def __post_init__(self):
if self.created_at is None:
self.created_at = datetime.now()
承認判定ロジック
def determine_approval_flow(risk_level: str, action_type: str) -> dict:
"""リスクレベルとアクション種類から承認フローを決定"""
rules = {
("LOW", "report"): {
"auto_approve": True,
"approver_role": None,
},
("LOW", "coupon"): {
"auto_approve": True,
"approver_role": None,
},
("MEDIUM", "coupon"): {
"auto_approve": False,
"approver_role": "manager",
},
("MEDIUM", "contract_change"): {
"auto_approve": False,
"approver_role": "senior_manager",
},
("HIGH", "contract_change"): {
"auto_approve": False,
"approver_role": "senior_manager",
},
("HIGH", "special_offer"): {
"auto_approve": False,
"approver_role": "director",
},
}
key = (risk_level, action_type)
return rules.get(key, {"auto_approve": False, "approver_role": "manager"})
フィードバックループの設計
@dataclass
class Feedback:
feedback_id: str
request_id: str
customer_id: str
action_taken: str
outcome: str # "retained", "churned", "unknown"
feedback_text: Optional[str] = None
accuracy_rating: Optional[int] = None # 1-5
usefulness_rating: Optional[int] = None # 1-5
timestamp: datetime = None
def __post_init__(self):
if self.timestamp is None:
self.timestamp = datetime.now()
class FeedbackCollector:
def __init__(self):
self.feedbacks = []
def collect(self, feedback: Feedback):
self.feedbacks.append(feedback)
def analyze(self):
"""フィードバックを分析して改善点を特定"""
if not self.feedbacks:
return {"message": "フィードバックなし"}
import numpy as np
accuracy_scores = [f.accuracy_rating for f in self.feedbacks if f.accuracy_rating]
usefulness_scores = [f.usefulness_rating for f in self.feedbacks if f.usefulness_rating]
outcomes = [f.outcome for f in self.feedbacks]
return {
"total_feedbacks": len(self.feedbacks),
"avg_accuracy": np.mean(accuracy_scores) if accuracy_scores else None,
"avg_usefulness": np.mean(usefulness_scores) if usefulness_scores else None,
"retention_rate": outcomes.count("retained") / len(outcomes) if outcomes else None,
"churn_rate": outcomes.count("churned") / len(outcomes) if outcomes else None,
}
信頼度閾値の設計
class ConfidenceThreshold:
"""モデルの予測確率に応じた信頼度閾値の管理"""
def __init__(self):
self.thresholds = {
"high_confidence": 0.8, # 80%以上で高信頼
"medium_confidence": 0.5, # 50-80%で中信頼
"low_confidence": 0.0, # 50%未満で低信頼
}
def classify_confidence(self, probability: float) -> str:
if probability >= self.thresholds["high_confidence"]:
return "high"
elif probability >= self.thresholds["medium_confidence"]:
return "medium"
else:
return "low"
def get_recommendation(self, confidence: str) -> dict:
recommendations = {
"high": {
"action": "自動実行可能",
"human_review": False,
"escalation": False,
},
"medium": {
"action": "人間のレビューを推奨",
"human_review": True,
"escalation": False,
},
"low": {
"action": "エスカレーション必須",
"human_review": True,
"escalation": True,
},
}
return recommendations[confidence]
LangGraphへの統合
from langgraph.graph import StateGraph, END
# HITL対応のState拡張
class HITLState(ChurnAnalysisState):
approval_required: Optional[bool]
approval_status: Optional[str]
confidence_level: Optional[str]
feedback: Optional[dict]
# 承認チェックノード
def check_approval(state: HITLState) -> dict:
"""承認が必要かチェックする"""
confidence = ConfidenceThreshold()
prob = state.get("churn_probability", 0)
conf_level = confidence.classify_confidence(prob)
rec = confidence.get_recommendation(conf_level)
return {
"confidence_level": conf_level,
"approval_required": rec["human_review"],
}
# 承認待ちノード(LangGraphのinterrupt機能)
def await_approval(state: HITLState) -> dict:
"""人間の承認を待つ(LangGraphのinterrupt)"""
# 実際にはLangGraphのinterrupt機能を使用
# ここではシミュレーション
return {
"approval_status": "pending",
"response": f"承認待ち: 信頼度={state['confidence_level']}, "
f"離反確率={state['churn_probability']:.1%}",
}
HITLの全体フロー
予測結果
↓
[信頼度チェック]
├── 高信頼 → 自動実行 → 結果をフィードバック収集
├── 中信頼 → 人間レビュー → 承認/却下 → フィードバック収集
└── 低信頼 → エスカレーション → 上位承認 → フィードバック収集
↓
[フィードバック分析]
↓
[閾値・ルールの更新]
まとめ
| 項目 | ポイント |
|---|---|
| HITLの3レベル | on-the-loop / in-the-loop / over-the-loop |
| 承認フロー | リスクレベル × アクション種類で決定 |
| フィードバック | outcome + rating の定量的収集 |
| 信頼度閾値 | 高(>0.8)/中(0.5-0.8)/低(<0.5) で対応を分岐 |
| 継続改善 | フィードバック → 閾値・ルールの更新 |
チェックリスト
- HITLの3つのレベルを説明できる
- リスクレベル別の承認フローを設計できる
- フィードバックループの仕組みを実装できる
- 信頼度閾値に基づく分岐を設計できる
- LangGraphにHITL機能を統合できる
次のステップへ
HITL設計ができた。次はコストとレイテンシの最適化に進み、エージェントの実運用を見据えた改善を行おう。
推定読了時間: 30分