Saídas estruturadas: obtendo JSON confiável de um modelo de linguagem
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:
- Batidas planas aninhadas. Cada nível de aninhamento é um lugar para o modelo perder o controle. Três objetos rasos extraídos em três cantos bateram em uma árvore profundamente aninhada.
- Enums vencem strings livres. Se um campo tiver cinco valores legítimos, liste-os. Você remove uma classe inteira de código de normalização e o decodificador restrito o aplica para você.
- Os nomes dos campos são instruções.
due_date_iso8601produz resultados melhores do quedatecom o formato explicado em um prompt separado. Os nomes estão no contexto; use-os. - Usar
descriptionem campos. A maioria dos provedores passa descrições de esquema para o modelo. Uma cláusula por campo ambíguo é mais barata e confiável do que um parágrafo de instruções em prosa acima. - Definir
additionalProperties: false. De qualquer forma, vários provedores exigem isso para o modo estrito, e isso impede o modelo de inventar uma chave extra que seu analisador ignora silenciosamente. - Prefira matrizes de objetos planos a mapas. Chaves dinâmicas - um objeto cujos nomes de propriedades são desconhecidos antecipadamente - são mal suportadas por modos estritos e difíceis de digitar no receptor.
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.
Criador do TextArray – criando ferramentas gratuitas que priorizam a privacidade e que rodam inteiramente no seu navegador.