24 KiB
Guide des agents autonomes (workflows) de MAIA
Un workflow fait travailler des agents sans interface : un agent parent (l'orchestrateur) reçoit une tâche, la découpe, confie des morceaux à des sous-agents, puis rend un résultat. On le lance en ligne de commande, à la main, depuis un script, une tâche planifiée ou un service, et on interroge MAIA pour savoir où il en est.
maia run …/Main/AGENT.md --task "…"
│
▼
┌───────────────┐ outil Agent ┌──────────────┐
│ agent parent │ ───────────────▶ │ sous-agent │ (un processus par lancement,
│ (Main) │ ◀─── résumé ──── │ (reviewer) │ en parallèle si besoin)
└───────────────┘ └──────────────┘
│ états, journal, demandes, résultat
▼
~/.maia/workflows/<id>/ ◀── maia workflows · workflow · logs · approve · reply · stop · result
Sommaire : 1. Préparer les agents · 2. L'agent parent · 3. Les sous-agents · 4. Lancer · 5. Autorisations · 6. Superviser · 7. Intervenir · 8. Scripts et services · 9. Registre · 10. Limites · 11. Dépannage · 12. Aide-mémoire
1. Préparer les agents
Les agents sont des fichiers AGENT.md, un dossier par agent :
X:/projX/
├── MAIA.md ← contexte du projet (lu par les agents, sauf omitMd: true)
└── .maia/
├── settings.json ← permissions du projet (allow / ask / deny)
└── agents/
├── Main/AGENT.md ← agent PARENT (orchestrateur)
├── reviewer/AGENT.md ← sous-agent du projet
└── test_runner/AGENT.md ← sous-agent du projet
~/.maia/
├── settings.json ← LiteLLM, DefaultModel, permissions globales
└── agents/
└── doc_checker/AGENT.md ← sous-agent GLOBAL (disponible pour tous les projets)
- Créer un agent : dans MAIA interactif,
/createAgent(niveau projet ou global) copie un modèle commenté ; il suffit de remplir la description et les sections. Exemples prêts à copier :examples/agents/release_check(parent) etexamples/agents/code_reviewer(sous-agent). - Le nom de l'agent (
name:) doit être celui de son dossier. - Un agent parent est un agent comme les autres : même format, mêmes champs. Ce qui en fait un parent, c'est qu'on le lance avec
maia run. Le même fichier peut servir de sous-agent dans un autre workflow.
2. L'agent parent
---
name: Main
description: Vérifie qu'une branche est prête à être livrée.
model: sonnet # facultatif (voir § 4, « Modèle »)
effort: high # étapes maximales : low 10 · medium 25 · high 50
timeout: 60m # facultatif : durée maximale du workflow (90s, 30m, 2h, 1d)
permissionMode: wait # facultatif : wait (défaut) | deny | allow (voir § 5)
permissions: # facultatif : règles ajoutées à celles des settings.json
allow: [Agent, Read, Grep, "Bash(git diff *)"]
---
# Instructions system
Tu es un orchestrateur de vérification avant livraison. Tu ne relis pas le code toi-même :
tu délègues à tes sous-agents et tu consolides leurs résultats.
# Missions
1. Lance reviewer et doc_checker en parallèle sur les fichiers modifiés (git diff --name-only main...HEAD).
2. Si reviewer ne signale aucun problème CRITICAL, lance test_runner.
3. Consolide les trois rapports.
# Constraints (optionnelle)
- ne modifie aucun fichier, ne fais ni commit ni push
- si un problème CRITICAL est trouvé : ne conclus pas seul, demande l'intervention de l'équipe sécurité
# Output
- Verdict : GO ou NO-GO
- Problèmes par gravité, avec fichier et ligne
- Décision de l'équipe sécurité, s'il y en a eu une
Champs de l'en-tête
| Champ | Rôle pour le parent |
|---|---|
name, description |
obligatoires (comme tout agent) |
model |
modèle du parent ; c'est aussi celui que ses sous-agents héritent |
effort |
nombre maximal d'étapes (un appel au modèle + ses outils) : 10 / 25 / 50. Au-delà, le parent rédige son résultat avec ce qu'il a. |
timeout |
durée maximale du workflow, sous-agents compris ; dépassée : arrêt, état « en erreur ». Sans ce champ : pas de limite. |
permissionMode |
traitement des demandes d'autorisation sans interface (§ 5) ; --permission-mode l'emporte |
permissions |
règles allow / ask / deny ajoutées à celles des settings.json (un deny ou ask des settings n'est jamais levé) |
tools |
restreint les outils du parent ; l'outil Agent et l'outil RequestIntervention restent toujours disponibles |
skills, omitMd |
comme pour un sous-agent (skills autorisés ; omitMd: true = sans MAIA.md / CLAUDE.md) |
Sections : # Instructions system, # Missions, # Output sont obligatoires ; # Constraints est facultative. Le résultat final du workflow est la dernière réponse du parent, au format de la section Output.
Bien écrire un parent
- Nommez les sous-agents dans les missions et les contraintes (« lance reviewer… ») : c'est ainsi que le parent sait qui appeler, et les agents cités apparaissent dès le départ en « non exécuté » dans le suivi.
- Parallèle ou séquence : écrivez-le. « Lance A et B en parallèle » → les deux sous-agents tournent en même temps ; « Si A …, lance C » → C attend le résultat de A.
- Contraintes qui demandent un humain : écrivez « demande l'intervention de … ». Le parent utilise alors l'outil
RequestInterventionet le workflow passe en attente jusqu'à votre réponse (§ 7). - Un sous-agent ne voit rien de la conversation du parent : le parent doit tout mettre dans la tâche qu'il confie (fichiers, objectif, contraintes). Rappelez-le dans les instructions si les sous-agents manquent de contexte.
3. Les sous-agents
Quels sous-agents le parent peut lancer
- tous ceux du dossier
.maia/agentsqui contient le parent (pourX:/projX/.maia/agents/Main/AGENT.md: toutX:/projX/.maia/agents/) ; - tous les agents globaux (
~/.maia/agents/) ; - à nom égal, celui du dossier du parent l'emporte ; le parent ne se lance jamais lui-même ;
- les agents d'un autre projet ne sont pas visibles.
Comment ils s'exécutent
- chaque lancement = un processus séparé, avec une conversation vide : le sous-agent ne connaît que la tâche reçue et son propre
AGENT.md(plus MAIA.md saufomitMd) ; - seul son résumé final (section Output) revient au parent ;
- modèle : celui du parent, sauf s'il a son propre champ
model(nom exact ou morceau :haiku→ premier modèle LiteLLM dont le nom contient « haiku ») ; - outils : ceux de l'agent principal (intégrés, personnalisés de
./.maia/toolset~/.maia/tools,Skill), limités partools:; jamais l'outilAgent; - permissions : celles des
settings.json+ celles de son en-tête (elles s'ajoutent, undenyhérité n'est jamais levé). Ses demandes d'autorisation remontent au workflow et sont traitées selonpermissionMode(§ 5), attribuées au sous-agent dans le suivi ; - étapes : selon son
effort(10 / 25 / 50) ; s'il les épuise, on lui demande son résumé ; - durée : pas de délai propre dans un workflow (c'est le
timeoutdu workflow qui borne tout) ; - conversation : toujours enregistrée dans le registre (
traces/<agent>.json,<agent>-2.jsonau 2e lancement…), quetracesoit àtrueou non ; - un sous-agent ne peut pas poser de question : devant un cas prévu par ses
Constraints, il s'arrête sur ce point et le signale dans son résumé ; c'est au parent de demander l'intervention.
Un bon sous-agent a une description claire (le parent choisit d'après elle), une mission étroite, une section Output structurée que le parent pourra consolider, et des tools / permissions limités au nécessaire (ex. tools: Read, Grep, Glob pour un relecteur).
4. Lancer un workflow
maia run X:/projX/.maia/agents/Main/AGENT.md --task "Vérifie la branche feature/x"
maia run ./.maia/agents/Main/AGENT.md "Vérifie la branche feature/x" # la tâche peut suivre le fichier
maia run ./.maia/agents/Main/AGENT.md --detach --name livraison-nuit # en arrière-plan
| Option | Effet |
|---|---|
--task "…" |
la tâche du parent ; sans tâche : « exécute ta mission telle qu'elle est décrite » |
--model <spec> |
modèle du parent (et donc des sous-agents sans model) |
--name <nom> |
nom du workflow, utilisable à la place de l'identifiant |
--detach / -d |
lance en arrière-plan : affiche l'identifiant et rend la main tout de suite |
--json |
résultat en JSON sur la sortie standard |
--permission-mode wait|deny|allow |
remplace permissionMode de l'en-tête |
--timeout 30m |
remplace timeout de l'en-tête |
--quiet / -q |
sans la progression |
Avec pnpm (depuis le dépôt MAIA, sans rien installer ni compiler : le script maia exécute les sources avec tsx) :
cd /chemin/vers/maia
pnpm maia run X:/projX/.maia/agents/Main/AGENT.md --task "Vérifie la branche feature/x"
pnpm maia workflows
pnpm maia workflow last --watch
pnpm maia approve last r1
# depuis le dossier du projet, sans s'y déplacer dans le dépôt MAIA :
cd X:/projX
pnpm --dir /chemin/vers/maia maia run ./.maia/agents/Main/AGENT.md --detach
pnpm --dir /chemin/vers/maia maia logs last -f
-
pnpm dev run …fonctionne aussi (même script) ; un--après le nom du script (pnpm maia -- run …) est accepté et ignoré. -
Les chemins relatifs (
./.maia/agents/Main/AGENT.md) sont résolus depuis le dossier où vous avez tapé la commande (pnpm le transmet dansINIT_CWD), pas depuis le dépôt MAIA.MAIA_CWD=/autre/dossierforce un autre dossier. -
Après
pnpm build, la commande compilée estnode /chemin/vers/maia/packages/cli/dist/index.js run …, oumaia run …si elle est installée dans le PATH (voir le README, « Lancer MAIA depuis un autre dossier »). Pour une tâche planifiée ou un service, préférez la version compilée : elle démarre plus vite et ne dépend pas de tsx. -
--detachfonctionne aussi avec pnpm : le processus détaché est relancé avec le même interpréteur (tsx compris). -
Dossier de travail : le projet qui contient
.maia/agents(X:/projX), quel que soit le dossier d'où vous lancez. Si leAGENT.mdn'est pas dans un dossier.maia/agents, c'est le dossier de lancement, et les sous-agents disponibles sont ceux du dossier qui contient le dossier de l'agent, plus les globaux. -
Modèle :
--model, sinonmodeldu parent, sinonDefaultModelde~/.maia/settings.json, sinon le premier modèle de LiteLLM (avec un avertissement). Conseil : fixezDefaultModelpour les workflows planifiés. -
Au premier plan, la progression s'affiche sur la sortie d'erreur et le résultat sur la sortie standard :
Workflow wf_20261027_000558_3f50 — suivi : maia workflow wf_20261027_000558_3f50
[00:05:58] workflow › Workflow wf_… démarré : agent Main, modèle litellm:sonnet, autorisations « wait », dossier X:/projX
[00:05:58] Main › Sous-agents disponibles : doc_checker, reviewer, test_runner
[00:05:59] reviewer › en exécution
[00:06:00] reviewer › terminé
…
VERDICT GO — …
- Codes de sortie :
0terminé ·1en erreur (dont délai dépassé) ·2mauvaise utilisation ·130arrêté parmaia stop. --json:{ "id", "state", "label", "exitCode", "result", "error" }. Avec--detach --json:{ "id", "pid" }.
5. Autorisations sans interface
Les règles des settings.json (et de l'en-tête de l'agent) s'appliquent comme en mode interactif : allow passe sans demander, deny refuse. Ce qui change, c'est une demande ask (ou un outil sans règle, qui demande par défaut) : personne n'est là pour répondre « o/n ». permissionMode décide :
| Mode | Une demande ask est… |
|---|---|
wait (défaut) |
enregistrée ; l'agent passe en attente jusqu'à maia approve <id> <demande> (ou --deny). Le travail déjà fait est conservé. |
deny |
refusée aussitôt ; le modèle en est informé et continue autrement |
allow |
acceptée aussitôt — à réserver à un environnement isolé (conteneur, machine jetable) |
Important : l'outil Agent demande une autorisation par défaut. Sans règle, chaque lancement de sous-agent attend donc un maia approve. Pour un workflow qui doit tourner seul, autorisez au moins le lancement des sous-agents :
# en-tête de l'agent parent
permissions:
allow: [Agent] # tous ses sous-agents
# ou, plus précis : allow: ["Agent(reviewer)", "Agent(doc_checker)"]
ou dans X:/projX/.maia/settings.json : "permissions": { "allow": ["Agent", "Read", "Grep", "Glob"] }.
Recette pour un workflow de nuit : des règles allow précises pour ce que le workflow doit faire (lecture, Bash(git diff *), Bash(npm test)…), des deny pour ce qui ne doit jamais arriver (Bash(git push *), Bash(rm *)), et wait pour le reste : seules les actions imprévues attendront une réponse. Syntaxe des règles : GUIDE_PERMISSIONS.md.
6. Superviser
maia workflows # workflows en cours
maia workflows --all # tous, y compris terminés et en erreur
maia workflow last # le plus récent
maia workflow livraison-nuit # par nom (--name)
maia workflow 3f50 --watch # par morceau d'identifiant, rafraîchi toutes les 2 s jusqu'à la fin
maia logs 3f50 -f # journal en continu
maia logs 3f50 reviewer # journal d'un seul agent
maia result 3f50 # résultat final
<id> accepte : l'identifiant complet, un morceau unique de celui-ci (3f50), le nom donné par --name (le plus récent s'il y en a plusieurs), ou last.
États
| État | Pour un agent | Pour le workflow |
|---|---|---|
non exécuté |
cité par le parent mais pas encore lancé | créé, pas encore démarré |
en exécution |
il travaille | au moins un agent travaille |
en attente |
il attend une réponse (autorisation ou intervention) | au moins un agent attend |
terminé |
il a rendu son résultat | le parent a rendu le résultat |
en erreur |
échec, délai, arrêt, ou workflow interrompu pendant qu'il tournait | échec du parent, délai, maia stop, ou processus disparu |
Un sous-agent en erreur ne fait pas échouer le workflow : le parent en est informé et décide (le workflow peut être « terminé » avec un agent « en erreur »).
Exemple de maia workflow
Workflow wf_20261027_000558_3f50 : en attente
Agent parent : Main (X:/projX/.maia/agents/Main/AGENT.md)
Modèle : litellm:sonnet · autorisations : wait · durée : 2 min 10 s
Dossier : X:/projX
Tâche : Vérifie la branche feature/x
Agents :
Main en attente parent · 2 min 10 s · litellm:sonnet
doc_checker terminé 41 s · litellm:haiku
reviewer en exécution 1 min 02 s · litellm:sonnet
test_runner non exécuté
Demandes en attente :
r3 Main (intervention : équipe sécurité) : Faille CRITICAL dans auth.ts:42, livre-t-on quand même ?
→ maia reply wf_20261027_000558_3f50 r3 "réponse"
Dans MAIA interactif : /workflows (en cours), /workflows all, /workflow <id>.
7. Intervenir : répondre, arrêter
maia approve <id> r1 # autoriser une demande d'autorisation
maia approve <id> r1 --deny # la refuser
maia reply <id> r3 "Non, on bloque la livraison." # répondre à une demande d'intervention
maia stop <id> # arrêter le workflow et ses sous-agents
maia workflow <id>affiche chaque demande en attente avec la commande exacte pour y répondre.- Chaque demande reçoit une seule réponse ; une autorisation se règle avec
approve, une intervention avecreply. - Le workflow lit les réponses et les demandes d'arrêt toutes les secondes.
- Stop : le tour en cours s'interrompt, les sous-agents en cours reçoivent l'ordre de s'arrêter, le workflow passe « en erreur (arrêté) », code 130. Ce qui a déjà été fait (fichiers, journal, traces) est conservé, mais le workflow ne reprend pas : relancez-en un nouveau.
8. Scripts, tâches planifiées et services
Script : lancer et attendre
#!/usr/bin/env bash
cd /srv/projX
maia run .maia/agents/Main/AGENT.md --task "Vérifie la branche $BRANCH" --json --permission-mode deny > resultat.json
case $? in
0) echo "OK : $(jq -r .result resultat.json)" ;;
130) echo "Arrêté" ;;
*) echo "Échec : $(jq -r .error resultat.json)"; exit 1 ;;
esac
Script : lancer en arrière-plan puis surveiller
id=$(maia run .maia/agents/Main/AGENT.md --detach --json | jq -r .id)
while :; do
state=$(maia workflow "$id" --json | jq -r .state) # pending | running | waiting | done | error
[ "$state" = done ] || [ "$state" = error ] && break
[ "$state" = waiting ] && maia workflow "$id" | sed -n '/Demandes en attente/,$p' | mail -s "MAIA attend" moi@exemple.fr
sleep 60
done
maia result "$id"
Le JSON utilise des codes stables (pending, running, waiting, done, error) et le libellé français dans label : testez les codes, affichez les libellés.
Tâche planifiée (cron, chaque nuit à 2 h 13) :
13 2 * * * cd /srv/projX && /usr/local/bin/maia run .maia/agents/Main/AGENT.md --name nuit --quiet >> /var/log/maia-nuit.log 2>&1
Sous Windows, le Planificateur de tâches lance la même commande (maia run X:\projX\.maia\agents\Main\AGENT.md --name nuit --quiet).
Service (systemd, en tant qu'utilisateur maia) : le service lance maia run … au premier plan (pas --detach : systemd suit lui-même le processus) ; pensez à WorkingDirectory= et aux variables d'environnement utiles (LITELLM_API_KEY, variables ${…} de mcp.json).
À savoir pour l'automatisation
- MAIA lit
~/.maia/settings.json,~/.maia/agents,~/.maia/mcp.jsonde l'utilisateur qui lance la commande : un service lancé sous un autre compte a son propre~/.maia, et ses workflows n'apparaissent que dans sonmaia workflows. - Aucun terminal n'est nécessaire :
maia runet les commandes de supervision fonctionnent sans TTY. NO_COLOR=1supprime les couleurs de l'affichage texte (elles sont déjà absentes hors terminal).
9. Le registre
Chaque workflow a un dossier dans ~/.maia/workflows/<id>/ (registre global, pour tous les projets) :
| Fichier | Contenu |
|---|---|
workflow.json |
agent parent, tâche, modèle, dossier, mode d'autorisation, pid, dates, état, erreur |
agents.json |
état de chaque agent (modèle, nombre de lancements, durée, erreur) |
events.jsonl |
journal horodaté, une ligne JSON par évènement (ce qu'affiche maia logs) |
requests/ |
r1.json… : demandes ; r1.answer.json : réponse déposée par approve / reply |
traces/ |
conversation complète de chaque agent (Main.json, reviewer.json, reviewer-2.json…) |
result.md |
résultat final du parent |
run.log |
sortie du processus en mode --detach |
stop |
présent si maia stop a été demandé |
- Pas de serveur : les commandes lisent ces fichiers. Seul le processus du workflow écrit
workflow.json,agents.jsonetevents.jsonl(écritures atomiques) ; les réponses et l'arrêt sont déposés dans des fichiers à part. - Nettoyage : rien n'est supprimé automatiquement. Supprimez les dossiers des anciens workflows à la main (ils sont nommés par date :
wf_AAAAMMJJ_HHMMSS_xxxx). - Les traces contiennent tout ce que les agents ont lu (code, sorties de commandes) : traitez ce dossier comme sensible.
10. Limites
- Un seul niveau d'agents : le parent lance des sous-agents, un sous-agent ne lance jamais d'agent (il n'a pas l'outil
Agent). Pour une chaîne plus longue, c'est le parent qui enchaîne les étapes. - Le parent ne voit que les résumés de ses sous-agents, pas leur conversation (elle est dans
traces/). - Une demande d'autorisation à la fois : même avec des sous-agents en parallèle, les demandes
asksont posées l'une après l'autre (la suivante apparaît quand la précédente a reçu sa réponse). D'où l'intérêt des règlesallow(§ 5). - Seul le parent peut demander une intervention (
RequestIntervention) ; un sous-agent signale le besoin dans son résumé. - Pas de reprise : un workflow arrêté, en erreur, ou dont le processus a disparu (redémarrage de la machine, fermeture de session) ne reprend pas ; il apparaît « en erreur » et il faut en relancer un.
- Pas de compactage du contexte pour le parent ni pour les sous-agents : une très longue conversation peut dépasser la fenêtre du modèle. Gardez des missions courtes et des sous-agents qui résument.
- Les sous-agents n'ont pas les outils MCP (le parent les a, si les serveurs sont activés pour le projet).
- Pas d'API HTTP : la supervision passe par la ligne de commande et les fichiers du registre, sur la même machine et pour le même utilisateur.
- Pas de nettoyage automatique du registre.
- Le délai par sous-agent n'existe pas dans un workflow : seul
timeout(du workflow) borne la durée totale. - Les sous-agents d'autres projets ne sont pas accessibles ; partagez un agent en le plaçant dans
~/.maia/agents.
11. Dépannage
| Symptôme | Cause probable / solution |
|---|---|
| Le workflow reste « en attente » dès le début | le lancement d'un sous-agent demande une autorisation : maia workflow <id> puis maia approve, ou ajoutez allow: [Agent] (§ 5) |
| « Agent parent invalide » | section obligatoire manquante, name différent du dossier, description absente : le message dit laquelle |
| « Aucun sous-agent disponible » | le AGENT.md n'est pas dans un dossier .maia/agents, ou ce dossier ne contient que le parent ; vérifiez aussi ~/.maia/agents |
| Le parent ne lance pas un sous-agent attendu | son nom n'apparaît pas dans les missions, ou sa description ne correspond pas à la tâche ; nommez-le explicitement |
| « en erreur — processus disparu » | le processus a été tué (machine éteinte, session fermée, kill) ; relancez. Pour un lancement durable, utilisez un service ou une tâche planifiée |
| « Aucun modèle indiqué… » | ni --model, ni model, ni DefaultModel, et LiteLLM injoignable : vérifiez ~/.maia/settings.json |
Rien ne s'affiche avec --detach |
normal : la sortie va dans ~/.maia/workflows/<id>/run.log ; suivez avec maia logs <id> -f |
workflow « x » introuvable / « ambigu » |
utilisez l'identifiant complet, last, ou un morceau plus long |
12. Aide-mémoire
# Lancer (ou : pnpm maia run … depuis le dépôt MAIA, pnpm --dir <maia> maia run … d'ailleurs)
maia run <AGENT.md> [--task "…"] [--model m] [--name n] [--detach] [--json]
[--permission-mode wait|deny|allow] [--timeout 30m] [--quiet]
# Superviser
maia workflows [--all] [--json]
maia workflow <id> [--json] [--watch]
maia logs <id> [agent] [-f]
maia result <id>
# Intervenir
maia approve <id> <rN> [--deny]
maia reply <id> <rN> "texte"
maia stop <id>
| Sous-agents disponibles | dossier .maia/agents du parent + ~/.maia/agents |
| Modèle | --model > model du parent > DefaultModel > 1er modèle LiteLLM ; sous-agents : celui du parent sauf model |
| Autorisations | wait (défaut) · deny · allow ; pensez à allow: [Agent] |
| États | non exécuté · en exécution · en attente · terminé · en erreur |
| Codes de sortie | 0 terminé · 1 erreur · 2 utilisation · 130 arrêté |
| Registre | ~/.maia/workflows/<id>/ |
| Niveaux | 1 seul : parent → sous-agents |