Contrôler les relais vers d'autres agents avec des hooks
Ce guide montre comment un agent qui en appelle d'autres (ask_agent, /ask-<nom>, mm --ask) peut vérifier, réécrire ou refuser chaque question avant qu'elle parte, et vérifier ou remplacer chaque réponse quand elle revient. Il suppose un agent avec au moins une entrée dans mmConfigs (voir Laisser l'agent appeler d'autres agents), et que vous savez écrire un hook (voir Protéger l'agent avec des hooks). Les exemples sont en Node.js.
Les hooks vont dans le fichier de l'agent qui appelle. L'agent appelé n'a pas à changer, ni même à le savoir.
Étapes
1. Choisir l'événement
| Vous voulez… | Événement | Réponse |
|---|---|---|
| refuser un relais (un budget, un agent qu'on ne doit pas appeler d'ici) | preAsk |
{"action":"deny","reason":"…"} |
masquer des secrets, ou couper la conversation envoyée avec with_context |
preAsk |
{"prompt":"…","transcript":"…"} |
| donner des consignes permanentes à l'agent appelé (langue, longueur, format) | preAsk |
{"context":"…"} |
| transformer un échec en quelque chose que le modèle qui appelle sait traiter | postAsk |
{"answer":"…"} |
| vérifier le format de la réponse, ajouter une note pour le modèle qui appelle | postAsk |
{"context":"…"} |
| journaliser chaque relais | postAsk |
rien |
2. Écrire le hook
Ce hook preAsk ajoute des consignes permanentes à chaque question envoyée à l'agent expert :
// hooks/house-rules.js
const input = JSON.parse(require("fs").readFileSync(0, "utf8"));
const rules = ["Answer in the language of the question.", "At most 120 words."];
// Via ask_agent, la réponse est lue par le modèle qui appelle, pas par une personne.
if (input.ask.via === "tool") rules.push("No greetings, no questions back.");
console.log(JSON.stringify({ context: rules.join("\n") }));
Et ce hook postAsk journalise le relais et, quand l'agent appelé a échoué, remplace l'erreur par une consigne pour le modèle qui appelle :
// hooks/after-ask.js
const fs = require("fs");
const path = require("path");
const input = JSON.parse(fs.readFileSync(0, "utf8"));
const line = { agent: input.ask.agent, via: input.ask.via, prompt: input.ask.prompt, durationMs: input.durationMs, error: input.error };
fs.mkdirSync(path.join(input.cwd, ".mm"), { recursive: true });
fs.appendFileSync(path.join(input.cwd, ".mm", "ask-audit.jsonl"), JSON.stringify(line) + "\n");
if (input.error && !(input.answer || "").trim()) {
console.log(JSON.stringify({ answer: `The ${input.ask.agent} agent is unavailable. Tell the user in one sentence; do not answer yourself.` }));
}
3. Les essayer sans mm
echo '{"event":"preAsk","cwd":"/tmp","ask":{"agent":"expert","via":"tool","prompt":"Qu'"'"'est-ce qu'"'"'un inode ?"}}' \
| node hooks/house-rules.js
Vous devriez voir :
{"context":"Answer in the language of the question.\nAt most 120 words.\nNo greetings, no questions back."}
4. Les déclarer dans le fichier de l'agent qui appelle
mmConfigs:
expert:
path: agent.expert.yaml
description: Technical expert. For every technical question.
hooks:
preAsk:
- matcher: ^expert$
command: node "$MM_CONFIG_DIR/hooks/house-rules.js"
postAsk:
- command: node "$MM_CONFIG_DIR/hooks/after-ask.js"
matcher est une expression régulière sur le nom de l'agent appelé ; sans lui, le hook s'exécute pour tous les agents.
5. Démarrer l'agent et vérifier
mm agent.yaml
La bannière se termine par hooks: preAsk×1 postAsk×1. Posez une question que l'agent transmet, puis regardez .mm/ask-audit.jsonl : une ligne par relais, avec "via":"tool". Tapez /ask-expert <question> et la ligne indique "via":"command".
Variantes
- Un budget par question. Déclarez le même script sur
preGeneration(remise à zéro d'un fichier compteur) et surpreAsk(comptage, etdenyau-delà de la limite). Ne comptez que"via":"tool": un relais tapé par l'utilisateur, ou lancé par un script, n'est pas le fait du modèle. - Masquer des secrets. Sur
preAsk, réécrivezinput.ask.promptetinput.ask.transcriptet renvoyez les deux."transcript":""n'envoie aucune conversation. - Un garde-fou. Mettez
onError: failsur un hookpreAskqui ne doit pas être sauté : quand il ne peut pas s'exécuter, le relais est refusé. - Avec
yolo: false, l'utilisateur approuve d'abord l'appelask_agent;preAsks'exécute après cette approbation, sur la question écrite par le modèle. mm --askdepuis une skill. Les hooks du fichier qu'il lit s'exécutent aussi ("via":"cli"). Avec un hookpostAsk, stdout porte la réponse une fois les hooks exécutés : le script appelant lit le remplacement.- Les hooks propres à l'agent appelé. Il exécute les hooks de son propre fichier, qui voient
"asked":{"depth":1}. Servez-vous-en pour ce qui appartient à cet agent quel que soit l'appelant ; gardezpreAsk/postAskpour ce qui appartient à ce relais.
Voir aussi
- Tous les champs et leurs effets : Référence des hooks
- Pourquoi ces hooks appartiennent à l'agent qui appelle : Les hooks — explication
- Un exemple complet :
demos/hooks-ask/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
|