Interações do Gemini API (Beta)

ADK-Rust fornece um cliente dedicado para as Interações API do Google — a nova direção do Google para o API Gemini. Ele substitui o formato de solicitação/resposta generateContent por um recurso Interaction com estado, estruturado em torno de uma linha do tempo de etapas tipadas, histórico no servidor e fluxos de trabalho agênticos nativos.

A API de Interações está em beta. O Google recomenda generateContent para cargas de trabalho de produção estáveis e pode fazer alterações incompatíveis no esquema de Interações. ADK-Rust fixa o contrato Api-Revision: 2026-05-20 (esquema de etapas).

Visão geral

┌─────────────────────────────────────────────────────────────────────┐
│                  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                            │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Quando usar qual API

AspectogenerateContent (GeminiModel)Interações API (create_interaction)
EndpointPOST /v1beta/models/{model}:generateContentPOST /v1beta/interactions
EstabilidadeEstável, recomendado para produçãoBeta, o esquema pode mudar
HistóricoCliente reenvia a transcrição completaNo lado do servidor via previous_interaction_id
Formato da respostacandidates + partsLinha do tempo steps
Runtime do agente (trait Llm)Transporte padrão ✅Adesão opcional via use_interactions_api
Novos modelos / ferramentasLançados aqui primeiro

O ambiente de execução do agente ADK (a característica Llm, o loop de ferramentas e Runner) usa generateContent por padrão. Você também pode executar as Interactions API por meio do mesmo ambiente de execução ativando use_interactions_api(true) em um GeminiModel — consulte Interactions como transporte do ambiente de execução abaixo. O cliente direto (documentado primeiro) continua disponível para chamadores que desejam histórico no servidor, etapas observáveis ou modelos exclusivos da versão beta sem envolver um agente.

Ativação

# 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"] }

O recurso não adiciona nenhuma dependência nova e é totalmente aditivo ao API generateContent existente.

Início rápido

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());

Streaming

Durante o streaming, API emite um modelo de eventos SSE orientado a etapas. O caminho mais comum é acumular fragmentos de texto de eventos 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}");
    }
}

Tipos de evento: interaction.created, step.start, step.delta, step.stop, interaction.status_update, interaction.completed e error. Eventos futuros desconhecidos são desserializados em InteractionSseEvent::Other em vez de fazer o stream falhar.

Vários turnos no servidor

Passe o id de uma interação anterior para continuar a conversa sem reenviar o histórico. Observe que tools, system_instruction e generation_config são específicos da interação e devem ser especificados novamente a cada turno:

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

Chamada de funções

API de Interactions expõe chamadas de ferramentas do lado do cliente como etapas function_call com um status requires_action. Forneça os resultados em um turno subsequente:

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

Saída estruturada

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

Ciclo de vida

As interações armazenadas (o padrão do servidor) podem ser recuperadas, excluídas ou canceladas:

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

Valores de status

InteractionStatus reflete o ciclo de vida de API: InProgress, RequiresAction, Completed, Failed, Cancelled, Incomplete, BudgetExceeded. Use is_terminal() e requires_action() para o fluxo de controle.

Limitações

O Interactions API ainda não oferece suporte a Batch API nem ao armazenamento em cache explícito (o armazenamento em cache implícito no lado do servidor está disponível via previous_interaction_id). O runtime do agente ADK usa generateContent por padrão; o Interactions API está disponível tanto como o cliente independente documentado acima quanto como um transporte de runtime opcional (veja abaixo).


Interactions como transporte de runtime (agentes + executor)

Tudo acima documenta o cliente de conexão direta (adk_gemini::interactions) — uma capacidade independente que você chama manualmente. Esta seção documenta o transporte de runtime criado sobre ele: uma opção em GeminiModel que permite que um LlmAgent, Runner, loop de ferramentas e sessões comuns conduzam o Interactions API com zero alterações no código do seu agente.

Isso reproduz o comportamento de ADK-Python, em que Gemini(model=..., use_interactions_api=True) mantém o mesmo Agent, executor e ferramentas. Um agente é independente do transporte: alterar a forma como o modelo se comunica com o backend não deve exigir um novo tipo de agente.

generateContent ainda é o padrão

generateContent continua sendo o transporte padrão e recomendado para cargas de trabalho estáveis em produção. O Interactions API está em beta e seu esquema pode mudar. Opte deliberadamente pelo transporte, por modelo. Quando você não chama use_interactions_api(true), um GeminiModel se comporta exatamente como antes — não há alteração de comportamento no caminho do generateContent.

Habilitando o transporte

O transporte é controlado pelo recurso gemini-interactions (encaminhado de adk-rustadk-modeladk-gemini/interactions):

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

Ative a opção no modelo e envolva-o em um LlmAgent e Runner comuns — nada mais na configuração do agente muda:

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

Padrões fiéis

O transporte assume como padrão a postura pretendida pelo Interactions API, configurada por meio de InteractionOptions (reexportado de adk_model::gemini):

OpçãoPadrãoSignificado
storetrueAs interações são armazenadas no servidor para que a continuação com estado e a observabilidade funcionem imediatamente.
statefultrueAs conversas com várias interações continuam por meio de previous_interaction_id; somente o conteúdo da interação atual é enviado ao encadear.
backgroundBackgroundMode::AgentTargetsOnlybackground=true para destinos de agentes (pesquisa aprofundada, execução prolongada); false para destinos de modelos, para que as interações de chat permaneçam com baixa latência.
poll_interval1sCom que frequência uma interação em segundo plano é consultada até atingir um estado terminal.

Substitua qualquer um destes por 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 tem três variantes: AgentTargetsOnly (padrão), Always e Never.

Quando store é false, as regras de incompatibilidade de API desabilitam a continuação com estado e a execução em segundo plano; o transporte então envia a entrada da transcrição, exatamente como generateContent.

Destinos compatíveis (lista de permissões)

O API de Interactions é compatível com um conjunto fixo de destinos. use_interactions_api(true) valida o id do modelo no momento da configuração e retorna um AdkError com a categoria InvalidInput (indicando os destinos compatíveis) quando o id não está na lista de permissões — em vez de adiar para uma rejeição opaca do servidor.

Um destino de modelo define o campo model da solicitação; um destino de agente define o campo agent.

Destinos de modelo:

  • 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

Esta é a lista de permissões de compatibilidade do transporte, não uma lista de recomendações. Aplicações novas devem começar com gemini-3.7-flash; IDs antigos e de pré-visualização continuam listados porque o endpoint Interactions ainda os aceita.

Destinos de agente:

  • 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, .. }

O enum InteractionTarget (também reexportado de adk_model::gemini) representa um destino validado caso você precise inspecionar a classificação diretamente.

Misturando ferramentas integradas e personalizadas (bypass_multi_tools_limit)

O API de Interactions proíbe misturar ferramentas integradas (do lado do servidor) com ferramentas de função personalizadas em uma única solicitação. Para usar, por exemplo, a Pesquisa Google junto com sua própria ferramenta de função, converta a ferramenta integrada em uma ferramenta de chamada de função para que todo o conjunto de ferramentas seja uniforme. Isso reflete o bypass_multi_tools_limit=True do ADK-Python.

A conversão reside no trait BypassMultiToolsLimit, implementado pelos wrappers de ferramentas integradas (GoogleSearchTool, UrlContextTool, GeminiFileSearchTool). with_bypass_multi_tools_limit(agent) recebe um agente interno de pesquisa fundamentada de turno único — um LlmAgent comum configurado com a ferramenta integrada e um modelo Gemini — e retorna um Arc<dyn Tool> que relata is_builtin() == false e executa internamente o comportamento integrado, retornando uma resposta de função normal.

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()?,
);

Se você deixar uma ferramenta integrada sem desvio ao misturá-la com ferramentas de função usando o transporte Interactions, a criação da solicitação retornará um AdkError com a categoria InvalidInput, indicando with_bypass_multi_tools_limit. O ID da chamada de função faz o percurso de ida e volta inalterado pelo loop da ferramenta, exatamente como em generateContent.

Continuidade com estado e o fallback de retenção

interaction_id é um campo de primeira classe, não um canal secundário. Todo LlmResponse contém interaction_id: Option<String> (preenchido pelo transporte Interactions, None caso contrário), e Event o expõe por meio do acessador event.interaction_id() — espelhando ADK-Python's event.interaction_id.

A continuidade é independente do provedor. LlmRequest contém um campo aditivo previous_response_id: Option<String> que LlmAgent preenche a partir do interaction_id do evento mais recente. O transporte Interactions o mapeia para previous_interaction_id da solicitação e envia somente o conteúdo do turno atual (em vez da transcrição completa). Nenhuma lógica específica do Gemini reside em adk-agent; o campo não é utilizado (é uma operação no-op) para generateContent e outros provedores.

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"

Fallback da janela de retenção. As interações armazenadas expiram. Se um previous_interaction_id fornecido estiver desatualizado ou expirado, o servidor retornará NotFound. O transporte trata isso de forma transparente: ele recorre ao envio da transcrição completa e inicia uma nova interação — nenhum erro é exposto ao agente ou ao executor. As conversas de múltiplos turnos continuam funcionando além do limite de retenção sem tratamento especial no seu código.

Tipos reexportados

Por trás do recurso gemini-interactions, os seguintes itens estão disponíveis em adk_model::gemini:

  • GeminiTransportGenerateContent (padrão) ou Interactions.
  • InteractionOptionsstore, stateful, background, poll_interval.
  • BackgroundModeAgentTargetsOnly (padrão), Always, Never.
  • InteractionTarget — um destino de modelo/agente validado.

A superfície de bypass está em adk-tool (acessível por meio de adk_tool ou do pacote abrangente):

  • Trait BypassMultiToolsLimit com with_bypass_multi_tools_limit(agent).
  • Implementado por GoogleSearchTool, UrlContextTool, GeminiFileSearchTool.

Os campos principais aditivos LlmResponse.interaction_id e LlmRequest.previous_response_id estão sempre presentes (não são condicionados por recursos), portanto o acessador event.interaction_id() é compilado independentemente de quais provedores estejam habilitados.