Constrained Decoding
La decodificación restringida enmascara los tokens no válidos en cada paso de generación, de modo que un LLM solo puede emitir etiquetas permitidas, coincidencias con expresiones regulares o JSON que cumpla un esquema.
La decodificación restringida es una técnica de generación de texto que limita los tokens que puede producir un modelo de lenguaje en cada paso, de modo que se garantiza que el resultado final coincida con un formato requerido: una etiqueta de un conjunto fijo, una expresión regular, una gramática o un esquema JSON. El modelo sigue asignando una puntuación a cada token de su vocabulario. Antes de elegir un token, el decodificador elimina todas las opciones que incumplirían el formato. Las aplicaciones que analizan los resultados de los modelos, como los backends de API, los agentes de IA y las canalizaciones de extracción de datos, la usan para que cada respuesta se pueda analizar a la primera.
Enmascaramiento de tokens no válidos#
Un modelo de lenguaje grande genera texto de un token en uno. En cada paso produce una puntuación, o logit, para cada token de su vocabulario, y una softmax convierte esas puntuaciones en probabilidades. La decodificación restringida añade una operación antes del muestreo: los tokens que no pueden conducir a un resultado válido reciben un logit de menos infinito, lo que les asigna probabilidad cero, y la probabilidad restante se renormaliza entre los tokens válidos.
import torch
# Raw next-token scores from a model over a toy vocabulary
vocab = ["approved", "rejected", "review", "maybe", "{", "}"]
logits = torch.tensor([1.2, 0.4, 0.9, 2.5, 0.1, 0.3])
# The schema allows only the three status labels at this position
allowed = torch.tensor([True, True, True, False, False, False])
masked_logits = logits.masked_fill(~allowed, float("-inf"))
# Softmax renormalizes probability over the valid tokens only
for name, raw, masked in zip(vocab, logits.softmax(-1), masked_logits.softmax(-1)):
print(f"{name:>9}: {raw:.2f} -> {masked:.2f}")El modelo sin restricciones asigna el 56 % de su probabilidad a «quizá». Al aplicar la máscara, «quizá» baja a cero y las tres etiquetas permitidas se reparten la probabilidad en proporción a sus puntuaciones originales; así, «aprobado» sube del 15 % al 46 %.
Los motores de inferencia de producción siguen el estado de la restricción a medida que avanza la generación. Una lista de opciones fija o una expresión regular se compila en una máquina de estados finitos. JSON y otros formatos anidados necesitan una gramática con una pila, porque una llave de cierre solo es válida después de una llave de apertura correspondiente. Tras cada token, el decodificador avanza el estado y vuelve a calcular el conjunto permitido. La tokenización produce fragmentos de subpalabras de longitud variable, por lo que un único token como "}, puede abarcar varios pasos gramaticales a la vez. Por eso las bibliotecas precalculan qué entradas del vocabulario son legales en cada estado, lo que mantiene bajo el coste por token incluso con vocabularios de más de 100.000 tokens. En 2023, Brandon Willard y Rémi Louf describieron esta indexación para expresiones regulares y gramáticas independientes del contexto en Efficient Guided Generation for Large Language Models, el artículo en que se basa la biblioteca Outlines.
Cómo elegir una restricción#
La restricción adecuada debe coincidir con el resultado que consume realmente la aplicación. Una lista de opciones sirve para la clasificación y el enrutamiento, donde la respuesta debe ser una de unas pocas cadenas. Las expresiones regulares abarcan valores estructurados cortos, como fechas, identificadores y números de teléfono. Las gramáticas independientes del contexto describen SQL, código y lenguajes específicos de dominio, y el formato gramatical GBNF de llama.cpp es un ejemplo muy utilizado. Para registros con campos con nombre, JSON Schema especifica claves obligatorias, tipos, enumeraciones y anidamiento. Las API alojadas, como OpenAI Structured Outputs, aceptan un esquema y lo hacen cumplir durante la generación; normalmente admiten un subconjunto documentado de la especificación completa de JSON Schema.
La decodificación restringida suele confundirse con técnicas similares:
- Resultados estructurados: El objetivo más amplio de obtener respuestas legibles por máquina, ya sea mediante decodificación restringida durante la generación o validando el resultado después y reintentándolo si falla.
- Modo JSON: Restringe el resultado a JSON sintácticamente válido de cualquier estructura, por lo que aún hay que comprobar las claves, los tipos y los valores.
- Ingeniería de prompts: Solicita un formato en las instrucciones, que los modelos suelen respetar. Las solicitudes con restricciones de esquema también suelen describir los campos en el prompt, para que el modelo sepa qué significa cada uno.
- Llamada a funciones: Los nombres y argumentos de las herramientas son algunos de los objetivos más habituales de las restricciones de esquema, ya que el código que las invoca espera nombres de campo exactos.
Compromisos en la práctica#
Un formato garantizado no garantiza nada sobre el contenido. Un esquema puede obligar a que {"total": 1290.00} sea un número, pero el número puede seguir siendo incorrecto; además, un modelo con restricciones puede alucinar con fluidez en campos válidos. Las aplicaciones siguen teniendo que validar las reglas de negocio después de la generación.
Las restricciones también pueden reducir la calidad de las respuestas si alejan al modelo del texto que escribiría de forma natural. Un esquema que exige que la respuesta final sea el primer campo no deja espacio para el razonamiento intermedio, por lo que los esquemas suelen colocar un campo de razonamiento o notas de texto libre antes del campo de respuesta. También hay que prestar atención a la longitud del resultado: si la generación alcanza el límite de tokens a mitad de un objeto, el resultado no es válido, independientemente de la restricción; por eso las solicitudes estructuradas fijan longitudes máximas con margen suficiente para toda la estructura. Los esquemas muy grandes o profundamente recursivos tardan más en compilarse, algo importante para los sistemas que crean un esquema nuevo para cada solicitud.
Las aplicaciones de visión usan el mismo mecanismo cuando un modelo multimodal debe devolver campos sobre una imagen. La interfaz LLM de Ultralytics reenvía sin cambios los argumentos de la solicitud a las API compatibles con OpenAI, por lo que un esquema JSON pasado junto con una solicitud de imagen limita la respuesta de un modelo de visión y lenguaje a campos como hazard y severity, listos para combinarse con las detecciones de YOLO26 del mismo fotograma.










