Pourquoi un chatbot oublie votre prénom
Un chatbot garde les messages d’une conversation et les renvoie avec chaque nouvelle question. C’est important, car un appel à un modèle de langage n’a aucune mémoire à lui.
Un client explique au chatbot d’une compagnie de bus qu’il a perdu son billet. Puis il demande « et pour mon frère ? ». Le bot ne suit que si les messages précédents voyagent avec la question.
À la fin de cette leçon
Remarque
Je peux garder une conversation d’une requête à l’autre.
Mots-clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Session | la conversation d’une personne avec le chatbot | |
| Identifiant de session | une étiquette qui désigne une session | camille |
| État | ce que le programme retient entre deux requêtes | |
| Dépôt | l’endroit où toutes les sessions sont gardées | |
| Liste de messages | les messages envoyés avec chaque appel |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Un appel au modèle part de zéro
Chaque appel à un modèle repart de rien. Le modèle ne connaît que les messages envoyés dans cet appel.
Pour faire une conversation, votre programme garde tous les messages. Il renvoie la liste entière à chaque fois, avec la nouvelle question à la fin. Cette liste est l’état du chat.
Exemple travaillé : trois requêtes
| Requête | Messages envoyés | Ce que le modèle peut voir |
|---|---|---|
| 1 | system, user | Seulement la règle et « Je m’appelle Camille. » |
| 2 | system, user, assistant, user | Le premier échange et « Comment je m’appelle ? » |
| 3 | system, user, assistant, user, assistant, user | Tout ce qui précède, et la question la plus récente |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Chaque personne a sa propre session
Un chatbot parle à beaucoup de gens en même temps. Le dépôt est donc classé par identifiant de session : une liste par conversation. Un nouvel identifiant ouvre une nouvelle liste. Un identifiant connu complète sa liste.
Le dépôt peut être une variable tant que le programme tourne. Un petit fichier JSON dans le dossier du projet le garde après l’arrêt.
À qui appartient l’état ?
Votre programme possède l’état, pas le modèle. C’est donc vous qui décidez ce qui est gardé et pour combien de temps.
Ne gardez ni mots de passe, ni numéros de carte, ni rien dont le chatbot n’a pas besoin. Donnez aux gens un moyen d’effacer une session. Effacer une session supprime toute la liste.
Une erreur fréquente
Attention
Beaucoup pensent que le modèle se souvient de la personne qui parle. Ce n’est pas le cas. Si deux personnes partagent une liste, chacune voit les mots de l’autre. Gardez une liste par identifiant de session.
É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 dépôt de sessions, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const fs = require(\"fs\");' 'const { demander } = require(\"./model\");' 'const FICHIER = \"sessions.json\"; // le dépôt est dans le dossier du projet' 'const REGLE = \"Vous êtes un assistant serviable. Répondez en une phrase courte.\";' '' 'function charger() {' ' try { return JSON.parse(fs.readFileSync(FICHIER, \"utf8\")); } catch (erreur) { return {}; }' '}' 'function sauver(depot) { fs.writeFileSync(FICHIER, JSON.stringify(depot, null, 1)); }' > chat1.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 dépôt de sessions, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function dire(depot, id, texte, echantillons) {' ' if (depot[id] === undefined) depot[id] = [{ role: \"system\", content: REGLE }];' ' depot[id].push({ role: \"user\", content: texte });' ' const reponse = await demander(depot[id], echantillons, 40);' ' depot[id].push({ role: \"assistant\", content: reponse });' ' sauver(depot);' ' return reponse;' '}' >> chat1.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 dépôt de sessions, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const depot = {};' ' // Échantillons enregistrés, utilisés seulement si aucun modèle ne répond.' ' const camille = [\"Enchanté, Camille.\", \"Vous vous appelez Camille.\"];' ' const hugo = [\"Je ne connais pas encore votre prénom.\"];' ' console.log(\"Camille :\", await dire(depot, \"camille\", \"Je m’appelle Camille. Retenez-le.\", camille));' ' console.log(\"Camille :\", await dire(depot, \"camille\", \"Comment je m’appelle ?\", camille));' ' console.log(\"Hugo :\", await dire(depot, \"hugo\", \"Comment je m’appelle ?\", hugo));' ' const lu = charger();' ' console.log(\"Enregistré : camille a \" + lu.camille.length + \" messages, hugo en a \" + lu.hugo.length);' '}' 'main();' >> chat1.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 deux conversations
docker exec -w /home/node/project agent node chat1.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 |
|---|---|
| Camille (première) | Une courte salutation qui peut employer le prénom Camille |
| Camille (seconde) | Une réponse qui peut dire le prénom, car on lui a envoyé les messages précédents |
| Hugo | Une réponse qui ne peut pas connaître le prénom de Camille, car Hugo a sa propre liste |
| Enregistré | camille a 5 messages, hugo en a 3 |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.