Assembler l’équipe
Une équipe de recherche d’agents est un orchestrateur, quelques exécutants, un contrôle de chaque résultat et une règle d’arrêt. Cela compte, car chaque élément vu jusqu’ici a un coût, et une équipe sans limite n’a pas de fin.
Cette leçon relie le module en une seule boucle, avec un budget de jetons.
À la fin de cette leçon
Remarque
Je peux faire tourner un orchestrateur et des exécutants avec un budget, un contrôle et une règle d’arrêt.
Mots-clés
| Mot | Ce que ça veut dire |
|---|---|
| Budget | le maximum de jetons qu’une exécution entière peut dépenser |
| Règle d’arrêt | un test qui termine l’exécution avant la fin des consignes |
| Accepté | un résultat qui a passé tous les contrôles |
| Refusé | un résultat écarté, avec ses raisons |
| Dépensé | les jetons utilisés jusqu’ici, bons résultats et mauvais |
| Assez | le nombre de résultats acceptés qui termine l’exécution |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une boucle, trois limites
L’orchestrateur prend les consignes dans l’ordre. Avant chacune, il pose deux questions. En ai-je déjà assez ? Cette consigne pourrait-elle me faire dépasser le budget ?
Puis il interroge l’exécutant et vérifie le résultat. Chaque réponse se paie, donc la dépense compte les bonnes comme les mauvaises.
Exemple travaillé : un tour de boucle
| Étape | Question | Si oui |
|---|---|---|
| 1 | Assez de résultats sont-ils acceptés ? | Arrêt, raison assez |
| 2 | Dépense plus limite de cette consigne au-dessus du budget ? | Arrêt, raison budget |
| 3 | Interroger l’exécutant, ajouter ses jetons à la dépense | Toujours, bon ou mauvais |
| 4 | Le résultat passe-t-il le contrôle ? | L’accepter, sinon le refuser avec ses raisons |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Pourquoi une limite passe avant l’appel
Vérifier le budget après un appel est trop tard : les jetons sont partis. La boucle réserve donc le pire cas. Elle prend la limite de la consigne elle-même, et s’arrête si elle ne tiendrait pas.
Ce contrôle ne peut pas arrêter un exécutant qui dépasse sa propre limite. C’est ce qui arrive dans le premier Run. La même idée protège le temps et le nombre de tours.
Une erreur fréquente
Attention
Beaucoup ne comptent que les bons résultats dans le budget. Un exécutant qui échoue lit et écrit quand même des jetons. Comptez tout ce que l’exécution a demandé, sinon la facture ne correspondra pas au budget.
É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 l’équipe de recherche, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' 'const CONSIGNES = [' ' { id: \"b1\", question: \"Combien de visiteurs veulent l’ouverture le dimanche ?\", sources: [\"s1\"], max: 900 },' ' { id: \"b2\", question: \"Que coûterait un dimanche en temps de personnel ?\", sources: [\"s2\"], max: 900 },' ' { id: \"b3\", question: \"Quelle est l’affluence un jour de week-end ?\", sources: [\"s3\"], max: 900 },' '];' '// Les réponses de trois exécutants scriptés, enregistrées.' 'const ENREGISTRE = {' ' b1: { reponse: \"212 visiteurs sur 400 la veulent\", sources: [\"s1\"], jetons: 700 },' ' b2: { reponse: \"108 euros chaque dimanche\", sources: [\"s2\"], jetons: 850 },' ' b3: { reponse: \"310 visites un samedi\", sources: [\"s3\"], jetons: 1200 },' '};' > equipe.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’équipe de recherche, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function problemes(c, r) {' ' const sortie = [];' ' if (r.reponse.trim().length === 0) sortie.push(\"réponse absente\");' ' r.sources.forEach(function (s) { if (c.sources.includes(s) === false) sortie.push(\"source inconnue \" + s); });' ' if (r.jetons > c.max) sortie.push(\"hors limite\");' ' return sortie;' '}' >> equipe.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’équipe de recherche, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function equipe(budget, assez) {' ' const bilan = { acceptes: [], refuses: [], depense: 0, arret: \"fini\" };' ' for (const c of CONSIGNES) {' ' if (bilan.acceptes.length >= assez) { bilan.arret = \"assez\"; break; }' ' if (bilan.depense + c.max > budget) { bilan.arret = \"budget\"; break; }' ' const r = ENREGISTRE[c.id];' ' bilan.depense += r.jetons;' ' const p = problemes(c, r);' ' if (p.length > 0) bilan.refuses.push(c.id + \" (\" + p.join(\", \") + \")\");' ' else bilan.acceptes.push(r.reponse);' ' }' ' return bilan;' '}' >> equipe.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’équipe de recherche, partie 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' let premier = null;' ' for (const essai of [[2500, 3], [1500, 3], [4000, 2]]) {' ' const b = equipe(essai[0], essai[1]);' ' premier = premier || b;' ' console.log(\"budget \" + essai[0] + \", assez \" + essai[1] + \" : acceptés \" + b.acceptes.length + \", refusés \" + b.refuses.length + \", dépensé \" + b.depense + \", arrêt \" + b.arret);' ' if (b.refuses.length > 0) console.log(\" refusés : \" + b.refuses.join(\" ; \"));' ' }' ' const q = [{ role: \"user\", content: \"Écrivez une phrase à partir de ces constats : \" + premier.acceptes.join(\" ; \") }];' ' const r = await demander(q, \"Les visiteurs veulent l’ouverture le dimanche et elle coûte 108 euros par dimanche.\", 40);' ' console.log(\"Résumé : \" + r.texte.trim());' '}' 'main();' >> equipe.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 l’équipe de recherche
docker exec -w /home/node/project agent node equipe.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.
Les exécutants sont enregistrés
Les trois exécutants sont des réponses enregistrées, avec leur nombre de jetons, pour que chaque étudiant voie les mêmes totaux. Le seul appel en direct rédige un résumé de ce qui a été accepté.
Une vraie équipe enverrait chaque consigne à un modèle, et la boucle autour reste la même.
Une sortie possible
| Ligne | Ce qu’elle montre |
|---|---|
| budget 2500, assez 3 : acceptés 2, refusés 1, dépensé 2750, arrêt fini | La troisième réponse a coûté 1200 au lieu de 900 : la dépense dépasse donc le budget de 2500 |
| refusés : b3 (hors limite) | La raison est gardée |
| budget 1500, assez 3 : acceptés 1, refusés 0, dépensé 700, arrêt budget | La deuxième consigne pouvait ne pas tenir |
| budget 4000, assez 2 : acceptés 2, refusés 0, dépensé 1550, arrêt assez | Deux bons résultats suffisaient |
| Résumé : … | Une phrase qui change à chaque exécution |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.