Artigos aprofundados sobre a tecnologia que molda o que vem a seguir.

Decodificação Restrita: Transforme LLMs em Modelos de Decisão Rápidos

Decodificação restrita transforma um LLM em classificador rápido. Veja como mascaramento de logits, calibração e temperature scaling fazem modelos de decisão funcionar.

Um feixe de luz se dividindo em cinco raios coloridos através de portões, ilustrando a saída de tokens restrita.
Mascarar o vocabulário força toda a distribuição de saída do modelo a passar por alguns portões permitidos.

Aqui vai um truque barato e silenciosamente útil: se você só se importa com o primeiro token que um LLM emitiria, pode fazer uma pergunta de múltipla escolha e obter a resposta em um único forward pass. Decodificação restrita — mascarar todos os tokens do vocabulário, exceto os poucos que você está disposto a aceitar — transforma um modelo generativo em algo que parece muito um classificador. Sem parsing de JSON, sem loops de retentativa, sem onze passos autorregressivos para produzir onze caracteres. Uma passada, um softmax, uma resposta com uma probabilidade associada a cada opção.

Essa ideia circula há tempos sob nomes como "modelos de decisão" ou inferência "sistema um", e recentemente viralizou no Hacker News com um tutorial mostrando como construir um desses a partir de um modelo Qwen de 1,7 bilhão de parâmetros em cerca de quarenta linhas de Python. Os veteranos do machine learning nos comentários, como era de se esperar, esbravejaram: isso é um classificador, já temos esses desde o perceptron. Eles têm razão, e ao mesmo tempo perdem um pouco o ponto. O que há de novo não é o conceito — é que você obtém um classificador zero-shot a partir de um modelo de linguagem de propósito geral sem treinar nada. A questão interessante de engenharia é quando essa abordagem restrita supera simplesmente deixar o modelo falar, e quando ela silenciosamente mente para você com probabilidades excessivamente confiantes.

Duas Formas de Obter uma Resposta de um LLM

O modo padrão de conversar com um modelo de linguagem é a geração. Você faz uma pergunta, o modelo emite tokens um a um e, mais adiante, você faz o parsing do que saiu. Se precisa de saída estruturada, acopla um esquema: modo JSON, amostragem com gramática, máquinas de estados finitos estilo outlines. Isso funciona, mas o modelo ainda percorre token por token toda a resposta. Uma resposta trivial de múltipla escolha pode levar onze passos de decodificação, e cada passo custa um forward pass completo sobre bilhões de parâmetros.

A alternativa é nunca deixá-lo percorrer nada. Depois que o prompt é processado, você olha os logits sobre o vocabulário na posição final, descarta tudo exceto os IDs de token correspondentes às suas opções — digamos "A", "B", "C", "D", "E" — e aplica softmax apenas sobre eles. O argmax é sua previsão; os valores do softmax são os scores. Custo total: um forward pass, o mesmo prefill que você já estava pagando. Aqui está o núcleo, adaptado da abordagem baseada em Qwen que vem circulando:

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_name = "Qwen/Qwen3-1.7B"
options = ["A", "B", "C", "D", "E"]
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
model_name, torch_dtype="auto", device_map="auto"
)
# First token the model would emit for each option
option_token_ids = [
tokenizer.encode(opt, add_special_tokens=False)[0] for opt in options
]
prompt = (
"What color is the sky?\n"
"A. Red\nB. Blue\nC. Green\nD. Purple\nE. I don't know\nAnswer:"
)
messages = [{"role": "user", "content": prompt}]
text = tokenizer.apply_chat_template(
messages, tokenize=False, add_generation_prompt=True,
enable_thinking=False
)
inputs = tokenizer(text, return_tensors="pt").to(model.device)
with torch.no_grad():
logits = model(**inputs).logits[0, -1]
# Constrained decoding: softmax over only the option tokens
probs = torch.softmax(logits[option_token_ids].float(), dim=-1)
print(options[probs.argmax().item()])  # -> B

É todo o motor. O vocabulário tem mais de 150.000 tokens e reduzimos a decisão a cinco números. Sem jogos de temperatura de amostragem na geração, sem parser que possa falhar, sem chance de o modelo alucinar uma opção que não está na lista. O espaço de saída é fechado por construção.

É um Classificador, e Tudo Bem

Vamos dar aos céticos o seu devido crédito. O que construímos é um classificador discriminativo sobre um conjunto fixo de rótulos — descendente de ideias que remontam ao perceptron de Rosenblatt nos anos 1950, passando pela regressão logística e por cada rede neural com cabeça softmax da era do deep learning. Se você olhar de relance, a decodificação restrita é apenas uma leitura linear sobre o estado oculto final, que é o que uma cabeça de classificação sempre foi. O engenheiro de ML que vem implorando ao time para simplesmente treinar um classificador há anos tem todo o direito de ficar um pouco irritado vendo isso ser rebatizado de "modelo de decisão".

Mas a história também mostra por que a nova versão importa. Os classificadores antigos eram restritos: você coletava dados rotulados, treinava e obtinha um modelo que conhecia uma tarefa e nada mais. A razão pela qual sistemas baseados em LLM continuam engolindo tarefas que "deveriam" usar um classificador de verdade é a propriedade zero-shot — o modelo base já absorveu o suficiente do mundo para que um prompt seja os dados de treino. Quando testei uma configuração assim em um conjunto de holdout do CommonsenseQA, o modelo de 1,7B chegou a cerca de 59% de acurácia sem nenhum fine-tuning, subindo para cerca de 62% após um ajuste rápido no split de treino. Não é estado da arte, mas custou uma tarde e nenhum dado rotulado próprio. Um classificador feito sob medida provavelmente teria um desempenho melhor; mas exigiria um pipeline, um dataset e uma história de retreinamento toda vez que os rótulos mudassem.

Há um enquadramento útil aqui que toma emprestado o velho debate entre modelos generativos e discriminativos. Classificadores generativos modelam a distribuição inteira e são desperdiçadores, porém flexíveis; os discriminativos modelam a fronteira de decisão e são eficientes, porém rígidos. A decodificação restrita é um híbrido curioso: um modelo generativo posto a serviço discriminativo em tempo de inferência. Você obtém a eficiência da leitura discriminativa com a amplitude do pré-treinamento generativo. Essa combinação genuinamente não estava disponível antes, mesmo que as peças sejam antigas.

Onde a Decodificação Restrita Vence

  • Latência. Um forward pass em vez de N passos autorregressivos. Em modelos pequenos, essa é a diferença entre 'rápido o suficiente para um caminho de requisição' e 'precisa de uma fila'. Praticantes que rodam modelos de decisão puros no navegador relatam respostas abaixo de 200ms — tente isso com JSON gerado.
  • Corretude estrutural. O modelo literalmente não consegue produzir saída fora do conjunto permitido. Sem JSON malformado, sem 'A resposta provavelmente é B porque...' divagações, sem necessidade de uma camada de guardrail para pegar violações de formato.
  • Throughput. Como cada requisição é um único passe de formato idêntico, o batching é trivial e previsível. Gerar respostas de tamanho variável destrói sua eficiência de batching.
  • Um score por opção. Você obtém a distribuição inteira, não só o vencedor. Isso abre espaço para lógica de abstenção: se a probabilidade do topo estiver abaixo de um limiar, encaminhe para um humano ou para um modelo maior.

O ponto sobre abstenção merece destaque. Uma resposta generativa é um único artefato em que você confia ou não. Uma distribuição de probabilidades sobre opções permite construir roteamento: casos de alta confiança passam automaticamente, os de baixa confiança são escalados. Esse é o padrão por trás de muitos sistemas de triagem em produção — roteamento de tickets de suporte, pré-triagem de moderação de conteúdo, detecção de intenção — e é aí que a técnica se paga. Se a sua tarefa naturalmente se decompõe em "escolha um de K rótulos e me diga o quão certo você está", a decodificação restrita quase certamente é a ferramenta certa.

Onde a Saída Generativa Vence

Agora o outro lado. No momento em que sua tarefa não cabe em um conjunto fixo de rótulos, a decodificação restrita desmorona. Se a resposta é uma entidade de forma livre, um número, um trecho de código ou qualquer coisa composicional, você precisa de geração — possivelmente com restrições de saída estruturada, mas geração de qualquer forma. Há também uma perda mais sutil: o raciocínio. Quando um modelo gera uma cadeia de pensamento antes de responder, costuma se sair materialmente melhor em perguntas difíceis. A cabeça de decisão de passo único não oferece nenhum rascunho. Você está pedindo pensamento do sistema um, rápido e intuitivo, e está obtendo exatamente isso — incluindo suas falhas características.

Há também o problema da sensibilidade à redação. Um classificador restrito sobre "A/B/C/D/E" está, na prática, medindo a preferência do modelo pelo texto de cada opção naquela posição. Reformule levemente a opção C, reordene a lista ou troque "Resposta:" por "A melhor resposta é" e os scores podem mudar. Respostas generativas com raciocínio tendem a ser mais robustas a perturbações superficiais, porque o modelo precisa se comprometer com o conteúdo, não apenas com um token. Se você está avaliando qualquer uma das abordagens, perturbe a apresentação e veja o que quebra — é um teste de robustez barato e incômodo.

Dois caminhos atravessando uma paisagem, um direto e outro sinuoso, simbolizando inferência rápida versus deliberativa.
A decodificação restrita é o caminho direto; a geração com raciocínio é a trilha longa que às vezes alcança terreno mais alto.

O Problema da Calibração: Seus Scores de Confiança Estão Mentindo

Aqui está a armadilha que pega todo mundo que constrói um desses. Você obtém probabilidades de um softmax, então elas devem ser probabilidades, certo? Não são. São a confiança do modelo de que um determinado token vem a seguir, o que é uma afirmação sobre linguagem, não sobre corretude. Pergunte ao modelo "Onde você mais provavelmente encontraria um morcego?" com opções incluindo "Caverna" e "Jogo de beisebol", e ele vai atribuir algo como 0,998 a "Caverna" — uma pergunta ambígua sem resposta defensavelmente certa, respondida com certeza quase total.

Agrupe as previsões por confiança em uma avaliação real e o quadro piora. Em uma execução do CommonsenseQA, o bucket de confiança 0,9–1,0 acertava apenas cerca de 70% das vezes, e o bucket 0,8–0,9 mal chegava a 40%. Um modelo bem calibrado deveria acertar cerca de 90% das vezes quando diz 0,9. Este é sistematicamente superconfiante — o que, pensando bem, espelha a velha observação de que redes neurais profundas modernas são superconfiantes em geral. Guo et al. mostraram em 2017 que as saídas softmax de uma ResNet comum estão mal calibradas em comparação com as redes rasas dos anos 1990. Tudo que é velho volta a ser novo; apenas reinventamos o problema com 1,7 bilhão de parâmetros.

A correção, felizmente, também é antiga e quase constrangedoramente simples: temperature scaling. Divida os logits por um único escalar T aprendido antes do softmax:

def scaled_probs(logits, option_ids, temperature):
selected = logits[option_ids].float() / temperature
return torch.softmax(selected, dim=-1)
# Fit T on a validation set by minimizing negative log-likelihood
# of the correct option. For the CommonsenseQA run above, T ~= 3.8
temperature = 3.7973
probs = scaled_probs(logits, option_token_ids, temperature)

T maior que 1 achata a distribuição; T menor que 1 a deixa mais pontiaguda. Ajustar um único número contra um conjunto de validação transformou aquela tabela de calibração miserável em algo honesto: o bucket 0,9–1,0 agora fica em torno de 95% de acurácia, o bucket 0,5–0,6 em torno de 55%. A acurácia não muda nada — o argmax é invariante a escalonamento monotônico —, mas os scores agora significam o que você achava que significavam. Se você pretende rotear com base nessas probabilidades, calibre primeiro ou nem se dê ao trabalho de coletá-las.

Ajustar a Temperatura em um Benchmark É Trapaça?

Uma pergunta justa levantada na discussão: não seria um pouco p-hacking ajustar T para que o modelo pareça calibrado em um benchmark? Seria, se você ajustasse no conjunto de teste. Feito corretamente — ajustar T em um split de validação e reportar a calibração em um split de teste separado —, é apenas uma regressão de um parâmetro, e é o padrão na literatura exatamente por esse motivo. A disciplina que importa é a mesma de sempre em ML: mantenha seus splits honestos e desconfie de qualquer alegação de calibração medida em dados que tocaram o ajuste. Se o domínio de deploy se afastar do domínio de avaliação, seu T também se afasta, então a recalibração pertence ao seu loop de manutenção junto com todo o resto.

Isso é, na verdade, uma instância de um problema mais amplo: a lacuna entre o que um sistema é especificado para significar e o que ele realmente computa, lacuna que já argumentei ser onde os bugs moram. "Confiança" é especificada como probabilidade de corretude; a implementação entrega plausibilidade do próximo token. Temperature scaling é um remendo sobre essa lacuna, não uma resolução dela. Mantenha essa distinção em mente toda vez que tiver a tentação de ligar saídas softmax a um limiar de alerta.

Um Procedimento Prático de Decisão

Quando escolho entre os dois modos hoje, percorro uma checklist curta:

  1. A saída é um conjunto fixo de rótulos? Se sim, a decodificação restrita está na mesa. Se não, gere.
  2. Preciso de raciocínio para obter acurácia aceitável? Prototipe as duas. Se a cabeça de passo único ficar atrás de chain-of-thought por mais do que você consegue tolerar, a geração vence apesar da latência.
  3. Preciso de scores por opção para roteamento ou abstenção? Se sim, decodificação restrita mais temperature scaling sai quase de graça, e a geração não oferece nada comparável.
  4. Qual o tamanho do conjunto de rótulos? Cinco opções é trivial; cinco mil é território de recuperação. Com espaços de rótulos grandes, gere embeddings dos rótulos, faça uma pré-seleção com busca vetorial e só então use o modelo para escolher entre os finalistas — o truque de escalonamento de classificadores que é padrão desde a era dos sistemas de recomendação.
  5. Os rótulos vão mudar com frequência? A propriedade zero-shot é o ponto central. Se os rótulos mudam toda semana, um classificador retreinado é um peso de manutenção; uma edição de prompt não é.

O softmax do modelo é uma afirmação sobre qual token vem a seguir, não sobre o que é verdade. Calibre-o, ou não confie nele.

Mais uma consideração: o tamanho do modelo interage com a escolha. A cabeça de passo único de um modelo pequeno é barata o suficiente para rodar em toda requisição, até no cliente; pessoas já estão distribuindo modelos de decisão que rodam no navegador com respostas abaixo de 200ms. Um design em cascata — modelo restrito pequeno para os 80% fáceis, modelo generativo grande para a cauda difícil — muitas vezes supera os dois extremos em custo e acurácia. É o mesmo instinto por trás do speculative decoding, aplicado no nível do sistema e não no nível do token.

A Recomendação

Minha posição: se sua tarefa é genuinamente uma decisão de escolha fixa — roteamento, triagem, intenção, avaliação de múltipla escolha —, construa a cabeça de decisão restrita e não olhe para trás. Só o ganho de latência já justifica, as garantias estruturais eliminam uma classe inteira de falhas de parsing, e os scores por opção oferecem uma lógica de roteamento que a geração não alcança. Mas trate o softmax puro como um instrumento não calibrado. Faça fine-tuning na sua tarefa real se conseguir reunir mesmo um dataset modesto, ajuste uma temperatura em um split de validação limpo e verifique a calibração em dados que o ajuste nunca viu.

Reserve a saída generativa — com restrições de saída estruturada quando necessário — para tarefas genuinamente composicionais ou que se beneficiam de raciocínio visível. E não deixe ninguém vender um "modelo de decisão" como categoria nova: é um classificador vestindo o casaco de um LLM, descendente de sessenta anos de modelagem discriminativa, e é mais útil justamente quando você respeita essa linhagem o suficiente para fazer o trabalho de calibração que os veteranos sempre insistiram. A ferramenta é nova. A disciplina não é.