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 SchemaDescriçãoExemplo
booleanVerdadeiro/falsoSpam/não-spam
stringTexto livre ou com patternExtração, enum
numberNuméricoScore, rating
objectObjeto com propriedadesEntidade estruturada
arrayLista tipadaTags, categorias
enumValor de lista fixaCategorias 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

AspectoRecomendação
Schemas aninhados demaisManter simples — profundidade degrada qualidade
pattern em stringsFunciona, mas regex complexo pode causar respostas vazias
Arrays sem maxItemsSempre definir — senão o modelo pode gerar listas enormes
Tipos numéricosUsar minimum/maximum quando possível
EnumsPreferir a strings livres para categorização
Context windowSchema 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