bots-garden/mini-mepublic Fork
⑂ feature/display-called-hooks
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/mini-me.git
git clone ssh://git@rickub.com/bots-garden/mini-me.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP
🛟 Updated. 5b2f986 · on feature/display-called-hooks · k33g · 7d ago
asking-another-agent.md · 46 lines · 11.1 KBmarkdown
Blame HistoryOpen raw

Interroger un autre agent — explication

De quoi s'agit-il ?

/ask-bob <prompt> permet à l'agent avec qui vous parlez de consulter un autre agent mm : un autre modèle, un autre prompt système, d'autres outils et skills. La réponse de bob est affichée, puis gardée dans votre conversation pour que votre propre modèle s'en serve à votre question suivante. L'outil ask_agent permet à votre modèle de faire de même de lui-même, quand une demande correspond au rôle d'un agent. Cette page explique comment c'est construit et pourquoi.

Pourquoi c'est conçu ainsi

Un autre agent est un autre processus. Bob tourne comme un enfant mm -acp -config <fichier de bob>, le même binaire que l'agent qui l'interroge. Le faire tourner dans le même processus était l'alternative évidente, et la mauvaise : la configuration de mm est une variable globale, lue par le moteur, les outils, la compaction et les sessions, et celle de bob diffère sur tous ces points. Un second moteur dans le même processus aurait d'abord demandé de démanteler cette variable globale partout. Un enfant garde chaque agent entier, avec ses propres outils, skills et serveurs MCP.

Les deux parlent ACP. mm parle déjà l'Agent Client Protocol comme serveur, pour les éditeurs. Interroger bob fait de l'agent qui pose la question un client ACP de son enfant. Le protocole apporte gratuitement ce que lire un terminal n'aurait jamais donné : la réponse de bob sous forme de texte, ses appels d'outils comme des événements structurés, ses demandes de permission comme des requêtes auxquelles on peut répondre, et une façon standard d'annuler. Le serveur ACP de mm n'appelle que deux méthodes du client — les mises à jour de session et les demandes de permission — donc le côté client est petit ; les requêtes de fichiers et de terminal sont refusées comme absentes.

Les permissions de bob, c'est vous qui les donnez. Bob est lancé avec AGENT_YOLO=false : chaque appel qu'il filtrerait devient une demande de permission adressée à votre agent, et c'est VOTRE yolo qui décide : activé, l'appel s'exécute ; désactivé, la question va à la façade que vous utilisez — le dialogue de l'éditeur, la zone de la fenêtre, l'invite 🔐 du mode ligne — exactement comme pour les outils de votre propre agent à cet endroit. Le yolo du fichier de bob ne compte pas tant qu'il vous répond — celui de l'entrée qui le décrit dans VOTRE fichier, si : mmConfigs: {bob: {path: …, yolo: true}} laisse s'exécuter les appels de bob alors que les vôtres demandent toujours (un codeur à qui l'on confie les modifications une fois le passage de main approuvé), yolo: false demande pour chacun de ses appels même quand les vôtres s'exécutent librement. Les outils de bob s'appellent bob/<outil>, donc autoriser le bash de votre agent « pour cette session » n'autorise pas celui de bob. Voir Le mode yolo et les permissions.

La réponse est une paire de messages. Elle entre dans votre historique comme un message utilisateur ([asked bob (fournisseur/modèle): …]) suivi d'un message de l'assistant ([bob's answer] et le texte). Un seul message aurait été plus simple, mais votre question suivante est aussi un message utilisateur, et plusieurs templates de chat (ceux de Gemma, de Mistral) refusent deux messages utilisateur à la suite. Les marques disent qui a dit quoi, pour que votre modèle ne prenne pas la réponse de bob pour la sienne. La réponse est plafonnée comme la sortie d'un outil, à maxOutput caractères : elle atterrit dans la fenêtre de votre modèle.

Chaque demande est une conversation neuve pour bob. Bob ne garde aucune mémoire entre deux demandes. Quand il doit savoir ce que vous faisiez, /ask-with-context-bob envoie une transcription : questions, réponses, une ligne par appel d'outil — jamais les sorties d'outils, qui coûteraient à sa fenêtre ce qu'elles ont déjà coûté à la vôtre, jamais votre prompt système ni le raisonnement. La continuité est explicite, choisie à chaque question, plutôt qu'une seconde conversation qui dérive de son côté.

Un enfant par agent, démarré une fois. Démarrer mm coûte la mise en place de son moteur, la sonde du serveur et ses serveurs MCP. La première demande démarre bob ; les suivantes le réutilisent, chacune avec une nouvelle session. Il s'arrête quand votre agent se termine.

L'enfant est protégé de ce qui est destiné à son parent. Des variables comme AGENT_MODEL, AGENT_PROVIDER ou AGENT_BASE_URL surchargent une configuration ; héritées par bob, elles en feraient silencieusement une copie de vous. Elles sont retirées de son environnement. Bob tourne aussi avec AGENT_SESSIONS=off : il travaille dans votre projet, et ses fichiers de session apparaîtraient sinon dans vos /sessions et /resume.

Votre modèle peut aussi décider : ask_agent. Dès que mmConfigs est renseigné, votre agent reçoit un outil intégré qui interroge l'un des agents et renvoie sa réponse comme résultat de l'outil. Sa description liste les agents avec le rôle de chacun — la description de l'entrée, à défaut l'acp.title de l'agent — et c'est tout ce dont le modèle dispose pour choisir : une description dit donc quand appeler l'agent, pas qui il est. L'outil s'appuie sur le même pool et le même protocole que /ask, à l'intérieur du tour : le texte, les pensées et les appels d'outils de l'agent défilent dans votre façade au fur et à mesure, et la fenêtre le nomme pendant qu'il travaille — son titre et son modèle dans l'en-tête, asked by votre agent, sa réponse étiquetée à son nom. L'appel lui-même est soumis comme bash : avec yolo: false, vous voyez le prompt écrit par votre modèle avant qu'il parte. La réponse est un résultat d'outil, pas une paire /ask dans l'historique : votre modèle la lit et poursuit dans le même tour — la rapporter, la vérifier, confier l'étape suivante à un autre agent.

Les chaînes s'arrêtent. Un modèle qui appelle ask_agent depuis un agent lui-même interrogé pourrait continuer à interroger ; chaque enfant connaît donc sa profondeur (MM_ASK_DEPTH) : votre agent peut interroger bob, bob peut interroger riker, riker ne peut interroger personne — que ce soit par un /ask tapé ou par ask_agent.

La même mécanique répond aux scripts. mm --ask <agent> et mm --ask-with-context <agent> sont les commandes slash de ce qui ne sait que lancer une commande — une skill, un script. Ils démarrent le même enfant en ACP, avec la même protection de l'environnement et la même limite de profondeur, mais la réponse va sur stdout et tout le reste sur stderr, pour que l'appelant récupère la réponse seule. mm exporte MM_BIN et MM_CONFIG vers les commandes de son outil bash, donc une skill appelle « ce mm, avec cette configuration » sans connaître aucun des deux chemins. Deux choses diffèrent des commandes slash, parce que l'appelant est une commande et non une personne. Les appels d'outils de l'agent suivent le yolo de la configuration appelante : autorisés quand il est activé — la personne a déjà validé la commande mm --ask elle-même —, refusés quand il est désactivé, puisque stdin portait la question et que personne n'est là pour répondre. Et mm --ask surveille son processus parent : l'outil bash tue bash quand son budget est écoulé, pas les enfants de bash, et une demande orpheline laissait son agent tourner — et écrire des fichiers — alors que plus personne n'attendait.

Le temps est le point faible. Un modèle local peut mettre une minute à répondre, et une commande bash disposait de 30 secondes ; bashTimeout règle désormais ce budget. Le watchdog de l'agent qui pose la question continue aussi de compter pendant qu'une commande tourne (ticket 57), il faut donc le relever aussi tant que ce ticket n'est pas corrigé. ask_agent fait mieux parce qu'il voit l'agent travailler : chaque événement que l'agent diffuse — du texte, une pensée, un appel d'outil — compte comme un signe de vie pour le watchdog de votre agent. Seul un long silence termine encore le tour : un agent qui charge un gros modèle, ou qui lit un long premier prompt ; watchdogTimeout doit couvrir cette durée.

Alternatives écartées

  • mm -p "<prompt>" en sous-processus. Un processus par question et rien à garder en vie — mais sa sortie est un écran de terminal (réponse, raisonnement, échos d'outils, couleurs, récapitulatif des commandes) qu'il faudrait analyser ; il ne demande jamais de permission, donc bob lancerait des commandes sans contrôle ; on ne peut l'annuler qu'en le tuant.
  • Un second moteur dans le même processus. Rapide, sans processus — au prix de transformer la configuration globale en configuration par agent dans tout le code.
  • Faire répondre le modèle courant tout de suite avec l'apport de bob. L'utilisateur a choisi autrement : la réponse de bob est affichée et gardée, et vous décidez de la question suivante.
  • Garder la conversation de bob d'une demande à l'autre. Deux conversations qui évoluent en parallèle, dont une invisible, sont plus difficiles à suivre qu'une transcription explicite envoyée quand on la demande.
  • Des skills qui lancent mm --ask pour le modèle. Ça marche — une skill par passage de main, la commande dans son texte — et mm --ask reste pour les scripts. Mais le travail de l'agent vous parvient comme la sortie d'une commande bash : pas de nom dans l'en-tête, pas d'étiquette sur sa réponse, ses appels d'outils en lignes de texte. Et un petit coordinateur doit recopier une longue commande à l'identique : mesuré avec Gemma 4 E4B, il a oublié les guillemets et un préfixe AGENT_YOLO=true, si bien que le codeur appelé ne pouvait pas écrire. Un appel d'outil avec un agent et un prompt n'a rien à recopier.
  • Seulement un /ask <nom> générique, y compris dans les éditeurs. Le menu / d'un éditeur complète un nom de commande, jamais son argument ; une commande par agent est la seule façon pour que le menu liste les agents.

Lien avec le reste

  • L'enfant est un agent ACP ordinaire ; le parent réutilise le protocole de l'autre côté.
  • Ses appels d'outils et ses permissions passent par la même interface d'événements que les trois façades implémentent pour les outils de votre propre agent.
  • L'échange fait partie de l'historique comme n'importe quel tour : il est enregistré avec la session, rejoué par /resume, et résumé par la compression du contexte quand il vieillit.
  • Les hooks preAsk et postAsk de l'agent qui interroge s'exécutent autour de chaque relais — /ask, ask_agent et mm --ask : ils peuvent le refuser, réécrire ce qui part, et remplacer ce qui revient. L'enfant n'exécute que les hooks de son propre fichier.
  • L'utiliser : comment interroger un autre agent. Toutes les commandes et messages : référence des commandes slash.
 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
# Interroger un autre agent — explication

## De quoi s'agit-il ?

`/ask-bob <prompt>` permet à l'agent avec qui vous parlez de consulter un autre agent mm : un autre modèle, un autre prompt système, d'autres outils et skills. La réponse de bob est affichée, puis gardée dans votre conversation pour que votre propre modèle s'en serve à votre question suivante. L'outil `ask_agent` permet à votre modèle de faire de même de lui-même, quand une demande correspond au rôle d'un agent. Cette page explique comment c'est construit et pourquoi.

## Pourquoi c'est conçu ainsi

**Un autre agent est un autre processus.** Bob tourne comme un enfant `mm -acp -config <fichier de bob>`, le même binaire que l'agent qui l'interroge. Le faire tourner dans le même processus était l'alternative évidente, et la mauvaise : la configuration de mm est une variable globale, lue par le moteur, les outils, la compaction et les sessions, et celle de bob diffère sur tous ces points. Un second moteur dans le même processus aurait d'abord demandé de démanteler cette variable globale partout. Un enfant garde chaque agent entier, avec ses propres outils, skills et serveurs MCP.

**Les deux parlent ACP.** mm parle déjà l'Agent Client Protocol comme serveur, pour les éditeurs. Interroger bob fait de l'agent qui pose la question un *client* ACP de son enfant. Le protocole apporte gratuitement ce que lire un terminal n'aurait jamais donné : la réponse de bob sous forme de texte, ses appels d'outils comme des événements structurés, ses demandes de permission comme des requêtes auxquelles on peut répondre, et une façon standard d'annuler. Le serveur ACP de mm n'appelle que deux méthodes du client — les mises à jour de session et les demandes de permission — donc le côté client est petit ; les requêtes de fichiers et de terminal sont refusées comme absentes.

**Les permissions de bob, c'est vous qui les donnez.** Bob est lancé avec `AGENT_YOLO=false` : chaque appel qu'il filtrerait devient une demande de permission adressée à votre agent, et c'est VOTRE `yolo` qui décide : activé, l'appel s'exécute ; désactivé, la question va à la façade que vous utilisez — le dialogue de l'éditeur, la zone de la fenêtre, l'invite `🔐` du mode ligne — exactement comme pour les outils de votre propre agent à cet endroit. Le `yolo` du fichier de bob ne compte pas tant qu'il vous répond — celui de l'entrée qui le décrit dans VOTRE fichier, si : `mmConfigs: {bob: {path: …, yolo: true}}` laisse s'exécuter les appels de bob alors que les vôtres demandent toujours (un codeur à qui l'on confie les modifications une fois le passage de main approuvé), `yolo: false` demande pour chacun de ses appels même quand les vôtres s'exécutent librement. Les outils de bob s'appellent `bob/<outil>`, donc autoriser le `bash` de votre agent « pour cette session » n'autorise pas celui de bob. Voir [Le mode yolo et les permissions](yolo-and-permissions.md).

**La réponse est une paire de messages.** Elle entre dans votre historique comme un message utilisateur (`[asked bob (fournisseur/modèle): …]`) suivi d'un message de l'assistant (`[bob's answer]` et le texte). Un seul message aurait été plus simple, mais votre question suivante est aussi un message utilisateur, et plusieurs templates de chat (ceux de Gemma, de Mistral) refusent deux messages utilisateur à la suite. Les marques disent qui a dit quoi, pour que votre modèle ne prenne pas la réponse de bob pour la sienne. La réponse est plafonnée comme la sortie d'un outil, à `maxOutput` caractères : elle atterrit dans la fenêtre de votre modèle.

**Chaque demande est une conversation neuve pour bob.** Bob ne garde aucune mémoire entre deux demandes. Quand il doit savoir ce que vous faisiez, `/ask-with-context-bob` envoie une transcription : questions, réponses, une ligne par appel d'outil — jamais les sorties d'outils, qui coûteraient à sa fenêtre ce qu'elles ont déjà coûté à la vôtre, jamais votre prompt système ni le raisonnement. La continuité est explicite, choisie à chaque question, plutôt qu'une seconde conversation qui dérive de son côté.

**Un enfant par agent, démarré une fois.** Démarrer mm coûte la mise en place de son moteur, la sonde du serveur et ses serveurs MCP. La première demande démarre bob ; les suivantes le réutilisent, chacune avec une nouvelle session. Il s'arrête quand votre agent se termine.

**L'enfant est protégé de ce qui est destiné à son parent.** Des variables comme `AGENT_MODEL`, `AGENT_PROVIDER` ou `AGENT_BASE_URL` surchargent une configuration ; héritées par bob, elles en feraient silencieusement une copie de vous. Elles sont retirées de son environnement. Bob tourne aussi avec `AGENT_SESSIONS=off` : il travaille dans votre projet, et ses fichiers de session apparaîtraient sinon dans vos `/sessions` et `/resume`.

**Votre modèle peut aussi décider : `ask_agent`.** Dès que `mmConfigs` est renseigné, votre agent reçoit un outil intégré qui interroge l'un des agents et renvoie sa réponse comme résultat de l'outil. Sa description liste les agents avec le rôle de chacun — la `description` de l'entrée, à défaut l'`acp.title` de l'agent — et c'est tout ce dont le modèle dispose pour choisir : une description dit donc quand appeler l'agent, pas qui il est. L'outil s'appuie sur le même pool et le même protocole que `/ask`, à l'intérieur du tour : le texte, les pensées et les appels d'outils de l'agent défilent dans votre façade au fur et à mesure, et la fenêtre le nomme pendant qu'il travaille — son titre et son modèle dans l'en-tête, `asked by` votre agent, sa réponse étiquetée à son nom. L'appel lui-même est soumis comme `bash` : avec `yolo: false`, vous voyez le prompt écrit par votre modèle avant qu'il parte. La réponse est un résultat d'outil, pas une paire `/ask` dans l'historique : votre modèle la lit et poursuit dans le même tour — la rapporter, la vérifier, confier l'étape suivante à un autre agent.

**Les chaînes s'arrêtent.** Un modèle qui appelle `ask_agent` depuis un agent lui-même interrogé pourrait continuer à interroger ; chaque enfant connaît donc sa profondeur (`MM_ASK_DEPTH`) : votre agent peut interroger bob, bob peut interroger riker, riker ne peut interroger personne — que ce soit par un `/ask` tapé ou par `ask_agent`.

**La même mécanique répond aux scripts.** `mm --ask <agent>` et `mm --ask-with-context <agent>` sont les commandes slash de ce qui ne sait que lancer une commande — une skill, un script. Ils démarrent le même enfant en ACP, avec la même protection de l'environnement et la même limite de profondeur, mais la réponse va sur stdout et tout le reste sur stderr, pour que l'appelant récupère la réponse seule. mm exporte `MM_BIN` et `MM_CONFIG` vers les commandes de son outil `bash`, donc une skill appelle « ce mm, avec cette configuration » sans connaître aucun des deux chemins. Deux choses diffèrent des commandes slash, parce que l'appelant est une commande et non une personne. Les appels d'outils de l'agent suivent le `yolo` de la configuration appelante : autorisés quand il est activé — la personne a déjà validé la commande `mm --ask` elle-même —, refusés quand il est désactivé, puisque stdin portait la question et que personne n'est là pour répondre. Et `mm --ask` surveille son processus parent : l'outil `bash` tue `bash` quand son budget est écoulé, pas les enfants de bash, et une demande orpheline laissait son agent tourner — et écrire des fichiers — alors que plus personne n'attendait.

**Le temps est le point faible.** Un modèle local peut mettre une minute à répondre, et une commande `bash` disposait de 30 secondes ; `bashTimeout` règle désormais ce budget. Le watchdog de l'agent qui pose la question continue aussi de compter pendant qu'une commande tourne (ticket 57), il faut donc le relever aussi tant que ce ticket n'est pas corrigé. `ask_agent` fait mieux parce qu'il voit l'agent travailler : chaque événement que l'agent diffuse — du texte, une pensée, un appel d'outil — compte comme un signe de vie pour le watchdog de votre agent. Seul un long silence termine encore le tour : un agent qui charge un gros modèle, ou qui lit un long premier prompt ; `watchdogTimeout` doit couvrir cette durée.

## Alternatives écartées

- **`mm -p "<prompt>"` en sous-processus.** Un processus par question et rien à garder en vie — mais sa sortie est un écran de terminal (réponse, raisonnement, échos d'outils, couleurs, récapitulatif des commandes) qu'il faudrait analyser ; il ne demande jamais de permission, donc bob lancerait des commandes sans contrôle ; on ne peut l'annuler qu'en le tuant.
- **Un second moteur dans le même processus.** Rapide, sans processus — au prix de transformer la configuration globale en configuration par agent dans tout le code.
- **Faire répondre le modèle courant tout de suite avec l'apport de bob.** L'utilisateur a choisi autrement : la réponse de bob est affichée et gardée, et vous décidez de la question suivante.
- **Garder la conversation de bob d'une demande à l'autre.** Deux conversations qui évoluent en parallèle, dont une invisible, sont plus difficiles à suivre qu'une transcription explicite envoyée quand on la demande.
- **Des skills qui lancent `mm --ask` pour le modèle.** Ça marche — une skill par passage de main, la commande dans son texte — et `mm --ask` reste pour les scripts. Mais le travail de l'agent vous parvient comme la sortie d'une commande `bash` : pas de nom dans l'en-tête, pas d'étiquette sur sa réponse, ses appels d'outils en lignes de texte. Et un petit coordinateur doit recopier une longue commande à l'identique : mesuré avec Gemma 4 E4B, il a oublié les guillemets et un préfixe `AGENT_YOLO=true`, si bien que le codeur appelé ne pouvait pas écrire. Un appel d'outil avec un `agent` et un `prompt` n'a rien à recopier.
- **Seulement un `/ask <nom>` générique, y compris dans les éditeurs.** Le menu `/` d'un éditeur complète un nom de commande, jamais son argument ; une commande par agent est la seule façon pour que le menu liste les agents.

## Lien avec le reste

- L'enfant est un [agent ACP](../reference/acp.md) ordinaire ; le parent réutilise le protocole de l'autre côté.
- Ses appels d'outils et ses permissions passent par la même interface d'événements que les trois façades implémentent pour les outils de votre propre agent.
- L'échange fait partie de l'historique comme n'importe quel tour : il est enregistré avec la [session](persistent-sessions.md), rejoué par `/resume`, et résumé par la [compression du contexte](context-compression.md) quand il vieillit.
- Les [hooks](hooks.md#des-hooks-autour-des-appels-dagents) `preAsk` et `postAsk` de l'agent qui interroge s'exécutent autour de chaque relais — `/ask`, `ask_agent` et `mm --ask` : ils peuvent le refuser, réécrire ce qui part, et remplacer ce qui revient. L'enfant n'exécute que les hooks de son propre fichier.
- L'utiliser : [comment interroger un autre agent](../how-to/ask-another-agent.md). Toutes les commandes et messages : [référence des commandes slash](../reference/slash-commands.md#interroger-un-autre-agent).