Assembler les cinq parties
Un chatbot qui fonctionne réunit cinq parties dans une seule boucle. Il garde un état, le coupe, suit un message system, envoie sa réponse en flux et montre ses sources. C’est difficile, car chaque partie marche seule, et les bogues apparaissent là où elles se rejoignent.
Le chatbot d’une bibliothèque répond à trois questions de suite. Il doit se souvenir de la première, rester dans son budget et citer une source à chaque fois.
À la fin de cette leçon
Remarque
Je peux réunir état, coupe, message system, flux et sources dans un seul chatbot.
Mots-clés
| Mot | Ce que ça veut dire |
|---|---|
| Boucle de chat | les étapes répétées pour chaque message de l’utilisateur |
| Tour | un message de l’utilisateur et la réponse qui lui correspond |
| Historique | tous les messages du chat jusqu’ici |
| Budget | le plus de jetons que vous choisissez d’envoyer |
| Repli | ce que fait le bot quand aucun modèle ne répond |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Un tour utilise les cinq parties
Chaque tour suit les mêmes étapes. Chargez l’historique. Ajoutez le message de l’utilisateur. Coupez ce que vous allez envoyer. Appelez le modèle. Contrôlez la réponse. Enregistrez l’historique.
Écrivez ces étapes dans une seule fonction, tour. Le chat n’est alors que cette fonction appelée encore et encore.
Exemple travaillé : les étapes d’un tour
| Étape | De la leçon | Ce qui se passe |
|---|---|---|
| Charger et ajouter | Garder une conversation | Le message de l’utilisateur rejoint la liste de la session |
| Couper | Couper un historique | Les vieux messages sont laissés de côté pour cet appel |
| Message et sources | Message system, Citations | Le message system contient les règles et les sources numérotées |
| Appeler et contrôler | Citations | On vérifie que la réponse n’a pas de repère sans source |
| Flux et enregistrement | Réponse en flux | Les morceaux vont à la page, l’historique est enregistré |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Garder n’est pas envoyer
L’historique garde tous les messages. L’appel n’envoie que ce qui tient dans le budget. Couper ne change donc jamais le dossier. Cela change un appel.
Vous pouvez aussi changer le budget plus tard, et une personne peut toujours lire le chat entier.
Prévoir un modèle qui ne répond pas
Parfois aucun modèle ne répond : l’IA est coupée, le crédit du jour est épuisé ou le réseau tombe. Un repli est un message clair qui le dit, pour que le chat ne reste pas bloqué et ne plante pas.
Dans ce labo, chaque Run affiche alors un échantillon enregistré, annoncé comme tel. Un vrai produit montrerait son propre avis.
Une erreur fréquente
Attention
Beaucoup pensent qu’un chatbot est fini quand la démonstration marche. Une démonstration pose des questions gentilles. Essayez des questions pénibles, un très long chat et aucun modèle du tout. Comptez les jetons que coûte un chat avant que d’autres l’utilisent.
É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 le chatbot, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const fs = require(\"fs\");' 'const { demander } = require(\"./model\");' 'const BUDGET = 180; // jetons pour le message system et l’historique ensemble' 'const SOURCES = [' ' \"Les adhérents peuvent emprunter jusqu’à six livres pour trois semaines.\",' ' \"Un livre rendu en retard coûte dix centimes par jour.\",' ' \"La bibliothèque ouvre à neuf heures et ferme à dix-sept heures en semaine.\",' '];' > robot6.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 chatbot, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function jetons(m) { return Math.ceil(m.content.length / 4) + 4; }' 'function total(liste) {' ' return liste.reduce(function (somme, m) { return somme + jetons(m); }, 0);' '}' 'function messageSysteme() {' ' const numerotees = SOURCES.map(function (t, rang) { return \"[\" + (rang + 1) + \"] \" + t; }).join(\" \");' ' const regle = \"Vous êtes l’assistant de la bibliothèque. Répondez en deux phrases au plus, avec ces sources seulement. Écrivez le numéro de la source entre crochets après chaque fait. Si elles ne le disent pas, répondez : Je ne sais pas. \";' ' return { role: \"system\", content: regle + numerotees };' '}' >> robot6.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 chatbot, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function couper(historique, systeme) {' ' const gardes = historique.slice();' ' while (total([systeme].concat(gardes)) > BUDGET && gardes.length > 1) gardes.shift();' ' return gardes;' '}' 'function reperesFaux(reponse) {' ' const faux = [];' ' const motif = /\[(\d+)\]/g;' ' let m = motif.exec(reponse);' ' while (m) {' ' if (Number(m[1]) < 1 || Number(m[1]) > SOURCES.length) faux.push(m[1]);' ' m = motif.exec(reponse);' ' }' ' return faux;' '}' >> robot6.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 chatbot, partie 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function tour(etat, texte, echantillon) {' ' etat.historique.push({ role: \"user\", content: texte });' ' const systeme = messageSysteme();' ' const envoyes = couper(etat.historique, systeme);' ' const detenus = etat.historique.length;' ' const reponse = await demander([systeme].concat(envoyes), echantillon, 70);' ' etat.historique.push({ role: \"assistant\", content: reponse });' ' fs.writeFileSync(\"chat.json\", JSON.stringify(etat, null, 1));' ' console.log(\"Vous : \" + texte);' ' console.log(\"Bot : \" + reponse);' ' console.log(\" envoyé \" + envoyes.length + \" sur \" + detenus + \" messages, faux repères : \" + reperesFaux(reponse).length);' '}' >> robot6.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 chatbot, partie 5
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const etat = { historique: [] };' ' // Échantillons enregistrés, utilisés seulement si aucun modèle ne répond.' ' await tour(etat, \"Combien de livres puis-je emprunter ?\", \"Vous pouvez emprunter six livres pour trois semaines [1].\");' ' await tour(etat, \"Et combien coûte un retard ?\", \"Un livre en retard coûte dix centimes par jour [2].\");' ' await tour(etat, \"Quand fermez-vous le vendredi ?\", \"La bibliothèque ferme à dix-sept heures en semaine [3].\");' ' console.log(\"Enregistré : \" + etat.historique.length + \" messages dans chat.json\");' '}' 'main();' >> robot6.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.
Tenir un chat de trois tours
docker exec -w /home/node/project agent node robot6.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 |
|---|---|
| Vous et Bot | Trois questions, chacune avec une courte réponse qui peut citer une source |
| envoyé n sur m | envoyé 1 sur 1, puis 3 sur 3, puis 5 sur 5 ou moins. Les réponses enregistrées donnent 4 sur 5 |
| faux repères | Le plus souvent 0. Un nombre au-dessus de 0 veut dire que la réponse cite une source qui n’existe pas |
| Enregistré | Enregistré : 6 messages dans chat.json |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.