Gemini Interactions API (ベータ版)
ADK-Rust は、Google の Interactions API — Gemini API のための Google の新しい方向性 — の専用クライアントを提供します。これは generateContent のリクエスト/レスポンスの形式を、型付きのステップタイムライン、サーバーサイドの履歴、ネイティブなエージェントワークフローを中心に構築されたステートフルな Interaction リソースに置き換えます。
Interactions API はベータ版です。Google は、安定した本番ワークロードには generateContent を推奨しており、Interactions スキーマに破壊的な変更を加える可能性があります。ADK-Rust は Api-Revision: 2026-05-20 (steps schema) の契約を固定します。
概要
┌─────────────────────────────────────────────────────────────────────┐
│ 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) |
|---|---|---|
| Endpoint | POST /v1beta/models/{model}:generateContent | POST /v1beta/interactions |
| 安定性 | 安定版、本番環境での使用を推奨 | ベータ版、スキーマが変更される可能性あり |
| 履歴 | クライアントが完全なトランスクリプトを再送信 | サーバーサイド(via) previous_interaction_id |
| レスポンスの形式 | candidates + parts | steps タイムライン |
エージェントランタイム (Llm trait) | ✅ デフォルトのトランスポート | ✅ オプトイン(via) use_interactions_api |
| 新しいモデル / ツール | — | ここで最初にリリース |
ADK エージェントランタイム(Llm トレイト、ツールループ、および Runner)は、デフォルトで generateContent を使用します。また、GeminiModel で use_interactions_api(true) を切り替えることで、同じランタイムを通じて Interactions API を駆動することもできます — 以下のランタイムトランスポートとしてのInteractionsを参照してください。直接クライアント(最初に文書化されているもの)は、エージェントを介さずにサーバーサイドの履歴、観測可能なステップ、またはベータ版のみのモデルを必要とする呼び出し元のために引き続き利用可能です。
有効化
# Direct client (adk-gemini)
adk-gemini = { version = "2.0.0", features = ["interactions"] }
# Through the model facade / umbrella
adk-model = { version = "2.0.0", features = ["gemini-interactions"] }
adk-rust = { version = "2.0.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)— 手動で呼び出すスタンドアロン機能 — を文書化しています。このセクションでは、その上に構築されたランタイムトランスポートについて説明します。これは、通常の LlmAgent, Runner, ツールループ、およびセッションが、エージェントコードに一切変更を加えることなくInteractions API を駆動できるようにする GeminiModel のトグルです。
これは 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.0.0", features = ["gemini-interactions"] }
adk-rust = { version = "2.0.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-2.5-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 の意図された姿勢にデフォルト設定されます。
| オプション | デフォルト | 意味 |
|---|---|---|
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-2.5-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.5-flashgemini-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
エージェントターゲット:
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 enum(adk_model::geminiからも再エクスポートされています)は、分類を直接検査する必要がある場合に、検証済みの宛先を表します。
組み込みツールとカスタムツールの混在 (bypass_multi_tools_limit)
Interactions APIは、単一のリクエストで組み込み(サーバーサイド)ツールとカスタム関数ツールを混在させることを禁止しています。例えば、Google Searchを独自の関数ツールと組み合わせて使用するには、組み込みツールを関数呼び出しツールに変換して、ツールセット全体を統一する必要があります。これは、ADK-Pythonのbypass_multi_tools_limit=Trueを反映しています。
この変換は、組み込みツールラッパー(GoogleSearchTool、UrlContextTool、GeminiFileSearchTool)によって実装されるBypassMultiToolsLimitトレイトに存在します。with_bypass_multi_tools_limit(agent)は、組み込みツールとGeminiモデルで構成された通常のLlmAgentである内部の単一ターン型グラウンデッド検索エージェントを受け取り、is_builtin() == falseを報告し、組み込みの動作を内部で実行して、通常の関数応答を返すArc<dyn Tool>を返します。
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-2.5-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-2.5-flash")?.use_interactions_api(true)?;
let agent = Arc::new(
LlmAgentBuilder::new("assistant")
.model(Arc::new(model))
.tool(search_tool)
.tool(weather_tool)
.build()?,
);
Interactionsトランスポートで組み込みツールを関数ツールと混在させながら、その組み込みツールをバイパスしないままにすると、リクエスト構築はカテゴリInvalidInputを持つAdkErrorを返し、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は、LlmAgentが最新イベントのinteraction_idから設定する追加のprevious_response_id: Option<String>フィールドを運びます。Interactionsトランスポートはそれをリクエストのprevious_interaction_idにマッピングし、現在のターンのコンテンツのみを送信します(完全なトランスクリプトではなく)。adk-agentにはGemini固有の接着剤は存在しません。このフィールドは、generateContentや他のプロバイダーでは未使用(no-op)です。
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を返します。トランスポートはこれを透過的に処理します。完全なトランスクリプトを送信するようにフォールバックし、新しいインタラクションを開始します。エージェントやRunnerにはエラーは表面化されません。マルチターン会話は、コードで特別な処理をすることなく、保持境界を越えて機能し続けます。
再エクスポートされた型
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()アクセサーはどのプロバイダーが有効になっているかに関係なくコンパイルされます。