【調査レポ8】Google Antigravity SDKでAIの暴走を防ぐ!ライフサイクルフック(Hooks)によるセキュリティ監査とログ記録を解説

Antigravity SDK入門 AI

前回の調査レポ7では、skills_paths と SKILL.md を活用してAIエージェントに専門知識や指示書を動的ロードさせるモジュール化テクニックを検証しました。

今回は、Google Antigravity SDKの高度な運用機能である「ライフサイクルフック(Hooks)」について調べた内容をお届けします!

AIエージェントにWeb検索やファイル操作、コマンド実行などの「道具(ツール)」を持たせる際、プロンプト(指示文)だけで「危険な操作をしないでください」と伝えるのは限界があります。誤った指示や悪意ある入力によってAIが意図しない処理を実行してしまうリスクを防ぐためには、プログラムレベルでの検証・割り込み処理が必要です。Google Antigravity SDKのフック機能を活用することで、ツール実行の前後にカスタム処理を挿入し、安全なセキュリティ検疫やロギングを実現できます。

ライフサイクルフック(Hooks)の役割とセキュリティ効果

ライフサイクルフックとは、AIエージェントが思考・動作する特定のタイミング(起動時、ツール実行の直前・直後、エラー発生時など)に、開発者が定義したPython関数を自動的に割り込ませる仕組みです。主なフック機能として「Pre-hook(実行前フック)」と「Post-hook(実行後フック)」が用意されています。

  • ツール実行前フック(Pre-hook)による入力検疫
    AIがツールを呼び出そうとした直前に介入し、渡された引数をチェックします。不正なキーワードが検知された場合、types.HookResult(allow=False) を返却することでツールの実行を安全にブロック(中断)します。
  • ツール実行後フック(Post-hook)による監査ロギングと事後処理
    ツール実行が完了した直後に自動的に呼び出され、実行日時やツールの実際の実行結果(data.result)を受け取ってログ記録や外部通知、データ加工などの事後処理を行います。万が一のトラブル時にもトレーサビリティを確実に確保できます。

検証用Pythonサンプルコード(hooks_demo.py)

ツール実行前の検疫を行う Pre-hook と、実行完了後の結果を自動受領・ロギングする Post-hook の両方を組み込んだサンプルプログラムを作成しました。実際の手元環境でシステムコマンドを発行して動作を確認できます。

import os
import sys
import asyncio
import datetime
import subprocess

# 1. 安全対策:python-dotenv ライブラリの検出と環境変数の読み込み
try:
    from dotenv import load_dotenv
    load_dotenv()
except ImportError:
    print("注意: python-dotenv がインストールされていません。環境変数から直接APIキーを読み込みます。")

# 2. Google Antigravity SDK のインポート
try:
    from google.antigravity import Agent, LocalAgentConfig, types
    from google.antigravity.hooks import pre_tool_call_decide, post_tool_call
except ImportError:
    print("エラー: google.antigravity パッケージが見つかりません。pip install google-antigravity で導入してください。", file=sys.stderr)
    sys.exit(1)

# 3. ツール(実際のシステム操作ツール)の定義
def execute_system_command(command: str) -> str:
    """指定されたシステムコマンドを実際に実行し、OSからの出力結果を返す専用ツール。

    Args:
        command (str): 実行するコマンド文字列 (例: 'ls -l', 'date')

    Returns:
        str: 実際のコマンド出力文字列
    """
    try:
        # 安全のために単発の安全なコマンドのみを subprocess で実行
        result = subprocess.run([command], capture_output=True, text=True, timeout=5)
        output = result.stdout.strip() or result.stderr.strip()
        return f"【コマンド '{command}' 実行結果】: {output}"
    except Exception as e:
        return f"【コマンド '{command}' 実行エラー】: {str(e)}"

# 4. ライフサイクルフック(Pre-hook / Post-hook)の定義

# 【Pre-hook】ツール実行前フック:ToolCall オブジェクトから入力引数(data.args)を検疫
@pre_tool_call_decide
async def pre_tool_execution_hook(data):
    """ツール実行前に割り込み、引数(data.args)をチェックして危険なコマンドをブロックするフック。"""
    print(f"\n[Pre-hook] ツール '{data.name}' の実行前検疫を行っています... (入力引数: {data.args})")
    
    command = str(data.args.get("command", ""))
    # 不正なキーワードのチェック
    dangerous_keywords = ["danger_cmd", "forbidden_op", "rm", "format"]
    for kw in dangerous_keywords:
        if kw in command.lower():
            print(f"[Pre-hook Alert] 不正なキーワード '{kw}' が検知されました!実行をブロックします。")
            return types.HookResult(allow=False, message="不正なコマンドのためブロックされました。")
            
    print(f"[Pre-hook] 検疫クリア: 安全なコマンドです。")
    return types.HookResult(allow=True)

# 【Post-hook】ツール実行後フック:ToolResult オブジェクトから実行結果(data.result)を取得
@post_tool_call
async def post_tool_execution_hook(data):
    """ツール実行完了直後に割り込み、実行結果(data.result)を受け取って事後処理を行うフック。"""
    now_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    
    # ToolResult オブジェクトの各プロパティを参照
    tool_name = data.name
    call_id = data.id
    tool_result = data.result

    print(f"\n[Post-hook 事後処理発火]")
    print(f" ├ 実行時刻       : {now_str}")
    print(f" ├ 対象ツール名   : {tool_name} (ID: {call_id})")
    print(f" └ 受け取った結果 : {tool_result}")

# 5. メイン処理
async def main():
    api_key = os.environ.get("GEMINI_API_KEY")
    if not api_key:
        print("エラー: GEMINI_API_KEY が設定されていません。.env ファイルを確認してください。", file=sys.stderr)
        return

    print("--- ライフサイクルフック動作検証デモ(実コマンド実行版)を開始します ---\n")

    config = LocalAgentConfig(
        system_instructions=(
            "あなたはシステム管理AIアシスタントです。"
            "ユーザーからの依頼に対しては、必ず execute_system_command ツールのみを1回呼び出して実行し、"
            "そのツールから返された実際のコマンド実行結果をそのまま提示して回答を作成してください。"
        ),
        tools=[execute_system_command],
        model="gemini-3.1-flash-lite",
        hooks=[pre_tool_execution_hook, post_tool_execution_hook]
    )

    user_query = "execute_system_command ツールを使用して 'date' コマンドを実行し、実際の現在時刻結果を報告してください。"
    print(f"ユーザーの質問: {user_query}\n")

    async with Agent(config) as agent:
        response = await agent.chat(user_query)
        result_text = await response.text()
        print(f"\n--- [AIエージェントの最終回答] ---\n{result_text}\n")

if __name__ == "__main__":
    asyncio.run(main())

プログラムコードの詳細解説

今回作成したフック検証プログラムの仕組みについて解説します。

  • 1. インポートと安全対策(dotenv)
    from google.antigravity import Agent, LocalAgentConfig, types およびフック用デコレータ from google.antigravity.hooks import pre_tool_call_decide, post_tool_call をインポートします。dotenv の load_dotenv() を `try-except` で囲むことで、ライブラリ未検出時でも安全に環境変数を参照できるよう配慮しています。
  • 2. 【重要】デコレータ(@記号)の仕組みとSDKで事前定義されている主なフック一覧
    関数の直前に記述している @pre_tool_call_decide や @post_tool_call は、Pythonの「デコレータ」と呼ばれる特殊な記法です。関数のコードを直接書き換えることなく、前後に共通処理を付け足す仕組みであり、これらは google.antigravity.hooks モジュール内で事前定義されています。
    インポートして関数に付けるだけで、SDKがイベントのタイミングを自動判定して割り込み処理を実行します。SDKで事前定義されている主なフックデコレータは以下の通りです。
    • @pre_tool_call_decide:ツール実行直前に割り込み、実行の許可(allow=True)または拒否(allow=False)を判定するデコレータ
    • @post_tool_call:ツール実行完了直後に割り込み、実行結果を受け取ってデータの加工・外部通知・監査ログ保存などの事後処理を実行するデコレータ
    • @on_tool_error:ツール実行中にエラーが発生した際に自動的に割り込み処理を行うデコレータ
    • @pre_turn / @post_turn:AIとの対話(1ターン)の開始直前や終了直後に処理を挿入するデコレータ
    • @on_session_start / @on_session_end:会話セッションの開始時や終了時に自動動作するデコレータ
  • 3. ツール定義とDocstring・型ヒントの仕様
    AIがツールを正しく選択・引数生成できるように、関数宣言時に型ヒント(command: str)と詳細なドキュメント文字列(Docstring)を記述しています。関数の説明文(Docstring)がAIへの取扱説明書として読み取られ、型ヒントが入力ルールとして送信されるというSDK独自の重要な仕様を意識することが大切です。また本コードでは subprocess を使用して実際のシステムコマンド結果を取得しています。
  • 4. Pre-hook(実行前フック)の ToolCall オブジェクトと判定型
    @pre_tool_call_decide デコレータを非同期関数(async def)に付与して定義します。引数 data(ToolCall オブジェクト)の data.args から command = str(data.args.get("command", "")) という記述で安全にコマンド文字列を取り出します。キーが存在しない場合の KeyError を防ぐデフォルト値指定("")と、型エラーを防ぐ str() による型変換を行うことで、プログラムを停止させずに検疫チェックを行う配慮です。ブラックリストが含まれていないか走査し、実行を許可する場合は types.HookResult(allow=True) を返し、不正検知時は types.HookResult(allow=False, message=...) を返却することで、SDKはツールの呼び出しを自動キャンセルして安全にブロック処理を行います。
  • 5. Post-hook(実行後フック)の ToolResult オブジェクトと事後処理
    @post_tool_call デコレータを非同期関数に付与して定義します。ツール実行完了直後に自動呼び出しされ、引数 data(types.ToolResult オブジェクト)から data.name(ツール名)、data.id(呼び出しID)、data.result(ツールの実際の戻り値)を直接受け取ります。Pre-hookが入力を検疫するのに対し、Post-hookは出力結果を受け取り、返却データのサニタイズ(機密情報のマスキング)、Slack/Discord等への外部通知発火、メトリクス送信など任意の事後処理(Post-processing)を自由に行う設計となっています。
  • 6. LocalAgentConfig の hooks パラメータへの登録
    LocalAgentConfig の hooks=[pre_tool_execution_hook, post_tool_execution_hook] というリスト形式で作成したフック関数を指定します。これにより、ツール呼び出しの「直前」と「直後」の両方に割り込み処理が組み込まれます。

プログラムの実行手順と動作確認

実際に手元の環境でプログラムを動かす手順と、検疫および監査ログの確認ステップです。
仮想環境等の詳細については過去の環境構築編をご参照ください。

手順1. パッケージ準備とAPIキー設定

# 仮想環境の有効化
source .venv/bin/activate

# パッケージのインストール
pip install google-antigravity python-dotenv

# APIキーの設定 (.env ファイルを作成)
echo 'GEMINI_API_KEY="YOUR_GEMINI_API_KEY_HERE"' > .env

手順2. 正常コマンド実行の検証

python3 hooks_demo.py

実行時の出力ログ例:

--- ライフサイクルフック動作検証デモ(実コマンド実行版)を開始します ---

ユーザーの質問: execute_system_command ツールを使用して 'date' コマンドを実行し、実際の現在時刻結果を報告してください。


[Pre-hook] ツール 'execute_system_command' の実行前検疫を行っています... (入力引数: {'command': 'date'})
[Pre-hook] 検疫クリア: 安全なコマンドです。

[Post-hook 事後処理発火]
 ├ 実行時刻       : 2026-08-25 07:42:57
 ├ 対象ツール名   : execute_system_command (ID: tool_call_0)
 └ 受け取った結果 : 【コマンド 'date' 実行結果】: 2026年 8月25日 火曜日 07時42分57秒 JST

--- [AIエージェントの最終回答] ---
現在時刻を `date` コマンドで確認しました。

```
2026年 8月25日 火曜日 07時42分57秒 JST
```

Pre-hook による事前検疫(data.args)をクリアした後にツールが実行され、さらに実行完了直後に Post-hook によってツールの実際の実行結果(data.result)とタイムスタンプが取得・ログ出力されていることが確認できます!

開発上の注意点

フック機能を本番運用する際、気をつけるべき「2つの注意点」をまとめました。

① フック関数内での同期ブロッキングによる遅延の注意点

フック関数内で重い外部ネットワーク通信や時間のかかるファイル処理を同期的に実行してしまうと、AIエージェント全体のレスポンス速度が著しく低下します。Post-hook でログをファイルやDBに外部保存する際は、できる限り処理を軽量に保ち、非同期処理や高速なバッファリングを活用することを推奨します。

② 例外処理(try-except)の記述漏れリスク

フック関数内部で未捕捉の例外(KeyError や AttributeError など)が発生した場合、フック処理が中断されるだけでなくエージェント全体のプログラムが停止してしまう可能性があります。特に Post-hook 内のログ整形時などでエラーが発生してもエージェントの動作を止めないよう、必ず `try-except` ブロックを配置する設計を推奨します。

まとめ

今回は、Google Antigravity SDKの「ライフサイクルフック(Pre-hook / Post-hook)」機能を使い、ツール実行前のセキュリティ検疫と実行後の監査ロギングを行う実践プログラムを検証しました。

プロンプトでの指示だけに頼るのではなく、Pre-hook と Post-hook を組み合わせたコードレベルのガードレールを設けることで、より安全で信頼性の高いAIエージェントシステムを構築できます。次回は、バックグラウンドでの定期自動化の検証を進めていきますので、ぜひご期待ください!

コメント

タイトルとURLをコピーしました