contenuto
Introduzione
Claude Code (v2.1.47) include una funzionalità sperimentale chiamata Agent Teams: più sessioni di Claude Code si coordinano su un lavoro condiviso attraverso una topologia con un coordinatore e più membri del team. Sto sviluppando Hive, un orchestratore di agenti di programmazione con obiettivi simili ma un'architettura molto diversa, quindi volevo capire come funziona internamente l'approccio di Anthropic.
Questo articolo documenta ciò che ho scoperto:
- Leggendo la documentazione ufficiale
- Esaminando gli artefatti effettivamente lasciati su disco dalle precedenti sessioni dei team
- Facendo analizzare a Claude il binario di Claude Code (v2.1.47) per ricavare dettagli sull'implementazione (ah ah!)
Indice
- 1. Panoramica dell'architettura
- 2. L'elenco condiviso delle attività
- 3. Comunicazione tra agenti
- 4. Avvio degli agenti e ciclo di vita
- 5. Controlli di qualità e hook
- 6. Economia dei token
- 7. Riepilogo dell'architettura
- Fonti
1. Panoramica dell'architettura
Un team di agenti è composto da quattro elementi:
| Componente | Ruolo |
|---|---|
| Coordinatore del team | La sessione principale di Claude Code che crea il team e ne avvia i membri |
| Membri del team | Istanze separate di Claude Code, ciascuna con la propria finestra di contesto |
| Elenco delle attività | Unità di lavoro condivise, memorizzate su disco come singoli file JSON |
| Casella di posta | File della posta in arrivo per ciascun agente, usati per la consegna dei messaggi |
L'intero livello di coordinamento è basato su file. Il filesystem in ~/.claude/ è l'unico substrato di coordinamento:
~/.claude/
├── teams/{team-name}/
│ ├── config.json # team membership registry
│ └── inboxes/{agent-name}.json # per-agent mailbox
└── tasks/{team-name}/
├── .lock # flock() for concurrent task claiming
├── .highwatermark # auto-increment counter
├── 1.json # individual task files
├── 2.json
└── ...
Si tratta di un'architettura fondamentalmente decentralizzata. Il coordinatore è semplicemente un'altra sessione di Claude con strumenti aggiuntivi (TeamCreate, TeamDelete, SendMessage). Non esiste alcun processo in background. Il coordinamento emerge dall'accesso condiviso ai file.
In una sessione attiva, se chiedi a Claude di avviare un team per svolgere un'attività e poi esegui quanto segue in un'altra finestra, puoi osservare gli aggiornamenti del filesystem in tempo reale.
watch -n 0.5 'tree ~/.claude/teams/ 2>/dev/null; echo "---"; tree ~/.claude/tasks/ 2>/dev/null'
Per esempio, con il seguente prompt:
can you spawn an agent team to examine this codebase?
- have one look for bugs
- have one look for complexity
- have one look for good things to call out and play devil's advocate against the other two agents
Ho osservato questo:
teams
└── code-review
├── config.json
└── inboxes
├── bug-hunter.json
├── complexity-analyst.json
├── devils-advocate.json
└── team-lead.json
Configurazione del team
La configurazione del team in ~/.claude/teams/{team-name}/config.json contiene un array members che i membri del team leggono per individuarsi a vicenda:
{
"members": [
{ "name": "team-lead", "agentId": "abc-123", "agentType": "leader" },
{
"name": "researcher",
"agentId": "def-456",
"agentType": "general-purpose"
}
]
}
I nomi sono il principale meccanismo di indirizzamento (gli UUID esistono, ma non vengono usati per l'instradamento). Tutti i messaggi e le assegnazioni delle attività usano il campo name.
2. L'elenco condiviso delle attività
Formato dei file
Ogni attività viene memorizzata come singolo file JSON in ~/.claude/tasks/{team-name}/. Ecco un esempio reale tratto da una sessione precedente:
{
"id": "1",
"subject": "Hunt for bugs across the codebase",
"description": "...",
"activeForm": "Hunting for bugs",
"owner": "bug-hunter",
"status": "completed",
"blocks": [],
"blockedBy": []
}
Schema delle attività:
| Campo | Tipo | Descrizione |
|---|---|---|
id |
string | ID numerico, incrementato automaticamente tramite .highwatermark |
subject |
string | Titolo in forma imperativa (ad esempio, "Esegui i test") |
description |
string | Requisiti dettagliati e criteri di accettazione |
activeForm |
string | Forma progressiva per l'indicatore di attività ("Esecuzione dei test in corso") |
status |
string | pending → in_progress → completed (oppure deleted) |
blocks |
string[] | ID delle attività bloccate da questa attività |
blockedBy |
string[] | ID delle attività che devono essere completate prima che questa possa iniziare |
Controllo della concorrenza
Due file speciali garantiscono il coordinamento:
.lock: Un file di 0 byte usato per la mutua esclusione a livello di filesystem (flock()). Presente in tutte le 42 directory delle attività osservate sulla mia macchina..highwatermark: Contiene un singolo intero (ad esempio,"3","13"). Il prossimo ID di attività disponibile per l'incremento automatico.
Presa in carico delle attività
La presa in carico delle attività usa il blocco dei file per evitare condizioni di competizione. I membri del team preferiscono procedere a partire dagli ID più bassi. Un'attività con un array blockedBy non vuoto non può essere presa in carico finché tutte le attività che la bloccano non si trovano in uno stato terminale.
Osservazione: la maggior parte delle directory delle attività è vuota
Delle 42 directory delle attività sulla mia macchina, solo 5 contenevano veri e propri file JSON delle attività. Le altre 37 contenevano solo .lock e .highwatermark. Questo probabilmente significa che le attività vengono eliminate dopo il completamento, oppure che si trattava di sessioni in cui Claude ha usato l'elenco interno delle attività (disponibile dal lancio della funzionalità dell'elenco delle attività) senza suddividere il lavoro in file di sottoattività.
3. Comunicazione tra agenti
Modello a caselle di posta
Ogni agente ha un file contenente un array JSON in ~/.claude/teams/{team-name}/inboxes/{agent-name}.json. Ecco una casella di posta reale di una sessione precedente, in cui un coordinatore ha assegnato del lavoro a un controlplane-agent:
[
{
"from": "team-lead",
"text": "{\"type\":\"task_assignment\",\"taskId\":\"1\",\"subject\":\"Phase 2: Control-plane - remove participants/presence\",\"description\":\"Remove multiplayer code from the control-plane package...\",\"assignedBy\":\"team-lead\",\"timestamp\":\"2026-02-18T02:37:16.890Z\"}",
"timestamp": "2026-02-18T02:37:16.890Z",
"read": false
}
]
Nota la codifica JSON-in-JSON: il campo text è una stringa JSON che contiene un oggetto messaggio serializzato. L'involucro esterno contiene i campi from, text, timestamp e read.
Tipi di messaggi
Il campo type all'interno del payload text supporta:
| Tipo | Direzione | Scopo |
|---|---|---|
task_assignment |
coordinatore → membro | Assegnare un'attività con tutti i dettagli |
message |
qualsiasi → qualsiasi | Messaggio diretto a un singolo destinatario |
broadcast |
coordinatore → tutti | Lo stesso messaggio a ogni membro del team |
shutdown_request |
coordinatore → membro | Richiedere un arresto ordinato |
shutdown_response |
membro → coordinatore | Approvare o rifiutare l'arresto |
plan_approval_request |
membro → coordinatore | Sottoporre un piano a revisione |
plan_approval_response |
coordinatore → membro | Approvare o rifiutare fornendo feedback |
idle_notification |
membro → coordinatore | Inviato automaticamente alla fine del turno del membro del team |
Meccanismo di consegna
Percorso di scrittura: Il mittente aggiunge una nuova voce in coda al file contenente l'array JSON della casella di posta del destinatario.
Percorso di lettura: Il destinatario controlla periodicamente il proprio file della casella di posta. I nuovi messaggi vengono inseriti come turni sintetici della conversazione (appaiono come se li avesse inviati un utente).
Broadcast: Scrive letteralmente lo stesso messaggio nel file della casella di posta di ogni membro del team. Il costo in token cresce in modo lineare con le dimensioni del team.
La comunicazione è semplicemente un'aggiunta in coda a un file + una lettura di un file. La latenza tra invio e ricezione dipende dall'intervallo di polling del destinatario.
Visibilità dei messaggi diretti tra pari
Quando un membro del team invia un messaggio diretto a un altro membro, un breve riepilogo viene incluso nella notifica di inattività inviata al coordinatore. Questo permette al coordinatore di seguire la collaborazione tra pari senza il contenuto completo del messaggio.
4. Avvio degli agenti e ciclo di vita
Come vengono creati i membri del team
Ogni membro del team è un processo CLI claude separato. Il coordinatore li avvia tramite lo strumento Task con i parametri team_name e name. Nel processo avviato vengono impostate queste variabili d'ambiente:
CLAUDE_CODE_TEAM_NAME: impostata automaticamente per i membri del team avviatiCLAUDE_CODE_PLAN_MODE_REQUIRED: impostata sutruese è richiesta l'approvazione del piano
Inizializzazione del contesto
I membri del team caricano lo stesso contesto del progetto di una nuova sessione:
- File
CLAUDE.mddalla directory di lavoro - Server MCP
- Skill
- Il prompt di avvio del coordinatore
La cronologia della conversazione del coordinatore NON viene trasferita. Ogni membro del team parte da zero, con il solo prompt di avvio come contesto.
Implementazione interna
Dall'analisi del binario di Claude Code v2.1.47 risulta che il contesto dei membri del team viene gestito tramite AsyncLocalStorage con questi campi:
agentId,agentName,teamNameparentSessionId,colorplanModeRequired
Funzioni interne principali:
isTeammate()/isTeamLead(): rilevamento del ruolowaitForTeammatesToBecomeIdle(): primitiva di sincronizzazione per il coordinatoregetTeammateContext()/setDynamicTeamContext(): gestione del contesto a runtime
Rilevamento dell'inattività
Dopo ogni turno dell'LLM, un membro del team passa automaticamente allo stato inattivo e invia un idle_notification al coordinatore. È il normale stato di riposo, non indica un errore o uno stato obsoleto. Inviare un messaggio a un membro del team inattivo lo riattiva (il ciclo di polling successivo rileva il messaggio nella casella di posta).
Protocollo di arresto
- Il coordinatore invia
shutdown_requesta un membro del team - Il membro del team può approvare (termina in modo ordinato) o rifiutare (continua a lavorare fornendo una spiegazione)
- La pulizia del team tramite
TeamDeleterimuove~/.claude/teams/{team-name}/e~/.claude/tasks/{team-name}/ - La pulizia non riesce se alcuni membri del team sono ancora attivi; devono essere prima arrestati
Ereditarietà dei permessi
I membri del team ereditano la modalità dei permessi del coordinatore al momento dell'avvio. Se il coordinatore viene eseguito con --dangerously-skip-permissions, lo stesso vale per tutti i membri del team. Le singole modalità possono essere modificate dopo l'avvio, ma non configurate per ciascun membro del team al momento dell'avvio.
5. Controlli di qualità e hook
Agent Teams si integra con il sistema di hook di Claude Code per applicare i controlli di qualità:
Hook TeammateIdle
Si attiva quando un membro del team sta per passare allo stato inattivo. Il codice di uscita 2 invia stderr come feedback e impedisce il passaggio allo stato inattivo, mantenendo il membro del team al lavoro.
{
"hook_event_name": "TeammateIdle",
"teammate_name": "researcher",
"team_name": "my-project"
}
Hook TaskCompleted
Si attiva quando un'attività viene contrassegnata come completata. Il codice di uscita 2 impedisce il completamento e restituisce stderr come feedback.
{
"hook_event_name": "TaskCompleted",
"task_id": "task-001",
"task_subject": "Implement user authentication",
"task_description": "Add login and signup endpoints",
"teammate_name": "implementer",
"team_name": "my-project"
}
Questo hook si attiva in due casi: (1) quando un qualsiasi agente contrassegna esplicitamente un'attività come completata tramite TaskUpdate, oppure (2) quando un membro di un team di agenti termina il proprio turno con attività in corso.
Tipi di gestori degli hook
| Tipo | Descrizione |
|---|---|
command |
Script shell. JSON su stdin, codici di uscita per le decisioni. |
prompt |
Valutazione tramite LLM in un solo turno. Restituisce {ok, reason}. |
agent |
Subagente con più turni e strumenti di lettura. Fino a 50 turni. |
6. Economia dei token
I team di agenti usano circa 7 volte più token rispetto alle sessioni standard quando i membri del team operano in modalità di pianificazione. Ogni membro del team mantiene la propria finestra di contesto completa come istanza separata di Claude.
Valori di riferimento
- Utilizzo medio di Claude Code: ~6 $/sviluppatore/giorno
- Team di agenti: consumo aggiuntivo rispetto al valore di base all'incirca proporzionale alle dimensioni del team
7. Riepilogo dell'architettura
| Aspetto | Claude Code Agent Teams |
|---|---|
| Substrato di coordinamento | File flat (~/.claude/tasks/, ~/.claude/teams/) |
| Formato delle attività | Un file JSON per attività + .lock per la presa in carico |
| Messaggistica | File JSON della posta in arrivo (aggiunta in coda + polling) |
| Ciclo di vita degli agenti | Processi CLI che si gestiscono autonomamente |
| Isolamento del lavoro | Directory di lavoro condivisa |
| Strategia di merge | Nessuna (gli agenti modificano direttamente i file) |
| Nuovi tentativi/escalation | Gestione manuale (decide il coordinatore o interviene l'utente) |
| Topologia | Coordinatore + membri di pari livello, messaggistica tra pari |
| Pianificazione | Presa in carico autonoma (i membri del team prendono l'attività successiva) |
| Persistenza dello stato | Solo file; nessuna ripresa dei membri del team all'interno del processo |
| Controlli di qualità | Hook della shell (TeammateIdle, TaskCompleted) |
| Monitoraggio dei token | Solo per sessione, nessuna aggregazione tra agenti |
| Rilevamento degli stalli | Manuale (l'utente nota che un membro del team si è fermato) |
| Controllo della concorrenza | Implicito (dimensioni del team = numero di membri del team) |
| Modello delle dipendenze | blocks/blockedBy nei file delle attività |
Fonti
Documentazione ufficiale
- Team di sessioni di Claude Code: architettura
- Modalità interattiva — Elenco delle attività
- Team di agenti — Assegnare e prendere in carico le attività
- Team di agenti — Contesto e comunicazione
- Team di agenti — Arrestare i membri del team
- Team di agenti — Pulizia del team
- Team di agenti — Permessi
- Team di agenti — Limitazioni
- Team di agenti — Buone pratiche
- Hook
- Costi — Costi dei token dei team di agenti
- Impostazioni
- Subagenti
Artefatti su disco (Claude Code v2.1.47)
Osservati in /Users/tau/.claude/:
- Directory dei team con file
config.jsoneinboxes/{agent-name}.json - Directory delle attività con
.lock,.highwatermarke singoli file JSON delle attività - Messaggio di esempio per l'assegnazione di un'attività da
team-leadacp-agent, con timestamp2026-02-18T02:37:16.890Z
Analisi del binario
Binario di Claude Code v2.1.47. Funzioni interne identificate tramite l'analisi delle stringhe: getTeamName, getAgentName, getAgentId, isTeammate, isTeamLead, waitForTeammatesToBecomeIdle, getTeammateContext, setDynamicTeamContext, createTeammateContext. Campi del contesto AsyncLocalStorage: agentId, agentName, teamName, parentSessionId, color, planModeRequired.