Como restringir o que o modelo pode gerar?
Você descreve o formato como uma gramática formal e o motor de inferência a aplica durante a geração: a cada token, um autômato calcula quais entradas do vocabulário mantêm a sequência válida e zera a probabilidade de todas as outras antes do sorteio. O modelo não é convencido a seguir o formato. Ele fica incapaz de sair dele.
É o mecanismo que sustenta o JSON mode e o schema estrito descritos em como forçar JSON válido. A diferença é que aqui você escreve a gramática, em vez de deixar o provedor derivá-la do seu schema, e com isso alcança formato que JSON Schema não descreve.
Duas coisas convém separar antes de continuar. Restringir a decodificação é diferente de validar depois, que é o assunto de JSON Schema e Zod. E é diferente de interromper a geração num marcador, que é o que stop sequences fazem: a stop sequence corta o fim, a gramática governa cada token do começo ao fim.
O mecanismo, um token por vez
O modelo gera um token por vez, e cada passo produz um número por entrada do vocabulário, o logit. Normalmente esses números viram probabilidades e um token é sorteado. A decodificação restrita se encaixa entre as duas coisas.
Antes de gerar, a gramática é compilada em um autômato. Durante a geração, o
estado desse autômato depois dos tokens já emitidos determina o conjunto de
tokens aceitáveis agora. Depois de {"qtd": esse conjunto contém dígitos e sinal
de menos, e não contém aspas nem a palavra doze. O motor substitui o logit de
todo token fora do conjunto por menos infinito, e só então normaliza e sorteia.
Repare no que isso implica: a restrição não olha o significado da resposta e não sabe nada sobre o prompt. Ela só sabe onde a sequência está dentro da gramática. Essa é a força e o limite da técnica ao mesmo tempo.
Por que o inválido fica inalcançável
A ordem das operações é o que dá a garantia. Menos infinito passa pela exponencial da softmax e vira exatamente zero, então o token ilegal recebe probabilidade zero e não zero-vírgula-alguma-coisa. Nenhum valor de temperatura, top-k ou top-p muda isso: os três operam sobre uma distribuição da qual aquele token já saiu.
Por isso a garantia é estrutural e não estatística. Não é que o modelo passou a errar menos; é que a sequência inválida deixou de existir no espaço de saída. A mesma frase vale ao contrário: se a sua gramática permite algo, o modelo pode produzir aquilo, por mais absurdo que seja no contexto.
O problema do vocabulário
A objeção óbvia é o custo. Testar o vocabulário inteiro a cada token é caro quando o vocabulário tem dezenas ou centenas de milhares de entradas, e uma implementação ingênua percorre tudo isso a cada passo, para cada requisição em paralelo.
Willard e Louf resolveram a versão regular do problema invertendo a conta. Em vez de perguntar “quais tokens cabem aqui” durante a geração, eles pré-computam, uma vez, um índice que associa cada estado do autômato ao conjunto de tokens aceitáveis naquele estado. Durante a geração a consulta é uma leitura, e o custo por token fica constante em média1.
O XGrammar atacou a versão livre de contexto, que é a que JSON de verdade exige. A ideia central é separar o vocabulário em duas classes: tokens cuja validade não depende do estado da pilha, que podem ser verificados uma única vez, e tokens que dependem do contexto e precisam ser interpretados em tempo de execução. Eles somam a isso uma pilha persistente para acelerar a segunda classe e sobrepõem o cálculo da gramática à execução na GPU. O resultado reportado chega a 100 vezes a velocidade das soluções anteriores, com sobrecarga próxima de zero na geração ponta a ponta2.
É essa engenharia, e não uma mudança nos modelos, que explica por que a decodificação restrita saiu do paper e virou parâmetro de API entre 2023 e 2025.
Regular ou livre de contexto
A escolha da classe de gramática decide o que você consegue expressar e quanto custa compilar.
Gramática regular dá conta de formato com estrutura fixa: uma data, um enum, um identificador com prefixo, um número com casas decimais. É o que uma expressão regular descreve, e é a mais barata de compilar e de indexar.
Gramática livre de contexto é necessária quando existe aninhamento arbitrário — e JSON tem, porque um objeto pode conter uma lista de objetos sem limite de profundidade. Contar chaves abertas exige uma pilha, e pilha é exatamente o que um autômato finito não tem. Motores que só trabalham com autômato finito aproximam isso fixando uma profundidade máxima, o que funciona até o dia em que o seu documento tem um nível a mais.
Na prática, a via mais direta de escrever uma gramática você mesmo é a GBNF do
llama.cpp: um arquivo de texto com regras em uma variante de BNF, passado junto
com o prompt. É o que aparece quando alguém serve um modelo de peso aberto e
precisa de um formato que nenhum provedor oferece. Uma gramática mínima para um
veredito com justificativa cabe em quatro linhas:
root ::= "MOTIVO: " justificativa "\nVEREDITO: " veredito
justificativa ::= [^\n]+
veredito ::= "aprovado" | "reprovado" | "indefinido"
Repare no que essa gramática de quatro linhas garante e o que ela não garante. Garante que a última palavra é uma das três e que existe texto antes dela, e garante isso sem nenhuma instrução no prompt. Não garante que a justificativa tenha relação com o veredito.
Vale registrar uma variação pouco usada e barata: aplicar a restrição só a parte da saída. Deixar o modelo escrever livremente enquanto raciocina e ligar a gramática apenas no trecho final recupera a maior parte da perda de qualidade que a restrição causa, e continua garantindo o formato do que o seu código vai consumir. Quem serve o próprio modelo faz isso trocando a gramática no meio da geração; nas APIs comerciais, o equivalente é o campo de raciocínio declarado antes do campo de resultado.
O que dá para restringir além de JSON
A técnica nasceu longe de JSON. O PICARD, em 2021, restringia a decodificação de modelos ajustados para texto-para-SQL fazendo análise sintática incremental: a cada passo, os tokens que tornariam a consulta inválida eram rejeitados. Isso transformou modelos T5 de desempenho razoável nas soluções de melhor resultado da época em Spider e CoSQL3. O ganho não veio de treino, veio de tirar do espaço de saída aquilo que já era erro.
Geng e colegas generalizaram o argumento em 2023: gramáticas formais descrevem o espaço de saída de uma faixa bem mais ampla de tarefas do que parsing e geração de código, e a decodificação restrita serve como estrutura unificada para tarefas estruturadas de PLN em geral. Eles introduziram gramáticas dependentes da entrada, que permitem a gramática mudar conforme o caso, e avaliaram em extração de informação, desambiguação de entidade e análise sintática de constituintes, com modelos restritos superando os não restritos e, em parte dos casos, modelos ajustados especificamente para a tarefa4.
O padrão que se repete: sempre que o espaço de saída correto é descritível e muito menor que o espaço de saída possível, restringir vale mais do que instruir.
Onde falha
A fragmentação de tokens sai do que o modelo conhece. Este é o modo de falha
menos intuitivo e o mais caro. O modelo aprendeu a distribuição de sequências de
tokens do treino, incluindo como palavras costumam ser quebradas. A máscara pode
forçar uma quebra que nunca aparece naturalmente: se o token mais provável era
"12 e a máscara só permite ", o modelo continua a partir de um prefixo que,
na distribuição dele, é raro. Beurer-Kellner e colegas mediram o estrago numa
versão de GSM8K codificada em JSON com Mistral 7B: 41,5% de acerto sem restrição,
30,8% com uma restrição ingênua e 41,8% com uma restrição alinhada a
subpalavras5. A perda inteira veio da implementação, não da ideia.
O mascaramento guloso distorce a distribuição. Mesmo com o alinhamento de subpalavra resolvido, zerar tokens a cada passo não é o mesmo que amostrar da distribuição do modelo condicionada à gramática. Park e colegas mostraram que a decodificação restrita produz saídas gramaticais com probabilidade desproporcional à que o modelo atribuiria, e propuseram amostrar levando em conta uma aproximação da gramaticalidade futura de cada prefixo, o que gera saídas de maior verossimilhança sem abrir mão da restrição6. A intuição do problema é local: a máscara escolhe o melhor token agora sem saber se aquele caminho leva a um beco onde só sobram continuações ruins.
A cobertura da especificação é parcial. Quando a gramática é derivada de um JSON Schema, parte do schema não vira restrição. Um benchmark de 2025 avaliou seis frameworks sobre 10 mil schemas reais e encontrou diferenças relevantes de cobertura entre eles7. Restrição numérica, tamanho mínimo de string e recursão são as que mais faltam. O efeito prático é ruim: o schema é aceito, a saída “obedece”, e a regra que você escreveu não foi verificada por ninguém.
Válido não é correto. A gramática garante a forma. Ela não sabe se o identificador existe no seu banco, se a categoria escolhida é a certa ou se o número faz sentido. Restringir troca uma falha barulhenta por uma silenciosa, e essa troca só é boa se você tiver a avaliação de conteúdo do outro lado.
A gramática vira uma segunda fonte de verdade. O que você escreve em BNF já existe em algum lugar do seu código, como tipo, coluna de banco ou validador. Agora existe duas vezes, em duas linguagens, mantidas à mão. O descompasso é silencioso e previsível: alguém acrescenta um quarto valor ao enum na aplicação, a gramática continua com três, e o modelo fica estruturalmente incapaz de emitir o valor novo. Nada dá erro, porque a saída continua gramatical. Gerar a gramática a partir da mesma declaração que gera os seus tipos dá mais trabalho no começo e é a única versão que sobrevive a um ano de mudanças.
Compilar custa, e o custo é de largada. Um schema grande vira um autômato grande, e a construção acontece na primeira chamada. Gerar a gramática de novo a cada requisição, com a ordem das regras variando, joga fora o cache de compilação e transforma um custo de uma vez em um custo por chamada.
O truncamento continua. Nada na máscara impede a resposta terminar no meio por limite de tokens. O resultado é um prefixo gramaticalmente perfeito e inutilizável, e a checagem do motivo de parada continua sendo obrigatória.
Quanto custa
Em tempo de geração, pouco, desde que a implementação pré-compute o índice. A abordagem de Willard e Louf reduz o trabalho por token a custo constante em média1, e a versão minimamente invasiva de Beurer-Kellner e colegas chega a superar a geração livre em throughput, porque a máscara restringe o espaço o suficiente para a decodificação especulativa acertar mais5. O XGrammar reporta sobrecarga próxima de zero ponta a ponta2.
O custo real está em três lugares menos visíveis: a compilação da primeira chamada de cada gramática nova, o trabalho de escrever e manter a gramática, e a perda de qualidade quando a biblioteca é ingênua. Os três são de engenharia, não de fatura.
Quando vale a pena
- Use quando o custo de uma saída malformada for alto, não quando a taxa de malformação for alta. Ingestão em lote sem revisão justifica; uma tela com alguém olhando raramente justifica.
- Prefira o schema estrito do provedor enquanto ele der conta. Escrever gramática só se paga quando o formato não é JSON ou quando a regra não cabe em JSON Schema.
- Escreva a gramática mais frouxa que resolve. Cada regra a mais é uma chance a mais de forçar uma fragmentação estranha e de derrubar a qualidade.
- Coloque o campo de raciocínio antes do campo de resultado. O modelo escreve na ordem da gramática, e resultado antes de justificativa elimina a justificativa.
- Meça com e sem restrição na mesma tarefa. É a única forma de saber se a sua biblioteca está na faixa dos 41,8% ou na dos 30,8%.
- Mantenha o validador. Ele cobre o truncamento e a parte do schema que a gramática não expressou.
Footnotes
-
Willard e Louf (2023) pré-computam um índice que mapeia estado do autômato para conjunto de tokens aceitáveis, o que torna o custo por token constante em média em vez de proporcional ao vocabulário. ↩ ↩2
-
Dong et al. (2024) separam tokens independentes de contexto, verificáveis uma vez, de tokens dependentes da pilha, usam uma pilha persistente e sobrepõem o cálculo da gramática à execução na GPU; reportam até 100 vezes a velocidade de soluções anteriores. ↩ ↩2
-
Scholak et al. (2021) restringem a decodificação por análise sintática incremental, rejeitando token inadmissível a cada passo, e levam modelos T5 ajustados ao melhor resultado da época em Spider e CoSQL. ↩
-
Geng et al. (2023) argumentam que gramáticas formais descrevem o espaço de saída de uma faixa ampla de tarefas de PLN, introduzem gramáticas dependentes da entrada e avaliam em extração de informação, desambiguação de entidade e análise sintática de constituintes. ↩
-
Beurer-Kellner et al. (2024) atribuem a perda de acurácia ao desalinhamento entre a máscara e as subpalavras do modelo, e apresentam um algoritmo alinhado que a elimina e melhora o throughput por permitir decodificação especulativa. ↩ ↩2
-
Park et al. (2024) mostram que a decodificação restrita distorce a distribuição do modelo, produzindo saídas gramaticais de baixa verossimilhança, e propõem uma amostragem que aproxima a gramaticalidade futura dos prefixos para corrigir isso. ↩
-
Geng et al. (2025) avaliam seis frameworks de decodificação restrita sobre 10 mil schemas reais de JSON Schema, em eficiência, cobertura e qualidade. ↩
Perguntas frequentes
- O que é grammar-constrained decoding?
- É restringir a amostragem para que a saída siga uma gramática formal. A cada passo, um autômato acompanha o que já foi emitido, calcula quais tokens mantêm a sequência válida e zera a probabilidade dos demais antes do sorteio. A saída inválida deixa de ser improvável e passa a ser inalcançável.
- O que é GBNF?
- É o formato de gramática do `llama.cpp`, uma variante de BNF adaptada para decodificação restrita. Você escreve as regras em um arquivo de texto e passa junto com o prompt; o motor compila as regras e aplica a máscara sobre os logits a cada token. É a via mais direta de restringir formato quando você mesmo serve o modelo.
- Restringir a decodificação piora a resposta?
- Pode piorar por dois motivos distintos. A máscara força fragmentações de token que o modelo não viu no treino, o que joga a distribuição seguinte para fora do que ele conhece. E o mascaramento guloso distorce as probabilidades, favorecendo saídas gramaticais que o modelo consideraria pouco prováveis.
- Dá para restringir formato que não seja JSON?
- Dá, e é o ponto da técnica. Qualquer linguagem descrita por uma gramática formal serve: SQL, uma chamada de função, uma sequência de ações, uma linguagem de marcação interna. Um trabalho de 2023 aplicou a mesma máquina a extração de informação, desambiguação de entidade e análise sintática.
- Decodificação restrita substitui a validação?
- Não. Ela garante que a sequência pertence à gramática, e a gramática costuma cobrir só parte da sua regra: limite numérico, tamanho de string e schema recursivo ficam de fora com frequência. Além disso ela não impede truncamento por limite de tokens, que produz saída válida e incompleta.
- Gramática regular ou livre de contexto, qual usar?
- Regular basta para formato de profundidade fixa, como uma data ou um enum, e é a mais barata de compilar. JSON com aninhamento arbitrário precisa de gramática livre de contexto, porque contar chaves abertas exige uma pilha. Motores que só têm autômato finito aproximam isso limitando a profundidade.
Referências
- Geng, S. et al.. Grammar-Constrained Decoding for Structured NLP Tasks without Finetuning (2023)arXiv:2305.13971
- Willard, B. T. e Louf, R.. Efficient Guided Generation for Large Language Models (2023)arXiv:2307.09702
- Dong, Y. et al.. XGrammar: Flexible and Efficient Structured Generation Engine for Large Language Models (2024)arXiv:2411.15100
- Beurer-Kellner, L. et al.. Guiding LLMs The Right Way: Fast, Non-Invasive Constrained Generation (2024)arXiv:2403.06988
- Park, K. et al.. Grammar-Aligned Decoding (2024)arXiv:2405.21047
- Scholak, T.; Schucher, N.; Bahdanau, D.. PICARD: Parsing Incrementally for Constrained Auto-Regressive Decoding from Language Models (2021)arXiv:2109.05093
- Geng, S. et al.. JSONSchemaBench: A Rigorous Benchmark of Structured Outputs for Language Models (2025)arXiv:2501.10868