# 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// ◀── maia workflows · workflow · logs · approve · reply · stop · result ``` Sommaire : [1. Préparer les agents](#1-préparer-les-agents) · [2. L'agent parent](#2-lagent-parent) · [3. Les sous-agents](#3-les-sous-agents) · [4. Lancer](#4-lancer-un-workflow) · [5. Autorisations](#5-autorisations-sans-interface) · [6. Superviser](#6-superviser) · [7. Intervenir](#7-intervenir-répondre-arrêter) · [8. Scripts et services](#8-scripts-tâches-planifiées-et-services) · [9. Registre](#9-le-registre) · [10. Limites](#10-limites) · [11. Dépannage](#11-dépannage) · [12. Aide-mémoire](#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) et `examples/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 ```markdown --- 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 `RequestIntervention` et 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/agents` qui contient le parent (pour `X:/projX/.maia/agents/Main/AGENT.md` : tout `X:/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 sauf `omitMd`) ; - 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/tools` et `~/.maia/tools`, `Skill`), limités par `tools:` ; jamais l'outil `Agent` ; - **permissions** : celles des `settings.json` + celles de son en-tête (elles s'ajoutent, un `deny` hérité n'est jamais levé). Ses demandes d'autorisation remontent au workflow et sont traitées selon `permissionMode` (§ 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 `timeout` du workflow qui borne tout) ; - **conversation** : toujours enregistrée dans le registre (`traces/.json`, `-2.json` au 2e lancement…), que `trace` soit à `true` ou 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 ```bash 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 ` | modèle du parent (et donc des sous-agents sans `model`) | | `--name ` | 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) : ```bash 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 dans `INIT_CWD`), pas depuis le dépôt MAIA. `MAIA_CWD=/autre/dossier` force un autre dossier. - Après `pnpm build`, la commande compilée est `node /chemin/vers/maia/packages/cli/dist/index.js run …`, ou `maia 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. - `--detach` fonctionne 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 le `AGENT.md` n'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`, sinon `model` du parent, sinon `DefaultModel` de `~/.maia/settings.json`, sinon le premier modèle de LiteLLM (avec un avertissement). Conseil : fixez `DefaultModel` pour 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** : `0` terminé · `1` en erreur (dont délai dépassé) · `2` mauvaise utilisation · `130` arrêté par `maia 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 ` (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 : ```yaml # 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`](GUIDE_PERMISSIONS.md). ## 6. Superviser ```bash 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 ``` `` 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 `. ## 7. Intervenir : répondre, arrêter ```bash maia approve r1 # autoriser une demande d'autorisation maia approve r1 --deny # la refuser maia reply r3 "Non, on bloque la livraison." # répondre à une demande d'intervention maia stop # arrêter le workflow et ses sous-agents ``` - `maia workflow ` 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 avec `reply`. - 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** ```bash #!/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** ```bash 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) : ```cron 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.json` **de l'utilisateur qui lance la commande** : un service lancé sous un autre compte a son propre `~/.maia`, et ses workflows n'apparaissent que dans **son** `maia workflows`. - Aucun terminal n'est nécessaire : `maia run` et les commandes de supervision fonctionnent sans TTY. - `NO_COLOR=1` supprime les couleurs de l'affichage texte (elles sont déjà absentes hors terminal). ## 9. Le registre Chaque workflow a un dossier dans **`~/.maia/workflows//`** (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.json` et `events.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 `ask` sont 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ègles `allow` (§ 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 ` 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//run.log` ; suivez avec `maia logs -f` | | `workflow « x » introuvable` / « ambigu » | utilisez l'identifiant complet, `last`, ou un morceau plus long | ## 12. Aide-mémoire ```bash # Lancer (ou : pnpm maia run … depuis le dépôt MAIA, pnpm --dir maia run … d'ailleurs) maia run [--task "…"] [--model m] [--name n] [--detach] [--json] [--permission-mode wait|deny|allow] [--timeout 30m] [--quiet] # Superviser maia workflows [--all] [--json] maia workflow [--json] [--watch] maia logs [agent] [-f] maia result # Intervenir maia approve [--deny] maia reply "texte" maia stop ``` | | | | --- | --- | | 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//` | | Niveaux | 1 seul : parent → sous-agents |