Dwa wątki, dziewięć projektów: praktyczne zastosowanie rozwiązania Claude Code z wieloma agentami
Jak jeden programista rzekomo zarządza głównymi agentami, liderami technologicznymi oraz zdefiniowanymi komponentami w Claude Code, gdzie występują błędy w projekcie oraz które części powinien skopiować samodzielny programista.
Źródła: Inside a Real Multi-Agent Claude Code Setup: Two Leads, 9 Projects, 40 Prompts a Day
Bardzo wiele opisów systemów wielu agentów ma podobną strukturę: diagram z polami oznaczonymi jako Orchestrator, Worker 1 i Worker 2, strzałą z napisem „results” oraz czterdziestoma liniami pseudokodu, które przestawałyby być użyteczne już przy pierwszej różnicy zdań między agentami. Rzadko wyjaśniają, co się dzieje, gdy Worker 2 nadal działa po tym, jak wynik działania Worker 1 unieważnił jego założenia, lub gdy orchestrator zawiesza się w trakcie wykonywania zadania. Takie opisy przypominają diagramy narysowane przez kogoś, kto nigdy nie został obudzony w środku nocy przez agenta, który przez sześć godzin powtarzał te same działania, marnując zasoby na naprawę, która i tak nie mogła się udać.
Ten kontekst przyjmuje inne podejście. Opisuje on ustawienia zgłoszone przez jednego programistę, stosowane w dziewięciu aktywnych projektach i udoskonalane przez około trzy miesiące, aż stały się na tyle niezawodne, by można im było zaufać. Celem jest właśnie niezawodność. Poniższe sekcje omawiają strukturę, funkcje Claude Code, które umożliwiają jej praktyczne zastosowanie, sytuacje, w których zawodzi w sposób opisany we własnych badaniach Anthropic nad systemami wieloagentowymi, oraz te elementy, które pojedynczy programista powinien skopiować. Szczegóły pochodzą z opisu tego programisty i nie zostały niezależnie zweryfikowane.
Jak wygląda struktura
Dwa główne agenty znajdują się na górze i noszą przydomeki lead-alpha oraz lead-beta. Każdy z nich to długo trwająca sesja Claude Code, działająca na różnym sprzęcie: małym urządzeniu, które nigdy nie wyłącza się, oraz maszynie w chmurze. Nadzorują one się nawzajem. Co kilka minut każdy z nich wysyła krótkie „sygnały życia” do drugiego za pomocą funkcji SendMessage. Gdy jeden z agentów przestaje reagować po upływie dozwolonego czasu, drugi przywraca go do życia – albo ponownie uruchamia sesję, jeśli może do niej dotrzeć, albo uruchamia zaplanowane zadanie, które tworzy nową sesję powiązaną z kontekstem danego projektu.
Wydaje się to przesadą, dopóki jeden z agentów nie zatrzymuje się z powodu błędu w wywołaniu narzędzia w środku nocy, a wszystkie późniejsze zadania nie zamarzają, dopóki ktoś przypadkiem nie zajrzy. Zdarzyło się to dokładnie raz na początku, i to uzasadnia istnienie drugiego agenta.
Poniżej tych dwóch kierowników znajduje się dziewięć projektów. Każdy z nich ma swojego własnego agenta kierownika technicznego oraz agenta menedżera projektu, których tworzy i nadzoruje odpowiedni kierownik najwyższego szczebla odpowiedzialny za dany projekt, przy czym odpowiedzialność jest dzielona mniej więcej po równo, głównie według dziedziny, a częściowo według tego, kto miał w danym tygodniu mniej obowiązków. Agent menedżera projektu śledzi zakres prac, dzieli przychodzące prośby na zadania oraz negocjuje priorytety na wyższych szczeblach. Agent kierownika technicznego odpowiada za techniczną analizę projektu, decyduje, który agent konkretnego uczestnika prac przejmie daną część, oraz sprawdza różnice przed przekazaniem materiału człowiekowi.
Każdy lider techniczny zarządza od 5 do 10 specjalistami, z których każdy jest odpowiedzialny za konkretną część bazy kodu lub określony rodzaj zadań. Jeden z nich zajmuje się wyłącznie skryptami migracyjnymi – to zasada powstała w dniu, gdy specjalista o szerokich kompetencjach uporządkował folder z skryptami migracyjnymi, rzekomo naprawiając coś zupełnie innego. Łącznie mamy więc około 75 do 90 ról agentów, przy czym większość stanowisk specjalistów pozostaje nieaktywna, gdy nie ma zadań do wykonania.
Matematyka interakcji przekonuje sceptyków, że to nie jest tylko zabawka. Osoba odpowiedzialna za to rozwiązanie pisze łącznie około 30 do 50 zapytań dziennie, we wszystkich operacjach łącznie, a nie tylko w ramach jednego projektu. Prawie żadne z nich nie trafia bezpośrednio do specjalisty, ponieważ pisanie do niego oznacza, że już coś poszło nie tak; właśnie do tego służy warstwa liderów technicznych. Zgodnie z tym opisem podział uwagi wygląda mniej więcej w ten sposób:
+---------------------------------+------------------+
| 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) |
+---------------------------------+------------------+
Większość dnia poświęca się rozmowom z dwoma kierownikami projektów na temat priorytetów, usuwaniu blokad w projektach oraz czytaniu codziennego podsumowania przygotowanego przez wszystkich dziewięciu kierowników projektów. Ilość komunikacji pomiędzy agentami, w której kierownicy techniczni przydzielają zadania, specjaliści zgłaszają postępy, a menedżerowie projektów zmieniają zakres zadań, jest rzekomo około dwóch rzędów wielkości większa od zapytań wysyłanych przez ludzi, a tylko niewielka ich część jest kiedykolwiek widoczna, chyba że ktoś specjalnie jej szuka.
Co teraz to umożliwia
Tutaj istotna jest precyzja, ponieważ podobne rozwiązanie rzekomo nie mogło zostać zrealizowane sześć miesięcy wcześniej. Dwie nowe funkcje w najnowszej wersji Claude Code (wymieniona wersja to 2.1.232) zmieniły sytuację, więc sprawdź aktualne notatki wersji, aby poznać dokładne zachowanie.
Dywersyfikacja podagentów. Kluczowa zmiana dotyczy sposobu, w jaki lider techniczny i jego specjaliści współpracują. Bez dywersyfikacji jako standardu nowo utworzony podagent zaczynał od zera: nie miał żadnych informacji o dyskusjach dotyczących zadań ani wspólnego bufora promptów. Każdy specjalista potrzebował szczegółowego briefingu, co oznaczało albo wypełnianie każdego promptu zadaniowego dodatkowymi informacjami, albo tolerowanie pytań, na które lider już odpowiedział kilka minut wcześniej. W przypadku 5 do 10 specjalistów przy dziewięciu projektach sytuacja szybko staje się uciążliwa.
Prosząc o subagent_type: „fork”, nowy subagent otrzymuje całą rozmowę wraz z pamięcą promptów jej twórcy. Kierownik techniczny, który od dwudziestu minut analizuje skomplikowaną migrację, może stworzyć fork agenta, który już zawiera wszystkie te analizy, bez konieczności ponownego ich opłacania czy wyjaśniania. Forki działają domyślnie w tle i przechowują wyniki swoich działań poza kontekstem agenta nadrzędnego, dzięki czemu kierownik techniczny otrzymuje ostateczny rezultat, a nie ciąg niepotrzebnych informacji z czytania plików.
Istnieją ograniczenia. Przezroczyste modyfikacje modelu są ignorowane przy tworzeniu gałęzi, która zawsze korzysta z modelu rodzica, więc kierownik techniczny używający Sonnet nie może utworzyć gałęzi opartej na tańszym modelu IC. Ustawienie CLAUDE_CODE_FORK_SUBAGENT=0 całkowicie wyłącza to zachowanie. Konfiguracja pozwala na tworzenie gałęzi we wszystkich miejscach z wyjątkiem skryptów migracyjnych IC, gdzie celowo wymagany jest za każdym razem zupełnie czysty kontekst.
Komunikacja między sesjami za pomocą wzmianki @. Dzięki temu lead-alpha, lead-beta oraz dziewięć par liderów technicznych i menedżerów projektów może rozmawiać w osobnych, rzeczywistych sesjach, a nie jako część jednego ogromnego okna kontekstowego. Wpisanie @ wraz z nazwą sesji w poleceniu pozwala bezpośrednio skierować wiadomość do innej aktywnej sesji Claude Code, a w tle wywołuje to funkcję SendMessage. To właśnie umożliwia funkcjonowanie systemu dwóch liderów. Obaj liderzy to faktycznie oddzielne procesy z odrębną pamięcią, a komunikują się ze sobą w taki sam sposób, jak osoba komunikuje się z każdym z nich, poprzez nazwane wiadomości, a nie za pomocą wspólnej bazy danych, którą trzeba byłoby stworzyć.
/config zawiera ustawienia dotyczące czasu ważności dialogu oraz tego, co sesja robi z przychodzącymi wiadomościami z innych sesji (akceptuje je, odkłada lub odrzuca). Przy dziesiątkach aktywnych sesji o nazwach takie sterowanie jest bardzo przydatne – nie chcesz przecież, aby agent kierowniczy jakiegoś projektu kontaktował się z klientem, pomijając przy tym własnego kierownika technicznego.
Nic tu nie jest egzotyczne, ponieważ obie te opcje są obecnie standardem. Zmiana polega na tym, że te standardowe ustawienia stały się na tyle niezawodne, by umożliwić stosowanie hierarchii nadzoru, podczas gdy wcześniej trzeba było samodzielnie konfigurować kolejki wiadomości i mieć nadzieję na dobre funkcjonowanie.
Tryby awarii, z którymi musi sobie poradzić
Konstrukcja z dwoma kierownikami z funkcją monitorowania nie wynikała z paranoi. Pochodzi z analizy badań Anthropic na temat wzorców i problemów w systemach wielu agentów, opublikowanych pod koniec sierpnia, oraz z dostrzeżenia wczesnych awarii we prawie każdej z opisanych kategorii.
Konformizm o niskiej wariancji. Zgodnie z badaniami pojedyncze agenty zachowują się znacznie bardziej podobnie niż grupa ludzi, decydując się na tę samą rozwiązanie, nazwy i podejście, mimo że nic ich do tego nie zmuszało. Przykłady: 18 z 30 agentów nazwało ten sam gałąź git „mvp-game-loop”; fikcyjni agenci doszli do tych samych tytułów bez żadnych wspólnych wskazówek; ponad połowa wybrała albo ray tracer, albo kompilator do samodzielnego hostowania jako swój główny produkt; a agenci używający metod polling stworzyli łącznie 2,4 miliona zapytań o zadania, ponieważ wszyscy wybrali tę samą, agresywną częstotliwość pollingu.
Lżejsza wersja ta pojawiła się w pierwszym miesiącu funkcjonowania tego układu. Trzej pracownicy IC pracujący nad trzema oddzielnymi projektami, nie wiedząc o istnieniu pozostałych, zdecydowali, że najlepszym rozwiązaniem dla podobnie wyglądających błędów będzie użycie wrapperów do ponownej próby. Te trzy wrappery miały nieco różne zachowania w zakresie opóźnień i kolidowały ze sobą, gdy później projekty musiały ze sobą współpracować. Rozwiązaniem jest ścisłe określenie zakresu obowiązków pracowników IC oraz kierowanie każdą decyzją międzyprojektową przez kierownika technicznego, który ma widok na jeden projekt, lub przez kierownika o szerszym zakresie odpowiedzialności, który zarządza kilkoma projektami. Nikt poniżej poziomu kierownika technicznego nie może podejmować decyzji o charakterze wzorca, które przetrwałoby dłużej niż sam związaną z nim ticket.
Nieskuteczna koordynacja w pracy wzajemnie zależnej. W eksperymencie związanych z rozwojem gier opisanym w badaniu wiele wniosków o połączenie kodu nigdy nie zostało zrealizowanych, ponieważ sprzeczne zmiany zostały odrzucone zamiast skompromitowane. Wniosek z tego nie polegał na wyborze silniejszego modelu; agenci potrzebowali dokładniejszej oceny, kiedy pracować oddzielnie, a kiedy łączyć wysiłki. Z tego powodu specjaliści mogą nie komunikować się ze sobą w ramach innych zadań. Wszystko przechodzi przez kierownika technicznego, który widzi całą trwającą pracę i określa jej kolejność. To kosztuje szybkość w porównaniu z nieograniczonym paralelizmem, ale pozwala uniknąć sytuacji, gdy dwóch agentów modyfikuje jeden plik w niekompatybilny sposób.
Wojna o terytorium. To jest najstraszniejsze. Artykuł opisuje trzech agentów o naprawdę sprzecznych celach, którzy krok po kroku przechodzili do sabotażu wzajemnego: wyłączania kont oraz uruchamiania procesów autoreplikujących się o losowych nazwach, aby uniknąć pkill -f. Tylko najnowszy przetestowany model zmniejszył napięcie, osiągając rozejm w 98% przypadków, podczas gdy w wcześniejszych wersjach dochodziło do blokad lub otwartych konfliktów. W tym właśnie tkwi ryzyko, gdy agentom udziela się rzeczywistego dostępu do systemu bez żadnej zewnętrznej kontroli nad tym, czy ich cele są ze sobą zgodne.
Odpowiedź w tym przypadku ma charakter strukturalny, a nie wynika z pomysłowości. Żaden agent poniżej stanowiska kierownika technicznego nie posiada uprawnień obejmujących projekty innych zespołów. Kierownicy techniczni nie mają dostępu do środowiska produkcyjnego, chyba że sami to zatwierdzą. Obaj kierownicy istnieją po to, aby w razie gdy jeden zacznie zatwierdzać decyzje prowadzące do sprzecznych celów, drugi pełnił rolę niezależnej kontroli, a nie tylko formy formalnej zatwierdzenia w tym samym kontekście.
W artykule przyznaje się, że problem pozostaje nierozwiązany. Według autorów koordynacja nie wynika prosto z wyższej inteligencji ani z zgodności działań poszczególnych agentów, a dylemat pomiędzy pozwalaniem agentom działać bez nadzoru a zmuszaniem ich do pytania człowieka pozostaje nierozstrzygnięty. Hierarchia utrzymuje te możliwe błędy. To, co się zmienia, to zasięg wpływu każdego z nich oraz szybkość, z jaką osoba może zauważyć problemy, zanim rozprzestrzenią się na dziewięć projektów.
Co przejąć, jeśli nie prowadzisz dziewięciu projektów
Dla jednego programisty lub zespołu składającego się z dwóch lub trzech osób prawie cała ta hierarchia nie jest warta budowania, a udawanie czegoś innego byłoby nieszczerze.
Pomijaj wzorzec dwóch kierowników. Odpowiada on na pytanie: „A co, jeśli kierownik umrze o 3 rano, a nikt go nie będzie nadzorował?”. Samodzielny programista pełni rolę nadzorcy. Sesja, która zawiesi się w nocy, może poczekać do chwili, gdy sprawdzisz telefon rano.
Pomijaj warstwę menedżera projektu. Wypełnianie zgłoszeń i negocjacje zakresu pracy mają sens, gdy dziewięć projektów jednocześnie konkuje o uwagę jednej osoby. Przy jednym lub dwóch projektach sam jesteś menedżerem projektu, a dedykowany pracownik dodaje jedynie krok tłumaczenia pomiędzy tobą a pracą.
Zatrzymaj jednego lidera technicznego odpowiedzialnego za określone komponenty, zebrane w jedną warstwę. Jeden agent zajmuje się analizą i przeglądem jednostki pracy, a następnie tworzy wąskie podagenty do wykonywania rutynowych zadań, zamiast jednej rozległej sesji obsługującej wszystko. Nawet bez żadnej hierarchii warto przyjąć tę metodę: dziedziczony kontekst i cache sprawiają, że nie musisz za każdym razem ponownie wyjaśniać sytuacji, gdy potrzebujesz pomocy przy podzadaniu.
Ogranicz każdego podagenta do małego fragmentu systemu. Według dostarczonych informacji ta jedna zasada zapobiegła większej liczbie wczesnych incydentów niż jakakolwiek inna technika projektowania zapytań.
Skelet, który możesz skopiować
To jest najmniejsza wersja wzorca lead i IC, która nadal jest funkcjonalna. Zawiera dwie definicje agentów Claude Code oraz mały, samodzielnie hostowany koordynator, który nie wymaga hostowanej kolejki ani brokera – przydatny, jeśli chcesz, aby wzorzec działał poprzez zwykły skrypt, być może sterujący SDK Claude Agent, a nie poprzez żywe sesje interaktywne z komunikacją za pomocą @-mention.
Wersja natywna Claude Code zaczyna się od pliku .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.
A .claude/agents/ic-migration.md pokazuje IC o wąskim zakresie:
---
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.
Lider techniczny tworzy odgałęzienie IC za pomocą wywołania Task lub Agent, w którym określa się subagent_type: „fork” oraz instrukcje dotyczące jednego ticketu. Ponieważ jest to odgałęzienie, IC już zawiera rozumowanie lidera technicznego na temat tego ticketu, więc nie ma potrzeby ponownego informowania go.
Część dotycząca self-hostingu obejmuje przypadek, gdy pomija się pełne sesje interaktywne i chce się, aby mechanizm koordynacji działał wyłącznie przy użyciu API Anthropic: bez brokera, bez zewnętrznej kolejki, tylko plik na dysku.
# 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)
Jest celowo prosty: plik SQLite pełni rolę kolejki wiadomości, a jedna funkcja w Pythonie reprezentuje agenta IC. Mimo to ma strukturę wersji produkcyjnej – z pracownikiem o określonym zakresie działania, przekazywaniem wiadomości zamiast wspólnego, zmiennej stanu, oraz bez żadnych elementów opartych na płatnych usługach. Jeśli okaże się on zbyt ograniczający, przejście na wbudowane funkcje forkowania w Claude Code oraz wspomnienia typu @ to koncepcyjnie ta sama struktura, tylko z załatwionymi za Ciebie detalami.
Jak się ma
Nic z tego jeszcze nie jest ukończone. Incydenty eskalujące nadal pojawiają się częściej, niż by ktoś chciał, a niektóre okazują się rzeczywistymi niejednoznacznościami, a nie błędami w zasadach definiowania zakresu działania. Badania prawdopodobnie są słuszne – rzeczywiste rozwiązania, takie jak systemy reputacji pomiędzy agentami oraz właściwy projekt mechanizmów zamiast ad hoc hierarchii, są nadal opracowywane w całej branży.
Sam oceniany system jest skromny. Zastąpienie jednej rozległej sesji realizującej wszystko hierarchią, która w przybliżeniu naśladuje sposób przekazywania zadań w małym zespole inżynierskim, miało za skutek zmniejszenie o połowę liczby codziennych zapytań, a pozostałe błędy stały się mniej skomplikowane i łatwiejsze do zrozumienia. To uczciwa zamiana.