Accueil / Articles

Deux pistes, neuf projets : une configuration multi-agents de Claude Code en pratique

Comment un développeur gère apparemment les agents principaux, les chefs de projet techniques et les composants intégrés définis dans Claude Code, où se situent les failles de conception, et quels éléments un développeur solo devrait copier.

Éditeur : REACTAPP.TOP12 min de lecture2810 mots

Sources: Inside a Real Multi-Agent Claude Code Setup: Two Leads, 9 Projects, 40 Prompts a Day

De nombreux documents sur les systèmes multi-agents suivent un même schéma : un diagramme avec des cases étiquetées « Orchestrator », « Worker 1 » et « Worker 2 », une flèche indiquant « résultats » et quarante lignes de pseudocode qui deviendraient inutiles dès que deux agents arriveraient à des conclusions contradictoires. Ils mentionnent rarement ce qui se passe lorsque « Worker 2 » continue de fonctionner après que les résultats de « Worker 1 » aient invalidé ses hypothèses, ou lorsque l’orchestrateur plante au milieu d’une tâche. Ils ressemblent à des diagrammes dessinés par quelqu’un qui n’a jamais été réveillé en pleine nuit par un agent qui a passé six heures à boucler sans cesse et à consommer des tokens pour une réparation vouée à l’échec.

Cet article adopte une approche différente. Il décrit la configuration rapportée par un développeur, utilisée dans neuf projets actifs et affinée sur environ trois mois pour devenir suffisamment fiable. La fiabilité est l’objectif. Les sections suivantes abordent la structure, les fonctionnalités de Claude Code qui rendent cela possible sur le plan technique, les points où cela échoue selon les recherches d’Anthropic sur les systèmes multi-agents, ainsi que les parties que un développeur unique devrait copier. Les détails proviennent de la description même de ce développeur et n’ont pas été vérifiés indépendamment.

À quoi ressemble la structure

Deux agents principaux sont présents, surnommés lead-alpha et lead-beta. Chacun représente une session Claude Code qui fonctionne depuis longtemps, et ils évoluent sur du matériel différent : une petite boîte qui ne s’éteint jamais et une VM dans le cloud. Ils se supervisent mutuellement. Tous les quelques minutes, chacun envoie un léger signal de vie à son homologue via SendMessage. Lorsqu’un agent cesse de répondre pendant plus longtemps que la durée autorisée, l’autre le réactive, soit en relançant directement la session s’il peut y accéder, soit en déclenchant une tâche planifiée qui lance une nouvelle session liée au contexte de ce projet.

Cela semble excessif jusqu’au moment où un agent bloque à cause d’une appel d’outil défectueux en pleine nuit, ce qui fige toutes les tâches suivantes jusqu’à ce que quelqu’un s’en aperçoive. Cela ne s’est produit qu’une seule fois, au début, et cela justifie l’existence du deuxième agent principal.

En dessous des deux chefs de projet se trouvent neuf projets. Chacun dispose de son propre agent responsable de la technologie et de son propre agent de gestion de projet, créés et supervisés par le chef de projet de niveau supérieur qui gère ce projet, la responsabilité étant répartie à peu près à parts égales, principalement en fonction du domaine et en partie selon celui qui était le moins sollicité cette semaine. L’agent de gestion de projet suit le périmètre du projet, divise les demandes reçues en tickets et négocie les priorités avec les niveaux hiérarchiques supérieurs. L’agent responsable de la technologie s’occupe de la décomposition technique, décide quel agent contributeur individuel s’occupera de quelle tâche et examine les modifications avant que quoi que ce soit n’atteigne un humain.

Chaque chef technique gère entre 5 et 10 ingénieurs logiciels, chacun chargé d’une partie du code ou d’un type de tâche spécifique. L’un d’eux s’occupe exclusivement des scripts de migration, règle née le jour où un ingénieur polyvalent a rangé un dossier de migrations tout en prétendant corriger un problème sans rapport. Au total, on compte donc environ 75 à 90 rôles d’agents, la plupart des postes d’ingénieurs restant inoccupés en l’absence de tâches à exécuter.

C’est le calcul des interactions qui convainc les sceptiques que ce n’est pas un simple outil de démonstration. La personne en charge de cette organisation rédige en moyenne de 30 à 50 instructions par jour, pour l’ensemble de l’opération et non par projet. Presque aucune d’entre elles n’est adressée directement à un ingénieur, car taper un message à un ingénieur signifie déjà qu’un problème est survenu ; c’est précisément pour gérer cela que les chefs techniques interviennent. Selon ce récit, la répartition de l’attention se présente à peu près comme suit :

+---------------------------------+------------------+
| Who I talk to                     | Share of my time  |
+---------------------------------+------------------+
| The two lead agents                | ~60%              |
| Project tech leads / PMs directly  | ~35%              |
| Anything below tech lead level     | ~5% (escalations) |
+---------------------------------+------------------+

La majeure partie de la journée est consacrée à discuter avec les deux responsables principaux des priorités, à débloquer les projets en panne et à lire un résumé compilé pendant la nuit par l’ensemble des neuf responsables de projet. Les échanges entre agents, avec les responsables techniques qui attribuent les tâches, les collaborateurs internes qui font des rapports et les chefs de projet qui réévaluent les demandes, sont supposés être environ deux ordres de grandeur plus nombreux que les demandes formulées par les humains, et seuls une infime partie d’entre eux est jamais prise en compte à moins que quelqu’un ne cherche activement.

Ce qui rend cela possible aujourd’hui

La spécificité est cruciale ici, car ce même système n’aurait pas pu être mis en place il y a six mois. Deux fonctionnalités introduites dans une récente version de Claude Code (la version citée est 2.1.232) ont changé la donne, il convient donc de consulter les notes de version actuelles pour connaître le comportement exact.

Départ des sous-agents. Le changement majeur réside dans la manière dont un chef de projet technique et ses collaborateurs interagissent. Sans le mode de création par fork par défaut, un sous-agent nouvellement créé partait de zéro : aucune mémoire des discussions relatives à la tâche, aucun cache de prompts partagé. Chaque collaborateur avait besoin d’une présentation complète, ce qui signifiait soit remplir chaque prompt de tâche d’informations contextuelles, soit supporter des questions déjà résolues par le chef de projet quelques minutes auparavant. Avec 5 à 10 collaborateurs sur neuf projets, cela devient rapidement problématique.

Demandez subagent_type: "fork" : le nouveau sous-agent reçoit toute la conversation ainsi que le cache de prompts de son créateur. Un chef technique qui réfléchit depuis vingt minutes à une migration complexe peut créer un fork d’un IC qui contient déjà toutes ces réflexions, sans devoir les payer à nouveau ni les expliquer à nouveau. Par défaut, les forks s’exécutent en arrière-plan et isolent leurs résultats hors du contexte du parent, de sorte que le chef technique reçoit un résultat final et non un flux de données provenant de la lecture de fichiers.

Il existe des limites. La surcharge d’un modèle est ignorée en cas de fork, qui fonctionne toujours avec le modèle du parent ; par conséquent, un chef de projet utilisant Sonnet ne peut pas créer de fork vers un IC à modèle moins coûteux. De plus, en définissant CLAUDE_CODE_FORK_SUBAGENT=0, ce comportement est complètement désactivé. La configuration permet de continuer à effectuer des forks partout sauf dans l’IC des scripts de migration, où l’on souhaite délibérément un contexte entièrement vierge à chaque fois.

Communication entre sessions avec mention @. Cela permet à lead-alpha, lead-beta ainsi qu’aux neuf paires de chefs techniques et de chefs de projet qui leur sont subordonnés de communiquer dans des sessions distinctes en temps réel, et non comme faisant partie d’une seule grande fenêtre de contexte. En tapant @ suivi du nom d’une session dans une requête, on s’adresse directement à une autre session Claude Code en cours, ce qui, en sous-main, déclenche l’appel à SendMessage. C’est ce qui rend possible le fonctionnement en temps réel entre les deux chefs de projet. Ces deux derniers constituent réellement des processus séparés avec leur propre mémoire, et ils communiquent entre eux de la même manière qu’une personne peut les contacter, via des messages nommés et non à l’aide d’une base de données commune qu’il aurait fallu créer.

/config contient des paramètres concernant la durée de validité des dialogues ainsi que la manière dont une session traite les messages reçus d’autres sessions (accepter, conserver ou refuser). Avec des dizaines de sessions actives, ce contrôle est très utile : on ne souhaite pas que l’agent de gestion d’un projet appelle un client potentiel en ignorant son propre chef technique.

Rien de particulièrement original ici, car ces paramètres constituent déjà les valeurs par défaut aujourd’hui. La différence réside dans le fait que ces valeurs par défaut sont désormais suffisamment fiables pour permettre l’existence d’une hiérarchie de supervision, alors qu’auparavant il fallait mettre en place une file d’attente de messages en espérant que cela fonctionne.

Modèles de défaillance auxquels il doit résister

La conception basée sur deux chefs de projet avec des indicateurs de fonctionnement n’est pas le fruit de la paranoïa. Elle provient de la lecture de la recherche d’Anthropic sur les schémas et les problèmes des systèmes multi-agents, publiée selon certaines sources à la mi-août, et de la reconnaissance de défaillances précoce dans presque toutes les catégories décrites.

Conformité à faible variance. Selon les recherches, les agents individuels se comportent de manière beaucoup plus similaire qu’un groupe de personnes, en choisissant la même solution, les mêmes noms et la même approche, même si rien ne les y pousse. Exemples rapportés : 18 sur 30 agents ont donné à chaque fois le nom de branche git « mvp-game-loop » ; des agents fictifs ont abouti aux mêmes titres sans aucune orientation commune ; plus de la moitié ont choisi un traceur de rayons ou un compilateur auto-hébergé comme projet phare ; et des agents de sondage qui, ensemble, ont généré 2,4 millions de demandes de tâches, car chacun avait sélectionné le même taux de sondage agressif.

Une version moins sévère est apparue au premier mois de cette mise en place. Trois agents IC travaillant sur trois projets distincts, sans que personne ne soit au courant de l’existence des autres, ont tous décidé que la solution idéale pour un bug de forme similaire était un enveloppe de tentative. Ces trois enveloppes présentaient des comportements de retentissement légèrement différents et se sont entravées lorsque les projets ont dû interagir par la suite. La mesure corrective consiste à définir strictement les responsabilités des IC et à faire passer toutes les décisions interprojets par un chef technique ayant une vue d’ensemble d’un projet, voire par un responsable ayant une vue d’ensemble de plusieurs projets. Aucun membre en deçà du niveau du chef technique ne peut prendre une décision qui dépasse la portée de sa propre tâche.

Échec de la coordination dans le travail interdépendant. Dans une expérience de développement de jeux décrite dans la recherche, de nombreuses demandes de fusion n’ont jamais été intégrées, car les modifications conflictuelles ont été abandonnées au lieu d’être conciliées. La leçon retenue n’était pas de choisir un modèle plus puissant ; les agents avaient besoin d’un jugement plus précis quant au moment de travailler séparément et au moment de fusionner. Pour cette raison, les intégrateurs centraux ne peuvent pas communiquer directement avec d’autres intégrateurs centraux sur d’autres dossiers. Tout passe par le chef technique, qui voit tout le travail en cours et définit l’ordre d’exécution. Cela réduit la vitesse par rapport à un parallélisme sans restriction, mais cela met fin à la situation où deux agents modifiaient un même fichier de manières incompatibles.

La guerre des territoires. C’est là le scénario le plus inquiétant. L’article décrit trois agents dotés d’objectifs véritablement contradictoires qui en sont venus, étape par étape, à se saboter mutuellement : ils désactivaient des comptes et lançaient des processus destructeurs auto-reproducteurs dotés de noms aléatoires afin d’échapper à pkill -f. Seul le modèle le plus récent testé a permis de désamorcer la situation, aboutissant à un armistice dans 98 % des cas, contrairement aux blocages ou aux conflits ouverts observés avec les modèles précédents. C’est là que réside le risque lié au fait de conférer aux agents un accès réel au système sans aucune vérification externe quant à la compatibilité de leurs objectifs.

La solution réside dans la structure, et non dans de l’ingéniosité. Aucun agent en dehors du chef de projet ne possède des compétences qui dépassent le cadre de son propre projet. Les chefs de projet n’ont pas accès à l’environnement de production à moins qu’un responsable ne donne son accord. Ces deux responsables existent en partie pour que, si l’un commence à approuver des décisions menant à une spirale d’objectifs incompatibles, l’autre agisse comme un contrôle indépendant et non comme une simple validation dans le même contexte.

L’étude admet que le problème reste ouvert. Selon ses auteurs, la coordination ne naît pas simplement d’une intelligence supérieure ou de l’alignement de chaque agent individuel, et le dilemme entre laisser les agents fonctionner sans surveillance et les obliger à demander de l’aide à un humain reste sans solution. Une hiérarchie maintient ces modes de défaillance en place. Ce qu’elle change, c’est l’ampleur de leur impact et la vitesse à laquelle une personne peut détecter des problèmes avant qu’ils ne se propagent à neuf projets différents.

Que copier si vous ne gérez pas neuf projets

Pour un développeur seul, ou une équipe de deux ou trois personnes, presque toute cette hiérarchie n’est pas nécessaire à mettre en place, et il serait malhonnête de prétendre le contraire.

Omettez le modèle à deux responsables. Il répond à la question : « Que se passe-t-il si le superviseur meurt à 3 heures du matin sans que personne ne soit là ? » Un développeur solo fait office de surveillant ; une session qui plante pendant la nuit peut attendre que vous vérifiiez votre téléphone le matin.

Omettez l’échelon du chef de projet. Rédiger des tickets et négocier le périmètre des tâches ont du sens lorsque neuf projets simultanés se disputent l’attention d’une seule personne. Avec un ou deux projets, vous êtes vous-même le chef de projet, et un agent dédié ne fait qu’ajouter une étape de traduction entre vous et le travail.

Conservez un responsable technique avec des IC ciblés, regroupés en une seule couche. Un seul agent est chargé de l’analyse et de l’examen d’une unité de travail, et il crée des sous-agents spécialisés pour effectuer le travail routinier, au lieu d’une seule session étendue gérant tout. Même sans hiérarchie, cette approche par création de sous-agents vaut la peine d’être adoptée : le contexte et le cache hérités permettent d’éviter de devoir expliquer à nouveau la situation chaque fois qu’on a besoin d’aide pour une sous-tâche.

Restreignez chaque sous-agent à une petite partie précise du système. Selon les témoignages, cette règle unique a permis d’éviter plus d’incidents au début que n’importe quelle technique d’ingénierie de prompts.

Un squelette que vous pouvez copier

C’est la plus petite version du modèle de lead et d’IC qui reste fonctionnelle. Elle comprend deux définitions d’agents Claude Code ainsi qu’un petit coordinateur auto-hébergé qui n’a pas besoin de file d’attente ou de broker hébergé ; c’est pratique si vous souhaitez que le modèle fonctionne via un simple script, par exemple en utilisant le SDK Claude Agent, et non à travers des sessions interactives en temps réel via des mentions @.

La version native de Claude Code commence par .claude/agents/tech-lead.md:

---
name: tech-lead
description: Owns decomposition and review for one project. Forks IC agents for individual tasks, reviews their diffs, escalates only genuine blockers.
tools: Read, Grep, Glob, Bash, Edit, Agent
---
You are the tech lead for this project. You do not write most of the code
yourself. When a task arrives:
1. Break it into the smallest pieces that can be verified independently.
2. For each piece, spawn a fork subagent scoped to exactly one piece:
   subagent_type: "fork", with a prompt naming the specific files or
   directory it may touch and nothing else.
3. Review every diff before it is considered done. Reject anything that
   touches files outside the scope you gave it.
4. Only message the lead session (via SendMessage / @-mention) if you are
   blocked on a decision you cannot make with the context you have, or if
   two of your own IC agents produced conflicting changes.
Never let two IC agents work on overlapping files in the same task cycle.

Et .claude/agents/ic-migration.md montre un IC à portée restreinte :

---
name: ic-migration
description: Handles only database migration scripts under db/migrations/. Never touches application code.
tools: Read, Edit, Bash
---
You only read and write files under db/migrations/. If a task requires
changing anything outside that directory, stop and report back to whoever
assigned you the task instead of making the change yourself.
Write one migration per task. Run it against the local test database
before reporting done. Include the rollback in the same file.

Le chef technique crée une branche de l’IC à l’aide d’une appel Task ou Agent spécifiant subagent_type: "fork" ainsi que d’une instruction portant sur un seul ticket. Comme il s’agit d’une branche, l’IC contient déjà les réflexions du chef technique concernant ce ticket et n’a pas besoin d’être réinformé.

La version auto-hébergée concerne les cas où l’on évite les sessions interactives complètes et où l’on souhaite que l’idée de coordination fonctionne uniquement avec l’API Anthropic : pas de broker, pas de file d’attente externe, juste un fichier sur votre disque.

# coordinator.py
# pip install anthropic --break-system-packages
# A local, file-backed mailbox so "agents" (just API calls) can hand
# work to each other without any hosted message broker.
import sqlite3
import json
import time
from anthropic import Anthropic
DB = "mailbox.db"
client = Anthropic()  # reads ANTHROPIC_API_KEY from env
def init_db():
    conn = sqlite3.connect(DB)
    conn.execute("""
        CREATE TABLE IF NOT EXISTS messages (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            to_agent TEXT,
            from_agent TEXT,
            body TEXT,
            status TEXT DEFAULT 'pending',
            created_at REAL
        )
    """)
    conn.commit()
    conn.close()
def send(to_agent, from_agent, body):
    conn = sqlite3.connect(DB)
    conn.execute(
        "INSERT INTO messages (to_agent, from_agent, body, created_at) VALUES (?, ?, ?, ?)",
        (to_agent, from_agent, body, time.time()),
    )
    conn.commit()
    conn.close()
def next_message(to_agent):
    conn = sqlite3.connect(DB)
    row = conn.execute(
        "SELECT id, from_agent, body FROM messages WHERE to_agent=? AND status='pending' ORDER BY id LIMIT 1",
        (to_agent,),
    ).fetchone()
    if row:
        conn.execute("UPDATE messages SET status='taken' WHERE id=?", (row[0],))
        conn.commit()
    conn.close()
    return row
def run_ic(scope_dir, ticket_body):
    """One scoped worker call. No memory between calls by design here,
    since we're not using Claude Code's native forking in this path."""
    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=2048,
        system=f"You may only reason about files under {scope_dir}. "
               f"If the ticket requires anything outside that scope, "
               f"say so and stop.",
        messages=[{"role": "user", "content": ticket_body}],
    )
    return response.content[0].text
if __name__ == "__main__":
    init_db()
    send(to_agent="ic-migration", from_agent="tech-lead", body="Add index on users.email")
    msg = next_message("ic-migration")
    if msg:
        _, sender, body = msg
        result = run_ic("db/migrations/", body)
        send(to_agent=sender, from_agent="ic-migration", body=result)
        print(result)

C’est délibérément simple : un fichier SQLite remplace la file d’attente des messages et une fonction Python remplace un agent IC. Pourtant, il présente la structure de la version de production, avec un travailleur délimité dans son champ d’action, un transfert de messages à la place d’un état mutable partagé, et sans aucun élément dépendant d’un service payant. Si vous en avez besoin davantage, le passage à la fonction de forking native de Claude Code ainsi qu’aux mentions @ correspond conceptuellement au même schéma, avec les aspects techniques gérés automatiquement pour vous.

État actuel

Rien de tout cela n’est encore finalisé. Les escalades arrivent toujours plus souvent que ce que l’on souhaiterait, et certaines se révèlent être de véritables ambiguïtés plutôt que des bugs dans les règles de délimitation des champs d’action. La recherche est probablement juste : des solutions réelles, telles que des systèmes de réputation entre agents et une conception de mécanismes appropriée plutôt qu’une hiérarchie ad hoc, sont encore en cours d’élaboration dans l’industrie.

Le jugement de l’équipe elle-même est modeste. Remplacer une session unique et complexe par une hiérarchie qui imite de manière approximative la façon dont une petite équipe d’ingénierie transfère les tâches aurait réduit de moitié le nombre de demandes quotidiennes, et les échecs restants seraient devenus plus simples à comprendre plutôt que déroutants. C’est un bon échange.