Ir para o conteúdo

Saídas estruturadas: obtendo JSON confiável de um modelo de linguagem

Blog / Publicado 

A primeira versão de cada integração LLM analisa a prosa. Você pede "os três riscos principais, um por linha", divididos em novas linhas, e funciona até o dia em que o modelo escreve primeiro uma frase introdutória. Existem saídas estruturadas para encerrar esse loop: você entrega ao modelo um esquema JSON, o decodificador é restrito para que não possa emitir nada que o viole e você recebe de volta uma carga útil com os campos solicitados. Essa garantia é real, mas mais restrita do que parece, e a diferença é onde vivem os bugs.

O que a garantia realmente cobre

Garantias de decodificação restritas forma. A resposta será analisada como JSON, terá suas chaves, os tipos corresponderão e os campos enum conterão um dos valores listados. Essa é uma grande melhoria em relação à rapidez e esperança.

Não garante nada sobre contente. Um campo digitado como string conterá uma string; se essa string é precisa ou inventada para satisfazer o esquema, permanece inalterado desde antes. Na verdade, um esquema estrito torna a invenção um pouco mais provável, porque um campo obrigatório deve ser preenchido com algo e o modelo não tem como dizer "não presente na fonte". Esta é a falha mais comum na produção: uma falha obrigatória invoice_number em um documento que não tem nenhum.

Projete em torno disso. Permitir nulo onde a ausência for possível, adicione um explícito found: boolean ou um "unknown" membro enum e nunca torne um campo obrigatório, a menos que a resposta exista genuinamente em todas as entradas.

Projetando um esquema que um modelo pode preencher

Os esquemas escritos para validação e os esquemas escritos para geração parecem diferentes. Algumas regras que se aplicam a todos os provedores:

Os provedores também restringem quais palavras-chave do esquema JSON eles suportam no modo estrito: oneOf, restrições de padrão, limites numéricos e recursão são comumente limitados ou ignorados. Suponha que o esquema restrinja apenas a estrutura e aplique você mesmo suas regras de negócios após a análise.

Valide mesmo assim

Mesmo com um modo estrito, valide cada resposta antes que ela chegue ao seu banco de dados. O modo estrito pode ser desabilitado por uma alteração de configuração, um caminho de fallback pode atingir um modelo que não o suporta, uma nova tentativa de rede pode retornar um objeto de erro e a resposta pode ser truncada por um objeto intermediário de limite de token - que produz JSON inválido, nenhum modo de esquema impede.

Quando uma carga falha, a questão útil é qual campo e por quê. O Validador de esquema JSON pega o documento e o esquema e relata uma linha por problema, com o caminho do ponteiro JSON, a palavra-chave que falhou e o valor realmente encontrado. Essa é exatamente a forma que você deseja para dois propósitos: corrigir seu esquema durante o desenvolvimento e retornar para uma nova tentativa - um relatório de validação com falha colado em uma mensagem de acompanhamento é um prompt de reparo extremamente eficaz, já que o modelo pode ver precisamente o que errou.

Limite as tentativas em uma ou duas. Um modelo que falha duas vezes em um esquema geralmente está sendo solicitado a fornecer algo que a entrada não contém, e a terceira tentativa o inventará.

De uma carga útil de amostra ao código

A maioria dos esquemas começa como um exemplo de resposta que alguém colou em um bate-papo. Transformar isso em código digitado e validado manualmente é tedioso e sujeito a erros, então não faça isso.

Cole a amostra em Esquema JSON para Zod e você obtém um esquema Zod mais o tipo TypeScript inferido. Ele infere por campo, em vez de digitar tudo como uma string: valores que se parecem com e-mails, URLs, UUIDs ou datas ISO são obtidos .email(), .url(), .uuid() e .datetime() em vez de um nu z.string(), as matrizes carregam seu tipo de elemento, e um campo cujo tipo varia entre as ocorrências torna-se uma união, em vez de escolher silenciosamente o primeiro que viu. Vale a pena alimentar deliberadamente esse último comportamento: cole três ou quatro respostas reais concatenadas, não uma, para que os campos opcionais e polimórficos apareçam como uniões em vez de serem bloqueados para o que quer que a primeira amostra contenha.

Se você precisar apenas de tipos e não de validação em tempo de execução - porque a carga vem de uma fonte em que você já confia - JSON para TypeScript produz as interfaces diretamente, com objetos aninhados divididos como subinterfaces nomeadas, em vez de blobs anônimos embutidos.

Mantenha a direção da verdade correta: uma definição de esquema, com todo o resto gerado a partir dela. Um esquema Zod e um esquema JSON mantidos separadamente irão discordar dentro de um mês, e a discordância surgirá como um erro de análise de produção em vez de uma falha no teste.

Lendo o que voltou

As respostas do modelo chegam reduzidas em uma linha, o que torna infeliz observá-las. O Formatador JSON recua e classifica-os em algo legível, e sua validação captura o caso de truncamento imediatamente - uma carga útil que termina no meio da string é a assinatura de um limite de token de acerto, e a correção é um limite de saída mais alto ou um esquema menor, não uma alteração imediata.

Ao comparar o que dois modelos produziram a partir da mesma entrada ou verificar se uma alteração imediata alterou a saída de uma maneira que você não pretendia, compare as cargas formatadas em vez de ler ambas. Diferenças estruturais que são invisíveis em uma parede de JSON — um campo que silenciosamente se tornou uma sequência de dígitos em vez de um número — destacam-se imediatamente em uma comparação.

Versionamento

Os esquemas mudam e a saída do modelo muda com eles. Coloque um campo de versão na carga útil desde o primeiro dia, para que os registros armazenados tenham a forma sob a qual foram escritos. Ajustar isso depois de ter um milhão de linhas de JSON não marcado é o tipo de migração para a qual ninguém agenda tempo.

Todas essas ferramentas são executadas localmente em seu navegador, o que é mais importante do que o normal para este fluxo de trabalho: as cargas úteis de amostra que você está transformando em esquemas geralmente são respostas reais de API com dados reais de clientes e não devem ser carregadas em um serviço de formatação para serem embelezadas.

Escrito por Ján Turský

Criador do TextArray – criando ferramentas gratuitas que priorizam a privacidade e que rodam inteiramente no seu navegador.