Structured Output na Prompt API — JSON Schema e Regex

A Prompt API suporta structured output — a capacidade de forçar o modelo a responder num formato específico usando JSON Schema ou expressões regulares. Com o campo responseConstraint, você garante que a resposta vai ser parseável e consistente. Chega de parsing frágil de texto livre.

Disponível desde o Chrome 137, o structured output é passado como opção nos métodos prompt() e promptStreaming().

O que é responseConstraint

O responseConstraint é um campo do segundo parâmetro de prompt() e promptStreaming() que aceita um JSON Schema ou uma RegExp. Quando você passa isso, o modelo é obrigado a gerar uma resposta que valida contra o schema.

const session = await LanguageModel.create();

const schema = {
  type: 'object',
  properties: {
    name: { type: 'string' },
    age: { type: 'number' },
    city: { type: 'string' }
  },
  required: ['name', 'age', 'city']
};

const resultado = await session.prompt(
  'Generate a fictional person with name, age, and city.',
  { responseConstraint: schema }
);

const pessoa = JSON.parse(resultado);
console.log(pessoa.name); // "Alice Johnson"
console.log(pessoa.age);  // 34
console.log(pessoa.city); // "Portland"

Tipos de JSON Schema suportados

Boolean

Força uma resposta true ou false:

const schema = { type: 'boolean' };

const resultado = await session.prompt(
  'Is the following text about pottery?\n\n' +
  'Mugs and ramen bowls, both a bit smaller than intended.',
  { responseConstraint: schema }
);

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

String

Força uma resposta em formato string:

const schema = { type: 'string' };

const resultado = await session.prompt(
  'What is the capital of France? Answer with just the city name.',
  { responseConstraint: schema }
);

console.log(JSON.parse(resultado)); // "Paris"

Number

Força uma resposta numérica:

const schema = { type: 'number' };

const resultado = await session.prompt(
  'Rate this review from 1 to 5: "Excellent product, fast delivery!"',
  { responseConstraint: schema }
);

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

Object

Força uma resposta como objeto JSON com propriedades específicas:

const schema = {
  type: 'object',
  properties: {
    sentiment: { type: 'string', enum: ['positive', 'negative', 'neutral'] },
    confidence: { type: 'number' },
    keywords: {
      type: 'array',
      items: { type: 'string' }
    }
  },
  required: ['sentiment', 'confidence', 'keywords']
};

const resultado = await session.prompt(
  'Analyze the sentiment of: "I love this product but the shipping was slow."',
  { responseConstraint: schema }
);

const analise = JSON.parse(resultado);
// { sentiment: "positive", confidence: 0.7, keywords: ["love", "slow shipping"] }

Array

Força uma resposta como array:

const schema = {
  type: 'array',
  items: {
    type: 'object',
    properties: {
      task: { type: 'string' },
      priority: { type: 'string', enum: ['high', 'medium', 'low'] }
    },
    required: ['task', 'priority']
  }
};

const resultado = await session.prompt(
  'Extract action items from: "We need to fix the login bug urgently, ' +
  'update the docs when possible, and consider redesigning the dashboard."',
  { responseConstraint: schema }
);

const tarefas = JSON.parse(resultado);
// [
//   { task: "Fix the login bug", priority: "high" },
//   { task: "Update the docs", priority: "medium" },
//   { task: "Redesign the dashboard", priority: "low" }
// ]

Enum

Restringe a resposta a valores específicos:

const schema = {
  type: 'string',
  enum: ['bug', 'feature', 'question', 'documentation']
};

const resultado = await session.prompt(
  'Classify this GitHub issue title: "Add dark mode support"',
  { responseConstraint: schema }
);

console.log(JSON.parse(resultado)); // "feature"

Combinações complexas

const schema = {
  type: 'object',
  properties: {
    valid: { type: 'boolean' },
    errors: {
      type: 'array',
      items: {
        type: 'object',
        properties: {
          field: { type: 'string' },
          message: { type: 'string' },
          severity: { type: 'string', enum: ['error', 'warning'] }
        },
        required: ['field', 'message', 'severity']
      }
    },
    suggestion: { type: 'string' }
  },
  required: ['valid', 'errors']
};

const resultado = await session.prompt(
  'Validate this email form data: { name: "", email: "notanemail", age: -5 }',
  { responseConstraint: schema }
);

const validacao = JSON.parse(resultado);
// {
//   valid: false,
//   errors: [
//     { field: "name", message: "Name is required", severity: "error" },
//     { field: "email", message: "Invalid email format", severity: "error" },
//     { field: "age", message: "Age must be positive", severity: "error" }
//   ],
//   suggestion: "Please fill in all required fields with valid data."
// }

responseConstraint com RegExp

Além de JSON Schema, você pode usar expressões regulares para restringir o formato:

// Forçar resposta no formato de data ISO
const regexData = /^\d{4}-\d{2}-\d{2}$/;

const resultado = await session.prompt(
  'What is today\'s date? Respond in YYYY-MM-DD format only.',
  { responseConstraint: regexData }
);

console.log(resultado); // "2026-06-06"
// Forçar resposta como hex color
const regexHex = /^#[0-9A-Fa-f]{6}$/;

const resultado = await session.prompt(
  'What color best represents "ocean"? Respond as a hex color code.',
  { responseConstraint: regexHex }
);

console.log(resultado); // "#1E90FF"

omitResponseConstraintInput

Por padrão, o JSON Schema ou regex é incluído na mensagem enviada ao modelo, consumindo tokens da context window. Para economizar tokens, use omitResponseConstraintInput: true:

const schema = {
  type: 'object',
  properties: {
    rating: { type: 'number' }
  },
  required: ['rating']
};

// Schema incluído no contexto (padrão) — consome tokens
const r1 = await session.prompt('Rate this: "Great product!"', {
  responseConstraint: schema
});

// Schema NÃO incluído no contexto — economiza tokens
const r2 = await session.prompt(
  'Rate this from 1-5 as JSON {"rating": <number>}: "Great product!"',
  {
    responseConstraint: schema,
    omitResponseConstraintInput: true
  }
);

Quando usar omitResponseConstraintInput

CenárioomitResponseConstraintInputMotivo
Schema simples, contexto amplofalse (padrão)Modelo entende melhor o formato esperado
Schema complexo, contexto limitadotrueEconomiza tokens significativos
Prompt já descreve o formatotrueEvita duplicação de instruções
Muitas chamadas na mesma sessãotruePreserva contexto para histórico

⚠️ Cuidado: Com omitResponseConstraintInput: true, inclua instruções claras sobre o formato no próprio prompt, pois o modelo não verá o schema automaticamente.

measureContextUsage com responseConstraint

Use session.measureContextUsage() para verificar quantos tokens o responseConstraint consumirá:

const schemaGrande = {
  type: 'object',
  properties: {
    // ... muitas propriedades
  }
};

const uso = await session.measureContextUsage({
  responseConstraint: schemaGrande
});

console.log(`O schema consumirá ${uso} tokens do contexto`);

// Se for muito, considere omitResponseConstraintInput: true
if (uso > session.contextWindow * 0.2) {
  console.warn('Schema consome >20% do contexto. Considere omitir.');
}

Structured output com streaming

O responseConstraint funciona tanto com prompt() quanto com promptStreaming():

const schema = {
  type: 'object',
  properties: {
    title: { type: 'string' },
    summary: { type: 'string' },
    tags: { type: 'array', items: { type: 'string' } }
  },
  required: ['title', 'summary', 'tags']
};

const stream = session.promptStreaming(
  'Analyze this article and extract metadata: [article text...]',
  { responseConstraint: schema }
);

let jsonAcumulado = '';
for await (const chunk of stream) {
  jsonAcumulado += chunk;
  // Mostrar progresso parcial (JSON incompleto durante streaming)
  document.getElementById('output').textContent = jsonAcumulado;
}

// Ao final, o JSON completo está disponível
const metadata = JSON.parse(jsonAcumulado);

Exemplos práticos

Classificador de e-mail

const session = await LanguageModel.create({
  initialPrompts: [
    { role: 'system', content: 'You are an email classifier.' }
  ]
});

const schema = {
  type: 'object',
  properties: {
    category: {
      type: 'string',
      enum: ['work', 'personal', 'marketing', 'spam', 'newsletter']
    },
    priority: {
      type: 'string',
      enum: ['urgent', 'normal', 'low']
    },
    requiresReply: { type: 'boolean' },
    summary: { type: 'string' }
  },
  required: ['category', 'priority', 'requiresReply', 'summary']
};

async function classificarEmail(assunto, corpo) {
  const resultado = await session.prompt(
    `Classify this email:\nSubject: ${assunto}\nBody: ${corpo}`,
    { responseConstraint: schema }
  );
  return JSON.parse(resultado);
}

Extrator de eventos de calendário

const schema = {
  type: 'object',
  properties: {
    events: {
      type: 'array',
      items: {
        type: 'object',
        properties: {
          title: { type: 'string' },
          date: { type: 'string' },
          time: { type: 'string' },
          location: { type: 'string' },
          duration: { type: 'string' }
        },
        required: ['title', 'date']
      }
    }
  },
  required: ['events']
};

async function extrairEventos(texto) {
  const resultado = await session.prompt(
    `Extract calendar events from this text:\n\n${texto}`,
    { responseConstraint: schema }
  );
  return JSON.parse(resultado).events;
}

const eventos = await extrairEventos(
  'Meeting with John tomorrow at 3pm in Room 201 for about 1 hour. ' +
  'Also, dentist appointment on Friday at 10am.'
);
// [
//   { title: "Meeting with John", date: "tomorrow", time: "3pm", location: "Room 201", duration: "1 hour" },
//   { title: "Dentist appointment", date: "Friday", time: "10am" }
// ]

Validador de formulário inteligente

const schema = {
  type: 'object',
  properties: {
    isValid: { type: 'boolean' },
    issues: {
      type: 'array',
      items: {
        type: 'object',
        properties: {
          field: { type: 'string' },
          issue: { type: 'string' },
          suggestion: { type: 'string' }
        },
        required: ['field', 'issue']
      }
    }
  },
  required: ['isValid', 'issues']
};

async function validarFormulario(dados) {
  const resultado = await session.prompt(
    `Validate this form data for a job application:\n${JSON.stringify(dados)}`,
    { responseConstraint: schema }
  );
  return JSON.parse(resultado);
}

Perguntas frequentes

O responseConstraint garante 100% que o JSON será válido?

Na grande maioria dos casos, sim. O modelo é forçado a gerar output que valida contra o schema. Porém, em edge cases com schemas muito complexos, é prudente envolver o JSON.parse() em try/catch.

Posso usar JSON Schema com $ref ou definitions?

A implementação atual suporta schemas inline simples. Schemas com referências externas ($ref) podem não funcionar como esperado. Prefira schemas auto-contidos.

O responseConstraint funciona com todos os idiomas de output?

Sim. O constraint se aplica ao formato da resposta, não ao idioma. Você pode combinar expectedOutputs em qualquer idioma suportado com um JSON Schema.

Regex ou JSON Schema — qual devo usar?

Use JSON Schema quando precisa de dados estruturados (objetos, arrays, tipos específicos). Use regex quando precisa apenas de um formato de string específico (datas, códigos, identificadores).

O structured output consome mais tokens?

Sim, quando omitResponseConstraintInput é false (padrão), o schema é incluído no contexto. Schemas complexos podem consumir dezenas ou centenas de tokens. Use measureContextUsage() para avaliar o impacto.


Referências