Gemini Interactions e Deep Research
GeminiInteractions é uma camada fina e específica do Gemini sobre a
Interactions API do SDK
@google/genai. Ela difere do LLM em dois pontos:
- O estado fica no servidor: cada chamada devolve um
ide a próxima continua a conversa compreviousInteractionId, sem reenviar o histórico. - É o caminho para os agentes gerenciados do Google, como o Deep Research, que roda em background por vários minutos.
A Interactions API e os agentes estão em preview: nomes de agente e campos mudam com frequência.
npm install jangada-ai @google/genai@latestExige @google/genai 1.33 ou mais novo (é a versão que traz
client.interactions). Com um SDK mais antigo, a jangada lança
UnsupportedError pedindo a atualização. A chave vem de GEMINI_API_KEY ou
GOOGLE_API_KEY, ou de apiKey no construtor.
import { GeminiInteractions, webSearch } from "jangada-ai";
const gi = new GeminiInteractions({ model: "gemini-3.8-flash" });
const r = await gi.create("Quem venceu a Copa do Mundo de 2002?", { tools: [webSearch()] });
console.log(r.text);
for (const c of r.citations) console.log("-", c.title, c.url);
// continua a conversa no servidor, sem reenviar histórico
const r2 = await gi.create("E o artilheiro?", { previousInteractionId: r.id ?? undefined });Quando usar GeminiInteractions e quando usar LLM
new LLM("gemini", ...): o caminho padrão. Troca de provider sem mudar código, retry, fallback, cache e guardrails. As tools nativas do Gemini também funcionam por lá.GeminiInteractions: quando você quer o estado no servidor, os agentes gerenciados ou tarefas longas em background com polling. Não tem retry nem fallback.
API
const gi = new GeminiInteractions({
apiKey: process.env.GEMINI_API_KEY, // padrão: GEMINI_API_KEY ou GOOGLE_API_KEY
model: "gemini-3.8-flash", // padrão do create(); dispensável quando a chamada usa agent
vertexai: false, // true usa o Vertex AI
clientOptions: {}, // repassado ao construtor do GoogleGenAI
});| Método | O que faz |
|---|---|
create(input, options) | Cria uma interação ou dispara um agente |
stream(input, options) | Igual ao create, produzindo InteractionEvent (async iterator) |
resumeStream(id, { lastEventId }) | Retoma um stream interrompido |
get(id), cancel(id), delete(id) | Estado, cancelamento e remoção |
wait(target, { pollIntervalMs, timeoutMs, onUpdate }) | Polling até sair de queued/in_progress |
run(input, { tools, maxIterations, onToolCall, onToolResult }) | Loop de function calling com execução local |
deepResearch(prompt, { agent, wait, pollIntervalMs, timeoutMs, onUpdate }) | Dispara o Deep Research em background |
close() | Fecha o cliente nativo |
Opções de create/stream: model ou agent (um dos dois), agentConfig,
tools, system, previousInteractionId, store, background,
responseFormat, toolChoice, maxTokens, seed, stop, thinkingLevel,
thinkingSummaries, extra (campos do corpo em snake_case) e signal.
InteractionResult
id,status(queued,in_progress,requires_action,completed,failed,cancelled,incomplete,budget_exceeded),done,requiresAction.text,steps(como vieram da API, em snake_case),citations({ type, url, title, start, end }).functionCalls: chamadas pendentes (InteractionFunctionCallcomid,name,argse.result(output, { isError })).usage,cost,parsed,model,agent,errors,raw.- Preenchidos pelo
run:toolTrace,iterations,stoppedByLimit,usageTotal,costTotal,costComplete. raiseForStatus(): lançaProviderErrorse terminou emfailed,cancelledoubudget_exceeded.
Tools
tools aceita:
- function tools da jangada (
tool({ name, parameters, execute })), que viram{ type: "function" }; - tools nativas canônicas:
webSearch(),urlContext(),codeExecution(),fileSearch(...),googleMaps(...)enativeTool("gemini", spec); - objetos crus da API (
{ type: "google_search" },{ type: "mcp_server", ... }).
Tools nativas que não existem na Interactions API (como imageGeneration()) ou
nativeTool de outro provider lançam UnsupportedError. Diferente do Python,
uma função solta não vira tool: use tool(...).
Function calling com execução local (run)
O run cria a interação, executa as funções que o modelo pedir e responde com
previousInteractionId até a resposta final ou maxIterations (padrão 10). Uma
tool que falha, que não tem execute ou que foi vetada (onToolCall devolvendo
false) vira resultado com erro, e o modelo pode reagir.
import * as z from "zod";
import { tool } from "jangada-ai";
const cotacao = tool({
name: "cotacao",
description: "Cotação da moeda em reais.",
parameters: z.object({ moeda: z.string() }),
execute: ({ moeda }) => ({ USD: 5.4, EUR: 5.9 })[moeda.toUpperCase()] ?? 0,
});
const r3 = await gi.run("Quanto custam 100 dólares em reais?", { tools: [cotacao] });
console.log(r3.text, r3.toolTrace.map((t) => t.name), r3.costTotal);Para controlar o loop à mão, use create e responda a functionCalls:
let r4 = await gi.create("Quanto custam 100 dólares?", { tools: [cotacao] });
if (r4.requiresAction) {
const respostas = [];
for (const c of r4.functionCalls) {
respostas.push(c.result(await cotacao.execute?.(await cotacao.parseArgs(c.args))));
}
r4 = await gi.create(respostas, { previousInteractionId: r4.id ?? undefined, tools: [cotacao] });
}Structured output
responseFormat aceita Zod 4.2+, Standard Schema ou jsonSchema(...). O
resultado validado fica em parsed; JSON fora do schema lança
OutputValidationError. Um objeto JSON Schema cru vai para a API sem validação e
sem parsed.
const Resumo = z.object({ titulo: z.string(), pontos: z.array(z.string()) });
const r5 = await gi.create<z.infer<typeof Resumo>>("Resuma a história do frevo.", {
responseFormat: Resumo,
});
console.log(r5.parsed?.titulo);Streaming
stream produz InteractionEvent com type em created, status,
step_start, text, thought, delta, step_stop, completed (com result
final) e error (lança ProviderError).
for await (const ev of gi.stream("Explique RAG em uma frase.")) {
if (ev.type === "text") process.stdout.write(ev.text ?? "");
}Deep Research (background)
const rel = await gi.deepResearch("Panorama do mercado de LLMs open-source em 2026, em 5 tópicos.", {
onUpdate: (x) => console.log("status:", x.status),
timeoutMs: 30 * 60_000,
});
console.log(rel.text);- Roda com
background: trueestore: true(a API exigestoreem background) e faz polling a cada 10 s (pollIntervalMs). wait: falsedevolve logo o resultado emqueued/in_progress. Depois usegi.wait(r)ougi.get(r.id).timeoutMsestourado lançaAPITimeoutError. A interação continua rodando no servidor e você pode retomar comwaitouget.signalinterrompe o polling e ogetem andamento.- O agente padrão é
DEEP_RESEARCH_AGENT("deep-research-preview-04-2026"). Outros agentes vão emagent, comagentConfigrepassado como veio. - Uma tarefa leva minutos e consome de 100 mil a milhões de tokens.
Usage e custo
O usage segue o contrato da lib: inputTokens (input mais os tokens de
resultado de tool), outputTokens (inclui os thoughts), reasoningTokens,
cacheReadTokens, toolUseTokens, serverToolRequests e totalTokens. O
custo é calculado quando há model. Com agent ele fica null, porque não há
tabela de preço por agente.
Limitações
temperaturenão existe na Interactions API: é aceita e ignorada.maxTokens,seed,stopethinkingLevelvão nogeneration_config.- Sem retry, fallback, cache ou guardrails (use o
LLMpara isso). streamnão roda o loop de tools dorun.vertexai: trueusa o Vertex AI, mas a disponibilidade da Interactions API lá depende do projeto e da região.
Diferenças em relação ao Python
- Só async. O
await_do Python éwaitaqui. - Tempos em milissegundos:
pollIntervalMsetimeoutMsno lugar depoll_intervaletimeout(segundos). - Toda chamada aceita
signal. - Requisito do SDK:
@google/genai1.33+ (no Python,google-genai2.3+).
Relacionado: Providers, Tools, Structured output.