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(단계 스키마) 계약을 고정합니다.

개요

┌─────────────────────────────────────────────────────────────────────┐
│                  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}: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.created, step.start, step.delta, step.stop, interaction.status_update, interaction.completederror. 향후 추가되는 알 수 없는 이벤트는 스트림을 실패시키는 대신 InteractionSseEvent::Other로 역직렬화됩니다.

서버 측 다중 턴

이력을 다시 전송하지 않고 대화를 계속하려면 이전 interaction의 id를 전달합니다. tools, system_instructiongeneration_config은 interaction 범위에 적용되므로 매 턴마다 다시 지정해야 합니다.

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?;

수명 주기

저장된 interaction(서버 기본값)은 조회, 삭제 또는 취소할 수 있습니다.

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(에이전트 + 실행기)

위의 내용은 직접 와이어 클라이언트(adk_gemini::interactions)를 설명합니다. 이는 직접 호출하는 독립형 기능입니다. 이 섹션에서는 그 위에 구축된 런타임 전송 방식을 설명합니다. 즉, GeminiModel의 토글을 통해 일반적인 LlmAgent, Runner, 도구 루프 및 세션이 에이전트 코드를 전혀 변경하지 않고 Interactions API을 구동할 수 있습니다.

이는 ADK-Python과 동일한 방식으로 작동하며, 여기서 Gemini(model=..., use_interactions_api=True)은 동일한 Agent, 실행기 및 도구를 유지합니다. 에이전트는 전송 방식에 종속되지 않습니다. 모델이 백엔드와 통신하는 방식을 변경하기 위해 새로운 에이전트 유형이 필요해서는 안 됩니다.

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

충실한 기본값

전송은 API의 의도된 동작을 기본값으로 사용하며, InteractionOptions을 통해 구성됩니다(adk_model::gemini에서 다시 내보냄):

옵션기본값의미
storetrue상호작용이 서버 측에 저장되므로 상태를 유지한 연속 실행과 관찰 가능성이 기본으로 제공됩니다.
statefultrueprevious_interaction_id을 통해 여러 차례의 대화를 계속할 수 있으며, 연결할 때는 현재 차례의 콘텐츠만 전송됩니다.
backgroundBackgroundMode::AgentTargetsOnly에이전트 대상(심층 연구, 장시간 실행)에는 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 (기본값), Always, 그리고 Never입니다.

storefalse인 경우, API의 비호환성 규칙에 따라 상태 유지 연속 실행과 백그라운드 실행이 비활성화됩니다. 그러면 전송 계층은 generateContent와 정확히 동일하게 대화 기록 입력을 전송합니다.

지원 대상(허용 목록)

Interactions API은 고정된 대상 집합을 지원합니다. use_interactions_api(true)은 구성 시 모델 ID를 검증하며, 해당 ID가 허용 목록에 없으면 불투명한 서버 거부로 처리를 미루는 대신, AdkError을 반환하고 범주를 InvalidInput (지원되는 대상의 이름)으로 지정합니다.

모델 대상은 요청의 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은 단일 요청에서 기본 제공(서버 측) 도구와 사용자 지정 함수 도구를 혼합하는 것을 금지합니다. 예를 들어 Google Search를 자체 함수 도구와 함께 사용하려면 기본 제공 도구를 함수 호출 도구로 변환하여 전체 도구 집합을 동일한 유형으로 만들어야 합니다. 이는 ADK-Python의 bypass_multi_tools_limit=True을 따르는 방식입니다.

변환은 BypassMultiToolsLimit 트레이트에 구현되어 있으며, 기본 제공 도구 래퍼(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은 사이드 채널이 아니라 일급 필드입니다. 모든 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에 매핑하고, 전체 대화 기록이 아니라 현재 턴의 콘텐츠만 전송합니다. adk-agent에는 Gemini 전용 연결 코드가 없으며, 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을 반환합니다. 전송 계층은 이를 투명하게 처리합니다. 전체 대화 기록을 전송하는 방식으로 대체하고 새로운 상호작용을 시작합니다 — agent 또는 runner에 오류가 표시되지 않습니다. 코드에서 별도의 처리를 하지 않아도 여러 차례에 걸친 대화가 보존 기간 경계를 넘어 계속 작동합니다.

다시 내보낸 타입

gemini-interactions 기능을 사용하는 경우 다음 항목을 adk_model::gemini에서 사용할 수 있습니다.

  • GeminiTransportGenerateContent(기본값) 또는 Interactions.
  • InteractionOptionsstore, stateful, background, poll_interval.
  • BackgroundModeAgentTargetsOnly(기본값), Always, Never.
  • InteractionTarget — 검증된 model/agent 대상.

우회 표면은 adk-tool에 있으며(adk_tool 또는 umbrella를 통해 접근 가능):

  • BypassMultiToolsLimit trait 및 with_bypass_multi_tools_limit(agent).
  • GoogleSearchTool, UrlContextTool, GeminiFileSearchTool에서 구현됩니다.

추가된 LlmResponse.interaction_idLlmRequest.previous_response_id 핵심 필드는 항상 존재하므로(feature-gated되지 않음), 어떤 provider가 활성화되어 있는지와 관계없이 event.interaction_id() accessor를 컴파일할 수 있습니다.