壊れたツール呼び出しを、モデル自身に「もう一度考えさせて」直させる──GoogleのADKが標準搭載したReflectAndRetryModelPlugin
Google Agent Development Kit(ADK)はv2.6.0で、モデルが不正な形式の関数呼び出しなどを返した際に、その場でエラー内容を伝えて再試行させる`ReflectAndRetryModelPlugin`を追加した。公式ドキュメントによれば既定のリトライ上限は3回で、超過するとRuntimeErrorを送出するかどうかも設定次第で選べる。並行実行を想定したロック付きカウンターや、モデルごとに独立した失敗カウントの仕組みも実装されている。

目次
LLMが不正な形式の関数呼び出し(malformed function call)を返してくると、多くの実装ではそのままエラーで処理が止まるか、使い物にならないターンとして扱われる。Google Agent Development Kit(ADK)はv2.6.0(2026年7月29日公開、コミット322f455)で、こうした失敗をその場でモデルに伝え、自己修正させるReflectAndRetryModelPluginを追加した。同じv2.6.0では、リリース直後に「ReflectAndRetryModelPlugin の遅延インポートパスを修正する」というバグ修正コミット(c59fd0e)も入っており、追加されて間もない機能であることがうかがえる。
3行まとめ
- Google ADK v2.6.0は、モデルの不正な形式の関数呼び出しなどを検知し、その場でモデルに自己修正させる
ReflectAndRetryModelPluginを追加した。既定のリトライ上限は3回(4回目の連続失敗で打ち切り)- 対になる
ReflectAndRetryToolPluginはツール実行時の失敗(例外・タイムアウト・不正な引数)を扱い、モデル側とツール側でそれぞれ独立したカウンターを持つ- 公式ドキュメントは「関数呼び出し機能に依存する」「モデルレベルの失敗のみが対象」など4つの制約を自ら明記している
「壊れた出力」をエラーではなく再試行の材料にする
公式ドキュメントは、このプラグインが対象とする問題をこう説明している。
"LLMs occasionally return outputs the framework cannot act on: a malformed function call (
FinishReason.MALFORMED_FUNCTION_CALL), a safety block, or a recitation block. Left unhandled, these either crash the invocation or yield an unusable turn.ReflectAndRetryModelPlugincatches such failures after the model responds, injects a reflection prompt describing the error, and re-runs the turn so the model can correct itself."
(LLMは時折、フレームワークが処理できない出力を返す:不正な形式の関数呼び出し(FinishReason.MALFORMED_FUNCTION_CALL)、安全性によるブロック、剽窃(recitation)によるブロックなどだ。これらを処理せずに放置すると、呼び出しがクラッシュするか、使い物にならないターンになる。ReflectAndRetryModelPluginは、モデルが応答した後にこうした失敗を捕捉し、エラー内容を説明する内省(reflection)プロンプトを差し込み、モデルが自己修正できるようターンを再実行する)
このプラグインは、ツール実行の失敗に対処するReflectAndRetryToolPluginと対になる、モデルレベルの失敗に対処する実装だと公式ドキュメントは位置づけている。ReflectAndRetryToolPlugin側のドキュメントも確認すると、対象とする失敗の種類が異なることが分かる。モデル側のプラグインが「不正な形式の関数呼び出し・安全性ブロック・剽窃ブロック」というモデルの応答そのものの問題を扱うのに対し、ツール側のプラグインは「関数が例外を投げた」「リモートAPIがタイムアウトした」「モデルが不正な引数でツールを呼んだ」といった、ツール実行時に起きる失敗を扱う。両者ともmax_retries・throw_exception_if_retry_exceeded・tracking_scopeという共通の設定オプション名を持ち、内部でも同じScopedFailureTracker/TrackingScopeという仕組みを共有しているが、カウンターの単位はモデル側が「モデル名ごと」、ツール側が「ツール名ごと」で、それぞれ独立している。
わずか数行で導入できる
公式ドキュメントに示されているセットアップ例は、シンプルだ。
# Retry a failing model turn up to 3 times before giving up.
retry_plugin = ReflectAndRetryModelPlugin(max_retries=3)
app = App(
name="model_retry_demo",
root_agent=agent,
plugins=[retry_plugin],
)
"If the model returns a malformed function call, the plugin injects reflection guidance and the agent tries again. After three consecutive failures it raises a
RuntimeError(the default behavior)."
(モデルが不正な形式の関数呼び出しを返すと、プラグインは内省ガイダンスを差し込み、エージェントは再試行する。3回連続で失敗するとRuntimeErrorを送出する——これが既定の挙動だ)
内部の仕組み:予約済みツールを使って「反省」を伝える
公式ドキュメントの「How it works」セクションには、実装の内部動作が4段階で説明されている。要約すると、モデル呼び出しの前にadk_handle_model_errorという予約済みの内部ツールを登録しておき、モデルが失敗した際には、このツールを呼び出す形で「エラー内容と試行回数」を含む内省ターンをモデルに送り返す、という仕組みだ。カウントはScopedFailureTrackerという機構で管理され、モデルごとに個別のカウンターを持つ。上限を超えると、RuntimeErrorを送出するか、失敗した元のレスポンスをそのまま返すかを、設定によって選べる。
設定オプションの一覧
公式ドキュメントには、設定できるオプションが表としてまとまっている。
| オプション | 型 | 既定値 | 説明 |
|---|---|---|---|
max_retries |
int |
3 |
連続失敗の上限回数。0以上の値が必須で、0はリトライを無効化する |
throw_exception_if_retry_exceeded |
bool |
True |
Trueなら上限超過時にRuntimeErrorを送出、Falseなら最後の失敗したLlmResponseをそのまま返す |
tracking_scope |
TrackingScope |
TrackingScope.INVOCATION |
失敗カウンターの生存範囲:呼び出しごとに独立させるか、プロセス全体で共有するか |
on_model_errors |
list[types.FinishReason] | None |
[FinishReason.MALFORMED_FUNCTION_CALL] |
reflect-and-retryループを発動させるFinishReasonの一覧 |
特に興味深いのはtracking_scopeの使い分けについての説明だ。
"
tracking_scopecould stayINVOCATIONfor multi-user servers (each request isolated);GLOBALshares one counter across invocations, which is useful as a circuit breaker."
(tracking_scopeは、マルチユーザーサーバーではINVOCATIONのままにしておく(各リクエストを分離する)とよい。GLOBALは複数の呼び出しにまたがって1つのカウンターを共有し、サーキットブレーカーとして有用だ)
つまり、通常のリクエスト単位での運用に加えて、「システム全体で一定回数以上モデルが失敗し続けたら、サーキットブレーカーのように全体を止める」という使い方も想定した設計になっている。
対象を安全性ブロックにも広げられる
既定ではMALFORMED_FUNCTION_CALL(不正な形式の関数呼び出し)のみがリトライ対象だが、公式ドキュメントの「Advanced applications」セクションには、対象を拡張する例も示されている。安全性ブロックや剽窃ブロックといった、モデル自身の出力内容に起因する失敗についても、同じ仕組みでリトライさせることができる、という設計だ。
公式ドキュメントが明記する4つの制約
公式ドキュメントの末尾には「Limitations」という節があり、次の4点が明記されている。
error_codeとfinish_reasonの両方が必要:error_codeが無い、またはfinish_reasonがon_model_errorsに含まれていない応答は、素通りする(検知されない)- 関数呼び出し機能に依存する:内省ガイダンスは「合成された関数呼び出し」として届けられるため、ツール呼び出しに対応していないモデルでは使えない
- モデルレベルの失敗のみを対象とする:ツールの失敗には
ReflectAndRetryToolPluginを使う必要がある - 解決可能なモデルを持つ
LlmAgentが必須:LlmAgentでない、またはモデルを解決できないエージェントに登録するとValueErrorを送出する
max_retriesの判定ロジックについても、公式ドキュメントに具体的な補足がある。判定はretry_count <= max_retriesという式で行われるため、既定値の3は「1回目・2回目・3回目の再試行」まで許容し、4回連続で失敗した時点で上限超過(exhaustion)の扱いになる、と説明されている。
「気づいてやり直す」を自動化しているという理解
筆者は、LLMのツール呼び出しが崩れて処理が止まる場面に、Claude Codeなど日常的に使うツールで何度も遭遇している。多くの場合、人間がそれに気づいてやり直しを指示するが、ADKのこのプラグインが自動化しているのは、まさにその「気づいてやり直す」というプロセスそのものだ。同じバッチで扱ったUnsloth Desktopの「Self-healing tool calling」も、名前は違えど同じ問題意識(壊れたツール呼び出しを自動修復する)を扱っている。エージェントフレームワーク各社が、ほぼ同時期にこの種の自己修復機構を標準機能として持たせ始めていることは、業界共通の課題認識だと考えられる。
ScopedFailureTrackerの実装詳細までは検証していない
ScopedFailureTrackerの実装詳細(ロックの粒度や、並行リクエストが多い場合のパフォーマンスへの影響)については、公式ドキュメントの説明レベルを超える検証は今回行っていない。またこのプラグインが実際の本番運用でどの程度の頻度で発動しているか、実測データは公開されている範囲では見当たらなかった。安全性ブロック・剽窃ブロックへの対応を拡張した場合の推奨設定(どのFinishReasonをどう組み合わせるべきか)についても、公式ドキュメントの例を超える具体的なガイダンスは確認できていない。
関連記事
出典・参照資料
AIニュースの解説を動画でも
YouTubeでは注目ニュースの背景を解説し、Xでは新着記事をお知らせしています。
コメント
まだコメントはありません。最初のコメントを書いてみませんか?
AIについて聞きたいことはありますか?
質問箱で無料で受け付けています。回答は公開され、他の方の参考にもなります。
質問箱を見る →新しい記事をメールで受け取る
AIの新しい発表を、出典付きで整理して届けます。