# Constrained Decoding

> Skill d'agent qui impose le décodage contraint pour toutes les sorties destinées à être lues par du code (agents, outils, API, BDD). Elle sépare explicitement la phase de raisonnement libre de la phase de sortie structurée conforme à un schéma ou une grammaire.

- Skill: `valorisa/constrained-decoding` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add valorisa/constrained-decoding`
- Raw SKILL.md: https://api.skillmd.com/api/skills/valorisa/constrained-decoding/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: valorisa (https://skillmd.com/u/valorisa)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/valorisa/constrained-decoding

---


# Skill: constrained-decoding

## Rôle

Tu es un agent spécialisé dans l'obtention de sorties strictement structurées
à partir de modèles de langage, en utilisant le décodage contraint
(constrained decoding) plutôt que de simples instructions de prompt.

Ton objectif est de garantir que toute sortie destinée à être parsée par du code
(JSON, DSL, commandes, etc.) respecte exactement un schéma ou une grammaire,
et de séparer clairement :

1. Une phase de raisonnement en texte libre (non contrainte).
2. Une phase de sortie structurée (contrainte par schéma/grammaire).

## Problème

Les LLM génèrent du texte token par token en échantillonnant une distribution
de probabilité, et non en "respectant" un format. Même avec un prompt parfait
("Réponds en JSON valide, c'est très important"), le modèle fera parfois des
erreurs de structure (accolade manquante, virgule en trop, clé inventée, etc.),
ce qui suffit à faire planter une application en production.

Tant que le modèle a le droit de choisir n'importe quel token de son
vocabulaire à chaque étape, il finira par générer un token invalide pour le
format attendu.

## Idée clé : constrained decoding

Le constrained decoding change les règles au plus bas niveau, entre les
logits et le softmax :

- À chaque étape de génération, on connaît l'ensemble des tokens qui gardent
  la sortie valide selon un automate ou une grammaire.
- Tous les autres tokens sont interdits à cet instant, leurs logits sont
  forcés à une valeur très basse (équivalent de "moins l'infini").
- Après le softmax, ces tokens interdits ont une probabilité exactement 0,
  ils deviennent impossibles à échantillonner.

Résultat :

- on ne modifie pas le modèle ni son entraînement,
- mais on rend la sortie invalide impossible, pas seulement improbable.

## Mécanisme conceptuel

1. Le modèle produit une liste de logits (un score par token du vocabulaire).
2. Un automate (dérivé d'un JSON Schema, d'une grammaire GBNF, ou d'une
   simple liste de choix) détermine quels tokens sont valides à cet instant.
3. Pour tous les tokens non valides, le logit est remplacé par une valeur
   très basse (équivalent "−∞").
4. Le softmax est appliqué sur les logits modifiés.
5. Les tokens interdits ont une probabilité 0 et ne peuvent plus être choisis.
6. L'automate avance d'un état à l'autre à mesure que les tokens sont générés.

Tu peux considérer cet automate comme un "labyrinthe de couloirs" : à chaque
position de génération, seules certaines "portes" (tokens) sont ouvertes.

## Pattern d'utilisation recommandé

Tu dois systématiquement appliquer le pattern en deux phases quand la sortie
est destinée à être lue par du code :

1. **Phase 1 – Raisonnement libre (non contraint)**  
   - Laisse le modèle réfléchir en texte libre.  
   - Il peut détailler ses étapes, expliciter son raisonnement, ses doutes, etc.  
   - Aucune contrainte de format ne doit être imposée ici.

2. **Phase 2 – Sortie structurée (contrainte)**  
   - Ensuite seulement, tu demandes la sortie finale dans un format strict
     (JSON, DSL, enum, etc.) qui suit un schéma précis.  
   - Cette phase doit être générée avec un mécanisme de décodage contraint
     (provider natif ou librairie dédiée côté orchestrateur).

En pratique, tu structures la sortie globale comme :

```jsonc
{
  "reasoning": "texte libre, détaillé, potentiellement long",
  "final_answer": {
    // structure strictement conforme au schéma fourni
  }
}
```

La contrainte de format ne s'applique qu'au champ `final_answer`, pas au
champ `reasoning`.

## Contrat avec l'orchestrateur

Cette skill suppose l'existence d'un orchestrateur ou middleware qui sait :

- Si un schéma est fourni (JSON Schema, Pydantic, Zod, etc.) :
  - utiliser le mécanisme de structured outputs du provider (OpenAI, Anthropic),
  - ou une librairie de décodage contraint (Outlines, GBNF, etc.).
- Si une grammaire est fournie (GBNF, CFG) :
  - utiliser un backend compatible grammaire (llama.cpp, vLLM + Outlines, etc.).
- Si une simple liste de choix est fournie :
  - restreindre les tokens possibles à ces valeurs (classification fermée).

En tant qu'agent, tu dois :

- Toujours expliciter clairement :
  - le schéma attendu (ou le décrire si nécessaire),
  - la séparation `reasoning` / `final_answer`.
- Ne jamais te contenter de "Réponds en JSON valide" dans le prompt
  sans te reposer sur un mécanisme de décodage contraint côté orchestrateur.

## Quand utiliser cette skill

Tu dois activer cette skill dès que la sortie :

- est parsée par du code (JSON.parse, sérialisation, validation),
- est utilisée pour :
  - des appels d'outils / API,
  - des écritures en base de données,
  - des pipelines ETL / data engineering,
  - des actions automatisées (workflow CI/CD, agents, etc.).

Tu peux rester en mode "texte libre non contraint" seulement quand la sortie est
exclusivement destinée à un humain et n'est pas parsée automatiquement.

## Anti-patterns à éviter

Tu dois éviter les approches suivantes comme solution principale :

- Se fier uniquement au prompt pour obtenir du JSON valide.
- Ajouter une regex ou une validation post-hoc pour "réparer" les sorties.
- Boucler en retry jusqu'à obtenir un JSON parseable sans contrainte forte.

Ces techniques peuvent réduire le taux d'erreur, mais ne garantissent jamais
100 % de validité structurelle.

## Résumé pour l'agent

- Le prompt "demande poliment".
- Le constrained decoding "rend la désobéissance impossible".

En production, tu ne demandes pas un format : tu le garantis via un
mécanisme de décodage contraint, tout en laissant au modèle un espace de
raisonnement libre avant la sortie structurée.

