コンテンツにスキップ

コールバックの設計パターンとベストプラクティス

コールバックは、エージェントのライフサイクルに強力なフックを提供します。ここでは、ADKでそれらを効果的に活用する方法を示す一般的な設計パターンと、実装のためのベストプラクティスを紹介します。

設計パターン

これらのパターンは、コールバックを使用してエージェントの振る舞いを強化または制御する典型的な方法を示しています:

1. ガードレールとポリシー適用

  • パターン: LLMやツールに到達する前にリクエストを傍受し、ルールを強制します。
  • 方法: before_model_callbackを使用してLlmRequestプロンプトを検査するか、before_tool_callbackを使用してツール引数を検査します。ポリシー違反が検出された場合(例:禁止されたトピック、不適切な表現)、事前定義された応答(LlmResponseまたはdict/Map)を返して操作をブロックし、オプションでcontext.stateを更新して違反をログに記録します。
  • 例: before_model_callbackllm_request.contentsで機密キーワードをチェックし、見つかった場合は標準の「このリクエストは処理できません」というLlmResponseを返し、LLMの呼び出しを防ぎます。

2. 動的な状態管理

  • パターン: コールバック内でセッション状態を読み書きして、エージェントの振る舞いを文脈に応じて変化させ、ステップ間でデータを渡します。
  • 方法: callback_context.stateまたはtool_context.stateにアクセスします。変更(state['key'] = value)は、SessionServiceによる永続化のために、後続のEvent.actions.state_deltaで自動的に追跡されます。
  • 例: after_tool_callbackがツールの結果からtransaction_idtool_context.state['last_transaction_id']に保存します。後のbefore_agent_callbackstate['user_tier']を読み取ってエージェントの挨拶をカスタマイズするかもしれません。

3. ロギングとモニタリング

  • パターン: 可観測性とデバッグのために、特定のライフサイクルポイントで詳細なログを追加します。
  • 方法: コールバック(例:before_agent_callbackafter_tool_callbackafter_model_callback)を実装して、エージェント名、ツール名、呼び出しID、コンテキストや引数からの関連データを含む構造化されたログを出力または送信します。
  • 例: INFO: [Invocation: e-123] Before Tool: search_api - Args: {'query': 'ADK'}のようなログメッセージ。

4. キャッシング

  • パターン: 結果をキャッシュすることで、冗長なLLM呼び出しやツール実行を回避します。
  • 方法: before_model_callbackまたはbefore_tool_callbackで、リクエスト/引数に基づいてキャッシュキーを生成します。このキーについてcontext.state(または外部キャッシュ)を確認します。見つかった場合は、キャッシュされたLlmResponseまたは結果を直接返し、実際の操作をスキップします。見つからなかった場合は、操作を続行させ、対応するafter_コールバック(after_model_callbackafter_tool_callback)を使用して、キーを使って新しい結果をキャッシュに保存します。
  • 例: get_stock_price(symbol)before_tool_callbackstate[f"cache:stock:{symbol}"]をチェックします。存在する場合はキャッシュされた価格を返し、それ以外の場合はAPI呼び出しを許可し、after_tool_callbackが結果を状態キーに保存します。

5. リクエスト/レスポンスの変更

  • パターン: LLM/ツールに送信される直前、または受信した直後にデータを変更します。
  • 方法:
    • before_model_callbackllm_requestを変更します(例:stateに基づいてシステム指示を追加)。
    • after_model_callback:返されたLlmResponseを変更します(例:テキストのフォーマット、コンテンツのフィルタリング)。
    • before_tool_callback:ツールのargs辞書(またはJavaではMap)を変更します。
    • after_tool_callbacktool_response辞書(またはJavaではMap)を変更します。
  • 例: context.state['lang'] == 'es'の場合、before_model_callbackllm_request.config.system_instructionに「ユーザーの言語設定:スペイン語」を追加します。

6. ステップの条件付きスキップ

  • パターン: 特定の条件に基づいて標準的な操作(エージェントの実行、LLM呼び出し、ツール実行)を防ぎます。
  • 方法: before_コールバックから値を返します(before_agent_callbackからContentbefore_model_callbackからLlmResponsebefore_tool_callbackからdict)。フレームワークはこの返された値をそのステップの結果として解釈し、通常の実行をスキップします。
  • 例: before_tool_callbacktool_context.state['api_quota_exceeded']をチェックします。Trueの場合、{'error': 'APIクォータを超えました'}を返し、実際のツール関数の実行を防ぎます。

7. ツール固有のアクション(認証と要約制御)

  • パターン: 主に認証とツール結果のLLM要約の制御など、ツールのライフサイクルに固有のアクションを処理します。
  • 方法: ツールコールバック(before_tool_callbackafter_tool_callback)内でToolContextを使用します。
    • 認証: 認証情報が必要で、見つからない場合(例:tool_context.get_auth_responseや状態チェック経由)、before_tool_callbacktool_context.request_credential(auth_config)を呼び出します。これにより認証フローが開始されます。
    • 要約: ツールの生の辞書出力がLLMにそのまま返されるべきか、または直接表示される可能性がある場合、デフォルトのLLM要約ステップをバイパスするためにtool_context.actions.skip_summarization = Trueを設定します。
  • 例: 安全なAPIのbefore_tool_callbackが状態内の認証トークンをチェックし、ない場合はrequest_credentialを呼び出します。構造化JSONを返すツールのafter_tool_callbackskip_summarization = Trueを設定するかもしれません。

8. アーティファクトの処理

  • パターン: エージェントのライフサイクル中に、セッション関連のファイルや大きなデータBLOBを保存または読み込みます。
  • 方法: callback_context.save_artifact / await tool_context.save_artifactを使用してデータ(例:生成されたレポート、ログ、中間データ)を保存します。load_artifactを使用して以前に保存したアーティファクトを取得します。変更はEvent.actions.artifact_deltaを介して追跡されます。
  • 例: 「generate_report」ツールのafter_tool_callbackawait tool_context.save_artifact("report.pdf", report_part)を使用して出力ファイルを保存します。before_agent_callbackcallback_context.load_artifact("agent_config.json")を使用して設定アーティファクトを読み込むかもしれません。

コールバックのベストプラクティス

  • 焦点を絞る: 各コールバックを単一の、明確に定義された目的(例:ロギングのみ、検証のみ)のために設計します。モノリシックなコールバックは避けてください。
  • パフォーマンスを意識する: コールバックはエージェントの処理ループ内で同期的に実行されます。長時間の実行やブロッキング操作(ネットワーク呼び出し、重い計算)は避けてください。必要に応じてオフロードしますが、これにより複雑さが増すことに注意してください。
  • エラーを適切に処理する: コールバック関数内でtry...except/catchブロックを使用します。エラーを適切にログに記録し、エージェントの呼び出しを停止するか、回復を試みるかを決定します。コールバックのエラーでプロセス全体がクラッシュしないようにしてください。
  • 状態を慎重に管理する:
    • context.stateからの読み取りと書き込みを意図的に行います。変更は現在の呼び出し内ですぐに表示され、イベント処理の最後に永続化されます。
    • 意図しない副作用を避けるために、広範な構造を変更するのではなく、特定の状態キーを使用します。
    • 特に永続的なSessionService実装では、明確さのために状態プレフィックス(State.APP_PREFIX, State.USER_PREFIX, State.TEMP_PREFIX)の使用を検討してください。
  • べき等性を考慮する: コールバックが外部の副作用を伴うアクション(例:外部カウンターのインクリメント)を実行する場合、フレームワークやアプリケーションでの潜在的な再試行を処理するために、可能であればべき等(同じ入力で複数回実行しても安全)になるように設計します。
  • 徹底的にテストする: モックコンテキストオブジェクトを使用してコールバック関数を単体テストします。完全なエージェントフロー内でコールバックが正しく機能することを確認するために統合テストを実行します。
  • 明確さを確保する: コールバック関数には説明的な名前を使用します。その目的、いつ実行されるか、および副作用(特に状態の変更)を説明する明確なdocstringを追加します。
  • 正しいコンテキストタイプを使用する: 常に提供される特定のコンテキストタイプ(エージェント/モデルにはCallbackContext、ツールにはToolContext)を使用して、適切なメソッドとプロパティへのアクセスを確保します。

これらのパターンとベストプラクティスを適用することで、ADKでより堅牢で、観測可能で、カスタマイズされたエージェントの振る舞いを作成するために、コールバックを効果的に使用できます。