Gemini インタラクション API(ベータ)
ADK-Rust は、Google の Interactions API 専用クライアントを提供します。これは Gemini API に対する Google の新しい方針です。generateContent のリクエスト/レスポンス形式を、型付きステップタイムライン、サーバー側の履歴、ネイティブなエージェントワークフローを中心としたステートフルな Interaction リソースに置き換えます。
Interactions API はベータ版です。Google は安定した本番ワークロードには generateContent を推奨しており、Interactions スキーマに破壊的変更を加える可能性があります。ADK-Rust は Api-Revision: 2026-05-20(ステップスキーマ)コントラクトを固定します。
概要
┌─────────────────────────────────────────────────────────────────────┐
│ Gemini Interactions API Client │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Endpoint: POST /v1beta/interactions │
│ Builder: Gemini::create_interaction() │
│ Feature: interactions (adk-gemini) │
│ gemini-interactions (adk-model / adk-rust) │
│ │
│ Capabilities: │
│ • Single-turn and streaming (step.delta events) │
│ • Server-side history via previous_interaction_id │
│ • Typed step timeline (thought, function_call, model_output, …) │
│ • Multimodal input (text, image, audio, document, video) │
│ • Structured output (response_format JSON schema) │
│ • Client-side function calling + built-in server tools │
│ • Background / long-running tasks (background = true) │
│ • Lifecycle: get / delete / cancel a stored interaction │
│ │
│ vs generateContent (GeminiModel): │
│ • Stateful conversations (server stores history) │
│ • Observable execution steps for agentic UIs │
│ • New models & tools launch here first │
│ │
└─────────────────────────────────────────────────────────────────────┘
どの API をいつ使うか
| 項目 | generateContent(GeminiModel) | インタラクション API(create_interaction) |
|---|---|---|
| エンドポイント | POST /v1beta/models/{model}:generateContent | POST /v1beta/interactions |
| 安定性 | 安定版、本番環境での使用を推奨 | ベータ版、スキーマは変更される可能性があります |
| 履歴 | クライアントが完全なトランスクリプトを再送信 | previous_interaction_id 経由のサーバー側 |
| 応答形式 | candidates + parts | steps のタイムライン |
エージェントランタイム(Llm トレイト) | ✅ デフォルトのトランスポート | ✅ use_interactions_api 経由でオプトイン |
| 新しいモデル / ツール | — | まずここで開始 |
ADK エージェントランタイム(Llm トレイト、ツールループ、および Runner)は、デフォルトで generateContent を使用します。また、GeminiModel で use_interactions_api(true) を有効に切り替えることで、同じランタイムを通じて Interactions API を実行することもできます — 詳細は以下の ランタイムトランスポートとしての Interactions を参照してください。ダイレクトクライアント(最初に説明)は、エージェントを介さずにサーバー側の履歴、観測可能なステップ、またはベータ版のみのモデルを利用したい呼び出し元向けに、引き続き使用できます。
有効化
# Direct client (adk-gemini)
adk-gemini = { version = "2.1.0", features = ["interactions"] }
# Through the model facade / umbrella
adk-model = { version = "2.1.0", features = ["gemini-interactions"] }
adk-rust = { version = "2.1.0", features = ["gemini-interactions"] }
この機能によって新しい依存関係は追加されず、既存の generateContent API に完全に追加されます。
クイックスタート
use adk_gemini::{Gemini, Model, ThinkingLevel};
let gemini = Gemini::new(std::env::var("GEMINI_API_KEY")?)?;
let interaction = gemini
.create_interaction()
.model(Model::Gemini35Flash)
.system_instruction("You are concise.")
.input_text("What is the capital of France?")
.thinking_level(ThinkingLevel::Low)
.send()
.await?;
println!("{}", interaction.output_text().unwrap_or_default());
ストリーミング
ストリーミング時、API はステップ単位の SSE イベントモデルを発行します。最も一般的な
パスは、step.delta イベントからテキストの断片を蓄積する方法です。
use futures::StreamExt;
let mut stream = gemini
.create_interaction()
.model(Model::Gemini35Flash)
.input_text("Write a haiku about Rust.")
.stream()
.await?;
while let Some(event) = stream.next().await {
if let Some(fragment) = event?.text_delta() {
print!("{fragment}");
}
}
イベントの種類: interaction.created、step.start、step.delta、step.stop、
interaction.status_update、interaction.completed、および error。未知の
将来のイベントはストリームを失敗させるのではなく、InteractionSseEvent::Other にデシリアライズされます。
サーバー側のマルチターン
会話を再送信せずに継続するには、以前のインタラクションのidを渡します。なお、tools、system_instruction、generation_configはインタラクション単位で適用されるため、各ターンで再指定する必要があります。
let first = gemini.create_interaction()
.model(Model::Gemini35Flash)
.input_text("My favorite color is teal.")
.send().await?;
let second = gemini.create_interaction()
.model(Model::Gemini35Flash)
.previous_interaction_id(&first.id)
.input_text("What is my favorite color?")
.send().await?;
関数呼び出し
Interactions APIは、クライアント側のツール呼び出しを、requires_actionステータスのfunction_callステップとして公開します。後続のターンで結果を提供します。
use serde_json::json;
let interaction = gemini.create_interaction()
.model(Model::Gemini35Flash)
.function("get_weather", "Get the weather",
json!({"type": "object", "properties": {"location": {"type": "string"}}}))
.input_text("Weather in Boston?")
.send().await?;
if interaction.status.requires_action() {
let follow_up = gemini.create_interaction()
.model(Model::Gemini35Flash)
.previous_interaction_id(&interaction.id);
let mut follow_up = follow_up;
for (call_id, name, _args) in interaction.pending_function_calls() {
follow_up = follow_up.function_result(call_id, name, json!({"temperature": "72F"}));
}
let final_interaction = follow_up.send().await?;
println!("{}", final_interaction.output_text().unwrap_or_default());
}
構造化出力
use serde_json::json;
let interaction = gemini.create_interaction()
.model(Model::Gemini35Flash)
.input_text("Summarize this article: ...")
.json_schema(json!({
"type": "object",
"properties": { "summary": { "type": "string" } },
"required": ["summary"]
}))
.send().await?;
ライフサイクル
保存されたインタラクション(サーバーのデフォルト)は、取得、削除、またはキャンセルできます。
let fetched = gemini.get_interaction(&interaction.id, /* include_input */ true).await?;
gemini.cancel_interaction(&interaction.id).await?; // background tasks only
gemini.delete_interaction(&interaction.id).await?;
ステータス値
InteractionStatus は API のライフサイクルを反映します: InProgress、RequiresAction、
Completed、Failed、Cancelled、Incomplete、BudgetExceeded。制御フローには
is_terminal() と requires_action() を使用します。
制限事項
Interactions API は、Batch API や明示的なキャッシュをまだサポートしていません
(サーバー側の暗黙的なキャッシュは previous_interaction_id を介して利用できます)。
ADK エージェントランタイムはデフォルトで generateContent を使用します。Interactions API は、
上記で説明したスタンドアロン クライアントとしてだけでなく、オプトインのランタイムトランスポートとしても
利用できます(以下を参照)。
ランタイムトランスポートとしての Interactions(エージェント + runner)
ここまでの内容では、ダイレクトワイヤークライアント(adk_gemini::interactions)
について説明しました。これは手動で呼び出すスタンドアロン機能です。このセクションでは、
その上に構築された ランタイムトランスポートについて説明します。GeminiModel の
トグルを有効にすると、通常の LlmAgent、Runner、ツールループ、セッションで
Interactions API を駆動できます。しかも、エージェントコードを一切変更する必要はありません。
これはADK-Python と同様で、Gemini(model=..., use_interactions_api=True)は同じAgent、runner、ツールを維持します。エージェントはトランスポートに依存しません。モデルがバックエンドと通信する方法を切り替える際に、新しいエージェント型は必要ありません。
generateContent は引き続きデフォルトです
generateContentは、安定した本番ワークロード向けのデフォルトかつ推奨されるトランスポートです。Interactions APIはベータ版であり、そのスキーマは変更される可能性があります。モデルごとに、意図的にこのトランスポートを選択してください。use_interactions_api(true)を呼び出さない場合、GeminiModelは従来どおり正確に動作します。つまり、generateContent パスの動作に変更はありません。
トランスポートの有効化
トランスポートは gemini-interactions 機能の背後で無効化されています(adk-rust → adk-model → adk-gemini/interactions から転送):
adk-model = { version = "2.1.0", features = ["gemini-interactions"] }
adk-rust = { version = "2.1.0", features = ["gemini-interactions"] }
モデルでスイッチを有効にし、通常の LlmAgent と Runner でラップします。エージェントのセットアップに関するその他の変更は必要ありません:
use adk_agent::LlmAgentBuilder;
use adk_core::{Content, Part, SessionId, UserId};
use adk_model::GeminiModel;
use adk_runner::Runner;
use adk_session::{CreateRequest, InMemorySessionService, SessionService};
use futures::StreamExt;
use std::collections::HashMap;
use std::sync::Arc;
// 1. Build a Gemini model and toggle the Interactions transport.
// `use_interactions_api` validates the model id against the allowlist and
// returns `Result<Self>`, so it is fallible (`?`).
let model = GeminiModel::new(std::env::var("GEMINI_API_KEY")?, "gemini-3.7-flash")?
.use_interactions_api(true)?;
// 2. Wrap it in a normal LlmAgent — unchanged agent API.
let agent = Arc::new(
LlmAgentBuilder::new("assistant")
.instruction("You are concise.")
.model(Arc::new(model))
.build()?,
);
// 3. Drive it through the standard Runner.
let sessions: Arc<dyn SessionService> = Arc::new(InMemorySessionService::new());
sessions
.create(CreateRequest {
app_name: "assistant".into(),
user_id: "user".into(),
session_id: Some("session-1".into()),
state: HashMap::new(),
})
.await?;
let runner = Runner::builder()
.app_name("assistant")
.agent(agent)
.session_service(sessions)
.build()?;
let mut stream = runner
.run(
UserId::new("user")?,
SessionId::new("session-1")?,
Content::new("user").with_text("What is the capital of France?"),
)
.await?;
while let Some(event) = stream.next().await {
let event = event?;
// The server-assigned interaction id is a first-class field on every event.
if let Some(id) = event.interaction_id() {
println!("interaction_id = {id}");
}
if let Some(content) = &event.llm_response.content {
for part in &content.parts {
if let Part::Text { text } = part {
print!("{text}");
}
}
}
}
忠実なデフォルト設定
トランスポートは、InteractionOptions(adk_model::gemini から再エクスポート)で設定された Interactions API's の意図された姿勢をデフォルトで使用します:
| オプション | デフォルト | 意味 |
|---|---|---|
store | true | インタラクションはサーバー側に保存されるため、状態を保持した継続と可観測性がすぐに利用できます。 |
stateful | true | マルチターンの会話は previous_interaction_id を介して継続されます。連鎖時には現在のターンの内容のみが送信されます。 |
background | BackgroundMode::AgentTargetsOnly | エージェントターゲット(Deep Research、長時間実行)向けのbackground=true、モデルターゲット向けのfalse。チャットターンのレイテンシを低く保ちます。 |
poll_interval | 1s | バックグラウンドインタラクションが完了状態になるまでポーリングされる頻度。 |
interaction_options を使用してこれらを上書きします。
use adk_model::gemini::{BackgroundMode, InteractionOptions};
use std::time::Duration;
let model = GeminiModel::new(api_key, "gemini-3.7-flash")?
.use_interactions_api(true)?
.interaction_options(InteractionOptions {
store: true,
stateful: true,
background: BackgroundMode::AgentTargetsOnly,
poll_interval: Duration::from_millis(500),
});
BackgroundMode には、AgentTargetsOnly(デフォルト)、Always、Never の3つのバリアントがあります。
storeがfalseの場合、API の非互換性ルールによって、ステートフルな継続とバックグラウンド実行が無効になります。その後、トランスポートは generateContent とまったく同じように、トランスクリプト入力を送信します。
サポートされるターゲット(許可リスト)
Interactions API は、固定されたターゲットのセットをサポートします。use_interactions_api(true)
は設定時にモデル ID を検証し、ID が許可リストにない場合は、曖昧なサーバー拒否に委ねるのではなく、
カテゴリ InvalidInput(サポートされているターゲットを示す)の AdkError を返します。
モデルターゲットはリクエストの model フィールドを設定し、エージェントターゲットは
agent フィールドを設定します。
モデルターゲット:
gemini-3.7-flashgemini-3.6-flashgemini-3.5-flashgemini-3.5-flash-litegemini-3.1-flash-litegemini-3.1-pro-previewgemini-3-flash-previewgemini-2.5-progemini-2.5-flashgemini-2.5-flash-litelyria-3-clip-previewlyria-3-pro-preview
これはトランスポートの互換性許可リストであり、推奨リストではありません。
新しいアプリケーションでは gemini-3.7-flash から開始してください。古い ID とプレビュー ID が
引き続き掲載されているのは、Interactions エンドポイントがまだそれらを受け付けるためです。
エージェントの対象:
deep-research-pro-preview-12-2025deep-research-preview-04-2026deep-research-max-preview-04-2026
// Unsupported targets fail fast at configuration time:
let result = GeminiModel::new(api_key, "gpt-4")?.use_interactions_api(true);
assert!(result.is_err()); // AdkError { category: InvalidInput, .. }
InteractionTarget 列挙型(adk_model::gemini からも再エクスポートされています)は、
分類を直接調べる必要がある場合に、検証済みの宛先を表します。
組み込みツールとカスタムツールの混在(bypass_multi_tools_limit)
Interactions API では、1つのリクエスト内で組み込み(サーバー側)ツールとカスタム関数ツールを混在させることは禁止されています。Google Search などを独自の関数ツールと併用するには、組み込みツールを関数呼び出しツールに変換し、ツールセット全体を統一してください。これは ADK-Python の
bypass_multi_tools_limit=True を反映したものです。
変換はBypassMultiToolsLimit trait に存在し、組み込みツールのラッパー(GoogleSearchTool、UrlContextTool、GeminiFileSearchTool)によって実装されています。with_bypass_multi_tools_limit(agent)は、内部の単一ターンのグラウンデッド検索エージェント、つまり組み込みツールと Gemini モデルで構成された通常の LlmAgent を受け取り、Arc<dyn Tool>を返します。これは is_builtin() == false を報告し、組み込みの動作を内部で実行して、通常の関数レスポンスを返します。
use adk_agent::LlmAgentBuilder;
use adk_tool::{BypassMultiToolsLimit, FunctionTool, GoogleSearchTool};
use adk_model::GeminiModel;
use std::sync::Arc;
// The grounded-search agent the bypass tool delegates to: a normal LlmAgent
// with the built-in GoogleSearchTool + a Gemini model.
let search_agent = Arc::new(
LlmAgentBuilder::new("grounded-search")
.instruction("Answer the query using Google Search. Be factual and concise.")
.model(Arc::new(GeminiModel::new(&api_key, "gemini-3.7-flash")?))
.tool(Arc::new(GoogleSearchTool::new()))
.build()?,
);
// Convert the built-in search tool into a function tool (is_builtin() == false).
let search_tool = GoogleSearchTool::new().with_bypass_multi_tools_limit(search_agent);
// A custom function tool to mix alongside it.
let weather_tool: Arc<dyn adk_core::Tool> = Arc::new(/* your FunctionTool */);
// Now the tool set is uniform (all function tools) and the Interactions
// transport accepts it.
let model = GeminiModel::new(&api_key, "gemini-3.7-flash")?.use_interactions_api(true)?;
let agent = Arc::new(
LlmAgentBuilder::new("assistant")
.model(Arc::new(model))
.tool(search_tool)
.tool(weather_tool)
.build()?,
);
Interactions トランスポートで、組み込みツールをバイパスしないまま関数ツールと組み合わせると、リクエストの構築時に AdkError が返され、カテゴリ InvalidInput によって with_bypass_multi_tools_limit が示されます。関数呼び出し ID は、generateContent の場合とまったく同様に、ツールループを通過しても変更されません。
ステートフルな継続性と保持のフォールバック
interaction_id はサイドチャネルではなく、第一級フィールドです。すべての
LlmResponse は interaction_id: Option<String> を保持します(Interactions トランスポートによって設定され、
それ以外の場合は None)。また、Event は
event.interaction_id() アクセサーを介してこれを公開します。これは
ADK-Python の event.interaction_id を反映したものです。
継続性はプロバイダーに依存しません。LlmRequest は追加の
previous_response_id: Option<String> フィールドを保持し、LlmAgent が
直近のイベントの interaction_id からその値を設定します。Interactions トランスポートはこれを
リクエストの previous_interaction_id にマッピングし、現在のターンの
コンテンツのみ(完全なトランスクリプトではなく)を送信します。Gemini 固有の連携ロジックは
adk-agent には存在しません。このフィールドは generateContent やその他のプロバイダーでは未使用(何もしない処理)です。
Turn 1: request (transcript) → interaction v1_abc → event.interaction_id() == "v1_abc"
Turn 2: request previous_response_id = "v1_abc"
→ previous_interaction_id = "v1_abc", sends only the new turn
→ interaction v1_def → event.interaction_id() == "v1_def"
保持期間のフォールバック。 保存されたインタラクションには有効期限があります。提供された
previous_interaction_id が古い、または期限切れの場合、サーバーは NotFound を返します。この
トランスポートはこれを透過的に処理します。完全なトランスクリプトの送信にフォールバックし、新しいインタラクションを開始します — エラーがエージェントやランナーに通知されることはありません。複数ターンの会話は、コード内で特別な処理を行わなくても、保持期間の境界を越えて引き続き機能します。
再エクスポートされる型
gemini-interactions 機能が有効な場合、以下を
adk_model::gemini から利用できます:
GeminiTransport—GenerateContent(デフォルト)またはInteractions。InteractionOptions—store、stateful、background、poll_interval。BackgroundMode—AgentTargetsOnly(デフォルト)、Always、Never。InteractionTarget— 検証済みのモデルまたはエージェントの宛先。
バイパスの境界は adk-tool にあります(adk_tool またはアンブレラ経由で到達可能です)。
BypassMultiToolsLimitトレイトとwith_bypass_multi_tools_limit(agent)。GoogleSearchTool、UrlContextTool、GeminiFileSearchToolによって実装されます。
追加された LlmResponse.interaction_id および LlmRequest.previous_response_id のコアフィールドは常に存在し(機能ゲートの対象外であるため)、event.interaction_id() アクセサーは有効なプロバイダーの種類にかかわらずコンパイルされます。