La première version d’une fonction d’IA
La première version d’une fonction d’IA est la plus simple que vous puissiez mesurer. Elle contient un prompt, un modèle et un petit jeu de questions de test. Elle compte, car on n’améliore pas ce que l’on ne sait pas noter.
La médiathèque des Tilleuls a maintenant sa note de cadrage. L’équipe construit un assistant qui répond à partir d’une fiche de neuf lignes, puis le note sur dix questions.
À la fin de cette leçon
Remarque
Je peux construire la version la plus simple qui se laisse mesurer.
Mots clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Fiche des faits | la courte liste de faits que l’assistant peut utiliser | |
| Cas de test | une question avec la réponse qu’elle doit contenir | c4 |
| Évaluation | des cas de test et une règle qui note chacun | |
| Contrôle | la règle qui note une réponse, réussie ou non | |
| Référence | le premier score, celui que les versions suivantes battent |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Un prompt, un modèle, une fiche
La version la plus simple a quatre parties. Un prompt système avec une règle. Une fiche de faits placée dedans. Un modèle. Un contrôle de la réponse.
Elle n’a ni recherche, ni mémoire, ni second modèle. Chaque partie absente ne peut pas encore échouer. Vous en ajoutez une seulement quand un score le demande.
Exemple : la version 1
| Partie | Dans la première version |
|---|---|
| Prompt | Répondre en une phrase courte |
| Fiche des faits | Neuf faits, collés dans le prompt |
| Modèle | Un seul, choisi par la plateforme |
| Contrôle | Le bon chiffre, aucun autre, 40 mots |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une évaluation : des cas et une règle
Un cas de test est une question et le chiffre que la réponse doit contenir. Le contrôle note chaque réponse avec la même règle.
Ici, le contrôle trouve quatre genres d’échec. Un refus. Un chiffre inventé qui n’est pas sur la fiche. Une réponse vague, sans le bon chiffre. Une réponse trop longue, au-delà de 40 mots.
Commencez avec dix cas
Dix cas, c’est peu, exprès. Vous pouvez lire chaque réponse et voir pourquoi chacune échoue.
Les cas viennent de ce que les gens demandent, pas de ce qui est facile. Quand un vrai échec apparaît plus tard, vous l’ajoutez comme cas 11. L’évaluation grandit avec les preuves.
Une erreur fréquente
Attention
Beaucoup pensent qu’une belle démonstration est une évaluation. Une démonstration est un cas que vous avez choisi. Une évaluation compte beaucoup de cas que vous n’avez pas choisis, notés par la même règle. Une démonstration dit ce qui peut marcher.
Écrire l’aide 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\";' '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 || 200 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"statut \" + res.status);' ' return (await res.json()).choices[0].message.content;' > 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’aide du modèle, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' } catch (erreur) {' ' console.log(\"Échantillon enregistré : aucun modèle n’a répondu (\" + erreur.message + \").\");' ' const liste = [].concat(echantillon);' ' const tour = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' ' return liste[Math.min(tour, liste.length - 1)];' ' }' '}' '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 l’évaluation, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' 'const FICHE = \"Prêt 3 semaines. Renouvellement 2 fois. Amende 20 centimes par jour. Ouvert de 9 à 17. Limite 8 documents. Carte perdue 5 euros. Salle 4 heures. Impression 10 centimes la page. Moins de 18 ans, gratuit.\";' 'const LIMITE = 40; // le plus grand nombre de mots d’une réponse' 'const NOMBRES_FICHE = FICHE.match(/[0-9]+/g);' > eval.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’évaluation, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const BRUT = [' ' [\"c1\", \"Combien de temps puis-je garder un livre ?\", \"3\", \"Vous pouvez garder un livre 3 semaines.\", 7, false],' ' [\"c2\", \"Puis-je renouveler un livre ?\", \"2\", \"Oui, vous pouvez le renouveler 2 fois.\", 7, false],' ' [\"c3\", \"Quelle est l’amende pour un retard ?\", \"20\", \"L’amende est de 50 centimes par jour.\", 7, false],' ' [\"c4\", \"À quelle heure ouvrez-vous ?\", \"9\", \"Nous ouvrons à 9 heures tous les jours.\", 8, false],' ' [\"c5\", \"À quelle heure fermez-vous ?\", \"17\", \"Nous fermons dans la soirée.\", 5, false],' >> eval.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’évaluation, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' [\"c6\", \"Combien de documents puis-je emprunter ?\", \"8\", \"Vous pouvez emprunter 8 documents à la fois.\", 8, false],' ' [\"c7\", \"Combien coûte une nouvelle carte ?\", \"5\", \"Une nouvelle carte coûte 7 euros.\", 6, false],' ' [\"c8\", \"Puis-je réserver une salle pour la journée ?\", \"4\", \"Je ne peux pas vous aider ici. Demandez à l’accueil.\", 10, true],' ' [\"c9\", \"Combien coûte l’impression ?\", \"10\", \"L’impression coûte 10 centimes la page. Le noir et blanc et la couleur coûtent pareil. Vous payez à l’accueil par carte ou en pièces. Les imprimantes sont près de la fenêtre de l’est, à côté des grandes tables, et les scanners y sont gratuits pour tous ceux qui ont une carte ou un pass journée.\", 55, false],' ' ' >> eval.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’évaluation, partie 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' [\"c10\", \"Les enfants peuvent-ils s’inscrire ?\", \"18\", \"Les moins de 18 ans ont une carte gratuite.\", 9, false],' '];' 'const CAS = BRUT.map(function (r) {' ' return { id: r[0], q: r[1], attendu: r[2], reponse: r[3], longueur: r[4], refuse: r[5] };' '});' >> eval.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’évaluation, partie 5
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function genre(c) {' ' if (c.refuse) return \"refus\";' ' const trouves = c.reponse.match(/[0-9]+/g) || [];' ' for (const n of trouves) {' ' if (NOMBRES_FICHE.includes(n) === false) return \"invente\";' ' }' ' if (trouves.includes(c.attendu) === false) return \"vague\";' ' if (c.longueur > LIMITE) return \"trop_long\";' ' return \"ok\";' '}' 'const comptes = {};' 'for (const c of CAS) {' ' const g = genre(c);' ' comptes[g] = (comptes[g] || 0) + 1;' ' console.log(c.id, g);' '}' 'console.log(\"Série enregistrée v1 :\", comptes.ok, \"sur\", CAS.length, \"réussis\");' 'console.log(JSON.stringify(comptes));' 'require(\"fs\").writeFileSync(\"eval-v1.json\", JSON.stringify(CAS, null, 1));' >> eval.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’évaluation, partie 6
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function direct() {' ' const systeme = \"Répondez en une phrase courte, en utilisant seulement cette fiche : \" + FICHE;' ' const messages = [{ role: \"system\", content: systeme }, { role: \"user\", content: \"À quelle heure ouvrez-vous ?\" }];' ' const reponse = await demander(messages, \"Nous ouvrons à 9 heures tous les jours.\", 40);' ' const trouves = reponse.match(/[0-9]+/g) || [];' ' console.log(\"Question en direct :\", reponse);' ' console.log(\"Contient le chiffre 9 :\", trouves.includes(\"9\"), \"(hors score)\");' '}' 'direct();' >> eval.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.
Noter la version 1
docker exec -w /home/node/project agent node eval.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 que le Run affiche
| Ligne | Ce qu’elle dit |
|---|---|
| c1 à c10 | Un genre chacun : ok, invente, vague, refus ou trop_long |
| Série enregistrée v1 | 5 sur 10 réussis |
| Ligne JSON | 5 ok, 2 invente, 1 vague, 1 refus, 1 trop_long |
| Question en direct | Un court appel, jamais dans le score |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.