Plus facile à lire avec un fond crème, une teinte, plus de contraste ou un texte plus grand ? Choisissez vos couleurs et votre texte, une fois pour toutes les pages.

Leçon gratuite

Fabriquer des cas d’évaluation synthétiques

Après cette leçon: Je peux fabriquer de nouveaux cas à partir d’une graine et dire qu’ils ne sont pas des copies.

Leçon de 70 min8 commandesLab agent de code IA : utiliser opencode pour expliquer et corriger du code, et vérifier son travail

Générer des cas de test sans tricher

Des données d’évaluation synthétiques sont des cas de test fabriqués par un programme. Écrire mille cas à la main est long, donc les équipes les génèrent à partir de gabarits.

Des cas fabriqués peuvent se répéter, ou donner leur propre réponse. Cette leçon fabrique des cas à partir d’une graine et retire les mauvais.

À la fin de cette leçon

Remarque

Je peux fabriquer de nouveaux cas à partir d’une graine et dire qu’ils ne sont pas des copies.

Mots clés

MotCe que ça veut direExemple
Gabaritune phrase à trous qu’il faut remplirÉchanger ma {objet}
Graineun nombre de départ qui fait se répéter un générateur
Quasi-doublonun cas presque identique à un autre
Fuiteun cas qui donne sa réponse ou son texte d’entraînement
Similaritéla part de mots que deux textes partagent, en pourcentage

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

Un gabarit et une graine fabriquent des cas

Un gabarit est une phrase à trous, comme « Échanger ma {objet} ». Une liste d’objets remplit le trou. Chaque gabarit a une étiquette : la réponse qu’une bonne fonction doit donner.

Une graine est un nombre qui lance un compteur. La même graine choisit toujours les mêmes gabarits et les mêmes objets, donc les cas se répètent.

Exemple détaillé : un générateur à graine

ÉtapeCe qui se passe
DépartLa graine est le premier nombre
TirageMultipliez par 16807, gardez le reste de la division par 2147483647
ChoixDivisez ce nombre par la taille de la liste et gardez le reste
RemplissageMettez l’objet choisi dans le trou
RépétitionLa même graine donne les mêmes cas à chaque fois

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

Les quasi-doublons gonflent un jeu

Deux cas qui ne diffèrent que d’un mot n’apprennent rien de nouveau. Comptez les mots qu’ils partagent. Divisez par tous les mots différents des deux textes.

À partir de 80 %, on parle de quasi-doublon. Gardez le premier cas et rejetez le suivant.

Une erreur courante

Attention

Beaucoup pensent que plus de cas générés fait toujours un meilleur test. Si la plupart sont des copies, le jeu est plus petit qu’il n’en a l’air. Ne comptez que les cas qui ne sont pas des quasi-doublons.

Deux sortes de fuite gâchent un test

Une fuite de la réponse est un cas qui contient sa propre étiquette, comme « Merci de traiter le remboursement de ma lampe » avec l’étiquette remboursement. Une fonction peut réussir en repérant le mot.

Une fuite d’entraînement est un cas qui reprend un texte déjà vu par la fonction. Une bonne note dessus prouve la mémoire, pas la compétence.

Écrire l’assistant du modèle, partie 1

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// Les jetons d’une réponse, ou null si elle n’en donne pas.' 'function compter(donnees, nom) {' '  return donnees.usage && typeof donnees.usage[nom] === \"number\" ? donnees.usage[nom] : null;' '}' > model.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire l’assistant du modèle, partie 2

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function demander(messages, echantillon, max) {' '  try {' '    const res = await fetch(base + \"/v1/chat/completions\", {' '      method: \"POST\",' '      headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' '      body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 100 }),' '      signal: AbortSignal.timeout(30000),' '    });' '    if (res.ok === false) throw new Error(\"statut \" + res.status);' '    const d = await res.json();' '    return { texte: d.choices[0].message.content, entree: compter(d, \"prompt_tokens\"), sortie: compter(d, \"completion_tokens\"), enregistre: false };' '  } catch (erreur) {' '    console.log(\"Échantillon enregistré : aucun modèle n’a répondu (\" + erreur.message + \").\");' '    return { texte: echantillon, entree: null, sortie: null, enregistre: true };' '  }' '}' 'module.exports = { demander };' >> model.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire le générateur, partie 1

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' 'const gabarits = [' '  [\"Ma {objet} est arrivée {defaut}, je veux qu’on me rende mon argent\", \"remboursement\"],' '  [\"Merci de traiter le remboursement de ma {objet}, elle était {defaut}\", \"remboursement\"],' '  [\"Où est ma {objet} ? Elle n’est pas arrivée\", \"suivi\"],' '  [\"Puis-je échanger ma {objet} contre une autre couleur\", \"echange\"],' '  [\"La {objet} est {defaut} et je voudrais être remboursée\", \"remboursement\"],' '  [\"Où en est la commande de ma {objet} s’il vous plaît\", \"suivi\"],' '];' 'const objets = [\"bouilloire\", \"lampe\", \"théière\", \"tasse\"];' 'const defauts = [\"cassée\", \"abîmée\", \"fissurée\"];' 'const entrainement = [\"Où est ma bouilloire ? Elle n’est pas arrivée\", \"Puis-je échanger ma lampe contre une autre couleur\"];' > cas.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire le générateur, partie 2

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'let s = 23;' 'function suivant(taille) { s = (s * 16807) % 2147483647; return s % taille; }' 'function generer(n) {' '  const sortie = [];' '  for (let i = 1; i <= n; i++) {' '    const g = gabarits[suivant(gabarits.length)];' '    let texte = g[0];' '    if (texte.indexOf(\"{objet}\") >= 0) texte = texte.replace(\"{objet}\", objets[suivant(objets.length)]);' '    if (texte.indexOf(\"{defaut}\") >= 0) texte = texte.replace(\"{defaut}\", defauts[suivant(defauts.length)]);' '    sortie.push({ id: i, texte: texte, etiquette: g[1] });' '  }' '  return sortie;' '}' >> cas.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire le générateur, partie 3

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function listeMots(texte) {' '  const m = texte.toLowerCase().replace(/[,?.]/g, \" \").split(\" \").filter(function (x) { return x.length > 0; });' '  return m.filter(function (x, i) { return m.indexOf(x) === i; });' '}' 'function similarite(a, b) {' '  const wa = listeMots(a);' '  const wb = listeMots(b);' '  const communs = wa.filter(function (m) { return wb.indexOf(m) >= 0; }).length;' '  return Math.floor(communs * 100 / (wa.length + wb.length - communs));' '}' 'function proche(texte, liste) {' '  return liste.some(function (t) { return similarite(texte, t) >= 80; });' '}' >> cas.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire le générateur, partie 4

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  const gardes = [];' '  const raisons = {};' '  generer(12).forEach(function (c) {' '    let pourquoi = \"\";' '    if (listeMots(c.texte).indexOf(c.etiquette) >= 0) pourquoi = \"fuite de la réponse\";' '    else if (proche(c.texte, entrainement)) pourquoi = \"fuite d’entraînement\";' '    else if (proche(c.texte, gardes.map(function (k) { return k.texte; }))) pourquoi = \"doublon\";' '    if (pourquoi === \"\") gardes.push(c);' '    else raisons[pourquoi] = (raisons[pourquoi] || 0) + 1;' '  });' >> cas.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire le générateur, partie 5

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' '  console.log(\"gardés \" + gardes.length + \" sur 12\");' '  console.log(\"rejetés : \" + JSON.stringify(raisons));' '  console.log(\"premier gardé : \" + gardes[0].texte + \" -> \" + gardes[0].etiquette);' '  const echantillon = \"Corrigez le gabarit pour que le mot de l’étiquette ne soit jamais dans le texte.\";' '  const q = \"En une phrase courte, comment empêcher un cas inventé de donner sa propre réponse ?\";' '  const reponse = await demander([{ role: \"user\", content: q }], echantillon, 60);' '  console.log(\"le modèle dit : \" + reponse.texte);' '}' 'main();' >> cas.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Lancer le générateur

Commande
docker exec -w /home/node/project agent node cas.js

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Ce que le Run affiche

LigneCe qu’elle montre
gardés 6 sur 12Les cas qui restent après les contrôles
rejetés : {...}Combien ont été écartés, et pourquoi
premier gardé : ... -> ...Un cas propre et son étiquette
le modèle dit : ...Une phrase du labo. Un échantillon enregistré si aucun modèle ne répond

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

À propos de ce labo et de sa source: Lab agent de code IA : utiliser opencode pour expliquer et corriger du code, et vérifier son travail

Vous enseignez à une classe ? Proposez ce module à vos étudiants avec un guide de l'enseignant, des devoirs corrigés et un laboratoire pour chaque étudiant.