prompt api jsonstructured output prompt apiresponseConstraint

Structured Output: como forçar JSON na Prompt API

Prompt API Brasil

Structured Output: como forçar JSON na Prompt API

Structured Output na Prompt API é o que te permite forçar o Gemini Nano a devolver JSON válido, obedecendo um schema que você definiu via responseConstraint. Disponível desde o Chrome 137, a mecânica é direta: você declara a estrutura, o modelo segue. Sem preâmbulos, sem markdown, sem “Certamente! Aqui está a resposta:”.

TL;DR

  • responseConstraint força o Gemini Nano a retornar JSON válido conforme um JSON Schema definido por você
  • Funciona com boolean, string, number, enum, arrays e objetos aninhados — constraint a nível de decodificação
  • Disponível desde Chrome 137, funciona com prompt() e promptStreaming() — elimina parsing manual

Para mim, é o recurso mais útil da API inteira. Sem ele, você fica parseando texto livre com regex — e quem já tentou extrair dados estruturados de output de LLM sabe o inferno que é.

Funil visual transformando texto desestruturado em blocos JSON validados por JSON Schema.

Por que isso importa

LLMs adoram ser verbosos. Mesmo quando você instrui o modelo a responder “true” ou “false”, ele pode soltar um “Certamente! Com base na análise, a resposta é: true”. Para código que precisa processar esses dados programaticamente, isso quebra tudo.

O responseConstraint resolve o problema na raiz. Em vez de pedir gentilmente que o modelo siga um formato — e torcer para ele obedecer — você impõe a estrutura via JSON Schema. O modelo gera apenas tokens compatíveis com o schema declarado. É constraint decoding, não prompt engineering. A diferença é como pedir versus exigir.

JSON Schema: o básico

Se você já usou TypeScript interfaces ou Zod, o conceito é familiar — declarativo, independente de linguagem:

const schema = {
  type: "object",
  properties: {
    resultado: { type: "string" },
    confianca: { type: "number" }
  },
  required: ["resultado", "confianca"],
  additionalProperties: false
};

Tipos suportados

Tipo JSON Schema Descrição Exemplo
boolean Verdadeiro/falso Spam/não-spam
string Texto livre ou com pattern Extração, enum
number Numérico Score, rating
object Objeto com propriedades Entidade estruturada
array Lista tipada Tags, categorias
enum Valor de lista fixa Categorias predefinidas

Na prática

A propriedade é passada como opção nos métodos prompt() e promptStreaming():

const session = await LanguageModel.create({
  expectedInputs: [{ type: "text", languages: ["pt"] }],
  expectedOutputs: [{ type: "text", languages: ["pt"] }],
});

const resultado = await session.prompt(
  "Este texto é uma pergunta? 'Quanto custa o frete?'",
  { responseConstraint: { type: "boolean" } }
);

console.log(JSON.parse(resultado)); // true

O retorno é sempre JSON válido que satisfaz o schema. Sem floreios.

Exemplos práticos

1. Classificação binária

O caso mais simples — forçar true ou false:

const session = await LanguageModel.create();

const isSpam = await session.prompt(
  "Classifique se é spam: 'GANHE R$5000 AGORA! Clique aqui!!!'",
  {
    responseConstraint: { type: "boolean" }
  }
);

console.log(JSON.parse(isSpam)); // true

Duas linhas de lógica. Sem parser customizado, sem regex, sem “espero que o modelo responda no formato certo”. Funciona.

2. Extração de entidades

Extrair dados estruturados de texto livre — o tipo de coisa que regex faz mal e LLMs fazem razoavelmente bem:

const schema = {
  type: "object",
  properties: {
    nome: { type: "string" },
    email: { type: "string", pattern: "^[^@]+@[^@]+\\.[^@]+$" },
    telefone: { type: "string" },
    assunto: { type: "string" }
  },
  required: ["nome", "assunto"],
  additionalProperties: false
};

const texto = `
  Olá, meu nome é Maria Silva, meu email é [email protected].
  Preciso de ajuda com a troca de um produto defeituoso.
`;

const entidades = await session.prompt(
  `Extraia as entidades de contato do texto:\n\n${texto}`,
  { responseConstraint: schema }
);

console.log(JSON.parse(entidades));
// { nome: "Maria Silva", email: "[email protected]", assunto: "troca de produto defeituoso" }

3. Classificação multi-label com array

Quando o texto pode pertencer a mais de uma categoria:

const schema = {
  type: "object",
  properties: {
    categorias: {
      type: "array",
      maxItems: 3,
      items: {
        type: "string",
        enum: ["tecnologia", "esportes", "política", "entretenimento", "ciência"]
      }
    },
    sentimento: {
      type: "string",
      enum: ["positivo", "negativo", "neutro"]
    }
  },
  required: ["categorias", "sentimento"],
  additionalProperties: false
};

const analise = await session.prompt(
  "Classifique: 'O novo chip da Apple supera benchmarks com IA on-device'",
  { responseConstraint: schema }
);

console.log(JSON.parse(analise));
// { categorias: ["tecnologia", "ciência"], sentimento: "positivo" }

O maxItems: 3 impede que o modelo retorne a lista inteira (porque ele vai tentar, acredite).

4. Validação de formulário

Usar o LLM para validar coisas que regex não pega — nomes absurdos, emails que são sintaticamente válidos mas claramente falsos, contextos semânticos:

const schema = {
  type: "object",
  properties: {
    valido: { type: "boolean" },
    erros: {
      type: "array",
      items: {
        type: "object",
        properties: {
          campo: { type: "string" },
          mensagem: { type: "string" }
        },
        required: ["campo", "mensagem"]
      }
    }
  },
  required: ["valido", "erros"],
  additionalProperties: false
};

const dadosFormulario = JSON.stringify({
  nome: "J",
  email: "nao-eh-email",
  cpf: "123.456.789-00"
});

const validacao = await session.prompt(
  `Valide os dados deste formulário. Verifique se nome tem ao menos 2 caracteres, email é válido, e CPF tem formato correto:\n\n${dadosFormulario}`,
  { responseConstraint: schema }
);

console.log(JSON.parse(validacao));
// { valido: false, erros: [{ campo: "nome", mensagem: "Nome muito curto" }, { campo: "email", mensagem: "Formato de email inválido" }] }

Não substitui validação backend (obviamente), mas como feedback instantâneo ao usuário enquanto ele preenche o formulário, funciona bem.

Prefix mode: controle extra sobre o início

Para cenários onde você quer forçar o modelo a começar de um jeito específico:

const resultado = await session.prompt([
  {
    role: "user",
    content: "Liste 3 cores primárias em JSON"
  },
  {
    role: "assistant",
    content: "```json\n",
    prefix: true
  }
]);

Força o modelo a continuar a partir do prefixo. Útil quando responseConstraint é pesado demais para schemas triviais.

Opções avançadas

omitResponseConstraintInput

Por padrão, o schema é enviado ao modelo como parte do contexto (consome tokens). Se seu prompt já descreve o formato desejado, pode pular essa injeção:

const resultado = await session.prompt(
  `Resuma este feedback num rating de 0 a 5. Retorne { "rating": número }:
  A comida estava deliciosa, serviço excelente.`,
  {
    responseConstraint: schema,
    omitResponseConstraintInput: true
  }
);

Quando usar: schemas complexos onde o contexto está apertado e você já descreveu tudo no prompt textual.

measureContextUsage com schema

Schemas consomem tokens. Para monitorar:

const session = await LanguageModel.create();

const usage = await session.measureContextUsage(
  "Meu prompt aqui",
  { responseConstraint: schemaGrande }
);

console.log(`Tokens usados: ${usage.usedTokens} / ${usage.totalTokens}`);

Limitações e boas práticas

Aspecto Recomendação
Schemas aninhados demais Manter simples — profundidade degrada qualidade
pattern em strings Funciona, mas regex complexo pode causar respostas vazias
Arrays sem maxItems Sempre definir — senão o modelo pode gerar listas enormes
Tipos numéricos Usar minimum/maximum quando possível
Enums Preferir a strings livres para categorização
Context window Schema consome tokens — monitorar

Na minha experiência, schemas com até 3 níveis de profundidade funcionam bem. Acima disso, a qualidade das respostas começa a cair.

Testando seu schema

O Google disponibiliza o JSON Schema Tester para validar antes de integrar.

Para validação em runtime, use Ajv:

import Ajv from 'ajv';
const ajv = new Ajv();
const validate = ajv.compile(schema);

const resposta = JSON.parse(await session.prompt(meuPrompt, { responseConstraint: schema }));

if (!validate(resposta)) {
  console.error('Schema inválido:', validate.errors);
  // retry ou fallback
}

Mockup de ferramenta de validação JSON Schema com checkmarks verdes e badge Schema Valid.

FAQ

O responseConstraint garante 100% de JSON válido?

Sim. No modo structured output, o modelo é constrained a nível de decodificação — gera apenas tokens que formam JSON válido conforme o schema. Diferente de pedir “responda em JSON” no prompt.

Quais versões do Chrome suportam isso?

Desde o Chrome 137. Para melhor experiência, use Chrome 148+.

Funciona com promptStreaming()?

Sim. O responseConstraint funciona tanto com prompt() quanto com promptStreaming(). No streaming, chunks são entregues progressivamente, mas o resultado final completo é JSON válido.

O schema consome tokens do context window?

Sim. É convertido internamente em instruções para o modelo. Use omitResponseConstraintInput: true se já instruiu via prompt textual.

Posso usar $ref e definitions no schema?

Não. A implementação atual suporta apenas schemas flat (inline). Sem referências externas ou $ref.

Conclusão

O responseConstraint é o que transforma a Prompt API de “brinquedo de geração de texto” em interface tipada para IA on-device. Com JSON Schema, você ganha dados estruturados, validáveis e previsíveis — rodando local no Gemini Nano, sem custo, sem rede.

É o tipo de feature que deveria ter existido desde o dia um. Mas está aqui agora, funciona, e muda fundamentalmente o que dá pra construir com IA no browser.

Combine com streaming para UX responsiva e multimodal para aplicações completas de IA no browser.


Próximo artigo: IA multimodal no browser: imagens e áudio com Gemini Nano

Referências