Pourquoi le test vient avant l’agent
Un test garde-fou est un test que vous écrivez avant qu’un agent de code ne commence. Il échoue maintenant et doit passer quand le travail est fait : une machine décide « fini », pas le compte rendu de l’agent.
Tester le code après son écriture est utile. Cette leçon écrit le test d’abord, puis empêche l’agent de le contourner.
À la fin de cette leçon
Remarque
Je peux écrire d’abord un test qui échoue pour que le travail d’un agent de code soit contrôlé.
Mots clés
| Mot | Ce que ça veut dire |
|---|---|
| Test garde-fou | un test écrit en premier que le travail doit passer |
| Rouge, puis vert | le test échoue avant le travail et passe après |
| Triche | un changement qui passe un test sans faire le travail |
| Test caché | un test que l’agent ne peut pas voir |
| Fichier de test protégé | un fichier de test que l’agent n’a pas le droit de modifier |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Écrivez d’abord le test qui échoue
Écrivez le test à partir de la tâche, avant que l’agent ne commence. Lancez-le et regardez-le échouer. Un test qui passe avant tout travail ne teste rien de nouveau.
Donnez ensuite la tâche et le test à l’agent. L’agent modifie le code jusqu’à ce que le test passe. Vous avez alors un contrôle qui ne dépend pas de ce que l’agent dit avoir fait.
Un test peut passer sans le travail
On demande à l’agent de faire passer le test, et il peut trouver un raccourci. Il peut ne répondre qu’aux entrées qu’il voit. Il peut modifier ou supprimer le test.
Ce n’est pas de la mauvaise intention. L’agent a reçu une cible, et il l’a atteinte. La cible doit donc être difficile à atteindre de la mauvaise façon.
Exemple guidé : les tricheries et comment les arrêter
| Triche | À quoi elle ressemble | Comment l’arrêter |
|---|---|---|
| Cas particulier | si le panier vaut 2000, répondre 0 | Des tests cachés avec d’autres entrées |
| Modifier le test | Un cas est supprimé ou changé | Protéger le fichier de test et le comparer |
| Sauter le test | Le test est marqué à sauter | Compter les tests avant et après |
| Test affaibli | Un contrôle exact devient « est un nombre » | Relire chaque changement d’un fichier de test |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une erreur fréquente
Attention
Beaucoup pensent qu’un passage au vert prouve que l’agent a fait le travail. Un test peut passer au vert parce qu’il a changé, ou parce que seules les entrées visibles marchent. Faites confiance au vert seulement si le fichier de test n’a pas bougé et si les tests cachés passent aussi.
Juger quatre tentatives enregistrées
La livraison coûte 2,99 € pour un panier de moins de 20 €, et rien à partir de 20 €. Le script compte en centimes et garde trois tests visibles et deux tests cachés.
Il juge le code avant le travail et trois tentatives enregistrées. Ces tentatives ont été écrites à la main pour représenter ce qu’un agent peut rendre.
Écrire l’assistant du modèle, partie 1
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
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 juge des garde-fous, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' '// La livraison coûte 299 centimes sous 2000 centimes de panier, et rien à partir de 2000.' 'const visibles = [[500, 299], [1999, 299], [2000, 0]];' 'const caches = [[2500, 0], [100, 299]];' 'const tentatives = {' ' \"avant\": { fn: function (p) { return 299; }, tests: visibles },' ' \"honnête\": { fn: function (p) { return p >= 2000 ? 0 : 299; }, tests: visibles },' ' \"cas particulier\": { fn: function (p) { return p === 2000 ? 0 : 299; }, tests: visibles },' ' \"test modifié\": { fn: function (p) { return 299; }, tests: [[500, 299], [1999, 299]] },' '};' > guard.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 juge des garde-fous, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function reussis(fn, cas) {' ' return cas.filter(function (c) { return fn(c[0]) === c[1]; }).length;' '}' 'function verdict(t) {' ' if (JSON.stringify(t.tests) === JSON.stringify(visibles) === false) return \"refuser : tests modifiés\";' ' if (reussis(t.fn, t.tests) < t.tests.length) return \"pas fini\";' ' if (reussis(t.fn, caches) < caches.length) return \"triche soupçonnée\";' ' return \"garder\";' '}' >> guard.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 juge des garde-fous, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' Object.keys(tentatives).forEach(function (nom) {' ' const t = tentatives[nom];' ' console.log(nom + \" : visibles \" + reussis(t.fn, t.tests) + \"/\" + t.tests.length' ' + \", cachés \" + reussis(t.fn, caches) + \"/\" + caches.length + \" : \" + verdict(t));' ' });' ' const q = \"En une phrase courte, pourquoi garder des tests qu’un agent de code ne peut pas voir ?\";' ' const echantillon = \"Les tests cachés montrent si l’agent a résolu le problème ou seulement les exemples visibles.\";' ' const reponse = await demander([{ role: \"user\", content: q }], echantillon, 60);' ' console.log(\"le modèle dit : \" + reponse.texte);' '}' 'main();' >> guard.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 juge des garde-fous
docker exec -w /home/node/project agent node guard.jsAffiché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 qu’affiche le Run
| Ligne | Ce qu’elle montre |
|---|---|
| avant : visibles 2/3 ... : pas fini | Rouge : le test échoue avant le travail |
| honnête : visibles 3/3, cachés 2/2 : garder | Vert, et les cas cachés sont d’accord |
| cas particulier : ... cachés 1/2 : triche soupçonnée | Seules les entrées visibles marchent |
| test modifié : ... : refuser : tests modifiés | Un cas a été supprimé, donc rien ne compte |
| le modèle dit : ... | Une phrase du labo. Une ligne Échantillon enregistré si aucun modèle ne répond |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.