treść
Wprowadzenie
Claude Code (v2.1.47) udostępnia eksperymentalną funkcję Agent Teams: wiele sesji Claude Code koordynuje wspólną pracę w układzie lider–członkowie zespołu. Od pewnego czasu tworzę Hive, orkiestrator wielu agentów programistycznych o podobnych celach, ale zupełnie innej architekturze, więc chciałem zrozumieć, jak podejście Anthropic działa od środka.
W tym wpisie opisuję ustalenia zebrane dzięki:
- Lekturze oficjalnej dokumentacji
- Analizie rzeczywistych artefaktów pozostawionych na dysku przez wcześniejsze sesje zespołowe
- Zleceniu Claude’owi analizy pliku binarnego Claude Code (v2.1.47) w poszukiwaniu szczegółów implementacji (ha!)
Spis treści
- 1. Przegląd architektury
- 2. Współdzielona lista zadań
- 3. Komunikacja między agentami
- 4. Uruchamianie agentów i ich cykl życia
- 5. Bramki jakości i hooki
- 6. Koszty tokenów
- 7. Podsumowanie architektury
- Źródła
1. Przegląd architektury
Zespół agentów składa się z czterech elementów:
| Element | Rola |
|---|---|
| Lider zespołu | Główna sesja Claude Code, która tworzy zespół i uruchamia jego członków |
| Członkowie zespołu | Osobne instancje Claude Code, każda z własnym oknem kontekstu |
| Lista zadań | Współdzielone zadania przechowywane na dysku jako osobne pliki JSON |
| Skrzynka wiadomości | Pliki skrzynek odbiorczych poszczególnych agentów służące do dostarczania wiadomości |
Cała warstwa koordynacji jest oparta na plikach. System plików pod ścieżką ~/.claude/ stanowi jedyną podstawę koordynacji:
~/.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
└── ...
To z gruntu zdecentralizowana architektura. Lider jest po prostu kolejną sesją Claude z dodatkowymi narzędziami (TeamCreate, TeamDelete, SendMessage). Nie ma żadnego procesu działającego w tle. Koordynacja wynika ze wspólnego dostępu do plików.
Jeśli w aktywnej sesji poprosisz Claude o uruchomienie zespołu do wykonania jakiegoś zadania, a następnie uruchomisz poniższe polecenie w innym oknie, możesz obserwować zmiany w systemie plików w czasie rzeczywistym.
watch -n 0.5 'tree ~/.claude/teams/ 2>/dev/null; echo "---"; tree ~/.claude/tasks/ 2>/dev/null'
Na przykład po użyciu następującego promptu:
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
Zaobserwowałem to:
teams
└── code-review
├── config.json
└── inboxes
├── bug-hunter.json
├── complexity-analyst.json
├── devils-advocate.json
└── team-lead.json
Konfiguracja zespołu
Konfiguracja zespołu w ~/.claude/teams/{team-name}/config.json zawiera tablicę members, którą członkowie zespołu odczytują, aby dowiedzieć się o sobie nawzajem:
{
"members": [
{ "name": "team-lead", "agentId": "abc-123", "agentType": "leader" },
{
"name": "researcher",
"agentId": "def-456",
"agentType": "general-purpose"
}
]
}
Podstawowym mechanizmem adresowania są nazwy (identyfikatory UUID istnieją, ale nie są używane do kierowania wiadomości). Cała komunikacja i przydzielanie zadań korzystają z pola name.
2. Współdzielona lista zadań
Format plików
Każde zadanie jest przechowywane jako osobny plik JSON w ~/.claude/tasks/{team-name}/. Oto rzeczywisty przykład z wcześniejszej sesji:
{
"id": "1",
"subject": "Hunt for bugs across the codebase",
"description": "...",
"activeForm": "Hunting for bugs",
"owner": "bug-hunter",
"status": "completed",
"blocks": [],
"blockedBy": []
}
Schemat zadania:
| Pole | Typ | Opis |
|---|---|---|
id |
string | Identyfikator liczbowy, automatycznie zwiększany za pomocą .highwatermark |
subject |
string | Tytuł w trybie rozkazującym (np. "Uruchom testy") |
description |
string | Szczegółowe wymagania i kryteria akceptacji |
activeForm |
string | Forma opisująca trwającą czynność, wyświetlana przy wskaźniku aktywności ("Uruchamianie testów") |
status |
string | pending → in_progress → completed (lub deleted) |
blocks |
string[] | Identyfikatory zadań blokowanych przez to zadanie |
blockedBy |
string[] | Identyfikatory zadań, które muszą się zakończyć, zanim to zadanie będzie mogło się rozpocząć |
Kontrola współbieżności
Koordynację zapewniają dwa specjalne pliki:
.lock: Plik o rozmiarze 0 bajtów używany do wzajemnego wykluczania na poziomie systemu plików (flock()). Występował we wszystkich 42 katalogach zadań zaobserwowanych na moim komputerze..highwatermark: Zawiera jedną liczbę całkowitą (np."3","13"). To następny dostępny identyfikator zadania używany przy automatycznym zwiększaniu numeracji.
Podejmowanie zadań
Podejmowanie zadań wykorzystuje blokady plików, aby zapobiegać wyścigom. Członkowie zespołu preferują kolejność od najniższego identyfikatora. Zadania z niepustą tablicą blockedBy nie można podjąć, dopóki wszystkie blokujące je zadania nie znajdą się w stanie końcowym.
Obserwacja: większość katalogów zadań jest pusta
Spośród 42 katalogów zadań na moim komputerze tylko 5 zawierało rzeczywiste pliki JSON zadań. W pozostałych 37 znajdowały się jedynie .lock i .highwatermark. Prawdopodobnie oznacza to, że zadania są usuwane po ukończeniu albo były to sesje, w których Claude korzystał z wewnętrznej listy zadań (dostępnej od wprowadzenia funkcji listy zadań), bez rozbijania pracy na pliki podzadań.
3. Komunikacja między agentami
Wzorzec skrzynki wiadomości
Każdy agent ma plik z tablicą JSON pod ścieżką ~/.claude/teams/{team-name}/inboxes/{agent-name}.json. Oto rzeczywista skrzynka odbiorcza z wcześniejszej sesji, w której lider zespołu zlecił pracę agentowi 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
}
]
Zwróć uwagę na kodowanie JSON w JSON-ie: pole text jest ciągiem znaków JSON zawierającym zserializowany obiekt wiadomości. Zewnętrzna koperta zawiera pola from, text, timestamp i read.
Typy wiadomości
Pole type w treści text obsługuje:
| Typ | Kierunek | Przeznaczenie |
|---|---|---|
task_assignment |
lider → członek zespołu | Przydzielenie zadania ze wszystkimi szczegółami |
message |
dowolny → dowolny | Bezpośrednia wiadomość do jednego odbiorcy |
broadcast |
lider → wszyscy | Ta sama wiadomość do każdego członka zespołu |
shutdown_request |
lider → członek zespołu | Prośba o kontrolowane zakończenie pracy |
shutdown_response |
członek zespołu → lider | Zgoda na zakończenie pracy lub odmowa |
plan_approval_request |
członek zespołu → lider | Przesłanie planu do oceny |
plan_approval_response |
lider → członek zespołu | Akceptacja lub odrzucenie z informacją zwrotną |
idle_notification |
członek zespołu → lider | Wysyłane automatycznie po zakończeniu tury członka zespołu |
Mechanizm dostarczania
Ścieżka zapisu: Nadawca dopisuje nowy wpis do tablicy JSON w pliku skrzynki odbiorczej odbiorcy.
Ścieżka odczytu: Odbiorca cyklicznie sprawdza własny plik skrzynki odbiorczej. Nowe wiadomości są wprowadzane jako syntetyczne tury rozmowy (wyglądają tak, jakby wysłał je użytkownik).
Rozgłaszanie: Dosłownie zapisuje tę samą wiadomość w pliku skrzynki odbiorczej każdego członka zespołu. Koszt tokenów rośnie liniowo wraz z wielkością zespołu.
Komunikacja to po prostu dopisywanie do pliku + odczyt pliku. Opóźnienie między wysłaniem a odebraniem zależy od odstępu między kolejnymi sprawdzeniami skrzynki przez odbiorcę.
Widoczność bezpośrednich wiadomości między członkami zespołu
Gdy członek zespołu wysyła bezpośrednią wiadomość do innego członka, krótkie podsumowanie trafia do powiadomienia o bezczynności przekazywanego liderowi. Dzięki temu lider ma wgląd we współpracę członków zespołu bez dostępu do pełnej treści wiadomości.
4. Uruchamianie agentów i ich cykl życia
Jak powstają członkowie zespołu
Każdy członek zespołu jest osobnym procesem CLI claude. Lider uruchamia ich za pomocą narzędzia Task z parametrami team_name i name. Dla uruchamianego procesu ustawiane są zmienne środowiskowe:
CLAUDE_CODE_TEAM_NAME: ustawiana automatycznie u uruchamianych członków zespołuCLAUDE_CODE_PLAN_MODE_REQUIRED: ustawiana natrue, jeśli wymagana jest akceptacja planu
Inicjalizacja kontekstu
Członkowie zespołu wczytują taki sam kontekst projektu jak każda nowa sesja:
- Pliki
CLAUDE.mdz katalogu roboczego - Serwery MCP
- Umiejętności
- Prompt uruchomieniowy od lidera
Historia rozmowy lidera NIE jest przekazywana. Każdy członek zespołu zaczyna od zera, mając w kontekście jedynie prompt uruchomieniowy.
Wewnętrzna implementacja
Z analizy pliku binarnego Claude Code v2.1.47 wynika, że kontekstem członka zespołu zarządza AsyncLocalStorage z następującymi polami:
agentId,agentName,teamNameparentSessionId,colorplanModeRequired
Najważniejsze funkcje wewnętrzne:
isTeammate()/isTeamLead(): wykrywanie roliwaitForTeammatesToBecomeIdle(): mechanizm synchronizacji dla lideragetTeammateContext()/setDynamicTeamContext(): zarządzanie kontekstem wykonania
Wykrywanie bezczynności
Po każdej turze LLM członek zespołu automatycznie przechodzi w stan bezczynności i wysyła do lidera idle_notification. To normalny stan spoczynku, a nie oznaka błędu czy zastoju. Wysłanie wiadomości do bezczynnego członka zespołu wybudza go (kolejny cykl sprawdzania skrzynki odbiorczej wykrywa wiadomość).
Protokół kończenia pracy
- Lider wysyła
shutdown_requestdo członka zespołu - Członek zespołu może zaakceptować żądanie (kończy pracę w kontrolowany sposób) lub je odrzucić (kontynuuje pracę, podając wyjaśnienie)
- Sprzątanie po zespole za pomocą
TeamDeleteusuwa~/.claude/teams/{team-name}/i~/.claude/tasks/{team-name}/ - Sprzątanie kończy się niepowodzeniem, jeśli którykolwiek członek zespołu jest nadal aktywny; najpierw trzeba zakończyć ich pracę
Dziedziczenie uprawnień
Członkowie zespołu dziedziczą tryb uprawnień lidera w momencie uruchomienia. Jeśli lider korzysta z --dangerously-skip-permissions, wszyscy członkowie zespołu również. Tryby poszczególnych członków można zmienić po uruchomieniu, ale nie można ich skonfigurować osobno dla każdego członka w momencie uruchamiania.
5. Bramki jakości i hooki
Agent Teams integruje się z systemem hooków Claude Code, aby egzekwować wymagania jakościowe:
Hook TeammateIdle
Uruchamia się, gdy członek zespołu ma przejść w stan bezczynności. Kod wyjścia 2 przekazuje zawartość stderr jako informację zwrotną i zapobiega przejściu w stan bezczynności, dzięki czemu członek zespołu kontynuuje pracę.
{
"hook_event_name": "TeammateIdle",
"teammate_name": "researcher",
"team_name": "my-project"
}
Hook TaskCompleted
Uruchamia się w chwili oznaczania zadania jako ukończonego. Kod wyjścia 2 blokuje ukończenie i przekazuje zawartość stderr jako informację zwrotną.
{
"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"
}
Uruchamia się w dwóch sytuacjach: (1) gdy dowolny agent jawnie oznacza zadanie jako ukończone za pomocą TaskUpdate lub (2) gdy członek zespołu agentów kończy swoją turę, mając zadania w toku.
Typy procedur obsługi hooków
| Typ | Opis |
|---|---|
command |
Skrypt powłoki. JSON na stdin, kody wyjścia określające decyzje. |
prompt |
Ocena przez LLM w jednej turze. Zwraca {ok, reason}. |
agent |
Subagent działający przez wiele tur, z narzędziami do odczytu. Do 50 tur. |
6. Koszty tokenów
Zespoły agentów zużywają około 7× więcej tokenów niż standardowe sesje, gdy członkowie zespołu pracują w trybie planowania. Każdy członek zespołu utrzymuje własne pełne okno kontekstu jako osobna instancja Claude.
Poziom odniesienia
- Średni koszt korzystania z Claude Code: ~6 USD/programistę/dzień
- Zespoły agentów: dodatkowy koszt ponad poziom bazowy jest w przybliżeniu proporcjonalny do wielkości zespołu
7. Podsumowanie architektury
| Aspekt | Claude Code Agent Teams |
|---|---|
| Podstawa koordynacji | Zwykłe pliki (~/.claude/tasks/, ~/.claude/teams/) |
| Format zadań | Jeden plik JSON na zadanie + .lock do podejmowania zadań |
| Komunikacja | Pliki skrzynek odbiorczych JSON (dopisywanie + cykliczne sprawdzanie) |
| Cykl życia agentów | Procesy CLI zarządzające własnym cyklem życia |
| Izolacja pracy | Współdzielony katalog roboczy |
| Strategia scalania | Brak (agenci bezpośrednio edytują pliki) |
| Ponawianie/eskalacja | Ręczne (decyduje lider lub interweniuje użytkownik) |
| Topologia | Lider + płaska struktura równorzędnych członków, komunikacja bezpośrednia między nimi |
| Planowanie pracy | Samodzielne podejmowanie (członkowie zespołu pobierają kolejne zadanie) |
| Trwałość stanu | Tylko pliki; brak wznawiania pracy członków zespołu wewnątrz procesu |
| Bramki jakości | Hooki powłoki (TeammateIdle, TaskCompleted) |
| Śledzenie zużycia tokenów | Tylko w obrębie sesji, bez sumowania między agentami |
| Wykrywanie zastoju | Ręczne (użytkownik zauważa, że członek zespołu przestał pracować) |
| Kontrola współbieżności | Niejawna (wielkość zespołu = liczba jego członków) |
| Model zależności | blocks/blockedBy w plikach zadań |
Źródła
Oficjalna dokumentacja
- Zespoły sesji Claude Code: architektura
- Tryb interaktywny — Lista zadań
- Zespoły agentów — Przydzielanie i podejmowanie zadań
- Zespoły agentów — Kontekst i komunikacja
- Zespoły agentów — Kończenie pracy członków zespołu
- Zespoły agentów — Sprzątanie po zespole
- Zespoły agentów — Uprawnienia
- Zespoły agentów — Ograniczenia
- Zespoły agentów — Zalecane praktyki
- Hooki
- Koszty — Koszty tokenów zespołu agentów
- Ustawienia
- Subagenci
Artefakty na dysku (Claude Code v2.1.47)
Zaobserwowane w /Users/tau/.claude/:
- Katalogi zespołów z plikami
config.jsoniinboxes/{agent-name}.json - Katalogi zadań z
.lock,.highwatermarki osobnymi plikami JSON zadań - Przykładowa wiadomość przydzielająca zadanie od
team-leaddocp-agent, ze znacznikiem czasu2026-02-18T02:37:16.890Z
Analiza pliku binarnego
Plik binarny Claude Code v2.1.47. Funkcje wewnętrzne zidentyfikowane na podstawie analizy ciągów znaków: getTeamName, getAgentName, getAgentId, isTeammate, isTeamLead, waitForTeammatesToBecomeIdle, getTeammateContext, setDynamicTeamContext, createTeammateContext. Pola kontekstu AsyncLocalStorage: agentId, agentName, teamName, parentSessionId, color, planModeRequired.