Primeiros Passos com a Prompt API — Tutorial Prático

A Prompt API permite enviar prompts em linguagem natural ao Gemini Nano direto no Chrome — sem servidor, sem API key e sem custo por inferência. Este tutorial te leva do zero até o primeiro prompt funcionando: verificação de ambiente, download do modelo, criação de sessão e streaming.

Pré-requisitos

Antes de começar, confere se seu ambiente atende aos requisitos:

RequisitoMínimo
ChromeVersão 148+
Sistema operacionalWindows 10/11, macOS 13+, Linux, ChromeOS (Chromebook Plus)
Armazenamento livre22 GB
GPU>4 GB VRAM (recomendado)
CPU (alternativa)16 GB RAM + 4 cores
ConexãoSem limite de dados (pro download inicial)

Passo 1: Verificar a versão do Chrome

Abre o Chrome e vai pra chrome://version. Você precisa da versão 148 ou superior.

Se sua versão é anterior, atualiza:

  1. Menu (⋮) → Ajuda → Sobre o Google Chrome
  2. O Chrome busca atualizações automaticamente
  3. Reinicia o navegador depois da atualização

Passo 2: Verificar o status do modelo

Acesse chrome://on-device-internals no Chrome. Essa página mostra:

  • O status atual do Gemini Nano (disponível, baixando, não baixado)
  • O tamanho do modelo no disco
  • Informações sobre as capacidades disponíveis

Se o modelo ainda não foi baixado, ele desce automaticamente quando uma origem usar a API pela primeira vez. Dá pra forçar o download por essa página de internals também.

Passo 3: Verificar disponibilidade via JavaScript

O primeiro passo no código é sempre checar se a Prompt API tá disponível e se o modelo tá pronto. Use LanguageModel.availability():

async function verificarDisponibilidade() {
  // Verifica se a API existe no navegador
  if (!('LanguageModel' in window)) {
    console.log('Prompt API não suportada neste navegador');
    return 'unsupported';
  }

  // Verifica disponibilidade com as modalidades desejadas
  const status = await LanguageModel.availability({
    expectedInputs: [{ type: 'text', languages: ['en'] }],
    expectedOutputs: [{ type: 'text', languages: ['en'] }]
  });

  console.log('Status:', status);
  return status;
}

const status = await verificarDisponibilidade();

Os valores possíveis de retorno:

StatusSignificadoO que fazer
'available'Modelo pronto pra uso imediatoSeguir com create()
'downloading'Modelo sendo baixado agoraMostrar progresso ao usuário
'downloadable'Modelo pode ser baixadoChamar create() pra iniciar download
'unavailable'Não disponível (hardware insuficiente ou SO não suportado)Usar fallback

Detalhe importante: passe as mesmas opções

Sempre passe as mesmas opções pra availability() que você vai usar em prompt() ou promptStreaming(). Isso é crítico porque algumas configurações (como áudio) podem não ser suportadas no hardware do usuário:

// Configuração que será usada em toda a aplicação
const config = {
  expectedInputs: [
    { type: 'text', languages: ['en'] },
    { type: 'image' }
  ],
  expectedOutputs: [{ type: 'text', languages: ['en'] }]
};

// Use a mesma config em availability() E em create()
const status = await LanguageModel.availability(config);
const session = await LanguageModel.create(config);

Passo 4: Criar uma sessão com monitor de download

Ao criar uma sessão pela primeira vez, o Chrome pode precisar baixar o modelo. Use o monitor pra acompanhar o progresso:

async function criarSessao() {
  const session = await LanguageModel.create({
    monitor(m) {
      m.addEventListener('downloadprogress', (e) => {
        const percentual = Math.round(e.loaded * 100);
        console.log(`Download: ${percentual}%`);

        // Atualiza a UI pro usuário
        document.getElementById('progresso').textContent =
          `Baixando modelo de IA: ${percentual}%`;
      });
    }
  });

  console.log('Sessão criada!');
  return session;
}

Tratando o download pro usuário

O download do Gemini Nano é de ~4,27 GB. Informar o usuário é fundamental — ninguém gosta de espera sem feedback:

async function criarSessaoComUI() {
  const statusEl = document.getElementById('status');
  const progressEl = document.getElementById('progress-bar');

  const status = await LanguageModel.availability({
    expectedInputs: [{ type: 'text', languages: ['en'] }],
    expectedOutputs: [{ type: 'text', languages: ['en'] }]
  });

  if (status === 'unavailable') {
    statusEl.textContent = 'IA não disponível neste dispositivo';
    return null;
  }

  if (status === 'downloadable' || status === 'downloading') {
    statusEl.textContent = 'Preparando modelo de IA...';
    progressEl.style.display = 'block';
  }

  try {
    const session = await LanguageModel.create({
      monitor(m) {
        m.addEventListener('downloadprogress', (e) => {
          const pct = Math.round(e.loaded * 100);
          progressEl.value = pct;
          statusEl.textContent = `Baixando: ${pct}%`;
        });
      }
    });

    statusEl.textContent = 'IA pronta!';
    progressEl.style.display = 'none';
    return session;

  } catch (error) {
    statusEl.textContent = `Erro: ${error.message}`;
    return null;
  }
}

Passo 5: Enviar seu primeiro prompt

Com a sessão criada, manda um prompt simples usando session.prompt():

async function primeiroPrompt() {
  const session = await LanguageModel.create();

  const resposta = await session.prompt('What is the capital of Brazil?');
  console.log(resposta);
  // "The capital of Brazil is Brasília..."

  return resposta;
}

O prompt() retorna uma Promise que resolve com a resposta completa do modelo como string. Simples assim.

Melhorando com system prompt

Pra resultados melhores, use initialPrompts pra definir o comportamento do modelo:

async function promptComContexto() {
  const session = await LanguageModel.create({
    initialPrompts: [
      {
        role: 'system',
        content: 'You are a concise assistant. Answer in one paragraph maximum.'
      }
    ]
  });

  const resposta = await session.prompt('Explain what WebAssembly is.');
  console.log(resposta);
}

Passo 6: Streaming de respostas

Pra respostas longas ou UX interativa, use promptStreaming() que retorna um ReadableStream:

async function primeiroStreaming() {
  const session = await LanguageModel.create();

  const stream = session.promptStreaming('Write a short story about a robot.');

  const outputEl = document.getElementById('output');
  outputEl.textContent = '';

  for await (const chunk of stream) {
    outputEl.textContent += chunk;
  }
}

Streaming com atualização progressiva

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

  const outputEl = document.getElementById('resposta');
  outputEl.textContent = '';

  const stream = session.promptStreaming(
    'Write a JavaScript function that validates an email address.'
  );

  for await (const chunk of stream) {
    outputEl.textContent += chunk;
  }
}

Passo 7: Cancelar um prompt

Use AbortController pra cancelar operações em andamento:

async function promptCancelavel() {
  const session = await LanguageModel.create();
  const controller = new AbortController();

  // Botão de cancelar
  document.getElementById('cancelar').onclick = () => {
    controller.abort();
    console.log('Prompt cancelado pelo usuário');
  };

  try {
    const resposta = await session.prompt(
      'Write a very long essay about artificial intelligence.',
      { signal: controller.signal }
    );
    console.log(resposta);
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('Operação cancelada');
    } else {
      throw error;
    }
  }
}

Exemplo completo: mini aplicação

Aqui vai um exemplo funcional que junta todos os passos:

class PromptAPIApp {
  constructor() {
    this.session = null;
    this.controller = null;
  }

  async inicializar() {
    // 1. Verificar suporte
    if (!('LanguageModel' in window)) {
      this.mostrarErro('Seu navegador não suporta a Prompt API. Use Chrome 148+.');
      return false;
    }

    // 2. Verificar disponibilidade
    const status = await LanguageModel.availability({
      expectedInputs: [{ type: 'text', languages: ['en'] }],
      expectedOutputs: [{ type: 'text', languages: ['en'] }]
    });

    if (status === 'unavailable') {
      this.mostrarErro('Hardware insuficiente para a Prompt API.');
      return false;
    }

    // 3. Criar sessão
    this.session = await LanguageModel.create({
      initialPrompts: [
        { role: 'system', content: 'You are a helpful, concise assistant.' }
      ],
      monitor(m) {
        m.addEventListener('downloadprogress', (e) => {
          console.log(`Download: ${Math.round(e.loaded * 100)}%`);
        });
      }
    });

    console.log('Prompt API pronta!');
    return true;
  }

  async enviarPrompt(texto) {
    if (!this.session) {
      throw new Error('Sessão não inicializada');
    }

    // Cancelar prompt anterior se existir
    if (this.controller) {
      this.controller.abort();
    }
    this.controller = new AbortController();

    const stream = this.session.promptStreaming(texto, {
      signal: this.controller.signal
    });

    let resposta = '';
    for await (const chunk of stream) {
      resposta += chunk;
      this.atualizarUI(resposta);
    }

    return resposta;
  }

  atualizarUI(texto) {
    document.getElementById('output').textContent = texto;
  }

  mostrarErro(msg) {
    document.getElementById('erro').textContent = msg;
  }

  destruir() {
    if (this.session) {
      this.session.destroy();
      this.session = null;
    }
  }
}

// Uso
const app = new PromptAPIApp();
await app.inicializar();
await app.enviarPrompt('What are the benefits of TypeScript?');

TypeScript: tipagem da API

Pra projetos TypeScript, instale as tipagens oficiais:

npm install @types/dom-chromium-ai

Isso adiciona tipos pra LanguageModel, LanguageModelSession e todos os métodos da API.

Depuração com chrome://on-device-internals

A página chrome://on-device-internals é sua melhor amiga pra diagnóstico:

  • Status do modelo: Confirme que o Gemini Nano tá baixado e ativo
  • Tamanho: Verifique o espaço ocupado
  • Testes: Execute prompts direto pra validar que o modelo funciona
  • Logs: Consulte logs de inferência pra troubleshooting

Checklist de verificação

Antes de declarar que tá tudo certo:

  • Chrome 148+ instalado
  • chrome://on-device-internals mostra modelo como disponível
  • LanguageModel.availability() retorna 'available'
  • LanguageModel.create() resolve sem erros
  • session.prompt('test') retorna uma resposta
  • Armazenamento com 22+ GB livres confirmado

Solução de problemas comuns

LanguageModel is undefined

  • Verifique se tá usando Chrome 148+
  • Confirme que não está em um Web Worker (não é suportado)
  • Verifique se está em uma janela top-level ou iframe same-origin

availability() retorna ‘unavailable’

  • Hardware não atende os requisitos mínimos (GPU >4GB ou CPU 16GB RAM + 4 cores)
  • Espaço livre insuficiente (precisa de 22+ GB)
  • SO não suportado (Android/iOS não funcionam)

Download travado

  • Verifique sua conexão (precisa ser ilimitada/não medida)
  • Tente reiniciar o Chrome
  • Consulte chrome://on-device-internals pro status detalhado

Respostas de baixa qualidade

  • O Gemini Nano é um modelo compacto — pra tarefas complexas, considere o polyfill com backend cloud
  • Use system prompts claros e específicos
  • Fique nos idiomas suportados (EN, ES, JA, DE, FR)

Perguntas frequentes

Preciso de uma API key pra usar a Prompt API?

Não. A Prompt API é completamente gratuita e não requer chave de API, registro ou autenticação. O modelo roda localmente no navegador.

O download do modelo acontece toda vez que o usuário visita meu site?

Não. O modelo é baixado uma única vez pelo Chrome e compartilhado entre todas as origens que usam a Prompt API. Visitas seguintes usam o modelo já em cache.

Posso usar a Prompt API em localhost pra desenvolvimento?

Sim. Funciona em qualquer origem, incluindo localhost e file://, desde que o Chrome e o hardware atendam aos requisitos.

Quanto tempo leva o download do modelo?

Depende da velocidade da conexão. O Gemini Nano tem ~4,27 GB. Numa conexão de 100 Mbps, leva uns 5-7 minutos. Use o monitor pra informar o progresso ao usuário.

A Prompt API funciona com Service Workers?

Não. Não está disponível em Web Workers nem em Service Workers. Funciona apenas em contextos de janela (top-level ou iframes com permissão).


Referências