Google の Agent Development Kit(ADK)には、長い会話を要約してプロンプトを小さくするコンテキスト圧縮、止まった実行を途中から続ける再開、ツールを実行する前に人の承認を挟む確認(HITL)の3つの仕組みがあります。圧縮と再開は Python 版 1.16.0(2025年10月)で入り、2026年11月7日公開の 2.14.0 で experimental の扱いが外れました。この記事では google-adk 2.14.0 をインストールし、APIキー不要のダミーモデルで実際に動かした結果をもとに、それぞれの設定方法と、版によって挙動が変わった点を説明します。ADK そのものの概要はADKとは?Google Agent Development Kitで作るエージェントの種類と始め方【2.13対応】にまとめています。
まとめ:ADKの圧縮・再開・承認の要点(2.14.0時点)
- コンテキスト圧縮は
AppにEventsCompactionConfigを渡して有効にします。呼び出し回数で発火する型と、トークン数で発火する型があり、両方設定するとトークン数の型が優先されます。 - 2.14.0 からトークン数の型は直前のツール結果も数えるため、大きなツール結果が返った直後、次のモデル呼び出しの前に圧縮されます。2.13.0 では同じ条件で圧縮されませんでした。
- 要約はイベントとして追加されるだけで、元のイベントはセッションに残ります。保存容量は減りません。
- ツールの承認は
FunctionTool(関数, require_confirmation=True)が最短です。承認者に日数や金額を入力させたいときは、ツールの中でtool_context.request_confirmation()を呼びます。この場合ツール関数は承認の前後で2回呼ばれます。 ResumabilityConfig(is_resumable=True)は、止まった呼び出しをinvocation_idで再開する設定です。ツールの実行保証は「少なくとも1回」なので、決済のような処理は二重実行を防ぐ作りにします。- 2.0 の Workflow では、ノードに
require_confirmationを付けて実行前に承認を挟めます(2.12.0〜)。こちらの確認要求はadk_request_inputという名前で返ります。 - Cloud Trace へは
--otel_to_cloudで送ります。2.13.0 からテレメトリの既定形式が変わり、call_llmスパンは出なくなりました。
ADK 1.16.0で追加された機能と2.14.0までの変化
1.16.0 のリリースノートで Core の新機能として載っているのは「LLM context compaction」と「pause and resume an invocation」の2つです。ツール確認(require_confirmation)はそれより前の 1.14.0 からあります。その後の主な変化は次のとおりです。
| 機能 | 1.16.0 時点 | 2.14.0 時点 |
|---|---|---|
| コンテキスト圧縮 | 呼び出し回数の型のみ | トークン数の型を追加(1.25.0〜) |
| トークン数の数え方 | - | 直前のツール結果も加算(2.14.0〜) |
| 停止・再開 | ResumabilityConfig を新設 | 同じ設定で継続 |
| experimental 警告 | 圧縮・再開の設定で出る | 出ない(2.14.0〜) |
| ツール確認 | 1.14.0 から提供 | Workflow ノードにも拡大(2.12.0〜) |
| Cloud Trace | OTLP へ切り替え | テレメトリ既定を v2 へ(2.13.0〜) |
2.14.0 のリリースノートは、app を含む26のモジュールを experimental から外したと書いています。手元でも、2.13.0 では App を作るたびに [EXPERIMENTAL] ResumabilityConfig と [EXPERIMENTAL] EventsCompactionConfig の警告が出て、2.14.0 では出ませんでした。一方、ツール確認の公式ドキュメントには 2026年11月時点でも Experimental の表示が残っています。
2.12.0 から Python 3.11 以上が必要です。1.x 系でも App に設定を渡す書き方は同じです。Python 以外では、圧縮の公式ドキュメントが Go v2.3.0・Java v0.2.0・TypeScript v0.6.0・Kotlin v0.7.0 以降の対応を示しています。設定項目は言語で違います。2.0 で追加された Workflow はADK 2.0とは?Workflowのグラフ実行と1.xからの移行手順【Python版ADK 2.11対応】で扱っています。
EventsCompactionConfigによるコンテキスト圧縮の設定
ADK はユーザーの入力、モデルの応答、ツールの呼び出しと結果をすべてセッションのイベントとして記録し、次のモデル呼び出しのたびにプロンプトへ組み込みます。古い部分を要約に置き換えてトークン数と応答時間を抑えるのが圧縮です。設定は App 単位で、エージェントごとには指定しません。2.14.0 ではプラグインの before_run_callback から、セッションごとに設定を差し替えたり外したりもできます。
呼び出し回数で発火する型:compaction_intervalとoverlap_size
呼び出し(ユーザーの1回の入力から応答まで)が compaction_interval 回たまるたびに要約を作る方式です。overlap_size は、前回の要約範囲の末尾から何回分を次の要約に含めるかを指定します。2つは必ず同時に指定し、片方だけだと compaction_interval and overlap_size must be set together. というエラーになります。
from google.adk.agents import LlmAgent
from google.adk.apps import App
from google.adk.apps.app import EventsCompactionConfig
root_agent = LlmAgent(
name="support",
model="gemini-flash-latest",
instruction="社内ヘルプデスクとして回答する",
)
app = App(
name="support_app",
root_agent=root_agent,
events_compaction_config=EventsCompactionConfig(
compaction_interval=2,
overlap_size=1,
),
)
google-adk 2.14.0・Python 3.11.15 で、上の圧縮設定に再開設定を加え、ダミーモデルと InMemorySessionService を使って4回呼び出した結果が次の表です(要約を作る部分は、件数を返すダミーに差し替えています)。2.11.0 で測ったときと同じ結果でした。
| 呼び出し | イベント総数 | 新しくできた要約 |
|---|---|---|
| 1回目 | 3 | なし |
| 2回目 | 7 | 1〜2回目の要約 |
| 3回目 | 10 | なし |
| 4回目 | 14 | 2〜4回目の要約 |
公式ドキュメントのサンプルには overlap_size=2 # Number of events to retain と「イベント数」と読めるコメントがありますが、ソースの定義と実測では単位は呼び出し回数です。1回の呼び出しでツールを何度も使うエージェントでは、イベント数のつもりで小さく設定すると重複範囲が想定より広くなります。
トークン数で発火する型:token_thresholdとevent_retention_size
1.25.0(2026年2月11日公開)で追加された方式です。プロンプトのトークン数が token_threshold 以上になったら要約し、最新の event_retention_size 件を少なくとも要約せずに残します。ツール呼び出しと結果を分断しないよう、残る件数が設定値より多くなる場合があります。1.26.0 からは、呼び出しの途中やモデル呼び出しの直前にも判定します。
EventsCompactionConfig(
token_threshold=50000,
event_retention_size=10,
compaction_interval=5,
overlap_size=1,
)
この例のように両方の組を指定すると、トークン数の型で要約ができた回は、呼び出し回数の型を飛ばします。どちらの組も指定しないと At least one compaction trigger must be configured で App を作れません。トークン数は、直近のモデル応答に含まれる prompt_token_count を基準にし、それが無いときは文字数を4で割った概算を使います。この概算は内容によって上にも下にもずれます。日本語の地の文は1トークンあたりの文字数が英語より少ないため小さめに出やすい一方、ツールの引数と結果は json.dumps した長さで数えるので、日本語が \uXXXX の6文字に展開されて大きめに出ます。
2.14.0 では、この基準に「最後のモデル応答の後に増えた分」の概算を足すようになりました。モデルが報告するトークン数は、その応答を生んだプロンプトの大きさなので、直後に返ったツール結果が含まれていなかったためです。token_threshold=1000、event_retention_size=1 とダミーの要約器を設定し、モデルが毎回100トークンと報告し、ツールが8,000文字の結果を返す条件で比べると、セッションのイベントは次の順になりました。
| 版 | 1回の呼び出しで記録されたイベントの順 |
|---|---|
| 2.13.0 | 入力 → ツール呼び出し → ツール結果 → 最終応答(圧縮なし) |
| 2.14.0 | 入力 → ツール呼び出し → ツール結果 → 要約 → 最終応答 |
同じ token_threshold のまま 2.14.0 へ上げると、検索や SQL の大きな結果を扱うエージェントでは要約が以前より早く、頻繁に作られます。要約にもモデルの呼び出しが1回かかるので、更新後は後述のトレースで圧縮の回数を見て、閾値を見直してください。
要約モデルとプロンプトの差し替え
summarizer を省略すると、ルートエージェントのモデルを使う LlmEventSummarizer が自動で作られます。ルートが LlmAgent でないとき(Workflow をルートにしたときなど)は No LlmAgent model available for event compaction summarizer. で止まるので、明示的に指定します。
from google.adk.apps.llm_event_summarizer import LlmEventSummarizer
from google.adk.models import Gemini
summarizer = LlmEventSummarizer(
llm=Gemini(model="gemini-flash-latest"),
prompt_template="次の会話を日本語で要約してください。ユーザーの主な使用言語を先頭に明記し、使用したツール名を正確に列挙してください。\n{conversation_history}",
)
config = EventsCompactionConfig(
compaction_interval=5, overlap_size=1, summarizer=summarizer,
)
既定のプロンプトは英語で、ユーザーの言語と使ったツール名を要約に明記させる指示が入っています。要約後に英語で答え始めたり、存在しないツールを呼んだりするのを抑える狙いなので、差し替えるときもこの2点は残してください。守られるかはモデル次第なので、要約後の応答は実際のモデルで確かめます。要約に渡すツールの引数と結果は、1件あたり2,000文字で切り詰められます。
圧縮後のイベント保持とストレージ容量
要約は actions.compaction を持つ新しいイベントとして追加されるだけで、元のイベントは削除されません。プロンプトを組み立てるときに要約済みの範囲を要約文へ置き換えるだけなので、保存容量は減りません。容量を抑えるには、古いセッションを消す運用が別に必要です。
呼び出し回数の型の要約は、応答を返した後に作られます。要約中に同じセッションへ次の入力が来た場合、その要約は破棄され、次の呼び出しの後で改めて判定されます。答え終えた呼び出しがこれでエラーになることはありません。ただし、最終応答のイベントは要約の前に受け取れますが、run_async の反復が終わるのは要約が済んでからです。反復の終了を待って次の処理に進む作りでは、その分だけ待ちが延びます。
require_confirmationによるツール実行前の承認設定
返金や外部へのメール送信のように、モデルの判断だけで実行させたくないツールには確認を付けます。やり方は、はい/いいえだけを聞く方法と、承認者に値を入力させる方法の2つです。
はい/いいえで承認する:require_confirmation
FunctionTool で包んで require_confirmation=True を指定します。True の代わりに関数を渡すと、ツールの引数を見て確認するかどうかを決められます。この関数にはツールの全引数が渡るので、引数を絞って定義すると TypeError になります。
from google.adk.tools import FunctionTool
def refund(order_id: str, amount: int) -> dict:
"""注文を返金する"""
return {"status": "refunded", "order_id": order_id, "amount": amount}
def needs_approval(order_id: str, amount: int) -> bool:
return amount >= 10000
root_agent = LlmAgent(
name="support",
model="gemini-flash-latest",
tools=[FunctionTool(refund, require_confirmation=needs_approval)],
)
この条件で試すと、amount=3000 の返金はそのまま実行され、amount=50000 では実行前に adk_request_confirmation という関数呼び出しが返りました。このイベントは long_running_tool_ids を持ち、呼び出しはここで止まります。アプリ側は、この関数呼び出しの id を宛先にした関数レスポンスを返します。以下は既存の非同期処理に組み込む抜粋です。runner と session_id は作成済みとし、confirmation_call_id には確認要求の id、paused_invocation_id にはそのイベントの invocation_id を入れます。
from google.genai import types
reply = types.Content(role="user", parts=[types.Part(
function_response=types.FunctionResponse(
id=confirmation_call_id,
name="adk_request_confirmation",
response={"confirmed": True},
)
)])
async for event in runner.run_async(
user_id="u_123", session_id=session_id,
invocation_id=paused_invocation_id, new_message=reply,
):
print(event)
{"confirmed": True} ではツールが実行され、{"confirmed": False} ではツールの結果として {'error': 'This tool call is rejected.'} がモデルに渡りました。拒否された後にどう振る舞うかはモデル任せなので、instruction に「拒否されたら理由を尋ねる」などの指示を書いておきます。2.14.0 では、Enum 型の引数がツール関数に Enum のメンバーとして渡るようになりましたが、require_confirmation に渡した関数には引き続き文字列のまま渡ります。
承認者に値を入力させる:tool_context.request_confirmation
「休暇を5日申請されたが3日だけ認める」のように、承認者に値を返してもらいたいときは、ツールの中で tool_context.request_confirmation() を呼びます。hint に承認者への説明、payload に返してほしい値のひな形を渡します。
from google.adk.tools import ToolContext
def request_time_off(days: int, tool_context: ToolContext) -> dict:
"""休暇を申請する"""
confirmation = tool_context.tool_confirmation
if not confirmation:
tool_context.request_confirmation(
hint="承認する日数を入れてください",
payload={"approved_days": 0},
)
return {"status": "manager approval required"}
if not confirmation.confirmed:
return {"status": "rejected"}
approved = (confirmation.payload or {}).get("approved_days")
if not isinstance(approved, int) or not 0 < approved <= days:
return {"status": "invalid approval", "approved_days": approved}
return {"status": "ok", "requested": days, "approved_days": approved}
2.14.0 で days=5 の申請を流すと、adk_request_confirmation の引数に {'hint': '承認する日数を入れてください', 'confirmed': False, 'payload': {'approved_days': 0}} が入って返りました。{"confirmed": True, "payload": {"approved_days": 3}} を返すと、ツールが {'status': 'ok', 'requested': 5, 'approved_days': 3} を返しました。
このときツール関数は、承認を求める1回目と承認後の2回目で合計2回呼ばれます。1回目に外部へ書き込む処理を置くと二重に実行されるので、1回目は確認を求めて戻るだけにしてください。{"confirmed": False} を返した場合も2回目は呼ばれ、confirmation.confirmed が False の状態で渡りました。require_confirmation=True のときのように ADK が拒否のエラーを返してくれるわけではないので、上の例のように拒否の分岐をツール側に書きます。
画面が無いときのREST経由の承認
ADK Web では確認用のダイアログが出ますが、承認をメールやチャットで回す場合は、adk api_server の /run か /run_sse に関数レスポンスを POST します。公式ドキュメントの例を、上の休暇申請に合わせると次のようになります。
curl -X POST http://localhost:8000/run_sse \
-H "Content-Type: application/json" \
-d '{
"app_name": "time_off_app",
"user_id": "user",
"session_id": "<セッションID>",
"new_message": {
"role": "user",
"parts": [{
"function_response": {
"id": "<adk_request_confirmation の関数呼び出しID>",
"name": "adk_request_confirmation",
"response": {"confirmed": true, "payload": {"approved_days": 3}}
}
}]
}
}'
id は確認要求の関数呼び出しの id と一致させ、name は adk_request_confirmation にします。ResumabilityConfig を有効にしている場合は、確認要求を出した呼び出しの invocation_id も付けます。付けないと、確認の返事が新しい呼び出しとして扱われます。
セッションサービスの制限
公式ドキュメントの既知の制限には、DatabaseSessionService と VertexAiSessionService ではツール確認を使えないと書かれています(2026年11月時点)。セッションをデータベースに保存する構成で承認フローを組むなら、導入前にこの制限が外れているかを確かめてください。
ResumabilityConfigによる中断した呼び出しの再開設定
ResumabilityConfig は、呼び出しの途中で処理が止まったときに、最初からやり直さず続きから再開するための設定です。公式ドキュメントは、ネットワーク切断・電源断・外部システムの停止を想定した機能と説明しています。
from google.adk.apps import App, ResumabilityConfig
app = App(
name="my_resumable_agent",
root_agent=root_agent,
resumability_config=ResumabilityConfig(is_resumable=True),
)
# 止まった呼び出しを invocation_id を指定して再開する
async for event in runner.run_async(
user_id="u_123", session_id="s_abc", invocation_id="e-1234",
):
print(event)
adk api_server で動かしている場合は、/run_sse に invocation_id を付けて POST すれば再開できます。ADK Web の画面と CLI からの再開には対応していません。再開時の挙動は、エージェントの種類で次のように変わります。
- SequentialAgent:保存された
current_sub_agentを読み、次のサブエージェントから再開する - LoopAgent:
current_sub_agentとtimes_loopedを使い、最後に終えた周回から再開する - ParallelAgent:終わっていないサブエージェントだけを実行し直す
- カスタムエージェント:そのままでは再開できず、
BaseAgentStateを継承した状態クラスの実装が必要
注意すべきなのはツールの扱いです。ツール A と B が成功し、C の途中で落ちた場合、A と B の結果は記録から復元され、C だけが再実行されます。ただし保証は「少なくとも1回」なので、成功した直後に落ちたツールは2回実行されることがあります。メモリ上の一時的な状態も再開時には失われます。止まった後にサブエージェントを足したり外したりしてから再開する使い方も、サポート対象外です。
ツール確認とResumabilityConfigの関係
ツール確認に ResumabilityConfig は必須ではありません。is_resumable を付けない App でも、確認用の関数レスポンスを新しいメッセージとして送れば、承認後にツールが実行されました(2.11.0・2.14.0 とも同じ結果)。is_resumable=True にすると、確認待ちの呼び出しを invocation_id で明示的に再開できます。サーバーの再起動をまたぐ承認待ちは、メモリ内セッションによるこの記事の検証では確かめていません。
ADKのHITL(人の介入)を組む3つの仕組みの使い分け
ADK で人の判断を挟む仕組みは、ツール確認だけではありません。2.0 の Workflow には、ノード単位の確認と、任意の入力を待つ RequestInput があります。どれも実行を止めて関数レスポンスを待つ点は同じですが、返ってくる関数呼び出しの名前と、返す値の形が違います。
| 仕組み | 止める単位 | 返る関数呼び出し | 向いている場面 |
|---|---|---|---|
| ツール確認 | LlmAgent のツール | adk_request_confirmation | モデルが選んだ操作の承認 |
| ノードの確認 | Workflow のノード | adk_request_input | 決まった手順の中の承認 |
| RequestInput | Workflow のノード | adk_request_input | 承認以外の値の入力待ち |
ノードの確認は 2.12.0 で追加され、@node(require_confirmation=True) と書けます。2.14.0 で試すと、ノードは実行されないまま adk_request_input が返り、{"confirmed": True} を同じ id で返すとノードが1回だけ実行されました。@node を使うと承認後にノードをもう一度動かす rerun_on_resume=True が自動で付きますが、FunctionNode を直接作るときは指定が必要で、省くと FunctionNode with require_confirmation requires rerun_on_resume=True. で止まります。
from google.adk.apps import App
from google.adk.workflow import START, Workflow, node
@node(require_confirmation=True)
def delete_records():
return {"deleted": 42}
app = App(
name="cleanup",
root_agent=Workflow(name="wf", edges=[(START, delete_records)]),
)
モデルにどのツールを使うか任せる対話型のエージェントならツール確認、承認の位置が業務手順で決まっているならノードの確認、という分け方が自然です。RequestInput の書き方と Workflow の全体像は、前出の ADK 2.0 の記事で扱っています。ノードの確認は応答の名前が adk_request_input になるので、ツール確認向けに adk_request_confirmation だけを拾う画面を作っていると、承認待ちを取りこぼします。
圧縮・再開・承認の本番採用を見送る条件
2.14.0 で圧縮と再開の設定から experimental の扱いが外れ、起動時の警告は出なくなりました。ただし、トークン数の型の数え方は 2.14.0 で変わったばかりです。次のどれかに当てはまる場合は、導入を見送るか、ADK の版を固定したうえで更新ごとに挙動を確かめる前提にしてください。
- 過去の発言を一字一句参照する必要がある業務(契約条件、金額、型番の確認など)。要約で数値や固有名詞が丸められると、エージェントが誤った前提で答えます。
- 二重実行が許されないツール(決済、在庫引き当て、外部への送信)を再開機能や payload 付きの確認と組み合わせる場合。ツール側で処理IDによる重複チェックを入れるまでは使わないでください。
- 各往復の入力・ツール結果が小さく、数往復で終わる用途。圧縮後に会話が続かなければ、要約に使ったトークンを後続リクエストの削減で回収できません。
反対に、調査や分析で数十往復以上続くセッションや、大きなツール結果を何度も扱うエージェントでは、トークン数の型の圧縮を入れる価値があります。単一のエージェントと複数エージェントのどちらで組むかによってもコンテキストの増え方は変わるので、構成の決め方はシングルエージェントとは?マルチエージェントとの違いと使い分けの判断基準【2026年10月】も参考にしてください。承認フローや再開を含む業務エージェントの設計・実装を外部に相談したい場合は、AIエージェント開発の支援も行っています。
Cloud TraceへのOTLP出力と圧縮スパンの確認
1.16.0 で、ADK が Cloud Trace へトレースを送る先が、Cloud Trace API から OpenTelemetry 標準の OTLP を受け付ける Telemetry API(https://telemetry.googleapis.com/v1/traces)に変わりました。
adk deploy agent_engine \
--project=$GOOGLE_CLOUD_PROJECT \
--region=$GOOGLE_CLOUD_LOCATION \
--otel_to_cloud \
$AGENT_PATH
# ローカルの開発サーバーでも同じフラグが使える
adk web --otel_to_cloud
送信元のプロジェクトでは Telemetry API を有効にし、実行するサービスアカウントに Cloud Telemetry Traces Writer(roles/telemetry.tracesWriter)を付与します。Cloud Logging にも送るので、Logs Writer(roles/logging.logWriter)も付けます。旧フラグの --trace_to_cloud は 2.14.0 でも動きますが、非推奨の警告が出ます。Agent Runtime へのデプロイ手順はVertex AI Agent Engine(現Agent Runtime)の使い方:ADKデプロイと料金で扱っています。
圧縮を有効にすると、トレースに compact_events sliding_window または compact_events token_threshold という名前のスパンが追加されます。属性 gen_ai.compaction.event_count で要約したイベント数がわかるので、閾値が適切かどうかを本番の数字で判断できます。プロンプトと応答の本文までトレースに残すには環境変数 OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true を設定しますが、個人情報が含まれる場合は保存先の権限を絞ってください。
2.13.0 のリリースノートによると、テレメトリの既定は OpenTelemetry GenAI 規約の新しい形式(otel_semconv_1_44)に変わり、モデル呼び出しごとの call_llm スパンは出なくなりました。call_llm を条件にしたダッシュボードやアラートがある場合は、環境変数 ADK_TELEMETRY_SCHEMA_VERSION_OPT_IN=otel_semconv_1_36 で旧形式に戻せます。属性名の体系はOpenTelemetryのGenAIセマンティック規約|gen_ai属性とメトリクスの体系・移行の判断で解説しています。
よくある質問
ADKでHITL(人の承認)を入れるにはどうすればよいですか?
ツールを FunctionTool(関数, require_confirmation=True) で包むのが最も簡単です。実行前に adk_request_confirmation が返るので、{"confirmed": True} を関数レスポンスで返すと実行されます。承認者に値を入力させたいときは tool_context.request_confirmation()、Workflow の手順に承認を挟むときは @node(require_confirmation=True) を使います。
EventsCompactionConfigで圧縮すると元の会話履歴は消えますか?
消えません。要約は新しいイベントとして追加され、元のイベントはセッションに残ります。モデルに送るプロンプトの中で、要約済みの範囲が要約文に置き換わるだけです。
ADKを2.14.0に上げると圧縮の頻度は変わりますか?
トークン数の型を使っている場合は変わることがあります。2.14.0 から直前のツール結果も数えるため、大きなツール結果を返すエージェントでは、同じ token_threshold でも早めに要約されます。呼び出し回数の型だけを使っている場合は、この記事の実測では変わりませんでした。
adk webで圧縮や再開の設定を使うにはどこに書けばよいですか?
agent.py(またはパッケージの __init__.py)に app = App(...) を定義します。ADK のローダーは app という名前の App を先に探し、無いときだけ root_agent を読むので、root_agent だけを定義していると設定が反映されません。
ADKのログを詳しく出すにはどうすればよいですか?
adk web や adk api_server に --log_level debug(短縮形は -v または --verbose)を付けます。既定は INFO です。本番のトレースは --otel_to_cloud で Cloud Trace と Cloud Logging に送れます。
ADKとはどのようなフレームワークですか?
Google が公開しているオープンソースのAIエージェント開発キットで、Python・Java・TypeScript・Go 版と、独立した SDK の Kotlin 版があります。定義と始め方はADKとは?Google Agent Development Kitで作るエージェントの種類と始め方【2.13対応】、ほかのフレームワークとの比較はAIエージェントフレームワークとは?主要8種の比較・動作コード・選定基準【2026年9月版】を参照してください。