Enviando Prompts — Guia de prompt() e promptStreaming()
O coração da Prompt API são os métodos session.prompt() e session.promptStreaming(). Ambos mandam conteúdo ao Gemini Nano pra inferência local, mas diferem em como a resposta chega até você. Vamos ver cada um em detalhe.
prompt() vs promptStreaming()
| Característica | prompt() |
promptStreaming() |
|---|---|---|
| Retorno | Promise<string> |
ReadableStream |
| Entrega | Resposta completa de uma vez | Chunks incrementais |
| Uso ideal | Respostas curtas, classificação, extração | Respostas longas, UX interativa |
| Cancelamento | Via signal (AbortController) |
Via signal (AbortController) |
| Tempo até primeira resposta | Espera geração completa | Imediato (primeiro chunk) |
Quando usar prompt()
Use prompt() quando:
- A resposta esperada é curta (classificação, extração, sim/não)
- Você precisa do resultado completo antes de processar (parsing JSON)
- A latência total é aceitável pro caso de uso
- Você usa
responseConstraintcom JSON Schema
const session = await LanguageModel.create();
// Classificação binária — resposta curta
const resultado = await session.prompt(
'Is the following text spam? Answer only "yes" or "no": ' +
'"Buy now! Limited offer! Click here for free prizes!"'
);
console.log(resultado); // "yes"
Quando usar promptStreaming()
Use promptStreaming() quando:
- A resposta pode ser longa (textos, explicações)
- Você quer mostrar progresso ao usuário em tempo real
- A percepção de velocidade importa pra UX
- O usuário pode querer cancelar durante a geração
const session = await LanguageModel.create();
const stream = session.promptStreaming(
'Explain the differences between REST and GraphQL APIs.'
);
const outputEl = document.getElementById('output');
outputEl.textContent = '';
for await (const chunk of stream) {
outputEl.textContent += chunk;
}
Anatomia do content
O parâmetro content aceita dois formatos:
String simples
O formato mais direto — uma string é tratada como mensagem do role user:
const resposta = await session.prompt('What is machine learning?');
Array de mensagens
Pra conversas multi-turno ou quando você precisa de contexto adicional:
const resposta = await session.prompt([
{
role: 'user',
content: 'What is the weather like in São Paulo?'
},
{
role: 'assistant',
content: 'I don\'t have access to real-time weather data.'
},
{
role: 'user',
content: 'Then tell me what the typical climate is like.'
}
]);
Roles disponíveis
| Role | O que faz | Onde usar |
|---|---|---|
system |
Define comportamento/persona do modelo | Sempre na posição 0 — no initialPrompts, ou como primeira mensagem do primeiro append() ou prompt() |
user |
Mensagem do usuário | Em prompt(), promptStreaming(), initialPrompts, append() |
assistant |
Resposta anterior do modelo | Contexto de conversa, prefixing |
A regra do system é posicional, não de método. Ele pode ser a primeira mensagem de qualquer uma das três formas, mas se aparecer em qualquer outra posição a chamada rejeita com TypeError:
// ✅ Os três caminhos válidos
await LanguageModel.create({
initialPrompts: [{ role: 'system', content: 'Você é um assistente técnico.' }],
});
const s2 = await LanguageModel.create();
await s2.append([{ role: 'system', content: 'Você é um assistente técnico.' }]);
const s3 = await LanguageModel.create();
await s3.prompt([
{ role: 'system', content: 'Você é um assistente técnico.' },
{ role: 'user', content: 'O que é WebAssembly?' },
]);
// ❌ TypeError: system não está na posição 0
await s3.prompt([
{ role: 'user', content: 'Oi' },
{ role: 'system', content: 'Você é um assistente.' },
]);
Uma consequência importante: como o system prompt é preservado em overflow de contexto, ele não pode ser usado na forma de multi-mensagem quando você está customizando roles para mediação. Nesse cenário, passe a persona no initialPrompts do create() e use só user/assistant no prompt().
Content multimodal (array de partes)
Quando o content de uma mensagem mistura tipos (texto + imagem, por exemplo), use um array de objetos:
const resposta = await session.prompt([
{
role: 'user',
content: [
{ type: 'text', value: 'Describe this image in detail:' },
{ type: 'image', value: document.getElementById('minha-imagem') }
]
}
]);
Cada parte tem:
type:'text','image'ou'audio'value: O conteúdo correspondente (string pra texto, elemento/blob pra mídia)
Prefixing: guiando o formato de resposta
O recurso de prefix permite iniciar a resposta do modelo com um texto específico, guiando o formato de saída:
const resposta = await session.prompt([
{
role: 'user',
content: 'Create a JSON object with name, age, and city for a fictional person'
},
{
role: 'assistant',
content: '```json\n{',
prefix: true
}
]);
console.log('{' + resposta); // A resposta continua de onde o prefix parou
O prefix: true na última mensagem assistant diz pro modelo continuar a partir daquele ponto, não gerar uma nova resposta. Na prática é útil pra:
- Forçar formato de saída (JSON, TOML, Markdown)
- Garantir que a resposta começa com determinado texto
- Guiar o modelo pra respostas mais estruturadas
Cancelamento com AbortController
Ambos os métodos suportam cancelamento via AbortSignal:
const controller = new AbortController();
// Timeout de 5 segundos
setTimeout(() => controller.abort(), 5000);
try {
const resposta = await session.prompt('Write a detailed essay.', {
signal: controller.signal
});
} catch (error) {
if (error.name === 'AbortError') {
console.log('Prompt cancelado (timeout)');
}
}
Cancelamento interativo com streaming
const controller = new AbortController();
document.getElementById('btn-parar').addEventListener('click', () => {
controller.abort();
});
const stream = session.promptStreaming('Write a long story.', {
signal: controller.signal
});
try {
for await (const chunk of stream) {
document.getElementById('output').textContent += chunk;
}
} catch (error) {
if (error.name === 'AbortError') {
document.getElementById('output').textContent += '\n[Geração interrompida]';
}
}
Padrões práticos
Classificação com resposta booleana
const session = await LanguageModel.create({
initialPrompts: [
{ role: 'system', content: 'You are a content classifier. Answer only true or false.' }
]
});
async function classificar(texto, criterio) {
const resposta = await session.prompt(
`Does the following text match this criteria: "${criterio}"?\n\nText: ${texto}`
);
return resposta.trim().toLowerCase() === 'true';
}
const isSpam = await classificar(
'Buy cheap watches now!!!',
'spam or unsolicited commercial content'
);
// true
Extração de dados com prompt estruturado
const session = await LanguageModel.create({
initialPrompts: [
{
role: 'system',
content: 'Extract information and respond in the exact format requested. No additional text.'
}
]
});
const email = 'Hi, I\'m John Smith from Acme Corp. Call me at 555-0123.';
const resposta = await session.prompt(
`Extract name, company, and phone from this text. ` +
`Respond as JSON: {"name":"","company":"","phone":""}\n\n${email}`
);
const dados = JSON.parse(resposta);
// { name: "John Smith", company: "Acme Corp", phone: "555-0123" }
Streaming com renderização Markdown
async function streamComMarkdown(prompt) {
const session = await LanguageModel.create();
const stream = session.promptStreaming(prompt);
const container = document.getElementById('output');
let textoAcumulado = '';
for await (const chunk of stream) {
textoAcumulado += chunk;
container.innerHTML = marked.parse(textoAcumulado);
}
}
Retry com backoff
async function promptComRetry(session, texto, maxTentativas = 3) {
for (let tentativa = 1; tentativa <= maxTentativas; tentativa++) {
try {
return await session.prompt(texto);
} catch (error) {
if (error.name === 'AbortError') throw error; // Não retry em abort
if (tentativa === maxTentativas) throw error;
// Backoff exponencial
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, tentativa)));
}
}
}
Múltiplos prompts em sequência
A sessão mantém contexto entre chamadas de prompt():
const session = await LanguageModel.create({
initialPrompts: [
{ role: 'system', content: 'You are a helpful tutor.' }
]
});
// Primeira pergunta
const r1 = await session.prompt('What is recursion in programming?');
console.log(r1);
// Segunda pergunta — o modelo lembra da conversa
const r2 = await session.prompt('Can you give me an example in JavaScript?');
console.log(r2); // Vai dar um exemplo de recursão, porque lembra do contexto
// Terceira pergunta
const r3 = await session.prompt('What are the risks of using it?');
console.log(r3); // Vai falar sobre stack overflow e riscos de recursão
Opções do segundo parâmetro
Tanto prompt() quanto promptStreaming() aceitam um segundo parâmetro com opções:
const resposta = await session.prompt(content, {
signal: controller.signal, // AbortSignal pra cancelamento
responseConstraint: jsonSchema, // JSON Schema ou regex
omitResponseConstraintInput: false // Se true, não inclui schema no contexto
});
| Opção | Tipo | O que faz |
|---|---|---|
signal |
AbortSignal |
Permite cancelar a operação |
responseConstraint |
object ou RegExp |
Força formato de resposta (JSON Schema) |
omitResponseConstraintInput |
boolean |
Não consome tokens de contexto com o schema |
thinking |
object |
Configura raciocínio estendido (effort, includeThoughts) |
Thinking mode: raciocínio estendido
Modelos modernos geram tokens de raciocínio intermediários antes da resposta final. Isso melhora precisão em matemática, lógica, geração de código e planejamento multi-step — ao custo de latência e bateria.
O thinking é configurado com effort e, opcionalmente, includeThoughts:
effort |
Comportamento | Use para |
|---|---|---|
"none" |
Sem raciocínio intermediário (padrão) | Tarefas simples |
"low" |
Orçamento mínimo de raciocínio | Multi-step direto |
"medium" |
Equilíbrio entre precisão e latência | Padrão para tarefas intermediárias |
"high" |
Orçamento máximo | Matemática, lógica, planejamento |
Você define um nível base na sessão e sobrescreve por chamada:
// Verificar suporte antes de criar a sessão
const status = await LanguageModel.availability({
thinking: { effort: 'high' },
});
if (status !== 'unavailable') {
const session = await LanguageModel.create({
thinking: { effort: 'high' },
});
// Turno 1: usa o padrão da sessão ("high") — tarefa complexa
const code = await session.prompt(
'Escreva uma função para achar o caminho mais curto em um grafo ponderado.',
);
// Turno 2: sobrescreve para "none" — tarefa simples, economiza latência
const doc = await session.prompt('Adicione comentários JSDoc.', {
thinking: { effort: 'none' },
});
}
Vendo os Thoughts intermediários
Por padrão includeThoughts é false, então você recebe só a resposta final. Com includeThoughts: true, prompt() e promptStreaming() passam a emitir dicionários { type, value } em vez de strings, com type sendo "thought" ou "text":
const session = await LanguageModel.create({
thinking: {
effort: 'medium',
includeThoughts: true,
},
});
const stream = session.promptStreaming('Monte um itinerário de 3 dias em Tóquio.');
for await (const chunk of stream) {
// Chunks de thought e text podem conter Markdown
if (chunk.type === 'thought') {
thinkingContainer.append(chunk.value);
} else if (chunk.type === 'text') {
responseContainer.append(chunk.value);
}
}
⚠️ Atenção ao breaking change de tipo. Todo código que assume string do
prompt()oupromptStreaming()quebra seincludeThoughts: true. Com o padrãofalse, o comportamento é o de sempre — string.
Os tokens de pensamento são removidos do histórico da conversa depois de cada turno, então não acumulam em session.contextUsage. O user agent mapeia cada nível de effort para um orçamento de raciocínio do modelo subjacente; o modelo para de pensar quando atinge o teto ou quando chega a uma conclusão.
Tool use: dando ferramentas ao modelo
Além de texto, o modelo pode invocar funções que você define. Isso transforma a Prompt API de “responde texto” em “age sobre o seu app”.
Uma tool é um objeto com name, description, inputSchema e execute. Quando o modelo decide usar a ferramenta, o user agent chama o execute e devolve o resultado para o modelo:
const session = await LanguageModel.create({
initialPrompts: [
{
role: 'system',
content: 'Você é um assistente útil. Use ferramentas para ajudar o usuário.',
},
],
// tool-response e tool-call precisam ser declarados explicitamente
expectedInputs: [
{ type: 'text', languages: ['en'] },
{ type: 'tool-response' },
],
expectedOutputs: [
{ type: 'text', languages: ['en'] },
{ type: 'tool-call' },
],
tools: [
{
name: 'getWeather',
description: 'Retorna o clima de uma localização.',
inputSchema: {
type: 'object',
properties: {
location: {
type: 'string',
description: 'A cidade para consultar a condição do tempo.',
},
},
required: ['location'],
},
async execute({ location }) {
const res = await fetch(`https://api.exemplo.com/clima?cidade=${location}`);
// Retorna o resultado como string JSON
return JSON.stringify(await res.json());
},
},
],
});
const result = await session.prompt('Como está o clima em Seattle?');
Sem declarar { type: 'tool-response' } em expectedInputs e { type: 'tool-call' } em expectedOutputs, a sessão não suporta tools.
Uso concorrente
O modelo pode chamar a mesma tool várias vezes em paralelo. Não assuma execução sequencial:
const result = await session.prompt(
'Qual dessas cidades tem a temperatura mais alta agora? Seattle, Tóquio, Berlim',
);
// pode chamar getWeather('Seattle'), getWeather('Tóquio') e getWeather('Berlim')
// em paralelo, aguardando os três antes de compor a resposta
O user agent usa algo equivalente a Promise.all() internamente. Isso significa que seu execute precisa ser idempotente e seguro para execução concorrente — e você deve tratar duplicação de requests (cache por argumento) para não disparar chamadas repetidas à mesma API.
Perguntas frequentes
O streaming retorna a resposta completa acumulada ou só os chunks novos?
O promptStreaming() retorna apenas os novos chunks a cada iteração. Você precisa acumular manualmente se quiser o texto completo.
Posso enviar múltiplos prompts ao mesmo tempo na mesma sessão?
Não é recomendado. Cada sessão processa um prompt por vez. Pra processamento paralelo, crie múltiplas sessões ou use session.clone().
Qual o tamanho máximo de um prompt?
Depende da contextWindow da sessão. Use session.contextWindow pra ver o máximo e session.contextUsage pra saber quanto já foi consumido. Se o prompt exceder o espaço disponível, um QuotaExceededError é lançado.
O prompt() pode retornar resposta vazia?
Sim, em casos raros o modelo pode não gerar conteúdo útil. Sempre valide a resposta antes de usar em produção.