Quand la conversation devient trop longue
Couper un historique de chat, c’est envoyer moins de messages au modèle pour que la liste tienne dans sa limite. C’est nécessaire, car un long chat grossit à chaque tour et chaque message coûte du temps et du crédit.
Après une heure avec un chatbot de voyage, les premières questions ne servent plus. Les retirer garde le chat rapide et peu cher.
À la fin de cette leçon
Remarque
Je peux garder une conversation dans la limite du modèle.
Mots-clés
| Mot | Ce que ça veut dire |
|---|---|
| Jeton | un petit morceau de texte que le modèle compte |
| Limite de contexte | le plus de jetons qu’un modèle accepte en un appel |
| Budget | le plus de jetons que vous choisissez d’envoyer |
| Couper | retirer les vieux messages jusqu’à ce que ça tienne |
| Résumé | un court texte qui remplace des messages retirés |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Comptez les jetons avant d’envoyer
Un modèle compte le texte en jetons, et il n’en accepte qu’un certain nombre par appel. Les vrais comptes viennent du découpeur du modèle. Une estimation grossière : un jeton pour quatre caractères.
Cette leçon ajoute quatre jetons par message, pour le rôle et l’enrobage. Additionnez tous les messages et comparez le total à votre budget.
Exemple travaillé : compter six messages
| Message | Caractères | Jetons |
|---|---|---|
| system : Vous aidez pour les voyages en train. Soyez bref. | 49 | 17 |
| user : Combien de temps dure le trajet de Lyon à Dijon ? | 49 | 17 |
| assistant : Environ deux heures. | 20 | 9 |
| user : Et pour un billet ? | 19 | 9 |
| assistant : Cela dépend de l’heure. | 23 | 10 |
| user : Y a-t-il une voiture calme ? | 28 | 11 |
| Total | 188 | 73 |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Retirez les plus anciens, gardez les règles
Pour couper, retirez d’abord les messages les plus anciens, jusqu’à ce que le total tienne dans le budget. Deux messages restent quel que soit le budget : le message system au début, et le message le plus récent à la fin.
Le message system contient les règles. Le message le plus récent est la question posée maintenant.
Un résumé remplace les messages retirés
Retirer fait perdre des faits. Un résumé garde les principaux en quelques mots. Demandez au modèle de résumer les messages retirés, puis ajoutez le résumé au message system.
Cela coûte un appel de plus à chaque coupe. Pour un chat court, retirer simplement suffit souvent.
Une erreur fréquente
Attention
Beaucoup pensent que le plus ancien message est toujours le moins important. C’est souvent le message system avec les règles, ou le seul fait dont le chat dépend. Gardez le message system, et résumez avant de retirer ce qui compte.
É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 script de coupe, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' 'const BUDGET = 50; // un petit budget pour que la coupe ait lieu' '' 'function jetons(message) { return Math.ceil(message.content.length / 4) + 4; }' 'function total(liste) {' ' return liste.reduce(function (somme, m) { return somme + jetons(m); }, 0);' '}' > coupe2.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 script de coupe, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function couper(liste, budget) {' ' const gardes = liste.slice();' ' const retires = [];' ' while (total(gardes) > budget && gardes.length > 2) {' ' const place = gardes[0].role === \"system\" ? 1 : 0;' ' retires.push(gardes.splice(place, 1)[0]);' ' }' ' return { gardes: gardes, retires: retires };' '}' >> coupe2.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 script de coupe, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const historique = [' ' { role: \"system\", content: \"Vous aidez pour les voyages en train. Soyez bref.\" },' ' { role: \"user\", content: \"Combien de temps dure le trajet de Lyon à Dijon ?\" },' ' { role: \"assistant\", content: \"Environ deux heures.\" },' ' { role: \"user\", content: \"Et pour un billet ?\" },' ' { role: \"assistant\", content: \"Cela dépend de l’heure.\" },' ' { role: \"user\", content: \"Y a-t-il une voiture calme ?\" },' '];' >> coupe2.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 script de coupe, partie 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const coupe = couper(historique, BUDGET);' ' console.log(\"Jetons avant : \" + total(historique) + \", après : \" + total(coupe.gardes) + \", retirés : \" + coupe.retires.length);' >> coupe2.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 script de coupe, partie 5
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' const ancien = coupe.retires.map(function (m) { return m.role + \" : \" + m.content; }).join(\" \");' ' // Échantillons enregistrés, utilisés seulement si aucun modèle ne répond.' ' const premier = \"L’utilisateur a demandé la durée du trajet de Lyon à Dijon et a appris qu’il dure environ deux heures.\";' ' const resume = await demander([{ role: \"user\", content: \"Résume en une phrase courte : \" + ancien }], premier, 50);' ' console.log(\"Résumé :\", resume);' ' const invite = coupe.gardes.slice();' ' invite[0] = { role: \"system\", content: invite[0].content + \" Plus tôt dans ce chat : \" + resume };' ' console.log(\"Réponse :\", await demander(invite, \"Oui, la plupart des trains ont une voiture calme.\", 50));' '}' 'main();' >> coupe2.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.
Couper et résumer
docker exec -w /home/node/project agent node coupe2.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 |
|---|---|
| Jetons | avant : 73, après : 47, retirés : 2 (toujours pareil, rien n’est deviné) |
| Résumé | Une phrase sur la durée du trajet qui a été retirée |
| Réponse | Une courte réponse sur une voiture calme |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.