Une pile de messages
Une chaîne du texte aux fiches transforme des messages brouillons en fiches contrôlées qu’un programme peut ranger. C’est utile, car chaque étape vue jusqu’ici est petite et qu’un vrai travail les demande toutes ensemble.
Le service des moyens d’un lycée reçoit une pile de demandes de fournitures en texte libre. Quelqu’un les veut sous forme de lignes : l’article, la quantité et l’urgence. Personne ne veut en taper trois cents.
À la fin de cette leçon
Remarque
Je peux construire une petite chaîne qui mène du texte brouillon à des fiches contrôlées.
Mots-clés
| Mot | Ce que ça veut dire |
|---|---|
| Chaîne | des étapes qui se passent chacune leur résultat |
| Lot | un groupe de textes traités ensemble |
| Rejeter | écarter un texte, avec une raison |
| Taux de rejet | la part des textes qui ont été rejetés |
| Enregistrer | écrire les fiches contrôlées quelque part |
| Données personnelles | des faits qui identifient une personne |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Quatre étapes, l’une après l’autre
Chaque message passe par les mêmes étapes. Extraire : demander les champs au modèle. Valider : contrôler la fiche avec le schéma. Réparer : en cas d’échec, redemander une fois avec la faute. Enregistrer : écrire la bonne fiche.
Un message qui échoue encore est rejeté. Il n’est ni enregistré ni perdu : vous gardez le texte et la raison.
Compter les rejets et dire pourquoi
Un rejet est une information. Gardez le texte et la raison, et calculez le taux de rejet. S’il est élevé, la consigne ou le schéma demandent du travail.
Soyez prudent avec les données personnelles. N’envoyez au modèle que le texte dont il a besoin. Écrivez dans le journal la raison, pas le texte d’un message qui parle d’une personne.
É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 la chaîne, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const fs = require(\"fs\");' 'const { demander } = require(\"./model\");' '' 'const textes = [' ' \"Merci d’envoyer 12 crayons à la salle d’arts, ce n’est pas urgent.\",' ' \"Il nous faut 3 boîtes de craie aujourd’hui, le plus vite possible.\",' ' \"Quelqu’un peut-il me dire où est la salle des professeurs ?\",' '];' '// Échantillons enregistrés, utilisés seulement si aucun modèle ne répond : une réponse propre, une' '// réponse qui demande une réparation, et un message sans commande, qui est rejeté.' 'const echantillons = [' ' JSON.stringify({ article: \"crayons\", quantite: 12, urgent: false }),' ' [\"Voici : \" + JSON.stringify({ article: \"craie\", quantite: \"trois\", urgent: true }), JSON.stringify({ article: \"craie\", quantite: 3, urgent: true })],' ' [JSON.stringify({ article: null, quantite: null, urgent: false }), \"Je ne trouve aucune commande dans ce message.\"],' '];' > chaine6.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 la chaîne, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function extraireJson(texte) {' ' const debut = texte.indexOf(\"{\");' ' const fin = texte.lastIndexOf(\"}\");' ' if (debut === -1 || fin < debut) return null;' ' try { return JSON.parse(texte.slice(debut, fin + 1)); } catch (erreur) { return null; }' '}' '' '// Le premier problème, ou un texte vide quand la fiche est bonne.' 'function problemeDe(fiche) {' ' if (fiche === null) return \"pas du JSON\";' ' if ((typeof fiche.article === \"string\") === false || fiche.article === \"\") return \"article invalide\";' ' if (Number.isInteger(fiche.quantite) === false || fiche.quantite < 1 || fiche.quantite > 20) return \"quantite invalide\";' ' if ((typeof fiche.urgent === \"boolean\") === false) return \"urgent invalide\";' ' return \"\";' '}' >> chaine6.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 la chaîne, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const DEMANDE = \"Extrais l’article, la quantité (de 1 à 20) et l’urgence (true ou false) de ce message. Réponds uniquement par le JSON.\n\nMessage : \";' '' 'async function traiter(texte, echantillon) {' ' const messages = [{ role: \"user\", content: DEMANDE + texte }];' ' let reponse = await demander(messages, echantillon, 80);' ' let probleme = problemeDe(extraireJson(reponse));' ' if (probleme.length > 0) {' ' // Une réparation : renvoyer la mauvaise réponse et le problème, puis redemander une fois.' ' messages.push({ role: \"assistant\", content: reponse });' ' messages.push({ role: \"user\", content: \"Corrigez ce problème et répondez uniquement par le JSON : \" + probleme });' ' reponse = await demander(messages, echantillon, 80);' ' probleme = problemeDe(extraireJson(reponse));' ' }' ' return { fiche: extraireJson(reponse), probleme: probleme };' '}' >> chaine6.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 la chaîne, partie 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const gardees = [];' ' const rejetees = [];' ' for (let rang = 0; rang < textes.length; rang = rang + 1) {' ' const resultat = await traiter(textes[rang], echantillons[rang]);' ' if (resultat.probleme.length > 0) rejetees.push({ texte: textes[rang], pourquoi: resultat.probleme });' ' else gardees.push({ article: resultat.fiche.article, quantite: resultat.fiche.quantite, urgent: resultat.fiche.urgent });' ' }' ' fs.writeFileSync(\"fiches.json\", JSON.stringify(gardees, null, 2));' ' console.log(\"Enregistré : \" + gardees.length + \" fiches dans fiches.json\");' ' for (const une of rejetees) console.log(\"Rejeté : \" + une.texte + \" (\" + une.pourquoi + \")\");' ' console.log(\"Taux de rejet : \" + Math.floor(rejetees.length * 100 / textes.length) + \" pour cent\");' '}' 'main();' >> chaine6.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.
La lancer sur trois messages
docker exec -w /home/node/project agent node chaine6.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.
Une sortie possible
| Ligne | Ce qu’elle peut montrer |
|---|---|
| Enregistré | Enregistré : 2 fiches dans fiches.json, par exemple |
| Rejeté | Le message sur la salle des professeurs, avec une raison comme article invalide |
| Taux de rejet | Taux de rejet : 33 pour cent si un message sur trois est rejeté |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une erreur fréquente
Attention
Beaucoup pensent qu’un message rejeté montre que la chaîne a échoué. Rejeter est juste quand un message ne contient aucune commande. Ce qui échoue, c’est de rejeter sans compter ni dire pourquoi.