Lección 2 — Sorties structurées

Modéliser avec Pydantic : énumérations, optionnels, descriptions

6 min de lectura

Este contenido aún no está traducido. Se muestra la versión original.

Modéliser avec Pydantic : énumérations, optionnels, descriptions

Pydantic en deux phrases

Pydantic est une bibliothèque Python qui décrit des données avec des classes : chaque attribut a un type, et Pydantic vérifie et convertit les données à la création de l'objet (une chaîne "2026-10-15" devient une date, "abc" pour un entier lève une erreur). Le SDK google-genai sait convertir ces classes en schéma pour Gemini : une seule définition sert à demander et à vérifier.

classDiagram
  class Extraction {
    confiance: float 0..1
    champs_incertains: list~str~
  }
  class CV {
    nom: str
    email: str ou null
    niveau_etudes: NiveauEtudes
    competences: list~str~
  }
  class Commande {
    client: str
    lignes: list~Ligne~
    date_livraison: date ou null
    urgence: Urgence
  }
  class CompteRendu {
    date_reunion: date
    statut: StatutProjet
    actions: list~Action~
  }
  Extraction <|-- CV
  Extraction <|-- Commande
  Extraction <|-- CompteRendu

Les choix de types, champ par champ

files/schemas.py (extrait)
class NiveauEtudes(str, Enum):
    BAC = "bac"
    BAC2 = "bac+2"
    BAC3 = "bac+3"
    BAC5 = "bac+5"
    DOCTORAT = "doctorat"


class CV(Extraction):
    nom: str = Field(description="Prénom et nom de la personne.")
    email: Optional[str] = Field(None, description="Adresse e-mail, null si absente.")
    ville: Optional[str] = Field(None, description="Ville de résidence, null si absente.")
    annees_experience: int = Field(ge=0, le=50, description="Années d'expérience professionnelle, arrondi à l'entier.")
    niveau_etudes: NiveauEtudes = Field(description="Plus haut diplôme obtenu (master ou ingénieur = bac+5, licence = bac+3, BTS/DUT = bac+2).")
    competences: list[str] = Field(description="Compétences techniques citées, une par élément, telles qu'écrites.")
BesoinType PydanticExemple du labPourquoi
Liste fermée de valeursEnumniveau_etudes, urgence, statutimpossible d'obtenir « Master » ici et « bac+5 » là
Information parfois absenteOptional[...] = Noneemail, date_livraisonautorise null au lieu d'une invention
Nombre bornéint = Field(ge=0, le=50)annees_experienceécarte les valeurs absurdes
Datedatedate_reunionformat AAAA-MM-JJ, converti en vraie date
Plusieurs éléments structuréslist[Ligne]lignes, actionsune ligne = une référence + une quantité
Champs communshéritageExtractionconfiance et champs incertains partout

La description est un prompt

Le modèle lit le nom et la description de chaque champ. C'est là qu'on met les règles de conversion : « une douzaine = 12 », « master = bac+5 », « garder la version finale si le client corrige ». Le schéma vu par Gemini contient ces descriptions :

vérifier ce que reçoit le modèle
CV.model_json_schema()["properties"]["niveau_etudes"]

Un CV ne mentionne pas de téléphone. Le champ telephone est déclaré str (obligatoire). Quel risque ?

Pour le statut d'un projet (dans les temps, en retard, bloqué), quel type choisir ?

Exercice : un schéma pour les tickets de support

Écrivez Ticket : categorie parmi facturation, technique, livraison, autre ; priorite parmi basse, normale, haute ; numero_commande facultatif ; produits (liste de textes) ; resume (texte, 20 mots au plus). Où mettre la règle « 20 mots au plus » ?

Voir la solution
ticket.py
from enum import Enum
from typing import Optional

from pydantic import BaseModel, Field, field_validator


class Categorie(str, Enum):
    FACTURATION = "facturation"
    TECHNIQUE = "technique"
    LIVRAISON = "livraison"
    AUTRE = "autre"


class Priorite(str, Enum):
    BASSE = "basse"
    NORMALE = "normale"
    HAUTE = "haute"


class Ticket(BaseModel):
    categorie: Categorie
    priorite: Priorite = Field(description="haute si le client ne peut plus travailler ou menace de partir.")
    numero_commande: Optional[str] = Field(None, description="Numéro de commande cité, null sinon.")
    produits: list[str]
    resume: str = Field(description="Résumé en 20 mots au plus.")

    @field_validator("resume")
    @classmethod
    def court(cls, v):
        if len(v.split()) > 20:
            raise ValueError("résumé de plus de 20 mots")
        return v

La règle va aux deux endroits : dans la description (pour que le modèle la suive) et dans un validateur (pour la vérifier). Le schéma Gemini ne sait pas compter les mots.

Résultat obtenu

Ce que le schéma Pydantic transmet pour deux champs (sortie réelle de CV.model_json_schema(), le 2026-10-15) :

sortie réelle, 2026-10-15
{"$ref": "#/$defs/NiveauEtudes", "description": "Plus haut diplôme obtenu (master ou ingénieur = bac+5, licence = bac+3, BTS/DUT = bac+2)."}
{"enum": ["bac", "bac+2", "bac+3", "bac+5", "doctorat"], "title": "NiveauEtudes", "type": "string"}
{"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Adresse e-mail, null si absente.", "title": "Email"}

Et deux extractions réelles de gemini-3.5-flash sur le jeu du lab :

sortie réelle, 2026-10-15 (cv-04 et cv-05)
{"nom": "Lucas Petit", "email": "lucas.petit@exemple.fr", "telephone": "07 12 12 12 12", "ville": null, "annees_experience": 1, "niveau_etudes": "bac+3", "competences": ["vente", "encaissement", "merchandising"]}
{"nom": "Amina Diallo", "email": "amina.diallo@exemple.net", "telephone": "+33 7 55 44 33 22", "ville": "Marseille", "annees_experience": 8, "niveau_etudes": "bac", "competences": ["gestion des stocks", "ERP", "CACES R489"]}

ville vaut null (absente du CV : rien d'inventé) ; lucas.petit[at]exemple.fr est remis au format e-mail ; la licence « en cours » d'Amina n'est pas comptée, son plus haut diplôme obtenu est le bac. Ce dernier cas est un piège : la version flash-lite s'y trompera (module 4).

Je sais faire

  • Choisir entre texte, énumération, optionnel, liste et objet imbriqué
  • Écrire des descriptions de champs qui guident le modèle
  • Partager des champs communs par héritage
  • Inspecter le schéma envoyé avec model_json_schema()
Siguiente