содержание
Введение
В Claude Code (v2.1.47) есть экспериментальная функция Agent Teams: несколько сессий Claude Code координируют совместную работу по схеме «лидер и участники команды». Я разрабатываю Hive — мультиагентный оркестратор для работы с кодом со сходными целями, но совсем другой архитектурой. Поэтому мне захотелось разобраться, как подход Anthropic работает изнутри.
В этой статье я описываю, что выяснил:
- Прочитав официальную документацию
- Изучив реальные артефакты, оставшиеся на диске после предыдущих сессий команд
- Поручив Claude проанализировать бинарный файл Claude Code (v2.1.47), чтобы разобраться в деталях реализации (ха!)
Оглавление
- 1. Обзор архитектуры
- 2. Общий список задач
- 3. Взаимодействие агентов
- 4. Запуск агентов и жизненный цикл
- 5. Проверки качества и хуки
- 6. Экономика токенов
- 7. Сводка по архитектуре
- Источники
1. Обзор архитектуры
Команда агентов состоит из четырёх компонентов:
| Компонент | Роль |
|---|---|
| Лидер команды | Основная сессия Claude Code, которая создаёт команду и запускает участников |
| Участники команды | Отдельные экземпляры Claude Code, каждый со своим контекстным окном |
| Список задач | Общие задачи, хранящиеся в отдельных JSON-файлах на диске |
| Почтовый ящик | Файлы входящих сообщений для каждого агента |
Весь слой координации основан на файлах. Файловая система в ~/.claude/ — единственная основа координации:
~/.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
└── ...
Это принципиально децентрализованная архитектура. Лидер — просто ещё одна сессия Claude с дополнительными инструментами (TeamCreate, TeamDelete, SendMessage). Фонового процесса нет. Координация возникает благодаря совместному доступу к файлам.
Если в активной сессии попросить Claude создать команду для какой-либо задачи, а затем выполнить следующее в другом окне, можно наблюдать изменения файловой системы в реальном времени.
watch -n 0.5 'tree ~/.claude/teams/ 2>/dev/null; echo "---"; tree ~/.claude/tasks/ 2>/dev/null'
Например, с таким промптом:
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
Я наблюдал следующее:
teams
└── code-review
├── config.json
└── inboxes
├── bug-hunter.json
├── complexity-analyst.json
├── devils-advocate.json
└── team-lead.json
Конфигурация команды
Конфигурация команды в ~/.claude/teams/{team-name}/config.json содержит массив members, который участники читают, чтобы узнавать друг о друге:
{
"members": [
{ "name": "team-lead", "agentId": "abc-123", "agentType": "leader" },
{
"name": "researcher",
"agentId": "def-456",
"agentType": "general-purpose"
}
]
}
Имена — основной механизм адресации (UUID существуют, но для маршрутизации не используются). При обмене сообщениями и назначении задач всегда используется поле name.
2. Общий список задач
Формат файлов
Каждая задача хранится в отдельном JSON-файле в ~/.claude/tasks/{team-name}/. Вот реальный пример из предыдущей сессии:
{
"id": "1",
"subject": "Hunt for bugs across the codebase",
"description": "...",
"activeForm": "Hunting for bugs",
"owner": "bug-hunter",
"status": "completed",
"blocks": [],
"blockedBy": []
}
Схема задачи:
| Поле | Тип | Описание |
|---|---|---|
id |
string | Числовой ID, автоматически увеличиваемый через .highwatermark |
subject |
string | Заголовок в повелительном наклонении (например, «Запусти тесты») |
description |
string | Подробные требования и критерии приёмки |
activeForm |
string | Формулировка в Present Continuous для индикатора загрузки («Выполняются тесты») |
status |
string | pending → in_progress → completed (или deleted) |
blocks |
string[] | ID задач, которые блокирует эта задача |
blockedBy |
string[] | ID задач, которые должны завершиться до начала этой задачи |
Управление конкурентным доступом
Координацию обеспечивают два специальных файла:
.lock: файл размером 0 байт для взаимного исключения на уровне файловой системы (flock()). Присутствует во всех 42 каталогах задач, которые я обнаружил на своём компьютере..highwatermark: содержит одно целое число (например,"3","13"). Это следующий доступный ID задачи для автоматической нумерации.
Взятие задач в работу
При взятии задач в работу используются файловые блокировки, чтобы предотвратить гонки. Участники предпочитают сначала брать задачи с наименьшим ID. Задачу с непустым массивом blockedBy нельзя взять в работу, пока все блокирующие задачи не перейдут в конечное состояние.
Наблюдение: большинство каталогов задач пусты
Из 42 каталогов задач на моём компьютере только 5 содержали реальные JSON-файлы задач. В остальных 37 были только .lock и .highwatermark. Вероятно, это означает, что задачи удаляются после завершения, либо это были сессии, в которых Claude использовал внутренний список задач (доступный с момента появления этой функции), не разбивая работу на файлы подзадач.
3. Взаимодействие агентов
Схема с почтовыми ящиками
У каждого агента есть файл с JSON-массивом в ~/.claude/teams/{team-name}/inboxes/{agent-name}.json. Вот реальный почтовый ящик из предыдущей сессии, в которой лидер команды поручил работу 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
}
]
Обратите внимание на кодирование JSON внутри JSON: поле text — это JSON-строка, содержащая сериализованный объект сообщения. Внешняя оболочка содержит поля from, text, timestamp и read.
Типы сообщений
Поле type внутри полезной нагрузки text поддерживает следующие значения:
| Тип | Направление | Назначение |
|---|---|---|
task_assignment |
лидер → участник | Назначить задачу со всеми подробностями |
message |
любой → любой | Личное сообщение одному получателю |
broadcast |
лидер → все | Одно и то же сообщение каждому участнику |
shutdown_request |
лидер → участник | Запросить корректное завершение работы |
shutdown_response |
участник → лидер | Одобрить или отклонить завершение работы |
plan_approval_request |
участник → лидер | Отправить план на проверку |
plan_approval_response |
лидер → участник | Одобрить или отклонить с пояснениями |
idle_notification |
участник → лидер | Автоматически отправляется по завершении хода участника |
Механизм доставки
Запись: отправитель добавляет новую запись в файл JSON-массива входящих сообщений получателя.
Чтение: получатель периодически опрашивает собственный файл входящих сообщений. Новые сообщения вставляются в диалог как искусственные ходы (они выглядят так, будто их отправил пользователь).
Широковещательная рассылка: буквально записывает одно и то же сообщение в файл входящих сообщений каждого участника. Расход токенов растёт линейно с размером команды.
Всё взаимодействие сводится к добавлению записей в файл + чтению файла. Задержка между отправкой и получением зависит от интервала опроса у получателя.
Видимость личных сообщений между участниками
Когда один участник отправляет личное сообщение другому, в уведомление о переходе в ожидание, которое получает лидер, включается краткая сводка. Так лидер может следить за взаимодействием участников, не получая полного текста сообщений.
4. Запуск агентов и жизненный цикл
Как создаются участники команды
Каждый участник — это отдельный процесс CLI claude. Лидер запускает их через инструмент Task с параметрами team_name и name. Для запускаемого процесса задаются переменные окружения:
CLAUDE_CODE_TEAM_NAME: автоматически задаётся для запускаемых участниковCLAUDE_CODE_PLAN_MODE_REQUIRED: получает значениеtrue, если требуется одобрение плана
Инициализация контекста
Участники загружают тот же контекст проекта, что и любая новая сессия:
- Файлы
CLAUDE.mdиз рабочего каталога - Серверы MCP
- Навыки
- Стартовый промпт от лидера
История диалога лидера НЕ передаётся. Каждый участник начинает с нуля, имея в контексте только стартовый промпт.
Внутренняя реализация
Анализ бинарного файла Claude Code v2.1.47 показал, что контекст участника управляется через AsyncLocalStorage со следующими полями:
agentId,agentName,teamNameparentSessionId,colorplanModeRequired
Ключевые внутренние функции:
isTeammate()/isTeamLead(): определение ролиwaitForTeammatesToBecomeIdle(): примитив синхронизации для лидераgetTeammateContext()/setDynamicTeamContext(): управление контекстом во время выполнения
Определение состояния ожидания
После каждого хода LLM участник автоматически переходит в ожидание и отправляет idle_notification лидеру. Это штатное состояние ожидания, а не ошибка или признак устаревшего состояния. Сообщение участнику в состоянии ожидания пробуждает его (при следующем опросе он обнаруживает сообщение в почтовом ящике).
Протокол завершения работы
- Лидер отправляет
shutdown_requestучастнику - Участник может одобрить запрос (корректно завершает работу) или отклонить его (продолжает работать, сообщив причину)
- Очистка команды через
TeamDeleteудаляет~/.claude/teams/{team-name}/и~/.claude/tasks/{team-name}/ - Очистка завершается ошибкой, если какие-либо участники ещё активны; сначала нужно завершить их работу
Наследование разрешений
При запуске участники наследуют режим разрешений лидера. Если лидер запускается с --dangerously-skip-permissions, все участники тоже. Режимы отдельных участников можно изменить после запуска, но задать их для каждого участника при запуске нельзя.
5. Проверки качества и хуки
Agent Teams интегрируется с системой хуков Claude Code для обеспечения качества:
Хук TeammateIdle
Срабатывает, когда участник собирается перейти в ожидание. Код выхода 2 передаёт содержимое stderr в качестве обратной связи и блокирует переход в ожидание, чтобы участник продолжал работу.
{
"hook_event_name": "TeammateIdle",
"teammate_name": "researcher",
"team_name": "my-project"
}
Хук TaskCompleted
Срабатывает при попытке отметить задачу как завершённую. Код выхода 2 не даёт завершить задачу и передаёт содержимое stderr в качестве обратной связи.
{
"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"
}
Этот хук срабатывает в двух случаях: (1) когда любой агент явно отмечает задачу как завершённую через TaskUpdate или (2) когда участник команды агентов завершает свой ход, имея задачи в работе.
Типы обработчиков хуков
| Тип | Описание |
|---|---|
command |
Скрипт оболочки. JSON на stdin, решения определяются кодами выхода. |
prompt |
Оценка с помощью LLM за один ход. Возвращает {ok, reason}. |
agent |
Субагент с инструментами чтения, работающий в несколько ходов. До 50 ходов. |
6. Экономика токенов
Команды агентов расходуют примерно в 7 раз больше токенов, чем обычные сессии, когда участники работают в режиме планирования. Каждый участник — отдельный экземпляр Claude с собственным полным контекстным окном.
Базовый ориентир
- Средние расходы на Claude Code: ~$6 на разработчика в день
- Команды агентов: затраты сверх базового уровня примерно пропорциональны размеру команды
7. Сводка по архитектуре
| Аспект | Claude Code Agent Teams |
|---|---|
| Основа координации | Плоские файлы (~/.claude/tasks/, ~/.claude/teams/) |
| Формат задач | Отдельный JSON-файл на задачу + .lock для взятия в работу |
| Обмен сообщениями | JSON-файлы входящих сообщений (добавление + опрос) |
| Жизненный цикл агентов | Самоуправляемые CLI-процессы |
| Изоляция работы | Общий рабочий каталог |
| Стратегия слияния | Отсутствует (агенты редактируют файлы напрямую) |
| Повторные попытки/эскалация | Вручную (решает лидер или вмешивается пользователь) |
| Топология | Лидер + участники одного уровня, прямой обмен сообщениями между участниками |
| Планирование | Самостоятельное взятие задач в работу (участники берут следующую задачу) |
| Сохранность состояния | Только файлы; возобновление работы участников внутри процесса не поддерживается |
| Проверки качества | Хуки оболочки (TeammateIdle, TaskCompleted) |
| Учёт токенов | Только по сессиям, без суммирования по агентам |
| Обнаружение зависаний | Вручную (пользователь замечает, что участник остановился) |
| Управление параллелизмом | Неявное (размер команды = число участников) |
| Модель зависимостей | blocks/blockedBy в файлах задач |
Источники
Официальная документация
- Команды сессий Claude Code: архитектура
- Интерактивный режим — список задач
- Команды агентов — назначение и взятие задач в работу
- Команды агентов — контекст и взаимодействие
- Команды агентов — завершение работы участников
- Команды агентов — очистка команды
- Команды агентов — разрешения
- Команды агентов — ограничения
- Команды агентов — рекомендации
- Хуки
- Затраты — расходы на токены для команды агентов
- Настройки
- Субагенты
Артефакты на диске (Claude Code v2.1.47)
Обнаружены в /Users/tau/.claude/:
- Каталоги команд с файлами
config.jsonиinboxes/{agent-name}.json - Каталоги задач с
.lock,.highwatermarkи отдельными JSON-файлами задач - Пример сообщения о назначении задачи от
team-leadкcp-agentс временной меткой2026-02-18T02:37:16.890Z
Анализ бинарного файла
Бинарный файл Claude Code v2.1.47. Внутренние функции, выявленные при анализе строк: getTeamName, getAgentName, getAgentId, isTeammate, isTeamLead, waitForTeammatesToBecomeIdle, getTeammateContext, setDynamicTeamContext, createTeammateContext. Поля контекста AsyncLocalStorage: agentId, agentName, teamName, parentSessionId, color, planModeRequired.