ランナー
adk-runner の実行ランタイムで、エージェント実行を調整します。
概要
Runner は、エージェント実行の完全なライフサイクルを管理します。
- セッション管理(セッションの作成/取得)
- メモリ注入(関連するメモリの検索と注入)
- アーティファクト処理(スコープ付きアーティファクトアクセス)
- イベントストリーミング(イベントの処理と転送)
- エージェント転送(複数エージェント間のハンドオフ処理)
インストール
[dependencies]
adk-runner = "2.0.0"
RunnerConfig
必要なサービスでランナーを設定します。
use adk_runner::{Runner, RunnerConfig};
use adk_session::InMemorySessionService;
use adk_artifact::InMemoryArtifactService;
use std::sync::Arc;
let config = RunnerConfig {
app_name: "my_app".to_string(),
agent: Arc::new(my_agent),
session_service: Arc::new(InMemorySessionService::new()),
artifact_service: Some(Arc::new(InMemoryArtifactService::new())),
memory_service: None,
plugin_manager: None,
run_config: None,
compaction_config: None,
context_cache_config: None,
cache_capable: None,
request_context: None,
cancellation_token: None,
};
let runner = Runner::new(config)?;
RunnerConfigBuilder(推奨)
typestate builder を使用して Runner を構築します。builder はコンパイル時に必須フィールドを強制し、すべての任意フィールドにはデフォルトを設定するため、将来のリリースで新しいフィールドが追加されてもコードは壊れません。
use adk_runner::Runner;
let runner = Runner::builder()
.app_name("my_app")
.agent(Arc::new(my_agent))
.session_service(Arc::new(InMemorySessionService::new()))
// Optional fields — only set what you need
.artifact_service(Arc::new(InMemoryArtifactService::new()))
.build()?;
builder には app_name、agent、session_service の 3 つのフィールドが必要です。それ以外はすべて任意で、適切なデフォルトがあります。build() メソッドは、3 つの必須フィールドがすべて設定された後にのみ使用できます。不足している場合は実行時エラーではなく、コンパイル時エラーになります。
設定フィールド
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
app_name | String | はい | アプリケーション識別子 |
agent | Arc<dyn Agent> | はい | 実行するルートエージェント |
session_service | Arc<dyn SessionService> | はい | セッションストレージバックエンド |
artifact_service | Option<Arc<dyn ArtifactService>> | いいえ | アーティファクトストレージ |
memory_service | Option<Arc<dyn Memory>> | いいえ | 長期メモリ |
plugin_manager | Option<Arc<PluginManager>> | いいえ | プラグインのライフサイクルフック |
compaction_config | Option<EventsCompactionConfig> | いいえ | コンテキスト圧縮設定 |
run_config | Option<RunConfig> | いいえ | 実行オプション |
context_cache_config | Option<ContextCacheConfig> | いいえ | ランナーレベルのコンテキストキャッシュのライフサイクル(実験的 — 以下を参照) |
cache_capable | Option<Arc<dyn CacheCapable>> | いいえ | キャッシュ対応モデル参照(実験的 — 以下を参照) |
request_context | Option<RequestContext> | いいえ | 認証ミドルウェアのコンテキスト |
cancellation_token | Option<CancellationToken> | いいえ | 協調的キャンセル |
プロンプトキャッシュ
キャッシュはプロバイダーレベルの関心事であり、Runner の設定は不要です。 各プロバイダー統合は、リクエストが組み立てられる場所でそれを処理します:
| プロバイダー | メカニズム | デフォルト |
|---|---|---|
| Anthropic / Bedrock | cache_control ブレークポイント | 有効 (AnthropicConfig::prompt_caching、with_prompt_caching(false) で無効化可能) |
| OpenAI | サーバー側プロンプトキャッシュ、保持のための PromptCacheRetention | 自動 |
| Gemini | 2.5/3.x での暗黙的キャッシュ — 共有プレフィックスにより、コード変更なしで割引が適用される | 自動 |
追加の配線なしでキャッシュヒットを観測できます。Gemini の統合は、各レスポンスごとに cachedContentTokenCount を記録します。
context_cache_configとcache_capableは実験的であり、 未設定のままにするべきです。 これらは Runner から Gemini の 明示的なcachedContentsAPI を駆動します。その API では、キャッシュがsystem_instruction、tools、tool_configを 置き換える 必要があります — それらのいずれかと一緒にキャッシュを送ると、INVALID_ARGUMENTで拒否されます。Runner は、エージェントがツールを解決する前にキャッシュを選択するため、そのリクエストを組み立てることができず、現時点ではこれらのフィールドを有効にしてもキャッシュヒットは発生しません。Gemini における保証された(ベストエフォートではない)キャッシュは、他のプロバイダと同様にモデル統合側に属します。
エージェントの実行
ユーザー入力でエージェントを実行します:
use adk_core::{Content, SessionId, UserId};
use futures::StreamExt;
let user_content = Content::new("user").with_text("Hello!");
let mut stream = runner.run(
UserId::new("user-123")?,
SessionId::new("session-456")?,
user_content,
).await?;
while let Some(event) = stream.next().await {
match event {
Ok(e) => {
if let Some(content) = e.content() {
for part in &content.parts {
if let Some(text) = part.text() {
print!("{}", text);
}
}
}
}
Err(e) => eprintln!("Error: {}", e),
}
}
文字列の簡便メソッド
単純な呼び出し箇所では、run_str() は通常の &str 引数を受け取り、新しい型への変換を内部で処理します:
let mut stream = runner.run_str(
"user-123",
"session-456",
Content::new("user").with_text("Hello!"),
).await?;
文字列の検証に失敗した場合(空、null バイトを含む、または長さ制限を超える)、run_str() はエージェントループを開始する前にエラーを返します。型付きの UserId/SessionId を使う既存の run() メソッドは変更ありません。
中断と Run の分離
Run は run() がストリームを返した時点で登録され、そのストリームが破棄されたときに登録解除されます — 一度もポーリングされないまま破棄された場合も含みます。
| メソッド | 範囲 |
|---|---|
interrupt(session_id) | その session ID に対する進行中のすべての実行を、アプリとユーザーをまたいでキャンセルします |
interrupt_identity(app_name, user_id, session_id) | 1つの完全一致する識別情報に対する実行をキャンセルします |
active_runs() | 実行中のすべてのランの識別子。識別子が重複している場合は、並行実行を意味します |
active_session_ids() | 実行中のランの重複排除されたセッションID |
// Cancel one tenant's run without touching another that shares the session ID
let cancelled = runner.interrupt_identity("my-app", "user-1", "session-1");
セッションIDはアプリとユーザーの範囲内でのみ一意であるため、interrupt(session_id) は
より広い形式であり、interrupt_identity はより厳密な形式です。単一の
interrupt_identity が複数のアプリまたはユーザーにまたがる場合は、Runner を優先してください。
Run はセッションIDではなく一意の run ID で追跡されるため、同じ ID に対する 2 つの run は別々に追跡され、それぞれが自分自身のみを登録解除します。
永続化は ID に紐づく
Runner が永続化するすべてのイベント — ユーザーのターン、モデル応答、転送イベント、
プラグインイベント、圧縮イベント — は、完全な
SessionService::append_event_for_identity と (app_name, user_id, session_id) の triple を使って書き込まれます。自然キーが
複合である SessionService は、したがって各イベントをそのテナントに結び付けられ、
raw-session-ID の append_event パスを完全に拒否または無視できます。
実行フロー
┌─────────────────────────────────────────────────────────────┐
│ Runner.run() │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 1. Session Retrieval │
│ │
│ SessionService.get(app_name, user_id, session_id) │
│ → Creates new session if not exists │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. Agent Selection │
│ │
│ Check session state for active agent │
│ → Use root agent or transferred agent │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 3. Context Creation │
│ │
│ InvocationContext with: │
│ - Session (mutable) │
│ - Artifacts (scoped to session) │
│ - Memory (if configured) │
│ - Run config │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 4. Agent Execution │
│ │
│ agent.run(ctx) → EventStream │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 5. Event Processing │
│ │
│ For each event: │
│ - Update session state │
│ - Handle transfers │
│ - Forward to caller │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 6. Session Save │
│ │
│ SessionService.append_event(session, events) │
└─────────────────────────────────────────────────────────────┘
InvocationContext
実行中にエージェントに提供されるコンテキスト:
pub trait InvocationContext: CallbackContext {
/// The agent being executed
fn agent(&self) -> Arc<dyn Agent>;
/// Memory service (if configured)
fn memory(&self) -> Option<Arc<dyn Memory>>;
/// Current session
fn session(&self) -> &dyn Session;
/// Execution configuration
fn run_config(&self) -> &RunConfig;
/// Signal end of invocation
fn end_invocation(&self);
/// Check if invocation has ended
fn ended(&self) -> bool;
}
RunConfig
実行オプション:
pub struct RunConfig {
/// Streaming mode for responses
pub streaming_mode: StreamingMode,
// ... other fields (tool_confirmation_decisions, cached_content, etc.)
}
ToolExecutionStrategy
単一の LLM 応答からの複数のツール呼び出しをどのようにディスパッチするかを制御します:
| 戦略 | 動作 |
|---|---|
Sequential (default) | LLMが返した順序でツールを1つずつ実行する |
Parallel | すべてのツールを同時に実行する。安全性は呼び出し側が担う |
Auto | 安全な読み取り専用のサブセットを並行実行し、その後、残りのすべての呼び出しを順次実行する |
LlmAgentBuilderごとに設定:
use adk_core::ToolExecutionStrategy;
let agent = LlmAgentBuilder::new("fast_agent")
.model(model)
.tool_execution_strategy(ToolExecutionStrategy::Auto)
.tool(Arc::new(
search_tool
.with_read_only(true)
.with_concurrency_safe(true),
))
.tool(Arc::new(save_tool)) // runs after the concurrent safe subset
.build()?;
Autoモードでは、ディスパッチループはis_read_only()とis_concurrency_safe()の両方を照会します。選択されたツールが両方のメソッドでtrueを返す呼び出しは、最初に並行して実行されます。その後、残りの呼び出しは順次実行されます。Parallelは、明示的な呼び出し元のオーバーライドとして、これらのメタデータチェックをバイパスします。結果は、戦略に関係なく、常に元のLLMが返した順序で再構成されます。失敗したツールは、バッチを中断せずにJSONのエラーレスポンスを生成します。
pub enum StreamingMode {
/// No streaming, return complete response
None,
/// Server-Sent Events (default)
SSE,
/// Bidirectional streaming (realtime)
Bidi,
}
エージェント転送
Runner は複数エージェント間の転送を自動的に処理します:
// In an agent's tool or callback
if should_transfer {
// Set transfer in event actions
ctx.set_actions(EventActions {
transfer_to_agent: Some("specialist_agent".to_string()),
..Default::default()
});
}
Runner は以下を行います:
- イベント内の転送要求を検出する
- sub_agents内で対象エージェントを見つける
- 新しいアクティブエージェントでセッション状態を更新する
- 新しいエージェントで実行を継続する
コンテキスト圧縮
長時間実行されるセッションでは、自動コンテキスト圧縮を有効にして、LLMのコンテキストウィンドウを有限に保ちます:
use adk_runner::{Runner, RunnerConfig, EventsCompactionConfig};
use adk_agent::LlmEventSummarizer;
use std::sync::Arc;
let summarizer = LlmEventSummarizer::new(model.clone());
let config = RunnerConfig {
// ... other fields ...
compaction_config: Some(EventsCompactionConfig {
compaction_interval: 3, // Compact every 3 invocations
overlap_size: 1, // Keep 1 event overlap for continuity
summarizer: Arc::new(summarizer),
}),
// ...
};
圧縮がトリガーされると、古いイベントは要約イベントに置き換えられます。conversation_history()は自動的に元のイベントの代わりに要約を使用します。
完全なドキュメントについては、Context Compactionを参照してください。
Launcher との統合
Launcherは内部でRunnerを使用します:
// Launcher creates Runner with default services
Launcher::new(agent)
.app_name("my_app")
.run()
.await?;
// Equivalent to using the builder:
let runner = Runner::builder()
.app_name("my_app")
.agent(agent)
.session_service(Arc::new(InMemorySessionService::new()))
.build()?;
カスタム Runner の使用
高度なシナリオでは、Runner を直接使用します:
use adk_runner::Runner;
// Production configuration using the builder
let runner = Runner::builder()
.app_name("production_app")
.agent(my_agent)
.session_service(Arc::new(SqliteSessionService::new(db_pool)))
.artifact_service(Arc::new(S3ArtifactService::new(s3_client)))
.memory_service(Arc::new(QdrantMemoryService::new(qdrant_client)))
.build()?;
// Use in HTTP handler with run_str() for convenience
async fn chat_handler(runner: &Runner, request: ChatRequest) -> Response {
let stream = runner.run_str(
&request.user_id,
&request.session_id,
request.content,
).await?;
// Stream events to client
Response::sse(stream)
}
前: ← Core Types | 次: Launcher →