Gerenciamento de Sessões na Prompt API — Contexto e Tokens

Na Prompt API, toda interação com o Gemini Nano acontece dentro de uma sessão. Sessões mantêm o contexto da conversa, controlam o uso de tokens e gerenciam o ciclo de vida da inferência. Se você não entender como elas funcionam, vai acabar criando sessões à toa, estourando contexto e desperdiçando recursos.

O que é uma sessão

Uma sessão é o objeto retornado por LanguageModel.create(). Ela encapsula:

  • O contexto conversacional (histórico de prompts e respostas)
  • A janela de contexto (limite máximo de tokens)
  • Os prompts iniciais (system prompt e contexto prévio)
  • As configurações de modalidade (texto, imagem, áudio)
const session = await LanguageModel.create({
  initialPrompts: [
    { role: 'system', content: 'You are a helpful assistant.' }
  ],
  expectedInputs: [{ type: 'text', languages: ['en'] }],
  expectedOutputs: [{ type: 'text', languages: ['en'] }]
});

Context Window: contextUsage e contextWindow

Cada sessão tem um limite de tokens que consegue processar. Monitore o uso com duas propriedades:

// Total de tokens disponíveis na sessão
console.log(session.contextWindow);  // ex: 4096

// Tokens já consumidos (prompts + respostas anteriores)
console.log(session.contextUsage);   // ex: 512

// Tokens restantes
const disponivel = session.contextWindow - session.contextUsage;
console.log(`Disponível: ${disponivel} tokens`);
PropriedadeTipoDescrição
session.contextWindownumberCapacidade total da janela de contexto
session.contextUsagenumberTokens consumidos até o momento

Monitorando uso em tempo real

async function promptComMonitoramento(session, texto) {
  const antes = session.contextUsage;
  const resposta = await session.prompt(texto);
  const depois = session.contextUsage;

  console.log(`Tokens usados neste turno: ${depois - antes}`);
  console.log(`Capacidade restante: ${session.contextWindow - depois}`);

  return resposta;
}

Context Overflow

Quando a janela de contexto fica cheia e um novo prompt chega, o Chrome remove automaticamente pares de prompt+resposta antigos (do início da conversa) pra liberar espaço. A exceção é o system prompt — esse nunca é removido.

Detectando overflow

Use o evento contextoverflow para reagir quando tokens antigos são descartados:

session.addEventListener('contextoverflow', () => {
  console.warn('Context overflow! Mensagens antigas foram removidas.');

  // Opções de reação:
  // 1. Avisar o usuário
  mostrarAviso('Parte do histórico foi descartada para continuar a conversa.');

  // 2. Criar nova sessão com resumo
  criarNovaSessaoComResumo();

  // 3. Simplesmente continuar (comportamento padrão)
});

QuotaExceededError

Se não for possível liberar tokens suficientes (por exemplo, um único prompt é maior que a janela), um QuotaExceededError é lançado:

try {
  const resposta = await session.prompt(textoMuitoLongo);
} catch (error) {
  if (error.name === 'QuotaExceededError') {
    console.error(`Prompt muito longo!`);
    console.error(`Tokens do input: ${error.requested}`);
    console.error(`Tokens disponíveis: ${error.contextWindow}`);

    // Dividir o texto ou usar resumo
    const textoCurto = textoMuitoLongo.substring(0, 2000);
    const resposta = await session.prompt(textoCurto);
  }
}

initialPrompts: configurando o contexto inicial

O initialPrompts define o contexto da sessão no momento da criação. Aceita um array de mensagens com roles system, user e assistant:

const session = await LanguageModel.create({
  initialPrompts: [
    // System prompt: define comportamento (nunca é removido por overflow)
    {
      role: 'system',
      content: 'You are a senior JavaScript developer. Answer concisely with code examples.'
    },
    // Contexto de conversa prévia (opcional)
    { role: 'user', content: 'What is a closure?' },
    {
      role: 'assistant',
      content: 'A closure is a function that captures variables from its lexical scope...'
    },
    // O próximo prompt continuará a partir deste contexto
    { role: 'user', content: 'Can you show a practical example?' }
  ]
});

// O modelo já tem todo o contexto e responderá sobre closures
const resposta = await session.prompt('What about memory leaks with closures?');

Caso de uso: Restaurar sessão após reinício do navegador

// Salvar contexto antes de fechar
function salvarContexto(historico) {
  localStorage.setItem('chat_historico', JSON.stringify(historico));
}

// Restaurar ao reabrir
async function restaurarSessao() {
  const historico = JSON.parse(localStorage.getItem('chat_historico') || '[]');

  const session = await LanguageModel.create({
    initialPrompts: [
      { role: 'system', content: 'You are a helpful assistant.' },
      ...historico
    ]
  });

  return session;
}

append(): adicionando contexto depois da criação

O método append() permite adicionar mensagens ao contexto da sessão após a criação, sem solicitar uma resposta do modelo. Isso é útil pra:

  • Pré-processar inputs multimodais enquanto o usuário interage
  • Adicionar contexto incremental antes de pedir inferência
  • Preparar o modelo com dados que chegam aos poucos
const session = await LanguageModel.create({
  initialPrompts: [
    {
      role: 'system',
      content: 'You are an image analyst. Compare images when asked.'
    }
  ],
  expectedInputs: [{ type: 'image' }, { type: 'text', languages: ['en'] }]
});

// Usuário faz upload da primeira imagem
fileInput1.onchange = async () => {
  await session.append([
    {
      role: 'user',
      content: [
        { type: 'text', value: 'Here is the first image:' },
        { type: 'image', value: fileInput1.files[0] }
      ]
    }
  ]);
  console.log('Primeira imagem processada');
};

// Usuário faz upload da segunda imagem
fileInput2.onchange = async () => {
  await session.append([
    {
      role: 'user',
      content: [
        { type: 'text', value: 'Here is the second image:' },
        { type: 'image', value: fileInput2.files[0] }
      ]
    }
  ]);
  console.log('Segunda imagem processada');
};

// Quando pronto, solicita a comparação
btnComparar.onclick = async () => {
  const analise = await session.prompt('Compare both images in detail.');
  document.getElementById('resultado').textContent = analise;
};

A Promise retornada por append() resolve quando a mensagem foi validada e processada. Ela rejeita se a mensagem não puder ser adicionada.

clone(): bifurcando uma sessão

O clone() cria uma cópia da sessão com todo o contexto e configurações preservados. Na minha experiência, os melhores usos são:

  • Testar continuações diferentes de uma conversa
  • Preservar um “checkpoint” antes de uma interação pesada
  • Criar ramificações de conversa
const session = await LanguageModel.create({
  initialPrompts: [
    { role: 'system', content: 'You are a creative writing assistant.' }
  ]
});

// Desenvolver uma história
await session.prompt('Start a mystery story set in a library.');

// Clonar antes de continuar — preserva o ponto atual
const sessaoAlternativa = await session.clone();

// Caminho A: continua na sessão original
const caminhoA = await session.prompt('The detective finds a hidden door.');

// Caminho B: continua na sessão clonada
const caminhoB = await sessaoAlternativa.prompt('The lights suddenly go out.');

Clone com AbortSignal

const controller = new AbortController();

const clonada = await session.clone({
  signal: controller.signal
});

// Pode cancelar a sessão clonada independentemente
controller.abort();

destroy(): liberando recursos

Quando uma sessão não é mais necessária, destrua pra liberar memória e recursos de GPU:

const session = await LanguageModel.create();
const resposta = await session.prompt('Quick question: what is 2+2?');

// Liberar recursos
session.destroy();

// Qualquer uso posterior lança erro
try {
  await session.prompt('Another question'); // ERRO!
} catch (error) {
  console.error('Sessão destruída:', error.message);
}

Quando destruir

  • Quando o componente/página que usa a sessão é desmontado
  • Após completar uma tarefa isolada (classificação batch, por exemplo)
  • Quando o usuário navega para outra seção da aplicação
  • Em caso de erro irrecuperável

Quando NÃO destruir

  • Se o usuário pode voltar e continuar a conversa
  • Se você pretende reutilizar a sessão em breve (criar sessão tem custo)
  • Em componentes persistentes (sidebar de chat, por exemplo)

Estratégias de gerenciamento

Estratégia 1: sessão por feature

Crie uma sessão dedicada pra cada funcionalidade da aplicação:

class AIFeatures {
  constructor() {
    this.sessions = {};
  }

  async getSessao(feature) {
    if (!this.sessions[feature]) {
      const configs = {
        classificador: {
          initialPrompts: [
            { role: 'system', content: 'Classify content. Answer: positive, negative, or neutral.' }
          ]
        },
        resumidor: {
          initialPrompts: [
            { role: 'system', content: 'Summarize text in 2-3 sentences.' }
          ]
        },
        extrator: {
          initialPrompts: [
            { role: 'system', content: 'Extract structured data from text.' }
          ]
        }
      };

      this.sessions[feature] = await LanguageModel.create(configs[feature]);
    }
    return this.sessions[feature];
  }

  destruirTodas() {
    Object.values(this.sessions).forEach(s => s.destroy());
    this.sessions = {};
  }
}

Estratégia 2: rotação de sessão por contexto

Quando o contexto fica cheio, crie uma nova sessão com resumo do que veio antes:

async function promptComRotacao(session, texto, historico) {
  try {
    return await session.prompt(texto);
  } catch (error) {
    if (error.name === 'QuotaExceededError') {
      // Criar nova sessão com resumo do contexto
      const resumo = historico.slice(-5).map(h =>
        `${h.role}: ${h.content.substring(0, 100)}`
      ).join('\n');

      session.destroy();

      const novaSessao = await LanguageModel.create({
        initialPrompts: [
          { role: 'system', content: 'You are a helpful assistant.' },
          { role: 'user', content: `Previous context summary:\n${resumo}` },
          { role: 'assistant', content: 'Understood. I have the context. How can I help?' }
        ]
      });

      return { sessao: novaSessao, resposta: await novaSessao.prompt(texto) };
    }
    throw error;
  }
}

Estratégia 3: pool de sessões

Pra aplicações com uso intensivo, manter um pool funciona bem:

class SessionPool {
  constructor(maxSessions = 3) {
    this.max = maxSessions;
    this.available = [];
    this.inUse = new Set();
  }

  async acquire() {
    if (this.available.length > 0) {
      const session = this.available.pop();
      this.inUse.add(session);
      return session;
    }

    if (this.inUse.size < this.max) {
      const session = await LanguageModel.create();
      this.inUse.add(session);
      return session;
    }

    // Esperar uma sessão ficar disponível
    return new Promise(resolve => {
      const check = setInterval(() => {
        if (this.available.length > 0) {
          clearInterval(check);
          resolve(this.acquire());
        }
      }, 100);
    });
  }

  release(session) {
    this.inUse.delete(session);
    this.available.push(session);
  }
}

measureContextUsage: medindo antes de enviar

Use session.measureContextUsage() pra saber quanto um prompt vai consumir antes de mandá-lo:

const uso = await session.measureContextUsage('A very long prompt...');
console.log(`Este prompt consumirá: ${uso} tokens`);

if (session.contextUsage + uso > session.contextWindow) {
  console.warn('Prompt não cabe no contexto atual');
}

Perguntas frequentes

Quantas sessões posso criar ao mesmo tempo?

Não tem um limite documentado, mas cada sessão consome memória. Na prática, mantenha o mínimo necessário e destrua as que não estão em uso.

O system prompt é contado na contextWindow?

Sim. O system prompt consome tokens da janela de contexto. Mantenha-o conciso. A diferença é que ele nunca é removido durante overflow — tudo mais é removido antes dele.

Posso alterar o system prompt depois de criar a sessão?

Não. O initialPrompts é imutável após a criação. Pra mudar o system prompt, destrua a sessão e crie uma nova.

O que acontece se eu chamar prompt() numa sessão destruída?

A Promise é rejeitada com um erro. Sempre verifique se a sessão tá ativa antes de usar.

clone() consome memória adicional?

Sim. Uma sessão clonada é independente e consome recursos próprios. Destrua clones que não são mais necessários.


Referências