Guide
Valider la sortie d'un LLM avec Pydantic et un schéma
Faire renvoyer un JSON conforme à un schéma : Pydantic, model_json_schema, sorties structurées d'Anthropic, limites et exemple annoncé comme tel.
Mis à jour le
Un modèle de langage répond en texte libre. Pour qu'un programme puisse l'utiliser, il faut une forme prévisible : des champs, des types, des valeurs attendues. Deux mécanismes se combinent. Côté fournisseur, les sorties structurées contraignent la réponse à suivre un schéma JSON. Côté application, Pydantic définit ce schéma et valide les données reçues. Ce guide explique comment les relier, ce que chacun garantit, et où se trouvent les limites.
Ce que garantit la sortie structurée
La documentation d'Anthropic indique que les sorties structurées contraignent la réponse de Claude à suivre un schéma JSON, par décodage contraint. Selon la page, la réponse est :
- toujours valide : pas d'erreur d'analyse du JSON ;
- typée : les types des champs et les champs
requiredsont garantis ; - fiable : pas de nouvelle tentative nécessaire pour cause de non-conformité au schéma.
Le JSON est renvoyé dans le bloc texte de la réponse. Le paramètre HTTP est output_config.format, avec "type": "json_schema" et un champ schema. Dans le SDK Python, la méthode client.messages.parse(...) prend un modèle Pydantic dans output_format, et renvoie l'objet dans parsed_output.
Ce que la page ne dit pas : elle ne garantit pas que la valeur est exacte. Un champ peut être bien typé et faux. C'est la différence essentielle entre forme et fond.
Ce que dit Pydantic
La documentation de Pydantic définit un modèle comme une classe qui hérite de BaseModel et déclare ses champs comme des attributs annotés. Pour valider une donnée, elle indique que Pydantic « will raise a ValidationError exception whenever it finds an error in the data it's validating ». Les méthodes model_validate_json (pour une chaîne JSON) et model_validate (pour un dictionnaire) valident les données, et lèvent ValidationError en cas d'échec.
Pour le schéma, BaseModel.model_json_schema() renvoie un dictionnaire jsonable décrivant le modèle. Sa documentation indique que json.dumps(m.model_json_schema()) produit une chaîne JSON valide. Le mode par défaut est 'validation'.
Exemple annoncé comme exemple
Le code suivant reprend les noms de classes, de méthodes et de paramètres relevés dans la documentation d'Anthropic et de Pydantic. Il ne doit pas être utilisé tel quel : le modèle, la clé d'API et les données sont à adapter.
from pydantic import BaseModel
class ContactInfo(BaseModel):
name: str
email: str
plan_interest: str
demo_requested: bool
response = client.messages.parse(
model="<modèle choisi>",
max_tokens=1024,
messages=[{"role": "user", "content": "..."}],
output_format=ContactInfo,
)
print(response.parsed_output)
Le même schéma peut être fourni brut, avec output_config={"format": {"type": "json_schema", "schema": ...}} dans client.messages.create, puis lu avec json.loads sur le bloc texte. Dans ce cas, la validation côté Pydantic reste à faire à la main avec model_validate_json, qui lève ValidationError si la donnée ne correspond pas.
Les limites à connaître
La documentation d'Anthropic liste des schémas non pris en charge, qui renvoient une erreur 400 :
- les schémas récursifs ;
- les types complexes dans les
enum; - les références externes (
$ref) ; - les contraintes numériques (
minimum,maximum,multipleOf) et de longueur de chaîne (minLength,maxLength) ; minItemsdifférent de 0 ou 1 ;additionalPropertiesdifférent defalse.
Deux conséquences pratiques. D'abord, additionalProperties: false est exigé sur les objets. Ensuite, une contrainte posée dans le modèle Pydantic peut produire un de ces mots-clés dans le schéma généré. Il faut donc afficher le schéma produit par model_json_schema() et le comparer à cette liste avant la mise en production. Les helpers du SDK retirent certaines contraintes du schéma envoyé et les ajoutent à la description ; la documentation précise que parse() revalide ensuite la réponse, ce qui n'est pas le cas du schéma brut.
La documentation signale aussi que la première utilisation d'un schéma ajoute une latence de compilation, et que le cache de ce schéma dure 24 heures après le dernier usage. Modifier la structure du schéma l'invalide.
Méthode en trois étapes
- Définir le modèle Pydantic avec les champs attendus, et vérifier le JSON Schema produit.
- Appeler le modèle avec ce schéma, par
parse()si le SDK le permet, sinon par le schéma brut. - Contrôler les valeurs : contrôles de cohérence (un total égal à la somme des lignes, une date postérieure à une autre), et tests sur des cas réels. La forme est garantie ; le contenu se vérifie.
Pour les valeurs douteuses, et la façon de fixer un seuil de confiance, voir le guide sur le seuil de confiance. Pour la sortie structurée côté API, voir sortie JSON structurée. Et pour choisir entre une bibliothèque et un appel direct, lire framework ou appel direct à l'API.
Questions fréquentes
Un JSON valide suffit-il à garantir la justesse des valeurs ?
Non. La sortie structurée garantit la forme : JSON valide, types et champs requis. Elle ne garantit pas que la valeur est juste. Il faut vérifier les valeurs par des contrôles et des tests sur des cas réels.
Faut-il écrire le schéma à la main ?
Non, quand le projet est en Python. Pydantic génère le JSON Schema d'un modèle avec model_json_schema(). Il reste à vérifier que le schéma généré respecte les limites du fournisseur.
Sources
Pages consultées pour écrire cet article :