Patterns complexes : listes imbriquées, enums, unions
Patterns complexes : listes imbriquées, enums, unions
Les cas d’extraction simples (un objet plat avec des champs texte) sont faciles à gérer. Les vrais défis arrivent avec les structures complexes : listes d’objets imbriqués, types conditionnels, champs polymorphiques. Cette leçon vous montre comment concevoir des schémas robustes pour ces cas avancés.
Listes imbriquées
Le pattern le plus courant en production : extraire une liste d’objets dont chacun contient lui-même des sous-listes.
import json
from openai import OpenAI
client = OpenAI()
# Schéma : extraire un programme de formation avec modules et leçons
formation_schema = {
"type": "object",
"properties": {
"titre": {"type": "string"},
"duree_totale_heures": {"type": "number"},
"modules": {
"type": "array",
"items": {
"type": "object",
"properties": {
"titre_module": {"type": "string"},
"objectifs": {
"type": "array",
"items": {"type": "string"}
},
"lecons": {
"type": "array",
"items": {
"type": "object",
"properties": {
"titre_lecon": {"type": "string"},
"duree_minutes": {"type": "integer"},
"type": {
"type": "string",
"enum": ["theorie", "pratique",
"evaluation"]
}
},
"required": ["titre_lecon",
"duree_minutes", "type"],
"additionalProperties": False
}
}
},
"required": ["titre_module", "objectifs", "lecons"],
"additionalProperties": False
}
}
},
"required": ["titre", "duree_totale_heures", "modules"],
"additionalProperties": False
}
Enums pour contraindre les valeurs
Les enums sont le moyen le plus fiable de limiter les valeurs possibles d’un champ. Le Structured Output les applique au niveau de la génération :
# Système de classification multi-niveaux
classification_schema = {
"type": "object",
"properties": {
"categorie_principale": {
"type": "string",
"enum": ["technique", "commercial", "juridique",
"rh", "finance"]
},
"sous_categorie": {
"type": "string",
"enum": [
"bug", "feature", "performance",
"devis", "facturation", "contrat",
"recrutement", "conge", "formation",
"budget", "audit", "conformite"
]
},
"priorite": {
"type": "string",
"enum": ["critique", "haute", "moyenne", "basse"]
},
"action_requise": {
"type": "string",
"enum": ["repondre", "escalader", "archiver",
"planifier", "deleguer"]
}
},
"required": ["categorie_principale", "sous_categorie",
"priorite", "action_requise"],
"additionalProperties": False
}
Simuler les unions de types
JSON Schema supporte anyOf pour les types conditionnels, mais le Structured Output strict d’OpenAI a des limitations. Le pattern recommandé est d’utiliser un champ discriminant :
# Pattern : un événement peut être une réunion, une tâche ou un rappel
event_schema = {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["reunion", "tache", "rappel"]
},
"titre": {"type": "string"},
"date": {"type": "string"},
"heure_debut": {"type": ["string", "null"]},
"heure_fin": {"type": ["string", "null"]},
"participants": {
"type": "array",
"items": {"type": "string"}
},
"lieu": {"type": ["string", "null"]},
"priorite": {
"type": ["string", "null"],
"enum": ["haute", "moyenne", "basse", None]
},
"description": {"type": "string"}
},
"required": ["type", "titre", "date", "heure_debut",
"heure_fin", "participants", "lieu",
"priorite", "description"],
"additionalProperties": False
}
L’astuce : rendez les champs spécifiques à un type nullable ("type": ["string", "null"]). Pour une réunion, participants et lieu sont remplis ; pour un rappel, ils sont null.
Objets récursifs
Pour les structures arborescentes (menus, organigrammes, fils de discussion), utilisez $defs pour les références récursives :
# Structure d'un commentaire avec réponses imbriquées
comment_schema = {
"type": "object",
"properties": {
"commentaires": {
"type": "array",
"items": {"$ref": "#/$defs/comment"}
}
},
"required": ["commentaires"],
"additionalProperties": False,
"$defs": {
"comment": {
"type": "object",
"properties": {
"auteur": {"type": "string"},
"contenu": {"type": "string"},
"sentiment": {
"type": "string",
"enum": ["positif", "negatif", "neutre"]
},
"reponses": {
"type": "array",
"items": {"$ref": "#/$defs/comment"}
}
},
"required": ["auteur", "contenu", "sentiment",
"reponses"],
"additionalProperties": False
}
}
}
Pattern : extraction multi-entités
Quand un texte contient plusieurs types d’entités :
multi_entity_schema = {
"type": "object",
"properties": {
"personnes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"nom": {"type": "string"},
"role": {"type": "string"},
"organisation": {"type": ["string", "null"]}
},
"required": ["nom", "role", "organisation"],
"additionalProperties": False
}
},
"organisations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"nom": {"type": "string"},
"type": {
"type": "string",
"enum": ["entreprise", "association",
"administration", "autre"]
},
"secteur": {"type": ["string", "null"]}
},
"required": ["nom", "type", "secteur"],
"additionalProperties": False
}
},
"dates": {
"type": "array",
"items": {
"type": "object",
"properties": {
"date_brute": {"type": "string"},
"date_iso": {"type": "string"},
"contexte": {"type": "string"}
},
"required": ["date_brute", "date_iso", "contexte"],
"additionalProperties": False
}
},
"montants": {
"type": "array",
"items": {
"type": "object",
"properties": {
"valeur": {"type": "number"},
"devise": {"type": "string"},
"contexte": {"type": "string"}
},
"required": ["valeur", "devise", "contexte"],
"additionalProperties": False
}
}
},
"required": ["personnes", "organisations", "dates", "montants"],
"additionalProperties": False
}
Bonnes pratiques pour les schémas complexes
- Commencez simple, complexifiez progressivement : testez d’abord un objet plat, puis ajoutez les imbrications
- Utilisez les nullable plutôt que les optionnels : avec
strict: true, tous les champs sont requis — utilisez"type": ["string", "null"]pour les champs facultatifs - Limitez la profondeur : au-delà de 3-4 niveaux d’imbrication, la qualité baisse
- Guidez avec le system prompt : les enums et le schéma contraignent la structure, le system prompt guide le contenu
Mise en pratique
- Définissez un schéma pour extraire un organigramme d’entreprise (structure récursive)
- Testez avec un texte décrivant une organisation de 15-20 personnes
- Vérifiez que les relations hiérarchiques sont correctement imbriquées
- Ajoutez des enums pour les rôles (direction, management, opérationnel)
Points clés à retenir
- Les listes imbriquées sont le pattern le plus courant en production
- Les enums sont appliqués au niveau de la génération — exploitez-les
- Simulez les unions avec un champ discriminant et des champs nullable
$defspermet les structures récursives (commentaires, arbres)- Limitez la profondeur d’imbrication à 3-4 niveaux