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 をいつ使うか

項目generateContentGeminiModelインタラクション API(create_interaction
エンドポイントPOST /v1beta/models/{model}:generateContentPOST /v1beta/interactions
安定性安定版、本番環境での使用を推奨ベータ版、スキーマは変更される可能性があります
履歴クライアントが完全なトランスクリプトを再送信previous_interaction_id 経由のサーバー側
応答形式candidates + partssteps のタイムライン
エージェントランタイム(Llm トレイト)✅ デフォルトのトランスポートuse_interactions_api 経由でオプトイン
新しいモデル / ツールまずここで開始

ADK エージェントランタイム(Llm トレイト、ツールループ、および Runner)は、デフォルトで generateContent を使用します。また、GeminiModeluse_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.createdstep.startstep.deltastep.stopinteraction.status_updateinteraction.completed、および error。未知の 将来のイベントはストリームを失敗させるのではなく、InteractionSseEvent::Other にデシリアライズされます。

サーバー側のマルチターン

会話を再送信せずに継続するには、以前のインタラクションのidを渡します。なお、toolssystem_instructiongeneration_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 のライフサイクルを反映します: InProgressRequiresActionCompletedFailedCancelledIncompleteBudgetExceeded。制御フローには is_terminal()requires_action() を使用します。

制限事項

Interactions API は、Batch API や明示的なキャッシュをまだサポートしていません (サーバー側の暗黙的なキャッシュは previous_interaction_id を介して利用できます)。 ADK エージェントランタイムはデフォルトで generateContent を使用します。Interactions API は、 上記で説明したスタンドアロン クライアントとしてだけでなく、オプトインのランタイムトランスポートとしても 利用できます(以下を参照)。

ランタイムトランスポートとしての Interactions(エージェント + runner)

ここまでの内容では、ダイレクトワイヤークライアントadk_gemini::interactions) について説明しました。これは手動で呼び出すスタンドアロン機能です。このセクションでは、 その上に構築された ランタイムトランスポートについて説明します。GeminiModel の トグルを有効にすると、通常の LlmAgentRunner、ツールループ、セッションで 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-rustadk-modeladk-gemini/interactions から転送):

adk-model = { version = "2.1.0", features = ["gemini-interactions"] }
adk-rust  = { version = "2.1.0", features = ["gemini-interactions"] }

モデルでスイッチを有効にし、通常の LlmAgentRunner でラップします。エージェントのセットアップに関するその他の変更は必要ありません:

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}");
            }
        }
    }
}

忠実なデフォルト設定

トランスポートは、InteractionOptionsadk_model::gemini から再エクスポート)で設定された Interactions API's の意図された姿勢をデフォルトで使用します:

オプションデフォルト意味
storetrueインタラクションはサーバー側に保存されるため、状態を保持した継続と可観測性がすぐに利用できます。
statefultrueマルチターンの会話は previous_interaction_id を介して継続されます。連鎖時には現在のターンの内容のみが送信されます。
backgroundBackgroundMode::AgentTargetsOnlyエージェントターゲット(Deep Research、長時間実行)向けのbackground=true、モデルターゲット向けのfalse。チャットターンのレイテンシを低く保ちます。
poll_interval1sバックグラウンドインタラクションが完了状態になるまでポーリングされる頻度。

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(デフォルト)、AlwaysNever の3つのバリアントがあります。

storefalse の場合、API の非互換性ルールによって、ステートフルな継続とバックグラウンド実行が無効になります。その後、トランスポートは generateContent とまったく同じように、トランスクリプト入力を送信します。

サポートされるターゲット(許可リスト)

Interactions API は、固定されたターゲットのセットをサポートします。use_interactions_api(true) は設定時にモデル ID を検証し、ID が許可リストにない場合は、曖昧なサーバー拒否に委ねるのではなく、 カテゴリ InvalidInput(サポートされているターゲットを示す)の AdkError を返します。

モデルターゲットはリクエストの model フィールドを設定し、エージェントターゲットは agent フィールドを設定します。

モデルターゲット:

  • gemini-3.7-flash
  • gemini-3.6-flash
  • gemini-3.5-flash
  • gemini-3.5-flash-lite
  • gemini-3.1-flash-lite
  • gemini-3.1-pro-preview
  • gemini-3-flash-preview
  • gemini-2.5-pro
  • gemini-2.5-flash
  • gemini-2.5-flash-lite
  • lyria-3-clip-preview
  • lyria-3-pro-preview

これはトランスポートの互換性許可リストであり、推奨リストではありません。 新しいアプリケーションでは gemini-3.7-flash から開始してください。古い ID とプレビュー ID が 引き続き掲載されているのは、Interactions エンドポイントがまだそれらを受け付けるためです。

エージェントの対象:

  • deep-research-pro-preview-12-2025
  • deep-research-preview-04-2026
  • deep-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 に存在し、組み込みツールのラッパー(GoogleSearchToolUrlContextToolGeminiFileSearchTool)によって実装されています。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 はサイドチャネルではなく、第一級フィールドです。すべての LlmResponseinteraction_id: Option<String> を保持します(Interactions トランスポートによって設定され、 それ以外の場合は None)。また、Eventevent.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 から利用できます:

  • GeminiTransportGenerateContent(デフォルト)または Interactions
  • InteractionOptionsstorestatefulbackgroundpoll_interval
  • BackgroundModeAgentTargetsOnly(デフォルト)、AlwaysNever
  • InteractionTarget — 検証済みのモデルまたはエージェントの宛先。

バイパスの境界は adk-tool にあります(adk_tool またはアンブレラ経由で到達可能です)。

  • BypassMultiToolsLimit トレイトと with_bypass_multi_tools_limit(agent)
  • GoogleSearchToolUrlContextToolGeminiFileSearchTool によって実装されます。

追加された LlmResponse.interaction_id および LlmRequest.previous_response_id のコアフィールドは常に存在し(機能ゲートの対象外であるため)、event.interaction_id() アクセサーは有効なプロバイダーの種類にかかわらずコンパイルされます。