Pourquoi un jeu de test ?
Un système à base de modèle de langage ne se teste pas comme une fonction classique : la même question peut recevoir des formulations différentes, et un changement de prompt qui améliore un cas peut en dégrader dix autres. Essayer trois questions à la main avant de mettre en production donne une impression trompeuse de qualité.
La base de toute évaluation est un jeu de test (on parle aussi de golden dataset) : une liste versionnée de cas, chacun avec une entrée et ce qui est attendu. Il joue pour l’IA le rôle des tests unitaires pour le code.
Structure d’un cas
CASES = [
{"id": "T01", "categorie": "frequent", "input": "Où est ma facture de mars ?", "expected": "facturation"},
{"id": "T02", "categorie": "limite", "input": "factur mars ???", "expected": "facturation"},
{"id": "T03", "categorie": "hors_perimetre", "input": "Quelle est la capitale du Pérou ?", "expected": "hors_sujet"},
]
Un bon jeu de test :
- part de demandes réelles (anonymisées) ou très réalistes, pas uniquement de cas imaginés par l’équipe ;
- couvre les cas fréquents, les cas limites (fautes de frappe, demandes ambiguës), les demandes hors périmètre et les tentatives de manipulation ;
- indique pour chaque cas sa catégorie, pour suivre les résultats par type ;
- est versionné avec le code, et enrichi à chaque incident constaté en production.
Quelques dizaines de cas bien choisis valent mieux que des centaines de cas redondants. Évitez d’y conserver des données personnelles dont vous n’avez pas besoin.
Vérifier automatiquement : correspondance normalisée
Quand la sortie attendue est courte (une catégorie, une date, un identifiant), on la compare au résultat attendu après normalisation : minuscules, accents retirés, espaces superflus supprimés.
import unicodedata
def normalize(text):
text = unicodedata.normalize("NFKD", text)
text = "".join(c for c in text if not unicodedata.combining(c))
return " ".join(text.lower().split()).strip(" .!")
Sans normalisation, « Facturation. » et « facturation » seraient comptés comme différents, et le score refléterait la mise en forme plutôt que la justesse.
Vérifier automatiquement : règles
Quand la réponse est un texte libre, il n’y a pas une seule bonne réponse. On vérifie alors des critères :
- éléments obligatoires : le délai de retour « 30 jours » doit apparaître ;
- éléments interdits : aucune promesse de remboursement, aucun numéro de carte ;
- format : longueur maximale, JSON valide, langue.
Ces règles sont simples, rapides et gratuites. Elles ne mesurent pas tout (le ton, la pertinence), ce que l’on complétera par un modèle juge dans la leçon suivante.
Score et analyse des échecs
On calcule un taux de réussite, mais on regarde surtout les cas en échec. Un score de 90 % peut cacher 0 % sur les demandes hors périmètre si elles sont peu nombreuses. D’où l’intérêt du score par catégorie.
Non-régression
Le jeu de test sert à chaque changement : nouveau prompt, nouveau modèle, nouvelle quantification, nouveaux documents dans la base. Le processus :
- exécuter le jeu de test sur la version actuelle et sur la version candidate ;
- comparer les scores globaux et par catégorie ;
- examiner chaque cas qui passait et qui échoue désormais ;
- décider, et conserver les résultats avec la version.
Pensez aussi à la variabilité : avec une température non nulle, un même cas peut réussir puis échouer. Fixez la température à 0 pour l’évaluation quand c’est possible, ou exécutez chaque cas plusieurs fois.
À retenir
- Un jeu de test versionné, représentatif et enrichi en continu est la base de l’évaluation.
- Sorties courtes : correspondance normalisée ; sorties libres : règles.
- On suit le score par catégorie et on lit les échecs.
- Tout changement passe par le jeu de test avant la production.
Exercices
Les solutions et les tests restent sur le serveur. Dans l'espace étudiant, ton code est exécuté et corrigé, et le tuteur donne des indices avant la solution.
Évaluer un classifieur simulé
Écrivez
normalize(text)(accents retirés, minuscules, espaces multiples réduits, espaces et.!retirés aux extrémités), puisevaluate(model, cases)qui renvoie un dictionnaire :total: nombre de cas ;correct: nombre de cas oùnormalize(model(input)) == normalize(expected);accuracy:correct / total;failures: liste desiden échec, dans l’ordre des cas.
import unicodedata CASES = [ {"id": "T01", "input": "Où est ma facture de mars ?", "expected": "facturation"}, {"id": "T02", "input": "Je n'arrive plus à me connecter", "expected": "accès"}, {"id": "T03", "input": "factur mars ???", "expected": "facturation"}, {"id": "T04", "input": "Quelle est la capitale du Pérou ?", "expected": "hors_sujet"}, ] def fake_model(text): """Classifieur simulé.""" t = text.lower() if "facture" in t: return "Facturation." if "connecter" in t: return " ACCES " return "facturation" def normalize(text): ... def evaluate(model, cases): ...Vérifier des règles sur une réponse libre
Écrivez
check_answer(answer, case)qui renvoie la liste des critères en échec, dans cet ordre :"manque: <texte>"pour chaque élément decase["must_include"]absent (comparaison insensible à la casse) ;"interdit: <texte>"pour chaque élément decase["must_not_include"]présent (insensible à la casse) ;"trop long"si le nombre de mots (split()) dépassecase["max_words"].
Les trois clés sont facultatives. Écrivez ensuite
pass_rate(answers, cases): la proportion de réponses sans aucun échec.def check_answer(answer, case): ... def pass_rate(answers, cases): ...Composer le jeu de test
Votre assistant classe les demandes clients. Votre jeu de test contient 200 cas, tous tirés des demandes les plus fréquentes. Quel est le principal risque ?
- Le jeu de test est trop grand pour être exécuté
- Les cas limites, hors périmètre et les tentatives de manipulation ne sont pas évalués
- Le score sera forcément trop bas
Quiz de fin de leçon
Que permet la normalisation avant comparaison ?
- D'améliorer le modèle
- De ne pas compter comme erreurs des différences de casse, d'accents ou d'espaces
- De réduire le coût de l'évaluation
Quand relancer le jeu de test ?
- Une fois par an
- À chaque changement de prompt, de modèle, de paramètres ou de données
- Seulement après une plainte d'utilisateur
Pourquoi suivre le score par catégorie de cas ?
- Pour avoir plus de chiffres dans le rapport
- Parce qu'un bon score global peut masquer un échec complet sur une catégorie rare
- Parce que c'est obligatoire légalement
La suite du cours
- Évaluation par un modèle juge20 min
- Mesurer qualité, coût et latence20 min
- Dérive et retours utilisateurs20 min
- Construire un tableau de suivi20 min