Un appel prend une liste de messages
Un appel de discussion à un modèle de langage prend une liste de messages. Chaque message a un rôle et un texte. Le modèle lit toute la liste, puis écrit le message suivant.
C’est important, car un modèle ne retient rien d’un appel à l’autre. Une discussion qui semble avoir de la mémoire est un programme qui renvoie les anciens messages à chaque fois.
À la fin de cette leçon
Remarque
Je peux construire la liste de messages que prend un appel à un modèle de langage.
Mots clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Message | un tour de la discussion : un rôle et un texte | |
| Rôle | l’auteur d’un message | user |
| Message système | la consigne qui règle le comportement du modèle | |
| Historique | les messages précédents, renvoyés à chaque appel | |
| Corps de la requête | le JSON qu’un programme envoie avec un appel |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Un message, c’est un rôle et un texte
Un message est un objet à deux clés. role dit qui parle et content contient les mots.
Il existe trois rôles. Le message system règle le comportement du modèle. Un message user est ce que dit la personne. Un message assistant est ce que le modèle a dit plus tôt.
Le corps de la requête d’un appel
{
"model": "lab",
"max_tokens": 60,
"messages": [
{ "role": "system", "content": "Vous répondez en une seule phrase courte." },
{ "role": "user", "content": "Qu’est-ce qu’une variable en programmation ?" }
]
}Envoyer un appel au modèle du labo
docker exec -w /home/node/project agent node -e '/* Le corps de la requête : un nom de modèle, une limite de réponse et une liste de messages. */ const corps = { model: "lab", max_tokens: 60, messages: [ { role: "system", content: "Vous répondez en une seule phrase courte." }, { role: "user", content: "Qu’est-ce qu’une variable en programmation ?" } ] }; (async () => { try { const url = (process.env.AI_URL || "http://ai:4000") + "/v1/chat/completions"; const reponse = await fetch(url, { method: "POST", body: JSON.stringify(corps), headers: { "content-type": "application/json", authorization: "Bearer lab" } }); if (!reponse.ok) throw new Error("HTTP " + reponse.status); const donnees = await reponse.json(); console.log("Réponse du modèle : " + donnees.choices[0].message.content); } catch (erreur) { console.log("Aucun modèle n’a répondu (" + erreur.message + ")."); console.log("Exemple enregistré : Une variable garde une valeur que le programme peut changer."); } })();'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.
Le modèle ne retient rien entre deux appels
Chaque appel est isolé. Le modèle ne voit que les messages de cet appel.
Pour tenir une conversation, votre programme garde une liste. Après chaque réponse, il y ajoute la question et la réponse. À l’appel suivant, il renvoie l’historique, avec la nouvelle question à la fin.
Écrire le fichier d’aide labo.js
docker exec -w /home/node/project agent node -e 'require("fs").writeFileSync("labo.js", ["// labo.js, partie 1 : l’endroit unique qui envoie un appel au modèle du labo.", "const base = process.env.AI_URL || \"http://ai:4000\";", "", "async function envoyer(corps, delai) {", " return fetch(base + \"/v1/chat/completions\", {", " method: \"POST\",", " headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },", " body: JSON.stringify({ model: \"lab\", ...corps }),", " signal: AbortSignal.timeout(delai || 30000),", " });", "}"].join("\n") + "\n")'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.
Ajouter la demande avec un exemple enregistré
docker exec -w /home/node/project agent node -e 'require("fs").appendFileSync("labo.js", ["", "// Partie 2 : demander une fois. Si le modèle ne peut pas répondre, rendre l’exemple enregistré.", "async function demander(corps, exemple) {", " try {", " const reponse = await envoyer(corps);", " if (!reponse.ok) throw new Error(\"HTTP \" + reponse.status);", " const donnees = await reponse.json();", " return { etiquette: \"Réponse du modèle\", texte: donnees.choices[0].message.content, donnees: donnees };", " } catch (erreur) {", " return { etiquette: \"Exemple enregistré\", texte: exemple, note: erreur.message };", " }", "}", "", "module.exports = { envoyer, demander };"].join("\n") + "\n")'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.
Demander avec l’historique, puis sans
docker exec -w /home/node/project agent node -e 'const labo = require("./labo.js"); const consigne = { role: "system", content: "Répondez en une phrase courte." }; const historique = [ { role: "user", content: "Je m’appelle Samir." }, { role: "assistant", content: "Enchanté, Samir." } ]; const question = { role: "user", content: "Comment je m’appelle ?" }; (async () => { const avec = await labo.demander({ max_tokens: 30, messages: [consigne, ...historique, question] }, "Vous vous appelez Samir."); console.log("Avec l’historique, " + avec.etiquette + " : " + avec.texte); const sans = await labo.demander({ max_tokens: 30, messages: [consigne, question] }, "Je ne connais pas votre nom."); console.log("Sans lui, " + sans.etiquette + " : " + sans.texte); console.log("Messages envoyés à chaque appel : " + [consigne, ...historique, question].length + " et " + [consigne, question].length); })();'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.
Une erreur fréquente
Attention
Beaucoup pensent que le modèle se souvient de l’appel précédent. Ce n’est pas le cas. Le second appel, sans historique, ne pouvait pas connaître le prénom. Pour que le modèle s’en serve, mettez-le dans les messages que vous envoyez.
Exemple travaillé : ce que chaque appel portait
| Appel | Messages envoyés | Ce que le modèle peut savoir |
|---|---|---|
| Avec l’historique | system, user, assistant, user (4) | Le prénom Samir |
| Sans lui | system, user (2) | Rien sur Samir |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
L’ordre des messages compte
Mettez le message système en premier. Ensuite, les tours alternent : user, assistant, user, et ainsi de suite. Le dernier message vient de l’utilisateur, car c’est à lui que le modèle répond.
La plupart des API de modèles refusent une liste qui rompt cet ordre, ou donnent des réponses étranges. Vérifiez l’ordre dans votre code avant d’envoyer.
Pour aller plus loin : changer le message système
Ouvrez le Run de l’historique et remplacez la consigne par « Répondez en anglais ». Relancez-le.
Notez ce qui a changé et ce qui n’a pas changé. Puis dites en une phrase pourquoi la consigne devait figurer dans les messages envoyés.