Runtime Event Loop¶
ADK Runtime は、ユーザー対話中にエージェントアプリを動かす基盤エンジンです。 定義したエージェント、ツール、コールバックを連携させ、 入力に応じた情報フロー・状態変更・LLM/ストレージ連携を管理します。
Runtime はエージェントアプリの エンジン と考えるとわかりやすいです。 開発者は部品(Agent/Tool)を定義し、 Runtime がそれらを接続して実行します。
コアアイデア: Event Loop¶
ADK Runtime の中心は Event Loop です。
Runner と実行ロジック(Agent、LLM 呼び出し、Callback、Tool)の
協調を回す仕組みです。

シンプルに言うと:
Runnerがユーザー問い合わせを受け取りメインAgentを開始Agentが応答/ツール要求/状態変更などをEventとしてyieldRunnerが受け取って処理(状態保存など)し上流へ転送AgentはRunner処理後にのみ再開- 現在の問い合わせでイベントが尽きるまで繰り返し
このイベント駆動ループが ADK 実行モデルの基本です。
Event Loop の内部¶
Note
メソッド名や引数名は SDK 言語ごとに異なります。
Runner の役割(オーケストレータ)¶
Runner は 1 回の invocation の中央調整役です。
- 開始: ユーザークエリを受け、SessionService に履歴追加
- Kick-off: メインエージェント実行開始
- 受信/処理:
yieldされたEventを即時処理 state_delta/artifact_deltaなどを Services でコミット- 上流転送: UI/呼び出し元へ転送
- 反復: エージェントを再開し次イベント生成へ
実行ロジックの役割(Agent/Tool/Callback)¶
実際の判断・計算は実行ロジック側が担います。
- 現在の
InvocationContextで実行 - 伝える内容を
Eventにしてyield yield直後に一時停止Runner処理後に再開- 再開時は前イベントでコミット済み状態を参照可能
この yield / pause / resume サイクルが Runtime の中核です。
Runtime の主要コンポーネント¶
-
Runner¶- 役割: 1 ユーザークエリ実行の入口 (
run_async) - 機能: Event Loop 管理、イベント処理/コミット、上流転送
- 役割: 1 ユーザークエリ実行の入口 (
-
実行ロジック¶
Agent(BaseAgent,LlmAgentなど)Tools(BaseTool,FunctionTool,AgentToolなど)Callbacks(フック関数)- 機能: 判断/計算/外部連携を行い
Eventをyield
-
Event¶- 役割: Runner と実行ロジック間のメッセージ
- 機能: コンテンツ + 副作用意図(
actions)を運ぶ
-
Services¶SessionService: Session 保存/読込、state 適用、履歴管理ArtifactService: バイナリアーティファクト管理MemoryService: (任意)長期意味記憶- 役割:
Runnerがイベント処理時に使うバックエンド層です。event.actionsに含まれるstate_deltaやartifact_deltaを反映し、会話やアーティファクトの永続化を担います。
-
Session¶- 役割: 1 会話の状態/履歴コンテナ
-
Invocation¶- 役割: 1 ユーザークエリに対する処理単位
- 機能: 複数の agent run / LLM call / tool 実行を
invocation_idで束ねる - 補足:
temp:プレフィックスの状態は 1 回の invocation にだけスコープされ、次のターンには引き継がれません。
シンプルな invocation フロー¶

典型例(ツール呼び出しを含む場合):
- ユーザー入力受信
- Runner が Session を読み込み、ユーザーイベント記録
- root agent 実行
- LLM がツール呼び出しを判断
- FunctionCall イベント
yield - Agent 一時停止
- Runner 処理/転送
- Agent 再開
- ツール実行
- ツール結果取得
- FunctionResponse イベント
yield - Agent 一時停止
- Runner が状態/アーティファクト差分をコミット
- Agent 再開
- 最終応答生成
- テキストイベント
yield - Agent 一時停止
- Runner 転送
- Agent 終了
- Runner ループ完了
重要な Runtime 振る舞い¶
状態更新コミットのタイミング¶
状態変更は、state_delta を含むイベントが yield され、
Runner が処理した後に永続化が保証されます。
したがって yield 後に再開したコードは、
直前イベントのコミット済み状態を前提にできます。
Session State の "Dirty Read"¶
同一 invocation 内では、コミット前のローカル状態が読める場合があります。
- 利点: 同一ステップ内コンポーネント連携がしやすい
- 注意: コミット前に失敗すると変更が失われる可能性
重要な状態遷移は、必ずイベント経由でコミットされる設計にしてください。
Streaming vs Non-Streaming (partial=True)¶
- ストリーミング時は
partial=Trueを即時転送 - 多くの場合、partial イベントの
actionsはコミットしない - 最終イベント(
partial=Falseなど)で状態差分をコミット - 非ストリーミングは単一イベントで処理
ストリーミングでは、UI への逐次表示を優先しつつ、状態の永続化は最終イベントで一度だけ行うため、
partial=True のイベントは通常そのまま上流へ流し、actions の反映は抑制されます。
非ストリーミングでは、1 つのイベントをまとめて処理するため、状態更新がより単純になります。
これにより、UI の逐次表示と状態一貫性を両立できます。
Async が基本 (run_async)¶
ADK Runtime は非同期実行を前提に設計されています。 LLM 応答待ちやツール実行を効率よく扱うためです。
- 主要入口:
Runner.run_async - 同期
runは便宜用ラッパーで、内部でrun_asyncを使うことが多い - Python は
asyncio - TypeScript は
Promise/AsyncGenerator - Java は
RxJava
同期コールバック/ツール利用時の注意¶
- ブロッキング I/O はイベントループ停滞の原因
- Python は
asyncio.to_threadなどで回避可能 - TypeScript は Promise ベース I/O を推奨
- CPU-bound 同期処理はスレッドを占有
これらを理解すると、状態整合性・ストリーミング更新・非同期実行に関する 設計/デバッグが容易になります。
言語別の補足¶
Runner.run_async の入口¶
async for でイベントを逐次処理します。
AsyncGenerator を for await で消費します。
チャネルまたはコールバックでイベントを受け取ります。
RxJava のストリームとして扱います。
状態コミットのタイミング¶
yield 後に Runner が state_delta を反映します。
イベント受信後に状態を反映し、UI 更新を行います。
イベントの順序を保ちながら永続化します。
同期処理は避け、非同期の完了後に状態を確定します。
Streaming と Non-Streaming¶
partial=True は逐次表示、partial=False は確定更新です。
途中チャンクと最終チャンクを分けて表示します。
部分イベントは UI へ、最終イベントは保存へ回します。
ストリーム中のイベントは表示用、完了イベントは永続化用に扱います。
Async を前提にする理由¶
asyncio で LLM / I/O 待ちを止めません。
Promise ベースでイベントループを塞ぎません。
Goroutine とチャネルで並行性を保ちます。
RxJava でバックプレッシャーを意識して扱います。