Une mémoire qui oublie
La mémoire d’un agent est un petit stock de notes que l’agent peut retrouver plus tard. Une bonne mémoire oublie exprès : les vieilles notes expirent, et certaines choses ne sont jamais gardées.
C’est utile, car une mémoire qui garde tout garde aussi les mots de passe, et des faits devenus faux. Le module « Construire des agents » a construit une mémoire. Cette leçon ajoute l’oubli.
À la fin de cette leçon
Remarque
Je peux donner à un agent une mémoire qui oublie exprès.
Mots clés
| Mot | Ce que ça veut dire |
|---|---|
| Mémoire | une liste de notes que l’agent peut retrouver |
| Mot clé | un mot qui sert à retrouver une note |
| Durée de vie | le nombre de jours qu’une note est gardée |
| Expirer | être oubliée parce qu’une note est trop vieille |
| Règle de non-stockage | un test qui refuse une note à l’entrée |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Les notes expirent avec l’âge
Chaque note porte le jour où elle a été écrite. Son âge est aujourd’hui moins ce jour. Quand l’âge dépasse la durée de vie, la note a expiré et n’est plus utilisée.
Pour chercher, comptez combien de mots de la requête sont dans chaque note. Classez par ce nombre, puis par le jour le plus récent.
Exemple détaillé : jour 40, durée de vie 30
| Note | Jour | Âge | Utilisée ? |
|---|---|---|---|
| livraison en recette vendredi | 5 | 35 | Non : expirée |
| le serveur de recette s’appelle boite7 | 20 | 20 | Oui, 1 mot commun |
| la livraison demande deux accords | 33 | 7 | Oui, 1 mot commun |
| la correction de livraison en recette est fusionnée | 39 | 1 | Oui, 2 mots communs, première |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une erreur courante
Attention
Beaucoup pensent qu’une mémoire doit tout garder, car plus d’informations aident plus. Ce n’est pas vrai. Les vieilles notes peuvent être fausses, et une note qui contient un mot de passe est une fuite qui attend. Décidez ce qu’il faut oublier avant de décider ce qu’il faut garder.
Certaines choses ne sont jamais gardées
Vérifiez chaque note à l’entrée, avant de la garder. Refusez des mots comme mot de passe, une adresse mail, ou une longue suite de chiffres comme un numéro de carte.
Ces règles sont simples et elles laissent passer des choses. C’est un filet de sécurité, qu’on associe à une consigne pour l’agent : ne jamais écrire un secret.
É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 la mémoire, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' 'const interdits = [\"mot de passe\", \"secret\", \"jeton\"];' 'function verifier(texte) {' ' const bas = texte.toLowerCase();' ' for (const b of interdits) { if (bas.indexOf(b) >= 0) return \"refuse : contient \" + b; }' ' if (texte.indexOf(\"@\") >= 0) return \"refuse : contient une adresse mail\";' ' if (/[0-9]{12}/.test(texte)) return \"refuse : contient un long nombre\";' ' return \"ok\";' '}' 'const memoire = [];' 'function ajouter(texte, jour) {' ' const pourquoi = verifier(texte);' ' if (pourquoi === \"ok\") memoire.push({ texte: texte, jour: jour });' ' return pourquoi;' '}' > memoire.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 mémoire, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function chercher(requete, aujourdhui, duree) {' ' const mots = requete.toLowerCase().split(\" \");' ' const vivantes = memoire.filter(function (n) { return aujourdhui - n.jour <= duree; });' ' const notees = vivantes.map(function (n) {' ' const a = n.texte.toLowerCase().split(\" \");' ' return { texte: n.texte, jour: n.jour, communs: mots.filter(function (m) { return a.indexOf(m) >= 0; }).length };' ' });' ' return notees.filter(function (n) { return n.communs > 0; }).sort(function (a, b) { return b.communs - a.communs || b.jour - a.jour; });' '}' >> memoire.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 mémoire, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const notes = [' ' [\"livraison en recette vendredi\", 5],' ' [\"le serveur de recette s’appelle boite7\", 20],' ' [\"la livraison demande deux accords\", 33],' ' [\"mon mot de passe est hunter2\", 38],' ' [\"écrivez-moi à [email protected]\", 38],' ' [\"la correction de livraison en recette est fusionnée\", 39],' '];' >> memoire.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 mémoire, partie 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' notes.forEach(function (n) { console.log(\"ajout jour \" + n[1] + \" : \" + ajouter(n[0], n[1])); });' ' console.log(\"gardées \" + memoire.length + \", encore valables \" + memoire.filter(function (n) { return 40 - n.jour <= 30; }).length);' ' chercher(\"livraison recette\", 40, 30).forEach(function (n) { console.log(n.communs + \" en commun, vieille de \" + (40 - n.jour) + \" j : \" + n.texte); });' ' const q = \"En une courte phrase, pourquoi un agent doit-il oublier les vieilles notes ?\";' ' const echantillon = \"Les vieilles notes peuvent être fausses aujourd’hui, et une mémoire qui garde tout garde aussi ce qu’elle ne devrait pas.\";' ' const r = await demander([{ role: \"user\", content: q }], echantillon, 60);' ' console.log(\"le modèle dit : \" + r.texte);' '}' 'main();' >> memoire.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 la mémoire
docker exec -w /home/node/project agent node memoire.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 montre |
|---|---|
| ajout jour 5 : ok | La note est gardée |
| ajout jour 38 : refuse : contient mot de passe | La règle a arrêté un secret |
| ajout jour 38 : refuse : contient une adresse mail | La règle a arrêté une adresse |
| gardées 4, encore valables 3 | Une note a expiré |
| 2 en commun, vieille de 1 j : la correction de livraison ... | La meilleure correspondance passe en premier |
| le modèle dit : ... | Une phrase. Les mots changent à chaque exécution |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.