Aller au contenu principal

Définir des schémas avec Pydantic

Mis à jour le 29 juillet 2026

Pydantic : le socle des Custom Structured Outputs en Python

Pydantic est la bibliothèque de validation de données la plus populaire en Python. Elle permet de définir des modèles de données avec des types stricts, et c’est exactement ce que l’API Mistral utilise pour les Custom Structured Outputs. Si vous travaillez en Python, Pydantic devient donc votre outil principal pour décrire les schémas de sortie : ce que vous écrivez dans une classe est ce que le modèle sera contraint de produire.

Bonne nouvelle pour l’installation : Pydantic est inclus comme dépendance du SDK Mistral. Si mistralai est déjà installé, vous n’avez rien à faire de plus.

pip install mistralai
# Pydantic v2 est inclus automatiquement

Une vérification rapide de la version évite les surprises, car la syntaxe des validateurs a changé entre les versions majeures :

import pydantic
print(pydantic.__version__)  # 2.x requis

Définir un modèle simple

Un modèle Pydantic est une classe qui hérite de BaseModel, dans laquelle chaque attribut est un champ avec un type explicite.

from pydantic import BaseModel

class Contact(BaseModel):
    name: str
    email: str
    phone: str
    company: str

Ce modèle dit à Mistral : « ta sortie doit être un objet JSON avec exactement ces 4 champs, tous de type string. » Aucune instruction supplémentaire n’est nécessaire dans le prompt pour obtenir cette structure.

Les types que vous utiliserez

Pydantic supporte tous les types Python standards, et Mistral les comprend tous. Le modèle suivant les rassemble dans un cas réaliste — l’analyse d’un ticket de support — et montre comment ils se combinent : types de base pour les champs simples, Optional pour un assigné pas encore désigné, Enum pour une priorité qui ne peut prendre que quatre valeurs, listes pour les tags et les identifiants liés.

from pydantic import BaseModel
from typing import Optional
from enum import Enum

class Priority(str, Enum):
    low = "low"
    medium = "medium"
    high = "high"
    critical = "critical"

class TicketAnalysis(BaseModel):
    # Types de base
    title: str
    description: str
    ticket_id: int
    confidence: float
    is_urgent: bool

    # Types optionnels
    assignee: Optional[str] = None

    # Enums (valeurs contraintes)
    priority: Priority

    # Listes
    tags: list[str]
    related_ids: list[int]

Voici le mémo correspondant :

TypeSignification
strchaîne de caractères
intnombre entier
floatnombre décimal
boolbooléen (true/false)
list[T]liste d’éléments de type T
Optional[T]champ pouvant être null
Enumvaleur parmi un ensemble fini

L’enum mérite une mention particulière : c’est le seul moyen de garantir qu’une priorité ne vaudra jamais "très urgent" ou "P1" selon l’humeur du modèle.

Modèles imbriqués

Les données réelles sont rarement plates. Un contact appartient à une entreprise, qui possède une adresse. Pydantic laisse imbriquer les modèles autant que nécessaire, et Mistral suit la hiérarchie.

from pydantic import BaseModel
from typing import Optional

class Address(BaseModel):
    street: str
    city: str
    postal_code: str
    country: str

class Company(BaseModel):
    name: str
    industry: str
    address: Address  # Modèle imbriqué

class Person(BaseModel):
    first_name: str
    last_name: str
    email: str
    role: str
    company: Company  # Imbrication à deux niveaux

Le JSON produit reproduit exactement cette arborescence :

{
  "first_name": "Marie",
  "last_name": "Dupont",
  "email": "[email protected]",
  "role": "Directrice commerciale",
  "company": {
    "name": "TechVision SAS",
    "industry": "Technologie",
    "address": {
      "street": "42 rue de la Paix",
      "city": "Paris",
      "postal_code": "75002",
      "country": "France"
    }
  }
}

Le même mécanisme sert à extraire plusieurs éléments d’un coup : un modèle enveloppe une liste d’un autre modèle, et vous récupérez un catalogue complet en un appel.

from pydantic import BaseModel

class Product(BaseModel):
    name: str
    price: float
    category: str
    in_stock: bool

class Catalog(BaseModel):
    products: list[Product]
    total_count: int

Guider le modèle avec les descriptions

Le nom d’un champ ne dit pas tout. score peut désigner une note sur 10, un pourcentage ou une valeur entre 0 et 1 — le modèle doit deviner. Les descriptions lèvent cette ambiguïté : elles sont incluses dans le schéma JSON envoyé à Mistral et agissent comme des instructions par champ.

from pydantic import BaseModel, Field

class SentimentAnalysis(BaseModel):
    sentiment: str = Field(
        description="Le sentiment global : 'positif', 'négatif' ou 'neutre'"
    )
    score: float = Field(
        description="Score de confiance entre 0.0 et 1.0"
    )
    keywords: list[str] = Field(
        description="Les 3-5 mots-clés principaux du texte"
    )
    summary: str = Field(
        description="Résumé en une phrase maximum"
    )

Plus ces descriptions sont précises, meilleure sera la qualité de la sortie. C’est le levier le plus rentable de tout le dispositif : quelques mots bien choisis remplacent souvent un paragraphe entier de prompt.

Field sert aussi à poser des valeurs par défaut. Dans l’exemple suivant, la langue vaut "fr" si le modèle ne trouve pas l’information, le nombre de mots reste None s’il n’est pas déterminable, et les tags partent d’une liste vide grâce à default_factory.

from pydantic import BaseModel, Field
from typing import Optional

class ArticleMetadata(BaseModel):
    title: str
    author: str
    language: str = Field(default="fr", description="Code langue ISO 639-1")
    word_count: Optional[int] = None
    tags: list[str] = Field(default_factory=list)

Les champs avec des valeurs par défaut seront remplis par le modèle s’il trouve l’information, ou laisseront la valeur par défaut sinon.

La validation ne se demande pas

Dernier avantage, et non le moindre : la validation est automatique. Si une valeur ne correspond pas au type déclaré, Pydantic lève immédiatement une ValidationError en indiquant précisément le champ fautif, sans que vous ayez écrit une seule ligne de contrôle.

from pydantic import ValidationError

try:
    analysis = SentimentAnalysis(
        sentiment="positif",
        score="pas un nombre",  # Erreur de type
        keywords=["test"],
        summary="Résumé"
    )
except ValidationError as e:
    print(e)
    # 1 validation error for SentimentAnalysis
    # score
    #   Input should be a valid number [type=float_parsing, ...]

Tout le code de vérification écrit à la main dans la leçon précédente disparaît ici, remplacé par la déclaration du modèle.

Points clés à retenir

  • Pydantic BaseModel est la base de tous les schémas Custom en Python
  • Utilisez des types explicites : str, int, float, bool, list[T], Optional[T]
  • Les modèles imbriqués permettent des structures JSON complexes
  • Field(description=...) améliore la qualité de la sortie en guidant le modèle
  • La validation est automatique — les erreurs de type sont détectées immédiatement