Plus facile à lire avec un fond crème, une teinte, plus de contraste ou un texte plus grand ? Choisissez vos couleurs et votre texte, une fois pour toutes les pages.

Leçon gratuite

Garder un agent dans un seul dossier

Après cette leçon: Je peux garder un agent dans un seul dossier en vérifiant le chemin résolu.

Leçon de 65 min6 commandesLab agent de code IA : utiliser opencode pour expliquer et corriger du code, et vérifier son travail

Garder un agent dans son dossier

Un dossier confiné est le seul dossier où un agent peut lire et écrire. Chaque chemin qu’il demande doit finir dans ce dossier.

C’est plus difficile qu’il n’y paraît. Un chemin peut en sortir avec deux points, ou passer par un lien qui pointe ailleurs. La solution est de vérifier où le chemin finit vraiment.

À la fin de cette leçon

Remarque

Je peux garder un agent dans un seul dossier en vérifiant le chemin résolu.

Mots clés

MotCe que ça veut direExemple
Dossier confinéle seul dossier qu’un agent peut utiliser
Résoudrecalculer où va vraiment un chemin
Lienun fichier qui pointe vers un autre endroitraccourci
Évasionfinir en dehors du dossier
Parentle dossier au-dessus, écrit avec deux points..

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

Résoudre d’abord, comparer ensuite

Résoudre un chemin, c’est suivre chaque paire de points et chaque lien jusqu’à l’adresse réelle. Seulement alors, demandez : est-il dans le dossier ?

Comparez avec une barre oblique à la fin. Le dossier bac n’est pas le dossier bac-ancien, même si un nom commence comme l’autre.

Exemple détaillé : dossier /travail/bac

DemandéVa vraiment àDedans ?
a.txt/travail/bac/a.txtOui
../dehors/s.txt/travail/dehors/s.txtNon
/etc/hosts/etc/hostsNon
../bac-ancien/x.txt/travail/bac-ancien/x.txtNon
raccourci/s.txt/travail/dehors/s.txtNon : raccourci est un lien

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

Une erreur courante

Attention

Beaucoup pensent qu’enlever les deux points d’un chemin le rend sûr. Ce n’est pas vrai. Un lien dans le dossier peut pointer dehors : le texte a l’air sûr et le vrai endroit ne l’est pas. Vérifiez toujours le vrai chemin, jamais le texte.

Si vous ne pouvez pas résoudre, refusez

Un chemin peut pointer vers un dossier qui n’existe pas, ou vers des liens qui bouclent sans fin. Vous ne pouvez pas dire où il va, donc vous ne pouvez pas dire qu’il est sûr.

Refusez-le. Un refus coûte une étape à l’agent. Un oui à tort peut coûter un disque entier.

Écrire l’assistant du modèle, partie 1

Commande
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

Commande
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 vérification du dossier, partie 1

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' 'const fs = require(\"fs\");' 'const path = require(\"path\");' 'const base = path.join(process.cwd(), \"bac-a-sable\");' 'const bac = path.join(base, \"bac\");' 'fs.rmSync(base, { recursive: true, force: true });' 'fs.mkdirSync(bac, { recursive: true });' 'fs.mkdirSync(path.join(base, \"bac-ancien\"));' 'fs.mkdirSync(path.join(base, \"dehors\"));' 'fs.writeFileSync(path.join(bac, \"note.txt\"), \"dedans\");' 'fs.writeFileSync(path.join(base, \"dehors\", \"secret.txt\"), \"secret\");' 'fs.symlinkSync(path.join(base, \"dehors\"), path.join(bac, \"raccourci\"));' > confiner.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 vérification du dossier, partie 2

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const racine = fs.realpathSync(bac);' '// Où va vraiment un chemin : suivre les liens, puis comparer au dossier.' 'function dedans(p) {' '  try {' '    const t = path.resolve(racine, p);' '    const reel = fs.existsSync(t) ? fs.realpathSync(t) : path.join(fs.realpathSync(path.dirname(t)), path.basename(t));' '    return reel === racine || reel.startsWith(racine + path.sep);' '  } catch (e) { return false; }' '}' 'function naif(p) { return path.resolve(racine, p).startsWith(racine); }' >> confiner.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 vérification du dossier, partie 3

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const demandes = [\"note.txt\", \"nouveau.txt\", \"../dehors/secret.txt\", \"/etc/hosts\", \"../bac-ancien/x.txt\", \"raccourci/secret.txt\"];' 'async function main() {' '  demandes.forEach(function (p) { console.log(p + \" texte seul:\" + naif(p) + \" resolu:\" + dedans(p)); });' '  console.log(\"fichiers du bac : \" + fs.readdirSync(bac).sort().join(\" \"));' '  const q = \"En une courte phrase, pourquoi faut-il résoudre un chemin avant de le vérifier ?\";' '  const echantillon = \"Un chemin peut utiliser des points ou un lien pour aller ailleurs, donc on vérifie où il finit vraiment.\";' '  const r = await demander([{ role: \"user\", content: q }], echantillon, 60);' '  console.log(\"le modèle dit : \" + r.texte);' '}' 'main();' >> confiner.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 vérification du dossier

Commande
docker exec -w /home/node/project agent node confiner.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.

Ce que le Run affiche

LigneCe qu’elle montre
note.txt texte seul:true resolu:trueUn fichier dans le dossier
../dehors/secret.txt texte seul:false ...Les deux vérifications repèrent les points
../bac-ancien/x.txt texte seul:true resolu:falseUn nom qui commence pareil trompe le texte
raccourci/secret.txt texte seul:true resolu:falseUn vrai lien qui sort du dossier
fichiers du bac : note.txt raccourciLe Run a créé ces fichiers pour de vrai
le modèle dit : ...Une phrase. Les mots changent à chaque exécution

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

À propos de ce labo et de sa source: Lab agent de code IA : utiliser opencode pour expliquer et corriger du code, et vérifier son travail

Vous enseignez à une classe ? Proposez ce module à vos étudiants avec un guide de l'enseignant, des devoirs corrigés et un laboratoire pour chaque étudiant.