mcpField
A
ag2-hitl

ag2-hitl

github.com/ag2aiエージェントワークフロー1268 回インストール13 週間前に更新
リポジトリ

ag2-hitl について

ag2 でエージェントに人の確認を組み込む開発者向けのスキルです。ツール実行中に入力を集める方法と、特定のツール実行前に承認を求める方法を説明します。

主な機能

  1. 01context.input() による入力収集
  2. 02hitl_hook による応答受付
  3. 03ミドルウェアによるツール呼び出しの制御
  4. 04プロンプトと拒否メッセージの設定
  5. 05タイムアウトと拒否への対応

活用シーン

  1. 01リスクのある操作前の確認
  2. 02会話中の認証情報や文脈の収集
  3. 03最終確定前の人によるレビュー

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/ag2ai/ag2-skills --skill ag2-hitl

インストール後、Claude Code で/ag2-hitlを入力して読み込まれたか確認します。

詳細

ヒューマン・イン・ザ・ループ

使用する場面

  • リスクのある操作を行う前に、エージェントが確認を求める必要がある場合。
  • 会話の途中で、エージェントがユーザーから情報を得る必要がある場合(パスワード、API キー、不足しているコンテキストなど)。
  • 特定のツール呼び出しを実行前に人間の承認が必要にしたい場合(取り消し不能、高コスト、機密性の高い操作)。
  • 品質保証 — 下書きを提示し、最終確定前に人間による編集や承認を得る場合。

目的に応じて、2 つの異なる仕組みを使い分けます。

必要なこと使用するもの
ツールが自由形式の質問をして、入力された回答を待つツール内の context.input() + エージェントの hitl_hook
ツール呼び出しの本体が実行される前に、特定の呼び出しを承認/拒否するapproval_required() ツールミドルウェア

パターン 1 — 自由形式の質問には context.input() を使用

ツールは Context.input(message, timeout=...) を使って入力を求めます。エージェントには、その入力を収集する hitl_hook が必要です。

from ag2 import Agent, Context, tool
from ag2.events import HumanInputRequest, HumanMessage

@tool
async def execute_query(context: Context) -> str:
    answer = await context.input(
        "Are you sure you want to run this query? (yes/no)",
        timeout=60.0,
    )
    if answer.strip().lower() != "yes":
        return "Query cancelled."
    return "Query executed successfully."

def hitl_hook(event: HumanInputRequest) -> HumanMessage:
    print(f"Agent asks: {event.content}")
    return HumanMessage(content=input("Your answer: "))

agent = Agent("dba", tools=[execute_query], hitl_hook=hitl_hook)

フックには HumanInputRequest が渡され(プロンプトは event.content に含まれます)、HumanMessage または単なる str を返します(フレームワークが HumanMessage.ensure_message を使って str をラップします)。def と async def のどちらのフックもサポートされています。

構築後にフックを登録することもできます。

agent = Agent("dba", tools=[execute_query])

@agent.hitl_hook
async def async_hitl_hook(event: HumanInputRequest) -> HumanMessage:
    answer = await collect_from_ui(event.content)
    return HumanMessage(content=answer)

デコレーターは、コンストラクターで設定されたフックを上書きします。ただし、すでにフックが設定されていた場合(例:コンストラクター経由)、@agent.hitl_hook を適用すると RuntimeWarning("You already set HITL hook, provided value overrides it")が出力されます。警告を避けるため、フックを設定する場所は 1 か所だけにしてください。

フックでは依存性注入も利用できます。Context、Inject、Variable、Depends はツールと同じように機能します。

context.input() が呼び出されたときにフックが登録されていない場合、フレームワークは HumanInputNotProvidedError を発生させます。

パターン 2 — 特定のツール呼び出しには approval_required() を使用

組み込みの承認ミドルウェアを使って、単一のツールに承認ゲートを設定します。ツール本体の実行前にユーザーに確認が表示され、承認または拒否できます。

from ag2 import Agent, tool
from ag2.config import OpenAIConfig
from ag2.middleware import approval_required

@tool(middleware=[approval_required()])
def delete_account(user_id: str) -> str:
    """Deletes a user account by ID permanently."""
    return f"Account {user_id} deleted."

agent = Agent(
    "support",
    config=OpenAIConfig(model="gpt-4o-mini"),
    tools=[delete_account],
    hitl_hook=lambda event: input(event.content),
)

エージェントが delete_account を呼び出すと、ユーザーには次のように表示されます(デフォルトの allow_always=True の場合)。

Agent wants to call the tool:
`delete_account`, {"user_id": "abc-123"}
Please approve or deny this request.
Y/N/Always?

照合前に回答は小文字に変換されます。y、yes、または 1 はこの呼び出しを承認します。always はこの呼び出しと、同じコンテキスト内での同じツールの以降の呼び出しをすべて承認します(コンテキストごとのバイパスフラグが設定されます)。それ以外はすべて拒否となり、エージェントには denied_message(デフォルトは "User denied the tool call request")が渡され、対応を調整できます。デフォルトの timeout は 30 秒です。allow_always=False を設定すると「Always」オプションがなくなり、プロンプトには Y/N? のみが表示されます。

approval_required() は内部で context.input() を呼び出すため、hitl_hook も必要です。フックがない場合は、実行時エラーが発生します。

カスタムプロンプト
@tool(middleware=[approval_required(
    message="⚠️ Run `{tool_name}` with {tool_arguments}? (y/n)",
    denied_message="Operation blocked by user.",
)])
def send_email(to: str, subject: str, body: str) -> str:
    """Send an email to the given address."""
    ...

{tool_name} と {tool_arguments} が埋め込まれます。

2 つのパターンの併用

実行中に入力を収集し、かつ承認も必要とするツールの場合:

@tool(middleware=[approval_required()])
async def schedule_report(name: str, context: Context) -> str:
    """Schedule a report — asks the user for the cadence, then runs after approval."""
    cadence = await context.input("How often? (daily / weekly / monthly)")
    return f"Scheduled '{name}' on {cadence} cadence."

承認ミドルウェアが最初に実行されます(最も外側)。承認されるとツール本体が実行され、context.input() によって 2 回目の人間とのやり取りが発生します。

詳細情報

  • ソースドキュメント:website/docs/user-guide/context/human_in_the_loop.mdx(context.input、hitl_hook)、website/docs/user-guide/tools/approval_required.mdx(approval_required ミドルウェア)。
  • ツールミドルウェア全般については website/docs/user-guide/tools/tool_middleware.mdx を参照してください。BaseMiddleware.on_human_input() によるエージェント全体の HITL インターセプトについては、ag2-middleware も参照してください。
  • HITL フックはツールと同じように依存性注入をサポートします。../ag2-add-custom-tool/references/dependency_injection.md を参照してください。

よくある落とし穴

  • hitl_hook を指定せずに approval_required() を使う — ミドルウェアは context.input() を呼び出すため、エージェントにはフックが必要です。指定しない場合、HumanInputNotProvidedError が発生します。
  • 拒否された場合の処理を忘れる — context.input() はフックが返した値を返します。「はい」の場合だけ分岐すると、ほかの回答(無回答やデフォルト値を含む)では操作が続行されます。必ず値を検証してください。
  • 非同期 UI で同期の input() を使う — input() はイベントループをブロックします。CLI 以外のアプリでは、非同期フック(async def)と非同期の入力収集機構(WebSocket、メッセージキュー)を使用してください。
  • タイムアウトを設定しない — context.input(prompt) は無期限に待機する可能性があります。本番環境で使う場合は、timeout=60.0(秒)を渡してください。
  • デコレーターのフックがコンストラクターのフックを上書きする — 両方を設定すると、デコレーターが優先され、RuntimeWarning が出力されます。設定場所はどちらか一方にしてください。
  • HumanMessage が会話履歴に自動的に追加されると思い込む — 呼び出し元のツールの戻り値には含まれますが、実行途中で ctx.input() を通じて収集した入力は、独立したユーザーターンとして扱われません。ツールのスコープ内に保持されます。

コメント