Saídas estruturadas em IA: como definir, validar e implementar um contrato JSON
Imagen generada con gpt-image-2.5-sunburst para InferamaFonte ↗
01

A saída estruturada é um contrato, não uma garantia de veracidade

Uma saída estruturada é uma resposta de um sistema de IA apresentada segundo uma forma de dados acordada, por exemplo, um objeto JSON com campos, tipos e valores permitidos. Numa aplicação, esse contrato permite analisar o resultado e encaminhá-lo para código subsequente sem ter de extrair dados de um parágrafo em texto livre. Por si só, não torna o conteúdo verdadeiro, completo ou apropriado para executar uma ação.

A distinção é importante. O texto «o pedido é para terça-feira» pode ser corretamente transformado numa cadeia de caracteres com uma data; ainda assim, a aplicação precisa de saber a que terça-feira se refere, se a pessoa indicou essa data e se o resultado é coerente com o resto do pedido. A estrutura define como uma resposta é representada. A correção dos dados também depende da entrada, da tarefa e das regras de negócio.

Por isso, é útil pensar em três verificações distintas: se a resposta pode ser lida como JSON; se respeita o esquema acordado; e se os valores fazem sentido e são sustentados pelas informações disponíveis. Uma resposta pode passar na primeira verificação e falhar nas outras duas. Também pode respeitar um esquema e, ainda assim, representar uma extração errada.

Este guia usa uma extração fictícia de pedidos de férias para ilustrar o desenho. O exemplo é uma proposta de implementação local, não uma afirmação sobre o formato obrigatório de qualquer API. As capacidades específicas de cada fornecedor, os nomes dos parâmetros, os modelos compatíveis e os estados de erro devem ser verificados na documentação atual do serviço escolhido.

02

Decidir se precisa de uma estrutura

Imponha uma saída estruturada quando o resultado for alimentar uma interface, uma pesquisa, uma fila de tarefas, uma regra automatizada ou uma base de dados, e quando o conjunto de campos esperado puder ser descrito de forma estável. Por exemplo, uma aplicação de gestão de pedidos pode precisar de um estado, de um tipo de pedido e de datas identificadas de forma consistente. Nesse caso, pedir texto livre e extrair os campos depois acrescenta outra etapa de interpretação e mais pontos de falha.

Nem sempre é necessário. Se uma pessoa só quiser uma explicação, uma resposta conversacional pode ser mais clara do que um objeto de dados. Se uma transformação determinística for suficiente — como normalizar espaços ou converter uma data que já tem um formato inequívoco —, uma função convencional pode ser mais simples e reproduzível do que pedir ao modelo que o faça. E se o problema ainda não tiver campos definidos, formalizar um esquema demasiado cedo pode esconder que a tarefa está mal formulada.

A decisão também deve ter em conta o custo do erro. Se uma resposta iniciar pagamentos, alterar permissões, eliminar informações ou influenciar uma decisão sensível, o esquema é apenas uma das medidas de controlo. A aplicação poderá precisar de confirmação humana, de permissões adicionais ou de uma regra que impeça a execução da ação até que uma ambiguidade seja resolvida. Não é prudente confiar essas decisões a uma suposta segurança do formato.

Antes de desenhar o contrato, escreva quem consome cada campo e para quê. Se nenhum componente utilizar um campo, talvez ele não deva fazer parte da saída. Se um componente exigir uma precisão que a entrada não garante, defina um estado de incerteza ou uma pergunta de esclarecimento, em vez de obrigar o modelo a inventar um valor.

Uma decisão inicial

NecessidadeOpção inicialPrecaução
Resposta destinada a uma pessoa, sem processamento subsequenteTexto livreUma estrutura rígida pode dificultar a leitura sem trazer benefícios.
Campos previsíveis que serão consumidos por códigoContrato de dados e validação localO formato não prova que os valores estejam corretos.
Transformação exata e determinísticaCódigo convencionalNão acrescente geração se uma pequena função resolver o problema.
Entrada ambígua ou insuficienteEstado de esclarecimento ou revisãoNão force valores fictícios para preencher campos.
03

Definir um contrato que represente a tarefa

Um contrato útil descreve a estrutura e as condições de utilização de cada campo. Comece por identificar os campos mínimos, atribua-lhes tipos e decida quais são obrigatórios. Depois, especifique os valores permitidos, a representação dos dados em falta e as relações entre os campos. Por fim, separe estas regras do texto de instruções dirigido ao modelo: a aplicação deve manter uma definição verificável que possa ser testada sem depender daquilo que o modelo parece ter compreendido.

Na extração de um pedido de férias, um contrato demasiado simples poderia exigir sempre uma data de início e uma data de fim. Mas um pedido como «gostaria de tirar uns dias no próximo mês» não permite determinar datas exatas. Obrigar ao preenchimento das datas incentiva o sistema a adivinhar. Uma alternativa é explicitar os estados da tarefa: pedido completo, esclarecimento necessário ou texto que não corresponde a um pedido. As datas podem ficar vazias quando não forem conhecidas, e uma pergunta pode indicar qual é a informação em falta.

O esquema abaixo ilustra uma opção possível. A condição de a data final não ser anterior à inicial e a relação entre o estado e os campos opcionais também têm de ser verificadas na aplicação. Não presuma que um modo de geração ou um validador aceita todas as palavras-chave aqui apresentadas: o esquema local e o formato aceite pela API podem não ser diretamente compatíveis. Se a interface remota aceitar apenas um subconjunto, adapte a representação remota e mantenha as verificações completas no seu código.

A convenção para representar a ausência deve ser inequívoca. Neste exemplo, uma data desconhecida é representada por null, não por uma cadeia vazia, «desconhecido» ou uma data inventada. Um único valor coerente facilita a validação e evita que diferentes consumidores interpretem o mesmo caso de formas distintas. A aplicação também deve decidir se aceita campos adicionais ou se os rejeita; permiti-los sem revisão pode ocultar erros de versão ou saídas inesperadas.

Esquema local ilustrativo · json

Este esquema documenta uma proposta de contrato. Não implica compatibilidade direta com a interface de geração de qualquer fornecedor.

{
  "type": "object",
  "additionalProperties": false,
  "required": ["status", "start_date", "end_date", "clarification_question"],
  "properties": {
    "status": {
      "type": "string",
      "enum": ["complete", "needs_clarification", "not_a_request"]
    },
    "start_date": {
      "type": ["string", "null"],
      "description": "Data ISO AAAA-MM-DD, apenas se for sustentada pelo pedido"
    },
    "end_date": {
      "type": ["string", "null"],
      "description": "Data ISO AAAA-MM-DD, apenas se for sustentada pelo pedido"
    },
    "clarification_question": {
      "type": ["string", "null"]
    }
  }
}
04

Separar formato, esquema e significado

A primeira validação pergunta se os bytes recebidos podem ser interpretados como JSON. Se a resposta contiver uma vírgula final, texto adicional fora do objeto ou uma aspa por fechar, a análise pode falhar. A segunda pergunta se, depois de analisado, o valor tem a estrutura permitida: um objeto, as chaves esperadas e valores dos tipos apropriados. A terceira pergunta se as informações representam corretamente o pedido e respeitam as regras da aplicação.

Por exemplo, este objeto pode ser JSON válido e respeitar os tipos básicos do contrato: status indica que o pedido está completo, ambas as datas são cadeias de caracteres e não há pergunta de esclarecimento. Ainda assim, se a pessoa pediu férias «de 12 a 10», a resposta é incoerente. Do mesmo modo, o intervalo pode ser internamente válido, mas não estar sustentado pelo texto. Nenhum teste de tipos substitui a verificação da correspondência com a entrada.

As regras semânticas devem ser formuladas como condições explícitas e, sempre que possível, como testes reproduzíveis. Para este exemplo: se status for complete, as duas datas têm de existir e ser datas válidas do calendário; a data final não pode ser anterior à inicial; se status for needs_clarification, deve ser indicado que informação está em falta; e se status for not_a_request, o fluxo de férias não deve ser iniciado. A política concreta pode variar, mas não deve ficar implícita na interpretação do consumidor.

Também é necessário distinguir uma resposta inválida de uma resposta válida que indica que a tarefa não pode ser concluída. needs_clarification pode ser uma resposta correta segundo o contrato, e não um erro técnico. Se a API comunicar uma recusa, um erro de geração ou uma interrupção através de um formato próprio, a aplicação deve tratar esse estado de acordo com a documentação do fornecedor. Não convém transformar todas essas situações num objeto aparentemente normal sem saber o que aconteceu.

05

Escolher um modo sem presumir compatibilidades

Algumas APIs oferecem opções específicas para solicitar saídas estruturadas, mas os nomes, requisitos e limites dependem de cada produto e podem mudar. A documentação oficial do Amazon Nova aborda o requisito de saída estruturada e formatos como JSON. A documentação da Anthropic descreve saídas estruturadas com um esquema JSON e o parâmetro output_config. A Google documenta resultados estruturados para a Gemini API e menciona esquemas JSON. A OpenAI apresenta a funcionalidade de saídas estruturadas na sua API. São referências a produtos concretos, não provas de que partilhem o mesmo contrato, o mesmo nível de restrição ou a mesma forma de gerir falhas.

O Microsoft Learn também tem uma página sobre saídas estruturadas para modelos de chat na documentação identificada como Foundry clássico. Essa delimitação é relevante: uma rota de documentação associada a um produto ou modo não deve ser tratada como uma especificação universal de todos os serviços Microsoft. Do mesmo modo, a documentação do Claude Code e a do Claude Platform correspondem a produtos diferentes; não se deve transpor automaticamente uma opção de um para o outro.

Antes de escolher, confirme na documentação primária atual que modo é oferecido, que modelos ou versões abrange, que partes do esquema são aceites, o que acontece com campos opcionais e enumerações, como são representadas as recusas ou gerações incompletas e que estrutura de resposta a aplicação recebe. Registe a data e a versão da documentação consultada. Se um requisito do seu contrato não estiver especificado, trate-o como uma incerteza e verifique-o com testes no ambiente correspondente, em vez de o considerar uma capacidade garantida.

Não confunda pedir JSON com exigir o cumprimento de um esquema. Um modo que produza uma resposta com sintaxe JSON pode reduzir o trabalho de análise, mas a aplicação continua a ter de verificar as chaves, os tipos e as regras da tarefa. Mesmo quando a documentação de um modo descreve restrições de esquema, mantenha a validação local: o contrato que protege o sistema consumidor deve ser verificado na fronteira de confiança da aplicação.

Verificar a compatibilidade antes de implementar

  1. 01Identifique a API, o produto, a versão e o modelo exatos que pretende utilizar.
  2. 02Consulte a documentação oficial atual sobre o modo de saída estruturada e as respetivas restrições documentadas.
  3. 03Compare os campos, tipos, opcionalidade, enumerações e regras aninhadas do seu contrato com o que a interface aceita.
  4. 04Procure como são descritas as recusas, os erros, as interrupções e as respostas que não completam o formato solicitado.
  5. 05Teste a integração no ambiente escolhido e mantenha a validação local, mesmo que o pedido inclua restrições de formato.
06

Implementar a validação na aplicação

A aplicação deve analisar a resposta num ponto controlado e validá-la antes de a entregar ao componente seguinte. Uma sequência prática é: receber o resultado da API; verificar primeiro os estados de transporte e de geração documentados; extrair o conteúdo relevante; analisar o JSON; verificar se a raiz é um objeto com as chaves permitidas; validar os tipos e valores; e, por último, aplicar as regras de negócio. Se uma etapa falhar, o fluxo deve preservar informações suficientes para diagnosticar o problema, sem agir como se o resultado estivesse correto.

O exemplo abaixo é código Python executável com a biblioteca padrão. Espera uma cadeia JSON, permite exatamente quatro chaves, verifica os tipos, interpreta as datas no formato ISO e aplica relações entre o estado, as datas e a pergunta. Não verifica se uma data apareceu realmente no texto de entrada: para isso, é necessário preservar o texto, definir uma estratégia de verificação da evidência e testá-la em separado. Também não substitui a gestão de erros da API, que deve ser adaptada ao serviço utilizado.

O exemplo rejeita deliberadamente campos adicionais. Essa política evita que uma alteração inesperada passe despercebida, embora um sistema com evolução controlada possa escolher outra regra. O importante é decidir explicitamente e versionar o contrato. Se utilizar uma biblioteca externa de validação em produção, fixe uma versão compatível, teste as respetivas regras e trate os erros do validador como parte da interface interna, não como motivo para ignorar a verificação.

Não transforme um erro num valor predefinido silencioso. Se faltar end_date, preenchê-la com start_date pode alterar o significado do pedido. Se uma data for inválida, tratá-la como null pode apagar um sinal útil de falha. É preferível devolver um resultado de validação tipado — válido, requer esclarecimento ou inválido — e decidir de forma explícita se deve haver uma nova tentativa, uma pergunta à pessoa ou encaminhamento para revisão.

Validação local com Python padrão · python

Guarde o código como validate_leave.py e execute-o com Python 3. O exemplo só apresenta os dados depois de validar o contrato e as respetivas relações básicas.

import json
from datetime import date

REQUIRED = {
    "status",
    "start_date",
    "end_date",
    "clarification_question",
}
STATUSES = {"complete", "needs_clarification", "not_a_request"}


def validate(raw):
    try:
        data = json.loads(raw)
    except json.JSONDecodeError as exc:
        raise ValueError("JSON inválido") from exc

    if not isinstance(data, dict) or set(data) != REQUIRED:
        raise ValueError("Objeto com chaves em falta ou inesperadas")
    if data["status"] not in STATUSES:
        raise ValueError("Estado não permitido")

    for key in ("start_date", "end_date", "clarification_question"):
        value = data[key]
        if value is not None and not isinstance(value, str):
            raise ValueError(f"Tipo incorreto: {key}")

    dates = []
    for key in ("start_date", "end_date"):
        value = data[key]
        if value is None:
            dates.append(None)
            continue
        parsed = date.fromisoformat(value)
        if parsed.isoformat() != value:
            raise ValueError(f"Formato de data incorreto: {key}")
        dates.append(parsed)

    start, end = dates
    if data["status"] == "complete":
        if start is None or end is None or end < start:
            raise ValueError("Intervalo completo em falta ou incoerente")
        if data["clarification_question"] is not None:
            raise ValueError("Um pedido completo não deve solicitar esclarecimentos")
    elif data["status"] == "needs_clarification":
        if not data["clarification_question"]:
            raise ValueError("Indique que informação precisa de esclarecimento")
    elif data["status"] == "not_a_request":
        if start is not None or end is not None:
            raise ValueError("Um texto que não é um pedido não deve incluir datas")

    return data


if __name__ == "__main__":
    sample = '{"status":"complete","start_date":"2027-06-12","end_date":"2027-06-14","clarification_question":null}'
    print(validate(sample))
07

Definir o que fazer perante cada falha

Uma política de falhas útil distingue pelo menos quatro tipos de situação. Primeiro, erros de transporte ou do serviço: não foi obtida uma resposta utilizável. Segundo, erros de geração ou recusas: a API pode informar que não produziu o resultado solicitado. Terceiro, erros de formato ou de esquema: o conteúdo não pode ser analisado ou não cumpre o contrato. Quarto, erros semânticos: a estrutura passa nas verificações, mas o conteúdo está incompleto, é contraditório ou não tem suporte.

Cada tipo precisa de um resultado explícito. Um erro temporário documentado pode permitir uma nova tentativa limitada; repetir um pedido ambíguo não cria informações que não existiam na entrada. Uma falha de esquema pode justificar uma nova tentativa com instruções mais claras, mas apenas se o número de tentativas for limitado e a saída for novamente validada. Um dado semanticamente incoerente pode exigir uma pergunta à pessoa ou revisão humana. Uma recusa deve ser apresentada ou tratada de acordo com a política do produto e os detalhes documentados pelo fornecedor.

Defina limites para evitar ciclos de novas tentativas e evite corrigir automaticamente valores de forma a alterar o significado. Se a nova tentativa produzir uma saída diferente, não escolha silenciosamente a que parecer mais conveniente. Preserve o estado da validação, a versão do contrato e o motivo categorizado da falha. Para operações com consequências, considere uma etapa de confirmação antes de executar o efeito real.

Os registos técnicos devem permitir diagnosticar problemas com a menor exposição de dados possível. Guarde identificadores de correlação, a versão do contrato, a categoria do erro e as métricas necessárias. Avalie se é necessário conservar o texto de entrada ou a saída completa; se não for, não os registe por predefinição. Se forem conservados, defina o acesso, o período de retenção e o tratamento adequado à sensibilidade desses dados.

Resposta por categoria de falha

SituaçãoResposta prudenteEvitar
Falha de transporte ou do serviçoAplicar a política de novas tentativas documentada, com limite e registo.Tratar uma resposta inexistente como uma extração vazia válida.
JSON malformado ou esquema não cumpridoMarcar a saída como inválida e decidir se é possível tentar uma correção limitada.Eliminar partes inesperadas até o resultado parecer aceitável.
Campo necessário em falta ou entrada ambíguaSolicitar a informação em falta ou encaminhar para revisão.Inventar um valor predefinido que altere o pedido.
Valores contraditórios ou sem suporteBloquear a ação subsequente e conservar a categoria da falha.Confiar que um formato válido equivale a conteúdo correto.
08

Testar entradas reais e casos-limite

Os testes devem verificar comportamentos, não apenas se a resposta de exemplo pode ser analisada. Prepare um conjunto pequeno e fácil de manter de entradas representativas, resultados esperados e regras a cumprir. Inclua casos normais, ambíguos, incompletos, contraditórios e alheios à tarefa. Cada caso deve testar tanto a saída considerada aceitável como a decisão da aplicação depois de a receber.

Para o pedido de férias, uma entrada clara pode especificar datas completas num intervalo coerente. Um caso ambíguo pode dizer «na sexta-feira» sem contexto temporal suficiente. Outro pode pedir datas, mas omitir o ano. Um caso incompleto pode não especificar a data final. Teste também um intervalo invertido, uma data impossível, um tipo incorreto, uma chave adicional e uma resposta que não seja JSON. Para cada caso, decida se a aplicação aceita, pede esclarecimentos ou rejeita; não deixe essa decisão para uma interpretação improvisada durante a integração.

Inclua testes semânticos com saídas preparadas, além de testes com o modelo. Se testar apenas gerações reais, o resultado pode variar e será difícil determinar se falhou a geração, o contrato ou o código consumidor. Os testes determinísticos do validador permitem verificar casos exatos; os testes de integração observam o comportamento do fluxo completo com a API e a configuração que serão efetivamente implementadas.

Ao alterar o esquema, acrescente testes de compatibilidade entre versões. Se um consumidor depender de um campo que deixa de estar disponível, uma alteração aparentemente pequena pode interromper o processamento. Versione o contrato e decida como serão implementados os componentes produtor e consumidor: por exemplo, admitindo um período de transição com ambas as versões ou coordenando a alteração. Não trate uma mudança de nomes ou de nulabilidade como uma modificação meramente cosmética.

Matriz mínima de testes

CasoVerificaçãoResultado esperado da aplicação
Pedido claro com datas coerentesTipos, formato, intervalo e correspondência com a entrada.Aceitar como completo se todas as regras forem cumpridas.
Data relativa ou ambíguaNão converter uma referência incompleta numa data segura sem fundamento.Pedir esclarecimentos ou assinalar incerteza.
Data em faltaVerificar a convenção null e a relação com status.Não preencher automaticamente com outra data.
Data impossível ou intervalo invertidoValidar o calendário e a ordem temporal.Rejeitar como saída semanticamente inválida.
Chave adicional, tipo incorreto ou JSON malformadoTestar os limites da análise e do contrato.Bloquear o processamento e classificar o erro.
Texto que não é um pedidoVerificar o estado not_a_request e a ausência de datas para ação.Não iniciar o fluxo de férias.
09

Implementar e manter o contrato

Um contrato implementado precisa de um responsável, uma versão e um processo de alteração. Guarde a definição junto do código que a valida e registe qual foi utilizada para cada resultado processado. Antes de alterar campos, tipos, enumerações ou regras para valores em falta, avalie os consumidores e acrescente testes de regressão específicos. Se o modelo, o prompt, a configuração ou a API mudarem, repita os testes relevantes: o contrato não substitui a verificação do comportamento do sistema completo.

Meça separadamente a proporção de respostas que podem ser analisadas, a que cumpre o esquema, a que passa as regras semânticas e a que exige esclarecimento ou revisão. Um único indicador de «sucesso» oculta diferenças importantes. Por exemplo, saber que quase todas as respostas são JSON válido não revela se as datas extraídas correspondem aos pedidos. As métricas devem ter definições estáveis e evitar a recolha de conteúdo sensível quando uma categoria agregada for suficiente.

Acompanhe alterações na documentação e nas versões do fornecedor, mas não presuma que uma atualização mantém exatamente as capacidades da configuração anterior. Registe a combinação testada e verifique as condições de compatibilidade antes de a alterar. A página de documentação de um serviço é uma referência para esse serviço e para o âmbito nela descrito; não certifica um comportamento idêntico noutro produto, versão ou modelo.

Se o sistema tomar decisões com consequências, acrescente uma barreira proporcional ao risco. Pode ser revisão humana, confirmação explícita ou uma verificação independente. Desenhe também uma via de recuperação: o que fazer com resultados pendentes, como corrigir um dado validado como incorreto e como evitar executar uma ação duas vezes após uma nova tentativa. O contrato de dados faz parte de um fluxo operacional; não substitui os respetivos controlos.

Lista de aceitação antes de ligar uma ação subsequente

  1. 01Cada campo tem um consumidor, um tipo e uma política explícita para valores em falta.
  2. 02As condições entre campos e as regras de negócio estão documentadas e são testadas localmente.
  3. 03O sistema distingue erros do serviço, erros de formato, estados de esclarecimento e incoerências semânticas.
  4. 04A aplicação não executa ações com base em respostas que não tenham passado todas as verificações necessárias.
  5. 05Existem testes para entradas ambíguas, campos em falta, tipos incorretos, valores-limite e respostas inesperadas.
  6. 06O contrato está versionado e as alterações são revistas com os componentes que o consomem.
  7. 07Os registos permitem investigar falhas sem conservar dados pessoais ou sensíveis desnecessários.
  8. 08A compatibilidade da API e do modelo foi verificada na documentação oficial atual e no ambiente de implementação.
10

Critérios práticos para concluir o desenho

Antes de dar o trabalho por concluído, confirme que a estrutura responde a uma necessidade real do produto e não apenas à preferência por receber JSON. O contrato deve ser suficientemente pequeno para que cada campo tenha uma utilização clara, mas suficientemente expressivo para representar a incerteza, a ausência e os resultados que não correspondem à tarefa. Se os casos ambíguos não tiverem uma representação segura, ainda não está pronto para produção.

Certifique-se de que o consumidor valida a resposta recebida, e não apenas as instruções enviadas. Verifique separadamente o formato, as chaves, os tipos e as regras semânticas. Defina o que é bloqueado, o que pode dar lugar a uma nova tentativa e aquilo que deve ser perguntado. Teste as decisões com exemplos verificáveis, incluindo a entrada que não contém informações suficientes e a saída que parece válida, mas contradiz o texto.

Por fim, registe as incertezas externas: compatibilidade específica da API, limites do esquema, cobertura dos modelos e formato dos erros. Resolva-as consultando a documentação oficial do produto e testando a versão que será implementada. Assim, o contrato local pode continuar a ser uma fronteira de segurança e manutenção, mesmo que o modo de geração mude.

Questões em aberto

  • As fontes fornecidas não detalham completamente os modelos, as versões e os subconjuntos de esquemas compatíveis com cada modo; estes aspetos devem ser verificados na documentação oficial atual para a implementação escolhida.
  • As fontes fornecidas não apresentam uma especificação unificada sobre como os fornecedores representam recusas, erros de geração ou respostas incompletas; o tratamento deve ser confirmado para a API específica.
  • O esquema JSON do exemplo e o código Python são propostas locais ilustrativas. Não afirmam que o esquema possa ser enviado sem adaptação para uma API específica.
  • As regras para resolver datas relativas, confirmar pedidos ou encaminhar casos ambíguos dependem da aplicação e não podem ser deduzidas da sintaxe JSON.
11

Continue a explorar

11

Fontes consultadas

03

Correções e transparência

Se encontrar informação incorreta ou desatualizada, envie-nos a página e a fonte que devemos rever.

Propor uma correção