はじめに:なぜHuman-in-the-LoopがAIエージェントに不可欠なのか
AIエージェントが自律的に意思決定し、外部システムへの書き込みや金銭的トランザクションを実行する時代が本格化している。しかし「完全自動化」の裏には見過ごせないリスクがある。誤ったAPI呼び出し、不適切なメッセージ送信、予期せぬコスト発生——これらは理論上の話ではなく、すでに実運用で報告されている課題だ。
2026年現在、Human-in-the-Loop(HITL)は単なる「安全装置」から「エージェント設計の中心的なアーキテクチャパターン」へと進化した。本記事では、LangGraph・CrewAI・OpenAI Agents SDKなど主要フレームワークのHITL実装パターンを、即実行可能なコード例とともに解説する。
この記事でわかること
- リスク階層別の承認ゲート設計(Tier 1〜3)
- LangGraphのinterruptとcheckpointを使った実装
- 承認疲れを防ぐUX設計パターン
- マルチエージェントでの監督評価パターン
1. 5分で動く:LangGraphで最小HITLを実装する
理論より実装。まずはLangGraphを使った最小限のHITLワークフローを動かしてみよう。
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
from langgraph.types import interrupt
from typing import TypedDict, Literal
class AgentState(TypedDict):
messages: list
action_approved: bool
final_output: str
def agent_decide(state: AgentState) -> AgentState:
"""エージェントが次のアクションを決定する"""
return {"messages": state["messages"] + ["決定: メール送信を実行"]}
def human_approval(state: AgentState) -> AgentState:
"""人間の承認を待つinterruptポイント"""
decision = state["messages"][-1]
# ここでinterrupt → 人間が判断
approved = interrupt({
"question": f"このアクションを承認しますか?n{decision}",
"options": ["approve", "reject", "edit"]
})
if approved == "approve":
return {"action_approved": True}
elif approved == "edit":
return {"action_approved": True, "messages": state["messages"] + ["編集後の内容で実行"]}
else:
return {"action_approved": False}
def execute_action(state: AgentState) -> AgentState:
if state["action_approved"]:
return {"final_output": "✅ メール送信完了"}
return {"final_output": "❌ 承認されずキャンセル"}
builder = StateGraph(AgentState)
builder.add_node("decide", agent_decide)
builder.add_node("approval", human_approval)
builder.add_node("execute", execute_action)
builder.add_edge(START, "decide")
builder.add_edge("decide", "approval")
builder.add_edge("approval", "execute")
builder.add_edge("execute", END)
# checkpointを有効化してinterruptを動作させる
checkpointer = MemorySaver()
graph = builder.compile(checkpointer=checkpointer)
# 実行
config = {"configurable": {"thread_id": "demo-1"}}
for event in graph.stream({"messages": [], "action_approved": False, "final_output": ""}, config):
print(event)
# interruptで停止 → ここで人間が判断
この例では、interrupt() が呼ばれた時点でグラフの実行が一時停止する。人間が Command(resume="approve") または Command(resume="reject") を送るまで、エージェントは次のノードに進まない。外部APIやメール送信の前に確実な承認ゲートを置く基本パターンだ。
2. HITL設計の基本:リスク階層別アプローチ
全てのアクションに承認を求める「マイクロ承認」は、エージェントの価値を殺す最大の罠だ。人間がボトルネックになり、自動化の意味がなくなる。2026年の実践的なHITL設計では、リスクに応じた3層のゲートを使い分ける。
Tier 1: 低リスク(承認不要)
読み取り専用操作、下書き生成、情報検索など。エージェントは自由に実行できる。具体例:データベース検索、ドキュメント読込、検索API呼び出し、Slack下書き作成。
Tier 2: 可逆的・非同期承認
状態変更を伴うがロールバック可能な操作。エージェントはアクションをキューに入れて他の作業を継続し、人間は非同期で承認/却下する。具体例:ドラフトPR作成、カレンダー招待送信、ナレッジベース更新。
Tier 3: 不可逆・同期ハードゲート
元に戻せない操作や高コスト操作。エージェントの実行を完全に停止し、人間がパラメータを編集できるUIを提供する。具体例:本番デプロイ、外部へのメール/SMS送信、決済実行、データベース削除。
# リスク分類ミドルウェアの実装例
from dataclasses import dataclass
from enum import Enum
class RiskTier(Enum):
LOW = 1 # 承認不要
REVERSIBLE = 2 # 非同期承認
IRREVERSIBLE = 3 # 同期ハードゲート
@dataclass
class ToolRisk:
tool_name: str
parameters: dict
RISK_POLICY = {
"read_file": RiskTier.LOW,
"search_docs": RiskTier.LOW,
"create_draft_pr": RiskTier.REVERSIBLE,
"send_calendar_invite": RiskTier.REVERSIBLE,
"deploy_production": RiskTier.IRREVERSIBLE,
"send_external_email": RiskTier.IRREVERSIBLE,
"delete_database_record": RiskTier.IRREVERSIBLE,
"execute_payment": RiskTier.IRREVERSIBLE,
}
def classify_risk(tool_name: str, params: dict) -> RiskTier:
"""一元化されたリスク分類器"""
base_risk = RISK_POLICY.get(tool_name, RiskTier.REVERSIBLE)
# パラメータから追加リスクを検出
if params.get("amount", 0) > 10000:
return RiskTier.IRREVERSIBLE
if params.get("recipients_count", 1) > 100:
return RiskTier.IRREVERSIBLE
return base_risk
このミドルウェアパターンの利点は、リスクポリシーを一元管理できることだ。新しいツールが増えても RISK_POLICY 辞書を更新するだけでよく、各エージェントノードにif文を散りばめる必要がない。
3. クラウド本番環境でのHITL:スケールする承認UX
ローカルのCLIで input() を呼ぶだけのHITLは、チームで使う本番エージェントでは通用しない。ここでは、Slackを承認UIとして使う実用的なパターンを紹介する。
from slack_sdk import WebClient
from langgraph.types import interrupt, Command
import time, json
slack = WebClient(token="xoxb-...")
def slack_approval_node(state: AgentState):
"""Slack経由で人間の承認を待つノード"""
# 承認リクエストをSlackに送信
blocks = [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"*🤖 エージェントが次のアクションの承認を求めています*n"
f"> {state['proposed_action']}nn"
f"パラメータ: `{json.dumps(state['action_params'], ensure_ascii=False)}`"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {"type": "plain_text", "text": "✅ 承認"},
"style": "primary",
"value": "approve",
"action_id": "agent_approve"
},
{
"type": "button",
"text": {"type": "plain_text", "text": "❌ 却下"},
"style": "danger",
"value": "reject",
"action_id": "agent_reject"
},
{
"type": "button",
"text": {"type": "plain_text", "text": "✏️ 編集"},
"value": "edit",
"action_id": "agent_edit"
}
]
}
]
msg = slack.chat_postMessage(
channel="#agent-approvals",
text="エージェント承認依頼",
blocks=blocks
)
# interruptで待機
decision = interrupt({
"slack_message_ts": msg["ts"],
"channel": "#agent-approvals"
})
# 承認後:冪等性チェック
if state.get("already_executed"):
return {"result": "duplicate_prevented"}
return {"approved": decision == "approve"}
# 再開時
# graph.invoke(Command(resume={"decision": "approve"}), config)
重要なのは、承認UIが単なるYes/Noだけでなく「編集」オプションを提供している点だ。パラメータの微調整だけで済む場合、人間が全文を書き直す必要はない。この「非バイナリ承認」が、実運用でのHITL採用率を大きく左右する。
4. マルチエージェントHITL:監督評価パターン
複数のAIエージェントが協調するシステムでは、人間一人が全ての承認を行うのは現実的ではない。ここで登場するのが「監督エージェント(Supervisor Agent)」パターンだ。
監督エージェントは以下の役割を担う。
- ワーカーエージェントの出力を品質基準と照合
- リスクポリシーに基づく自動承認/却下
- 閾値以下の確信度のケースのみ人間にエスカレーション
- 人間のフィードバックをポリシーに反映
たとえばCrewAIでは、human_input=True フラグで特定タスクにHITLを導入できる。
from crewai import Agent, Task, Crew, Process
reviewer = Agent(
role="品質監督エージェント",
goal="ワーカー出力をポリシーに照らして評価し、必要時のみ人間にエスカレーションする",
backstory="QA専門。リスク評価と承認判断を行う。"
)
writer = Agent(
role="コンテンツ作成エージェント",
goal="与えられたトピックの記事を作成する",
backstory="経験豊富なテクニカルライター。"
)
draft_task = Task(
description="AIエージェントのHITL設計について3000字の記事を作成",
expected_output="Markdown形式の記事本文",
agent=writer
)
review_task = Task(
description="ドラフトをレビューし、品質が不十分な場合のみ人間にエスカレーション",
expected_output="承認/却下/修正指示のいずれか",
agent=reviewer,
human_input=True # 監督が判断できない場合のみ人間介入
)
crew = Crew(
agents=[writer, reviewer],
tasks=[draft_task, review_task],
process=Process.sequential,
verbose=True
)
result = crew.kickoff()
このパターンの威力は、95%のケースを監督エージェントが自動処理し、本当に判断が必要な5%だけ人間に回せる点にある。完全な人間監視を維持しながら、実質的な自動化率を劇的に高められる。
5. HITL実装のアンチパターンと対策
アンチパターン1: 全アクション承認
全てのツール呼び出しに interrupt() を入れる実装。1時間で50回の承認ボタンクリックが必要になり、人間が「考えるより押す」状態に陥る。対策:Tier 1の低リスク操作は承認なしで自動実行させる。
アンチパターン2: 冪等性の欠如
承認後のタイムアウトや再送で、同じアクションが2回実行される。対策:アクション実行前に already_executed フラグをチェックし、冪等性キー(例: idempotency_key)を導入する。
アンチパターン3: コンテキスト不足の承認リクエスト
「メールを送信します。承認しますか?」だけのUI。人間は何を送るのか、誰に送るのかを知らずに判断を迫られる。対策:承認リクエストに完全なコンテキスト(推論トレース、入力データ、影響範囲)を含める。
アンチパターン4: 単一障害点としてのHITL
人間が休暇中や就寝中にエージェントが完全停止する設計。対策:Tier 2の操作にはタイムアウト付き自動承認/却下ルールを設定し、緊急時は別の承認者が対応できるロールベースの承認フローを実装する。
6. 主要フレームワークのHITL対応比較
| フレームワーク | HITL方式 | 特徴 | 適したユースケース |
|---|---|---|---|
| LangGraph | interrupt() + Command | checkpointで状態永続化、再開が容易 | 複雑な分岐を持つエンタープライズワークフロー |
| CrewAI | human_input=True | タスク単位の宣言的指定、シンプル | 役割分担が明確なマルチエージェント |
| OpenAI Agents SDK | Guardrail + handoff | ガードレール関数でポリシー評価 | OpenAIエコシステム中心の構成 |
| AutoGen | UserProxyAgent | 人間をエージェントとして会話に参加 | 対話型の協調ワークフロー |
| Anthropic (Claude) | tool_use + stop_reason | ツール呼び出しのたびに制御を返す | シングルエージェントの精密制御 |
選択の指針:シンプルな単一エージェントならOpenAI Agents SDKやAnthropicのネイティブHITL、複数エージェントの協調ならCrewAIやAutoGen、エンタープライズの複雑なワークフローにはLangGraphが適している。スタートアップやPoC段階ではCrewAIのシンプルさが強みになる。
7. 2026年のHITLトレンド:承認疲れから戦略的介入へ
2026年に入り、HITLの考え方は大きく変化している。アマゾンやグーグル、マイクロソフトなど大手テック企業のAIエージェントチームは、「承認疲れ(approval fatigue)」を深刻な問題として認識し始めた。1日に何十回も承認ボタンを押すうちに、人間の判断精度は著しく低下する。
この課題への回答として、3つの新しいパターンが登場している。
1. スコープド・パーミッション(Scoped Permission):エージェントに事前に予算や権限範囲を設定し、その範囲内では自律的に動作させる。人間の承認は範囲を超えた操作にのみ必要。
2. サンドボックス実行+リアルタイム監視:DeepMindが提唱するパターン。エージェントは常にサンドボックス内で実行され、危険な操作が検出された場合のみ強制停止される。日常的な承認は不要。
3. 適応的フィードバックループ:人間の承認/却下パターンを学習し、類似ケースでは自律判断する。たとえば「同じ種類のメール下書きで10回連続承認」なら、11回目以降は自動承認する。
8. 実践ワークフロー:HITLを既存エージェントに導入する6ステップ
- ツール棚卸し:エージェントが呼び出す全ツールをリストアップ
- リスク分類:各ツールをTier 1〜3に分類(上記のリスク分類ミドルウェアを使用)
- 承認UI選定:Slack/メール/カスタムダッシュボードから選択
- チェックポイント配置:Tier 2とTier 3の直前にinterruptを挿入
- 冪等性確保:全アクションにidempotency_keyを追加
- 監視と調整:1週間の運用データを基に、Tier分類を最適化
この手順を踏むことで、過剰な承認要求による生産性低下を避けつつ、確実な安全制御を実現できる。
9. 関連記事・次に読む
- AIエージェントのセキュアサンドボックス設計|gVisor/Firecracker — HITLと組み合わせる実行環境の安全設計
- AIエージェントガードレール比較|NeMo/Guardrails-AI — HITLの前段として使える自動ガードレール
- マルチエージェントオーケストレーション比較 — CrewAI/AutoGen/LangGraphの選び方
この記事を読んで導入イメージが固まってきた方へ
UravationではAIエージェント導入の研修・コンサルを行っています。
参考情報と一次ソース
本記事の内容は以下の一次情報に基づいている。HITLの実装を深掘りする際の参考にしてほしい。
- LangGraph公式: Human-in-the-Loop — interruptとCommandの詳細な実装リファレンス
- CrewAI公式: Human Input — human_inputフラグの使い方と制約
- Anthropic公式: Tool Use — Claudeにおけるツール呼び出しとHITLパターン
- OpenAI Agents SDK — Guardrailとhandoffの実装例
これらのドキュメントを参照しながら実装することで、より堅牢なHITLシステムを構築できる。また、実際の運用では、必ず本番環境と同等のステージング環境で承認フローをテストし、想定外の停止やタイムアウトがないことを確認してほしい。
まとめ:HITLは戦略的かつ段階的に導入せよ
Human-in-the-Loopで最も重要なのは「何を自動化し、何を人間に残すか」の設計判断そのものだ。完璧なリスク分類を最初から目指すのではなく、まずは最もリスクの高い操作(外部送信、決済、削除)に絞ってTier 3のハードゲートを導入し、1〜2週間運用したデータを見ながらTier 1とTier 2の境界線を調整していくのが実践的だ。
2026年のエージェント開発では、HITLはもはやオプションではない。適切に設計されたHITLは、エージェントの信頼性を高め、チームの安心感を生み、結果として自律化率の向上につながる。本記事のコード例を足がかりに、自社のエージェントにHITLを組み込んでほしい。
