Python & AI Tutorials Logo
LangChain & LangGraph

7. Sortie structurée avec Pydantic

Dans les chapitres précédents, nous avons travaillé avec les sorties des LLM sous forme de chaînes de texte brutes. Cela fonctionne bien pour les chatbots où les humains lisent les réponses, mais lors de la construction d'agents IA où les programmes doivent analyser et interpréter les sorties des LLM, nous avons besoin de données structurées et prévisibles. Dans ce chapitre, vous apprendrez à utiliser les schémas Pydantic pour faire en sorte que le LLM retourne des objets Python structurés.

7.1) Pourquoi une sortie structurée ?

Le problème avec la sortie LLM en texte libre

Commençons par comprendre pourquoi les réponses en texte brut créent des problèmes dans les applications réelles. Considérez ce scénario courant :

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
# Demander au LLM des informations sur un produit
message = HumanMessage(content="""
Extrayez les informations du produit à partir de ce texte :
"L'UltraWidget Pro coûte 299,99 $ et est actuellement en stock."
""")
 
response = llm.invoke([message])
print(response.content)

Sortie :

Nom du produit : UltraWidget Pro
Prix : 299,99 $
Disponibilité : En stock

La sortie semble correcte. Mais supposons maintenant que vous ayez besoin d'utiliser ces données dans votre application Python. Comment extraire le prix sous forme de nombre ? Comment vérifier la disponibilité de manière programmatique ? Vous pourriez essayer une analyse de chaîne comme ceci :

python
# Approche d'analyse fragile
text = response.content
price_line = [line for line in text.split('\n') if 'Prix :' in line][0]
price_str = price_line.split('$')[1]
price = float(price_str)  # Fragile - et si le format change ?

Cette analyse semble fonctionner. Mais en réalité, elle ne fonctionne pas. Voici pourquoi :

Pourquoi cette approche échoue :

  1. Le LLM pourrait formater la réponse différemment la prochaine fois ("Prix : 299,99 USD" ou "Prix de détail : 299,99 $")

Voici des exemples de différentes sorties qui peuvent se produire pour le même prompt :

# Exemple 1
"Le produit est UltraWidget Pro, au prix de 299,99 $, et il est disponible."
 
# Exemple 2
"Produit : UltraWidget Pro
Coût : 299,99 dollars
Statut : Disponible"
 
# Exemple 3
"UltraWidget Pro - 299,99 $ (en stock)"
 
# Exemple 4
"J'ai trouvé l'UltraWidget Pro. Il coûte 299,99 $ et est actuellement disponible à l'achat."

Lorsque la réponse du LLM change, vous avez besoin d'une logique d'analyse complètement différente. Cela rend difficile la construction d'applications fiables.

  1. Les réponses des LLM sont imprévisibles : Le même prompt peut produire des formats différents à chaque fois
  2. L'analyse de chaînes est plus difficile qu'il n'y paraît : Vous devez gérer $, les espaces, les sauts de ligne, les virgules, et plus encore
  3. Aucune sécurité de type : Vous ne pouvez pas être sûr que price est un float, une chaîne ou None
  4. La gestion des erreurs est difficile : Si le LLM dit "Prix non disponible", votre appel float() plante
  5. Impossible à maintenir : Changez légèrement le prompt et vous réécrivez tout le code d'analyse

L'idée centrale : Python a besoin de contrats, pas de prose

Pensez à la communication avec un serveur API en Python. Lorsque vous appelez une API REST spécifique, vous vous attendez à ce qu'elle retourne une réponse JSON définie :

python
# Vous attendez cette structure
{
    "product_name": "UltraWidget Pro",
    "price": 299.99,
    "in_stock": true
}

Lors de la construction d'applications IA, vous avez besoin du même principe. La sortie du LLM doit être retournée sous forme de données avec une structure définie, et non sous forme de texte libre à chaque fois.

Prose vs Contrat :

  • Prose : Texte naturel en forme libre. Bon pour que les humains le lisent, mais difficile à traiter pour les programmes.
  • Contrat : Données avec structure et types définis. Une promesse que "ces champs existeront avec ces types."

La sortie structurée signifie définir un contrat : "LLM, j'ai besoin exactement de ces champs, avec exactement ces types, dans exactement ce format."

C'est là que Pydantic intervient. Pydantic est la bibliothèque de validation de données la plus populaire de Python, et LangChain l'utilise pour recevoir les sorties des LLM sous forme structurée.

Le changement de modèle mental :

  • Avant : "LLM, parle-moi de ce produit" → Analyser du texte imprévisible
  • Après : "LLM, réponds dans un format défini" → Recevoir un objet Python structuré

Ce passage de la prose aux contrats est fondamental pour construire des agents IA fiables. Lorsqu'un agent doit décider de sa prochaine action en fonction des réponses du LLM (par exemple, acheter si en stock, s'inscrire pour une notification sinon), il doit recevoir des réponses dans un format défini.

Analyse manuelle

Sortie structurée

Casse souvent

Sécurisé par type

Sortie texte LLM

Logique de chaîne fragile

Objet Python typé

Erreurs d'exécution

Code fiable

7.2) Votre première sortie structurée

En 7.1, nous avons appris pourquoi les LLM devraient répondre avec une structure définie au lieu de texte libre. Voyons maintenant comment implémenter cela réellement.

L'idée clé : Simplement demander au LLM "veuillez répondre dans ce format" ne suffit pas. Vous devez définir la structure de données exacte dans le code Python et faire en sorte que LangChain la transmette au LLM. Cette structure de données définie s'appelle un schéma.

Qu'est-ce qu'un schéma ?

Un schéma est un plan qui définit la structure des données. Il spécifie :

  • Quels champs doivent être présents
  • Quel type chaque champ devrait avoir (chaîne, nombre, booléen, etc.)
  • Quelles contraintes s'appliquent (optionnel vs obligatoire, plages valides, etc.)

En Python, nous définissons les schémas en utilisant la classe BaseModel de Pydantic. Voici l'exemple le plus simple possible :

python
from pydantic import BaseModel
 
class ProductInfo(BaseModel):
    product_name: str
    price: float
    in_stock: bool

Ce schéma dit : "Un objet ProductInfo doit avoir exactement trois champs : un product_name (chaîne), un price (float) et un in_stock (booléen)."

Le modèle en trois étapes : Définir, Lier, Invoquer

L'utilisation de la sortie structurée est simple. Rappelez-vous simplement trois étapes :

  1. Définir : Créer un schéma avec une classe Pydantic
  2. Lier : Connecter le schéma au LLM en utilisant .with_structured_output()
  3. Invoquer : Appeler .invoke() pour obtenir un objet typé

Ceci est le modèle standard que vous utiliserez pour la plupart des tâches d'extraction structurée :

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from pydantic import BaseModel, Field
 
# Étape 1 : Définir le schéma
class ProductInfo(BaseModel):
    product_name: str = Field(description="Le nom complet du produit")
    price: float = Field(description="Prix en USD")
    in_stock: bool = Field(description="Si le produit est disponible")
 
# Étape 2 : Lier le schéma au LLM
llm = ChatOpenAI(model="gpt-4o-mini")
structured_llm = llm.with_structured_output(ProductInfo)
 
# Étape 3 : Invoquer et obtenir un objet typé
message = HumanMessage(content="""
Extrayez les informations du produit à partir de ce texte :
"L'UltraWidget Pro coûte 299,99 $ et est actuellement en stock."
""")
 
result = structured_llm.invoke([message])
 
# result est maintenant un objet ProductInfo, pas une chaîne
print(type(result))  # <class '__main__.ProductInfo'>
print(result.product_name)  # UltraWidget Pro
print(result.price)  # 299.99
print(result.in_stock)  # True

Que s'est-il passé ?

  1. Définition du schéma : Nous avons défini les champs et les types que nous voulons
  2. Liaison : .with_structured_output(ProductInfo) configure le LLM pour utiliser la sortie structurée
  3. Invocation & Réponse : Lorsque .invoke() est appelé, LangChain transmet le schéma JSON au LLM, et le LLM répond avec du JSON correspondant à cette structure
  4. Conversion automatique : LangChain convertit le JSON en un objet ProductInfo - aucun code d'analyse nécessaire

Pas d'analyse. Pas de conversion de type. Pas d'erreurs.

Utilisez ce modèle en 3 étapes comme votre modèle. Suivez-le chaque fois que vous avez besoin d'une sortie structurée.

Descriptions de champs : La clé pour guider le LLM

Dans l'exemple de définition de schéma ci-dessus, nous avons utilisé Field(description="..."). Cette description n'est pas seulement de la documentation. Ce sont des instructions que le LLM lit et suit.

Dans l'utilisation typique de Pydantic, les descriptions de Field sont optionnelles :

python
# Pydantic régulier - la description est une documentation pour les humains
class User(BaseModel):
    name: str = Field(description="Nom de l'utilisateur")  # Fonctionne bien sans elle

Mais lorsque vous travaillez avec des LLM, elles sont essentielles :

python
# Avec les LLM - la description détermine le comportement du LLM
class CustomerFeedback(BaseModel):
    sentiment: str = Field(
        description="Sentiment global : 'positive', 'negative' ou 'neutral'"
    )

Le LLM lit cette description et l'utilise pour décider comment répondre.

Voyons cela en action :

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from pydantic import BaseModel, Field
 
class CustomerFeedback(BaseModel):
    sentiment: str = Field(
        description="Sentiment global : 'positive', 'negative' ou 'neutral'"
    )
    main_issue: str = Field(
        description="La plainte ou préoccupation principale, le cas échéant. Utilisez 'none' si aucun problème n'est mentionné."
    )
    urgency: str = Field(
        description="Quelle est l'urgence du problème : 'low', 'medium' ou 'high'"
    )
 
llm = ChatOpenAI(model="gpt-4o-mini")
structured_llm = llm.with_structured_output(CustomerFeedback)
 
message = HumanMessage(content="""
Analysez ce retour client :
"Le produit fonctionne bien, mais la livraison a pris 3 semaines. J'ai déjà manqué la date limite de mon projet. Veuillez répondre immédiatement."
""")
 
result = structured_llm.invoke([message])
print(result.sentiment)    # negative
print(result.main_issue)   # Livraison lente
print(result.urgency)      # high

Comment les descriptions façonnent les décisions du LLM :

  • Description de sentiment → Le LLM apprend que les valeurs valides sont 'positive', 'negative', 'neutral' → Le problème de livraison a causé une date limite manquée, donc choisit 'negative'
  • Description de main_issue → Le LLM est instruit de "trouver la plainte principale" → Identifie "livraison lente" comme le problème
  • Description de urgency → Le LLM apprend que l'urgence doit être 'low', 'medium' ou 'high' → Voit "Veuillez répondre immédiatement" et choisit 'high'

Que se passe-t-il sans descriptions ?

python
sentiment: str  # Pas de description

Le LLM pourrait retourner "negative", "bad", "unsatisfied", "2/5", "disappointed" dans des formats imprévisibles, rendant difficile la gestion des valeurs par votre code.

Point clé : Les descriptions de champs font partie de votre code qui contrôle le comportement du LLM. Écrivez-les clairement et spécifiquement.

Champs catégoriels : Spécifier les valeurs autorisées

Dans l'exemple ci-dessus, le champ sentiment ne peut avoir que trois valeurs : 'positive', 'negative' ou 'neutral'. Les champs qui doivent être l'une d'un ensemble spécifique de valeurs sont appelés champs catégoriels.

Pour les champs catégoriels, listez toutes les valeurs possibles dans la description :

python
sentiment: str = Field(
    description="Sentiment : exactement 'positive', 'negative' ou 'neutral' (minuscules)"
)

En spécifiant "exactement" et "(minuscules)", nous soulignons que le LLM devrait répondre avec précisément l'une de ces trois valeurs.

Cependant, il n'y a aucune garantie que le LLM répondra toujours avec l'une des valeurs spécifiées. C'est pourquoi vous devez écrire du code défensif.

Cas où le LLM retourne des valeurs inattendues :

python
result.sentiment = "Positive"    # Majuscule
result.sentiment = "NEGATIVE"    # Tout en majuscules
result.sentiment = "good"        # Mot différent entièrement

Écrire du code défensif :

python
allowed = {"positive", "negative", "neutral"}
 
# Convertir en minuscules et vérifier
sentiment = result.sentiment.lower()
 
if sentiment not in allowed:
    sentiment = "neutral"  # Utiliser la valeur par défaut pour les valeurs inattendues
 
# Maintenant sentiment est garanti d'être l'une des valeurs autorisées

Point clé :

  1. Spécifier les valeurs autorisées dans la description → Le LLM est plus susceptible de répondre correctement
  2. Valider dans le code → Gérer les valeurs inattendues en toute sécurité

Note : Le chapitre 18 montre des modèles plus forts utilisant les enums Python pour l'application.

Comparaison : Analyse manuelle vs Sortie structurée

Comparons la même tâche avec et sans sortie structurée pour voir la différence :

Analyse manuelle :

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
message = HumanMessage(content="""
Extrayez le nom du produit, le prix et la disponibilité à partir de :
"L'UltraWidget Pro coûte 299,99 $ et est actuellement en stock."
Format : nom | prix | disponibilité
""")
 
response = llm.invoke([message])
text = response.content
 
# Analyse manuelle
parts = text.split('|')
product_name = parts[0].strip()
price_str = parts[1].strip().replace('$', '')
price = float(price_str)
availability = parts[2].strip().lower()
in_stock = 'en stock' in availability or 'disponible' in availability
 
print(f"Nom : {product_name}")
print(f"Prix : {price} $")
print(f"En stock : {in_stock}")

Sortie structurée :

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from pydantic import BaseModel, Field
 
class ProductInfo(BaseModel):
    product_name: str = Field(description="Le nom complet du produit")
    price: float = Field(description="Prix en USD")
    in_stock: bool = Field(description="Si le produit est disponible")
 
llm = ChatOpenAI(model="gpt-4o-mini")
structured_llm = llm.with_structured_output(ProductInfo)
 
message = HumanMessage(content="""
Extrayez les informations du produit à partir de :
"L'UltraWidget Pro coûte 299,99 $ et est actuellement en stock."
""")
 
result = structured_llm.invoke([message])
 
print(f"Nom : {result.product_name}")
print(f"Prix : {result.price} $")
print(f"En stock : {result.in_stock}")

Différences clés :

  1. Pas de logique d'analyse : La version structurée n'a aucun code d'analyse
  2. Sécurité de type : result.price est garanti d'être un float
  3. Code plus simple : Pas de regex, pas de division de chaîne, pas de conversion de type manuelle
  4. Validation : Pydantic garantit que tous les champs requis sont présents
  5. Maintenabilité : Changer le schéma est plus facile que de mettre à jour la logique d'analyse

7.3) Considérations de conception de schéma

Maintenant que vous savez comment utiliser la sortie structurée, apprenons à concevoir de bons schémas. Cette section couvre les principes de conception pratiques pour distinguer les champs obligatoires des champs optionnels.

Champs obligatoires

Par défaut, tous les champs d'un modèle Pydantic sont obligatoires. Cela signifie que le LLM doit extraire ou déduire une valeur pour chaque champ obligatoire à partir du prompt de l'utilisateur et la fournir dans la réponse.

python
from pydantic import BaseModel
 
class ProductInfo(BaseModel):
    product_name: str
    price: float
    in_stock: bool

Lorsque vous utilisez ce schéma, le LLM tentera de trouver des valeurs pour les trois champs (product_name, price, in_stock) dans le texte d'entrée.

Mais que se passe-t-il lorsque le prompt manque d'informations pour un champ obligatoire ?

Nous pourrions nous attendre au comportement suivant :

  1. Le LLM ne peut pas trouver l'information dans le prompt
  2. Le LLM omet ce champ de sa réponse
  3. LangChain ne peut pas créer une instance ProductInfo valide
  4. ValidationError est levée

Cependant, cela ne se produit pas toujours.

La raison est que différents LLM peuvent gérer les informations manquantes différemment.

Certains LLM (tels que les modèles OpenAI) ont tendance à générer des valeurs même lorsque les informations requises ne sont pas présentes dans le prompt. Dans ce cas, ValidationError ne se produit pas, mais cela peut causer de plus gros problèmes car votre application Python peut traiter des informations fabriquées comme si elles étaient réelles.

Nous aborderons comment résoudre ce problème dans la Section 7.4 : Quand les choses tournent mal.

Pour l'instant, sachez simplement que tous les LLM ne gèrent pas les informations manquantes de la même manière.

Champs optionnels

Vous pouvez avoir besoin de champs qui peuvent légitimement être présents ou absents, même dans des cas normaux. Par exemple, une note de livraison (delivery_note) peut ou non être fournie par le client, même pour une commande valide.

Quand utiliser Optional :

  • Les données elles-mêmes peuvent ne pas exister (par exemple, lorsque les avis anonymes sont autorisés, les avis anonymes n'ont pas de nom de réviseur)
  • Vous voulez que le LLM indique explicitement les informations manquantes plutôt que de fabriquer une valeur

Pour rendre un champ optionnel, utilisez le type Optional de Python du module typing :

python
from typing import Optional
 
class ProductReview(BaseModel):
    rating: int
    review_text: str
    reviewer_name: Optional[str] = None  # Les avis anonymes n'ont pas de nom de réviseur

Note : Les utilisateurs de Python 3.10+ peuvent utiliser str | None au lieu de Optional[str].

Lorsqu'un champ est Optional :

  • Le LLM peut l'omettre de la réponse si l'information n'est pas trouvée dans le prompt
  • Les champs omis sont définis sur la valeur par défaut (None)

Voici un exemple complet :

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from pydantic import BaseModel, Field
from typing import Optional
 
class ProductInfo(BaseModel):
    product_name: str
    price: float
    in_stock: bool
    discount_percentage: Optional[float] = None
    warranty_years: Optional[int] = None
 
llm = ChatOpenAI(model="gpt-4o-mini")
structured_llm = llm.with_structured_output(ProductInfo)
 
message = HumanMessage(content="""
Extrayez les informations du produit : "L'UltraWidget Pro coûte 299,99 $ et est en stock."
""")
 
result = structured_llm.invoke([message])
print(result.product_name)  # UltraWidget Pro
print(result.price)  # 299.99
print(result.in_stock)  # True
print(result.discount_percentage)  # None (non mentionné)
print(result.warranty_years)  # None (non mentionné)

Liste de vérification de conception de schéma

Avant de finaliser votre schéma, posez-vous la question :

Sélection de champs :

  • Les champs obligatoires sont-ils vraiment essentiels ? (Que se passe-t-il si ce champ manque dans le prompt ?)
  • Les champs optionnels peuvent-ils légitimement être absents même dans des cas normaux ?

Spécification de champs :

  • Chaque champ a-t-il une description claire ?
  • Les champs catégoriels sont-ils explicitement contraints ? (par exemple, "doit être exactement 'A', 'B' ou 'C'")

7.4) Quand les choses tournent mal

Deux problèmes peuvent survenir lors de l'utilisation de la sortie structurée :

  1. Le LLM omet les valeurs de champs obligatoires → ValidationError se produit
  2. Le LLM fabrique des informations manquantes → Pas de ValidationError, mais votre code traite des données incorrectes

Cette section couvre comment gérer chacun.

Comprendre les erreurs de validation

Lorsque le prompt de l'utilisateur manque d'informations pour les champs obligatoires définis dans le schéma, le LLM ne peut pas fournir de valeurs pour ces champs. Le programme Python lève alors une ValidationError :

python
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import HumanMessage
from pydantic import BaseModel, ValidationError
 
class ProductInfo(BaseModel):
    product_name: str
    price: float
    in_stock: bool
 
llm = ChatAnthropic(model='claude-sonnet-4-5')
structured_llm = llm.with_structured_output(ProductInfo)
 
# Entrée manquant d'informations requises
message = HumanMessage(content="""
Extrayez les informations du produit à partir de : "Le widget est génial ! Hautement recommandé."
""")
 
try:
    result = structured_llm.invoke([message])
    print(result)
except ValidationError as e:
    print("ValidationError s'est produite")

Note : Lorsque le prompt manque d'informations pour les champs obligatoires, certains LLM peuvent fabriquer des valeurs et les fournir dans la réponse. Dans ce cas, ValidationError ne se produira pas, mais un problème plus grave survient. Nous couvrirons cela dans la section suivante.

Lorsque le prompt manque d'informations pour les champs obligatoires et que ValidationError se produit, c'est en fait utile pour votre application Python. L'application peut détecter qu'un problème s'est produit et gérer l'erreur de manière contrôlée. Les stratégies de récupération d'erreur sont couvertes dans le Chapitre 14 (récupération d'erreur au niveau de l'agent) et le Chapitre 17 (logique de nouvelle tentative avec gestion d'état).

Le problème plus grave : Le LLM fabrique des informations manquantes

Comme nous l'avons discuté dans la Section 7.3, certains LLM présentent un comportement plus dangereux : ils fabriquent des valeurs et les fournissent dans les réponses lorsque des informations manquent dans le prompt.

Comment le problème se produit :

  1. Le prompt manque d'informations requises
  2. Le LLM génère quand même des valeurs plausibles
  3. ValidationError ne se produit PAS
  4. Votre application Python traite les données fabriquées comme si elles étaient réelles

Exemple :

python
class ProductInfo(BaseModel):
    product_name: str
    price: float
    in_stock: bool
 
message = HumanMessage(content="""
Extrayez les informations du produit à partir de : "Le widget est génial !"
""")
 
# Lorsque certains LLM fabriquent des valeurs pour product_name, price et in_stock
result = structured_llm.invoke([message])
# Aucune erreur levée !
print(result.product_name)  # "widget" (extrait du texte)
print(result.price)  # 0.0 (fabriqué !)
print(result.in_stock)  # False (fabriqué !)
 
# Problème : Vous ne pouvez pas dire quelles valeurs sont réelles vs fabriquées

C'est pire qu'une ValidationError parce que :

  • Votre application Python continue de s'exécuter avec de mauvaises données
  • Vous ne savez pas quels champs sont réels vs fabriqués
  • La logique en aval peut prendre de mauvaises décisions basées sur de fausses données

Solution : Utiliser des champs optionnels avec validation

La solution consiste à définir tous les champs obligatoires comme Optional, puis à utiliser un validateur pour vérifier que tous les champs obligatoires ont des valeurs.

python
from typing import Optional
from pydantic import BaseModel, model_validator, ValidationError
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
 
class ProductInfo(BaseModel):
    # Ceux-ci sont en fait obligatoires, mais déclarés comme Optional
    # La vraie validation se produit dans le validateur ci-dessous
    product_name: Optional[str] = None
    price: Optional[float] = None
    in_stock: Optional[bool] = None
    
    @model_validator(mode='after')
    def check_required_fields(self):
        """Valide que tous les champs essentiels sont présents"""
        if self.product_name is None or self.price is None or self.in_stock is None:
            raise ValueError("Tous les champs (product_name, price, in_stock) doivent être fournis")
        return self
 
llm = ChatOpenAI(model="gpt-4o-mini")
structured_llm = llm.with_structured_output(ProductInfo)
 
# Tester avec des données incomplètes
message = HumanMessage(content="""
Extrayez les informations du produit à partir de : "Le widget est génial !"
""")
 
try:
    result = structured_llm.invoke([message])
    # Si nous arrivons ici, tous les champs sont garantis d'être présents
    print(f"Produit : {result.product_name}")
    print(f"Prix : {result.price} $")
except ValidationError as e:
    # Les champs obligatoires manquent - l'extraction a échoué
    print(f"Extraction incomplète : {e}")

Ce qui se passe ici :

  • @model_validator est le décorateur de Pydantic qui ajoute une logique de validation personnalisée
  • mode='after' signifie que la validation s'exécute après que tous les champs ont été analysés
  • Si un champ est None, nous levons ValueError pour signaler des données incomplètes
  • Pydantic enveloppe automatiquement cette ValueError dans une ValidationError

Pourquoi cela fonctionne :

Lorsque le prompt manque d'informations pour les champs :

  • Le LLM ne fabrique pas de valeurs et omet ces champs de la réponse
  • Dans ce cas, ces champs deviennent None
  • Si ces champs sont en fait obligatoires, le validateur Pydantic lève ValueError
  • Pydantic l'enveloppe comme ValidationError

De cette façon, votre application Python reçoit une erreur explicite à gérer, plutôt que des données fabriquées.

Point clé : Lorsque le prompt manque d'informations pour les champs obligatoires, obtenir une ValidationError est parfaitement normal et attendu. Le vrai danger est les données fabriquées. Utilisez des champs Optional avec des validateurs pour empêcher le LLM de fabriquer des informations manquantes, tout en détectant explicitement quand les champs obligatoires manquent.


Résumé du chapitre :

Dans ce chapitre, vous avez appris à transformer la sortie du LLM en objets Python fiables :

  • Pourquoi c'est important : L'analyse de texte libre est fragile ; la sortie basée sur un schéma fournit la sécurité de type
  • Comment l'utiliser : Définir des schémas avec BaseModel de Pydantic → Lier avec .with_structured_output()
  • Principes de conception : Choisir les champs obligatoires vs optionnels, guider le LLM avec des descriptions de champs
  • Gérer les problèmes : ValidationError est normale ; le vrai danger est les données fabriquées