airflow-hitl について
Airflow DAG の作成者向けに、人の応答を待つ HITL タスクの実装方法を案内します。承認、選択、分岐、フォームに対応し、Airflow Registry で最新のパラメーターを確認する手順も説明します。
主な機能
- 01承認・選択・分岐・フォーム用オペレーター
- 02待機中のワーカースロット解放
- 03Airflow UI または REST API からの応答
- 04Registry での最新パラメーター確認
- 05タイムアウト・コールバック・通知の設定
活用シーン
- 01DAG への人による承認ゲートの追加
- 02人の選択に応じたタスク分岐
- 03外部連携経由での応答送信
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ astronomer/ agents --skill airflow-hitlインストール後、Claude Code で/airflow-hitlを入力して読み込まれたか確認します。
詳細
Airflow Human-in-the-Loop Operators
Airflow UI または REST API を介して人間から応答があるまで、DAG を一時停止します。HITL オペレーターは遅延可能なため、待機中にワーカースロットを解放します。
Airflow 3.1+ が必要です(
af config version)。UI の場所:Browse → Required Actions。タスクインスタンスページの Required Actions タブから応答します。
相互参照:AI/LLM タスクデコレーターについては
migrating-ai-sdk-to-common-ai、以下で使用するレジストリおよび API の確認コマンドについてはairflowを参照してください。
ステップ 1 — 必要な機能を選ぶ
| 機能 | クラス(ステップ 2 で確認) |
|---|---|
| 承認または却下し、却下時は後続タスクをスキップする | ApprovalOperator |
| N 個の選択肢を提示し、どれが選ばれたかを返す | HITLOperator |
| 選択に基づいて、1 つ以上の後続タスクに分岐する | HITLBranchOperator |
| フォームを収集する(承認/選択ステップなし) | HITLEntryOperator |
| HITL トリガーを直接使用する(高度な用途/カスタムオペレーター) | HITLTrigger |
クラス名をハードコードするのはここだけです。プロバイダーはリリースごとにパラメーターを追加、改名、削除するため、記憶を頼りにパラメーター一覧をコピーしないでください。コードを書く前に、現在のシグネチャを取得してください。
ステップ 2 — Airflow Registry から現在のシグネチャを確認する
HITL コードを書く前に、以下を実行して最新のクラス一覧とコンストラクターのパラメーターを確認してください(af registry の全リファレンスについては airflow スキルを参照してください)。
# Every HITL-related module in the standard provider
af registry modules standard \
| jq '.modules[] | select(.import_path | test("\\.hitl\\.")) | {name, type, import_path, short_description, docs_url}'
# Constructor signatures: name, type, default, required, description
af registry parameters standard \
| jq '.classes | to_entries[] | select(.key | test("\\.hitl\\.")) | {fqn: .key, parameters: .value.parameters}'
# Pin to the exact installed provider version
af config providers \
| jq '.providers[] | select(.package_name == "apache-airflow-providers-standard") | .version'
# then: af registry parameters standard --version <VERSION>
レジストリにこのスキルで触れていないパラメーターが表示された場合は、レジストリの情報を優先してください。ステップ 1 にないクラスがレジストリに表示された場合は、追加されたものとして扱ってください。上の選択表が古くなっている可能性があります。
ステップ 3 — 基本例(承認ゲート)
あらゆる HITL タスクの出発点です。ステップ 2 に従ってクラス名とパラメーターを入れ替えて調整してください。
from airflow.providers.standard.operators.hitl import ApprovalOperator
from airflow.sdk import dag, task, chain, Param
from pendulum import datetime
@dag(start_date=datetime(2025, 1, 1), schedule="@daily")
def approval_example():
@task
def prepare():
return "Review quarterly report"
approval = ApprovalOperator(
task_id="approve_report",
subject="Report Approval",
body="{{ ti.xcom_pull(task_ids='prepare') }}",
defaults="Approve", # Auto-selected on timeout
params={"comments": Param("", type="string")},
)
@task
def after_approval(result):
print(f"Decision: {result['chosen_options']}")
chain(prepare(), approval)
after_approval(approval.output)
approval_example()
ステップ 1 の他のクラスも基本的な形は同じです(task_id、subject にクラス固有のパラメーターを加えます)。ステップ 2 で各コンストラクターを確認してください。たとえば、HITLBranchOperator では、すべての選択肢が後続タスク ID と直接一致するか、レジストリに表示されるマッピング用パラメーターを介して解決される必要があります。
ステップ 4 — 動作上の仕様(バージョンを問わず安定)
タイムアウト
defaultsを設定した場合:タイムアウト時にタスクは成功し、デフォルトの選択肢が選択されます。defaultsを設定しない場合:タイムアウト時にタスクは失敗します。
body 内の Markdown と Jinja
body は Markdown に対応し、Jinja でテンプレート化できます。XCom コンテキストを直接レンダリングします。
body = """**Total Budget:** {{ ti.xcom_pull(task_ids='get_budget') }}
| Category | Amount |
|----------|--------|
| Marketing | $1M |
"""
コールバック
すべての HITL オペレーターは、標準的な Airflow のコールバック引数(on_success_callback、on_failure_callback など)を受け付けます。
Notifier
HITL オペレーターは notifiers リストを受け付けます。Notifier の notify(context) メソッド内で、HITLOperator.generate_link_to_ui_from_context(context, base_url=...) を使って保留中のタスクへのリンクを作成してください。
応答できるユーザーの制限
パラメーター名と受け付ける識別子の形式は、アクティブな認証マネージャーによって異なります。ハードコードしないでください。どの認証マネージャーがアクティブか、また現在のプロバイダーがどのキーワード引数を公開しているかを確認してください。
af config show | jq '.auth_manager // .core.auth_manager'
次に、ステップ 2 で現在のキーワード引数を確認してください(執筆時点では assigned_users で、アクティブな認証マネージャーに応じた形式の識別子を受け付けます。Astro は Astro のユーザー ID、FabAuthManager はメールアドレス、SimpleAuthManager はユーザー名を使用します)。
ステップ 5 — 外部連携から応答する
Slack ボット、カスタムアプリ、スクリプト向けです。パスをハードコードせず、最新のエンドポイントを確認してください。
af api ls --filter hitl # live endpoint list
af api spec \
| jq '.paths | to_entries[] | select(.key | test("hitl"))' # request/response schemas
PATCH で応答するパターンは安定していますが、正確なパスは確認が必要です。一般的な形式は次のとおりです。
import os, requests
HOST = os.environ["AIRFLOW_HOST"]
TOKEN = os.environ["AIRFLOW_API_TOKEN"]
HEADERS = {"Authorization": f"Bearer {TOKEN}"}
# List pending — use the path from `af api ls --filter hitl`
requests.get(f"{HOST}/<path>", headers=HEADERS, params={"state": "pending"})
# Respond — same discovered path family, PATCH
requests.patch(
f"{HOST}/<path>/{dag_id}/{run_id}/{task_id}",
headers=HEADERS,
json={"chosen_options": ["Approve"], "params_input": {"comments": "ok"}},
)
ステップ 6 — 安全性チェック
- Airflow のバージョンが 3.1 以上であること(
af config version)。 - コンストラクターの kwargs がステップ 2 の最新のレジストリ出力と一致していること —
respondentsとassigned_usersのような不整合がないこと。 - 分岐の場合、すべての選択肢が後続タスクの ID に解決されること(直接、またはステップ 2 のマッピング用 kwargs を介して)。
-
defaultsのすべての値がoptionsにも含まれていること。 -
execution_timeoutが設定されていること。タイムアウト時に失敗ではなく成功させる場合は、defaultsが設定されていること。 - 外部の応答者がワークフローに含まれる場合、API トークンが設定されていること。
参考資料
上流のドキュメント URL はレジストリによってモジュールごとに提示されます — ハードコードしないでください。
af registry modules standard \
| jq '.modules[] | select(.import_path | test("\\.hitl\\.")) | {name, docs_url}'
関連スキル
- airflow —
af registry、af api、af configコマンドのリファレンス。 - migrating-ai-sdk-to-common-ai — AI/LLM タスクのデコレーターと GenAI のパターン(common-ai プロバイダー)。
- authoring-dags — DAG 作成全般のベストプラクティス。
- testing-dags — テスト → デバッグ → 修正を繰り返すサイクル。