Un journal qui aide et ne fuit pas
Un journal est une trace que l’application écrit sur ce qu’elle a fait. Quand une réponse est lente ou fausse, c’est là qu’on regarde.
Un journal négligent copie chaque message et chaque courriel dans un fichier que beaucoup de gens peuvent lire. Le but est d’en garder assez pour réparer, et pas plus.
À la fin de cette leçon
Remarque
Je peux journaliser assez pour déboguer une requête, et pas plus qu’il ne faut.
Mots clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Fiche de journal | une ligne qu’un programme écrit sur un événement | |
| Identifiant de requête | une étiquette qui relie toutes les fiches d’une requête | r-001 |
| Empreinte | un court code calculé à partir d’une valeur, qu’on ne peut pas défaire | |
| Sel | un mot secret mêlé à la valeur avant l’empreinte | |
| Tronquer | couper une valeur à quelques caractères |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Gardez ce qu’il faut pour déboguer
Demandez-vous à quoi servira le journal. Pour réparer une réponse lente, il faut l’identifiant de requête, l’heure, le modèle et la longueur. Il ne faut pas les mots.
Une fiche sans les mots peut toujours se relier à sa requête : l’identifiant de requête est la clé.
Si un texte doit rester, on le tronque : on le coupe à quelques caractères.
Exemple détaillé : une fiche, avant et après
| Champ | Fiche brute | Fiche sûre | Pourquoi |
|---|---|---|---|
| id_requete | r-001 | r-001 | Il relie le journal à la requête |
| courriel | [email protected] | 7e9c70d3 | Une empreinte salée compte encore les utilisateurs |
| message | Remboursez ma commande svp | 26 (sa longueur) | Les mots restent hors du journal |
| ms | 420 | 420 | Utile pour trouver les réponses lentes |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une empreinte cache la valeur, pas la personne
Une empreinte transforme un courriel en court code. Le même courriel donne toujours le même code, donc on peut encore compter les utilisateurs. Personne ne peut défaire le code en le lisant.
Un sel rend le code plus difficile à deviner. Décidez aussi qui peut lire le journal. Un journal que personne n’a besoin de lire ne devrait pas exister.
Une erreur courante
Attention
Beaucoup pensent qu’une empreinte est anonyme. Ce n’est pas vrai. Qui possède une liste de courriels peut tous les hacher et comparer les codes. Une empreinte est un pseudonyme, et le journal reste fait de données personnelles. Utilisez un sel, et gardez-le loin du journal.
É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 le journal sûr, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' 'const crypto = require(\"crypto\");' 'const brutes = [' ' { id_requete: \"r-001\", courriel: \"[email protected]\", message: \"Remboursez ma commande svp\", modele: \"lab\", ms: 420 },' ' { id_requete: \"r-002\", courriel: \"[email protected]\", message: \"Rappelez-moi au 0600000123\", modele: \"lab\", ms: 380 },' ' { id_requete: \"r-003\", courriel: \"[email protected]\", message: \"Où est mon colis\", modele: \"lab\", ms: 910 },' '];' > journalsur.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 journal sûr, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function empreinte8(v) {' ' return crypto.createHash(\"sha256\").update(\"sel-labo:\" + v).digest(\"hex\").slice(0, 8);' '}' 'function fiche(r) {' ' return { id_requete: r.id_requete, utilisateur: empreinte8(r.courriel), longueur: r.message.length, modele: r.modele, ms: r.ms };' '}' 'function contientCourriel(r) {' ' return Object.values(r).some(function (v) { return typeof v === \"string\" && v.includes(\"@\"); });' '}' >> journalsur.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 journal sûr, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const sures = brutes.map(fiche);' ' brutes.forEach(function (r, i) {' ' console.log(r.id_requete + \" brute avec courriel : \" + contientCourriel(r) + \" | sûre avec courriel : \" + contientCourriel(sures[i]));' ' console.log(\" \" + JSON.stringify(sures[i]));' ' });' ' console.log(\"r-001 et r-003 même utilisateur : \" + (sures[0].utilisateur === sures[2].utilisateur));' ' console.log(\"r-001 et r-002 même utilisateur : \" + (sures[0].utilisateur === sures[1].utilisateur));' ' const q = \"En une courte phrase, pourquoi journaliser la longueur d’un message et non le message ?\";' ' const echantillon = \"Une longueur aide à déboguer les réponses lentes et ne dit rien sur la personne.\";' ' const r = await demander([{ role: \"user\", content: q }], echantillon, 60);' ' console.log(\"le modèle dit : \" + r.texte);' '}' 'main();' >> journalsur.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 le journal sûr
docker exec -w /home/node/project agent node journalsur.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 |
|---|---|
| r-001 brute avec courriel : true | sûre avec courriel : false | La fiche sûre n’a pas de courriel |
| {"id_requete":"r-001","utilisateur":"7e9c70d3",...} | L’empreinte et la longueur, rien d’autre |
| r-001 et r-003 même utilisateur : true | La même personne se retrouve |
| r-001 et r-002 même utilisateur : false | Deux personnes restent distinctes |
| le modèle dit : ... | Une phrase. Les mots changent à chaque exécution |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.