contenido
Introducción
Claude Code (v2.1.47) incluye una función experimental llamada Agent Teams: varias sesiones de Claude Code se coordinan para realizar un trabajo compartido mediante una topología con un líder y miembros del equipo. He estado desarrollando Hive, un orquestador de agentes de programación con objetivos similares pero una arquitectura muy diferente, así que quería entender cómo funciona por dentro el enfoque de Anthropic.
Este artículo documenta lo que descubrí mediante:
- La lectura de la documentación oficial
- El examen de artefactos reales que dejaron en disco sesiones anteriores del equipo
- El análisis del binario de Claude Code (v2.1.47) que dejé en manos de Claude para conocer los detalles de implementación (¡ja!)
Índice
- 1. Visión general de la arquitectura
- 2. La lista de tareas compartida
- 3. Comunicación entre agentes
- 4. Creación y ciclo de vida de los agentes
- 5. Controles de calidad y hooks
- 6. Economía de tokens
- 7. Resumen de la arquitectura
- Fuentes
1. Visión general de la arquitectura
Un equipo de agentes consta de cuatro componentes:
| Componente | Función |
|---|---|
| Líder del equipo | La sesión principal de Claude Code que crea el equipo e inicia a sus miembros |
| Miembros del equipo | Instancias independientes de Claude Code, cada una con su propia ventana de contexto |
| Lista de tareas | Elementos de trabajo compartidos almacenados como archivos JSON individuales en disco |
| Buzón | Archivos de bandeja de entrada por agente para la entrega de mensajes |
Toda la capa de coordinación está basada en archivos. El sistema de archivos en ~/.claude/ es la única base de coordinación:
~/.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
└── ...
Se trata de un diseño fundamentalmente descentralizado. El líder es simplemente otra sesión de Claude con herramientas adicionales (TeamCreate, TeamDelete, SendMessage). No hay ningún proceso en segundo plano. La coordinación surge del acceso a archivos compartidos.
En una sesión activa, si le pides a Claude que cree un equipo para realizar algún tipo de tarea y luego ejecutas lo siguiente en otra ventana, puedes observar cómo se actualiza el sistema de archivos en tiempo real.
watch -n 0.5 'tree ~/.claude/teams/ 2>/dev/null; echo "---"; tree ~/.claude/tasks/ 2>/dev/null'
Por ejemplo, con el siguiente 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
Observé esto:
teams
└── code-review
├── config.json
└── inboxes
├── bug-hunter.json
├── complexity-analyst.json
├── devils-advocate.json
└── team-lead.json
Configuración del equipo
La configuración del equipo en ~/.claude/teams/{team-name}/config.json contiene un array members que los miembros leen para descubrir a los demás:
{
"members": [
{ "name": "team-lead", "agentId": "abc-123", "agentType": "leader" },
{
"name": "researcher",
"agentId": "def-456",
"agentType": "general-purpose"
}
]
}
Los nombres son el principal mecanismo de direccionamiento (existen UUID, pero no se usan para el enrutamiento). Todos los mensajes y las asignaciones de tareas utilizan el campo name.
2. La lista de tareas compartida
Formato de los archivos
Cada tarea se almacena como un archivo JSON individual en ~/.claude/tasks/{team-name}/. Este es un ejemplo real de una sesión anterior:
{
"id": "1",
"subject": "Hunt for bugs across the codebase",
"description": "...",
"activeForm": "Hunting for bugs",
"owner": "bug-hunter",
"status": "completed",
"blocks": [],
"blockedBy": []
}
Esquema de las tareas:
| Campo | Tipo | Descripción |
|---|---|---|
id |
string | ID numérico, con incremento automático mediante .highwatermark |
subject |
string | Título en imperativo (p. ej., «Ejecuta las pruebas») |
description |
string | Requisitos detallados y criterios de aceptación |
activeForm |
string | Forma progresiva para el indicador de actividad («Ejecutando las pruebas») |
status |
string | pending → in_progress → completed (o deleted) |
blocks |
string[] | ID de las tareas que esta tarea bloquea |
blockedBy |
string[] | ID de las tareas que deben completarse antes de que esta tarea pueda empezar |
Control de concurrencia
Dos archivos especiales permiten la coordinación:
.lock: Un archivo de 0 bytes utilizado para la exclusión mutua a nivel del sistema de archivos (flock()). Presente en los 42 directorios de tareas observados en mi ordenador..highwatermark: Contiene un único número entero (p. ej.,"3","13"). Es el siguiente ID de tarea disponible para el incremento automático.
Reserva de tareas
La reserva de tareas utiliza bloqueos de archivos para evitar condiciones de carrera. Los miembros prefieren empezar por las tareas con el ID más bajo. Una tarea con un array blockedBy no vacío no puede reservarse hasta que todas las tareas que la bloquean estén en un estado final.
Observación: la mayoría de los directorios de tareas están vacíos
De los 42 directorios de tareas de mi ordenador, solo 5 contenían archivos JSON de tareas. Los otros 37 solo tenían .lock y .highwatermark. Esto probablemente significa que las tareas se eliminan tras completarse, o que se trataba de sesiones en las que Claude utilizó la lista de tareas interna (disponible desde el lanzamiento de la función de lista de tareas) sin descomponer el trabajo en archivos de subtareas.
3. Comunicación entre agentes
Patrón de buzón
Cada agente tiene un archivo que contiene un array JSON en ~/.claude/teams/{team-name}/inboxes/{agent-name}.json. Esta es una bandeja de entrada real de una sesión anterior en la que el líder del equipo asignó trabajo 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
}
]
Fíjate en la codificación JSON dentro de JSON: el campo text es una cadena JSON que contiene un objeto de mensaje serializado. El sobre externo tiene los campos from, text, timestamp y read.
Tipos de mensajes
El campo type dentro de la carga útil text admite:
| Tipo | Dirección | Propósito |
|---|---|---|
task_assignment |
líder → miembro | Asignar una tarea con todos los detalles |
message |
cualquiera → cualquiera | Mensaje directo a un destinatario |
broadcast |
líder → todos | El mismo mensaje para todos los miembros |
shutdown_request |
líder → miembro | Solicitar un cierre ordenado |
shutdown_response |
miembro → líder | Aprobar o rechazar el cierre |
plan_approval_request |
miembro → líder | Enviar un plan para su revisión |
plan_approval_response |
líder → miembro | Aprobar o rechazar con comentarios |
idle_notification |
miembro → líder | Se envía automáticamente cuando termina el turno del miembro |
Mecanismo de entrega
Ruta de escritura: El remitente añade una entrada al final del array JSON del archivo de bandeja de entrada del destinatario.
Ruta de lectura: El destinatario consulta periódicamente su propio archivo de bandeja de entrada. Los mensajes nuevos se inyectan como turnos de conversación sintéticos (aparecen como si los hubiera enviado un usuario).
Difusión: Literalmente escribe el mismo mensaje en el archivo de bandeja de entrada de cada miembro. El coste en tokens aumenta linealmente con el tamaño del equipo.
La comunicación consiste simplemente en añadir datos a un archivo y leerlo. La latencia entre el envío y la recepción depende del intervalo de sondeo del destinatario.
Visibilidad de los mensajes directos entre miembros
Cuando un miembro envía un mensaje directo a otro, se incluye un breve resumen en la notificación de inactividad que recibe el líder. Esto permite al líder conocer la colaboración entre miembros sin recibir el contenido completo del mensaje.
4. Creación y ciclo de vida de los agentes
Cómo se crean los miembros del equipo
Cada miembro es un proceso independiente de la CLI claude. El líder los inicia mediante la herramienta Task con los parámetros team_name y name. Se establecen variables de entorno en el proceso creado:
CLAUDE_CODE_TEAM_NAME: se establece automáticamente en los miembros creadosCLAUDE_CODE_PLAN_MODE_REQUIRED: se establece entruesi se requiere la aprobación del plan
Inicialización del contexto
Los miembros cargan el mismo contexto del proyecto que cualquier sesión nueva:
- Archivos
CLAUDE.mddel directorio de trabajo - Servidores MCP
- Skills
- El prompt de creación del líder
El historial de conversación del líder NO se transfiere. Cada miembro empieza de cero, con el prompt de creación como único contexto.
Implementación interna
Según el análisis del binario de Claude Code v2.1.47, el contexto de los miembros se gestiona mediante AsyncLocalStorage con estos campos:
agentId,agentName,teamNameparentSessionId,colorplanModeRequired
Funciones internas clave:
isTeammate()/isTeamLead(): detección de roleswaitForTeammatesToBecomeIdle(): primitiva de sincronización para el lídergetTeammateContext()/setDynamicTeamContext(): gestión del contexto en tiempo de ejecución
Detección de inactividad
Después de cada turno del LLM, un miembro pasa automáticamente a estar inactivo y envía un idle_notification al líder. Este es el estado normal de reposo, no una condición de error ni una señal de que se haya quedado sin actualizar. Enviar un mensaje a un miembro inactivo lo reactiva (el siguiente ciclo de sondeo recoge el mensaje de la bandeja de entrada).
Protocolo de cierre
- El líder envía
shutdown_requesta un miembro - El miembro puede aprobarlo (termina de forma ordenada) o rechazarlo (continúa trabajando y ofrece una explicación)
- La limpieza del equipo mediante
TeamDeleteelimina~/.claude/teams/{team-name}/y~/.claude/tasks/{team-name}/ - La limpieza falla si todavía hay miembros activos; primero hay que cerrarlos
Herencia de permisos
Los miembros heredan el modo de permisos del líder al crearse. Si el líder se ejecuta con --dangerously-skip-permissions, todos los miembros también. Los modos individuales pueden cambiarse después de la creación, pero no pueden configurarse por miembro en el momento de crearlos.
5. Controles de calidad y hooks
Agent Teams se integra con el sistema de hooks de Claude Code para exigir el cumplimiento de los criterios de calidad:
Hook TeammateIdle
Se activa cuando un miembro está a punto de pasar a estar inactivo. El código de salida 2 envía el contenido de stderr como comentarios e impide que pase a estar inactivo, por lo que sigue trabajando.
{
"hook_event_name": "TeammateIdle",
"teammate_name": "researcher",
"team_name": "my-project"
}
Hook TaskCompleted
Se activa cuando una tarea se está marcando como completada. El código de salida 2 impide que se complete y devuelve el contenido de stderr como comentarios.
{
"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"
}
Esto ocurre en dos situaciones: (1) cuando cualquier agente marca explícitamente una tarea como completada mediante TaskUpdate, o (2) cuando un miembro de un equipo de agentes termina su turno con tareas en curso.
Tipos de manejadores de hooks
| Tipo | Descripción |
|---|---|
command |
Script de shell. JSON en stdin y códigos de salida para las decisiones. |
prompt |
Evaluación con un LLM en un solo turno. Devuelve {ok, reason}. |
agent |
Subagente de varios turnos con herramientas de lectura. Hasta 50 turnos. |
6. Economía de tokens
Los equipos de agentes utilizan aproximadamente 7 veces más tokens que las sesiones estándar cuando los miembros se ejecutan en modo de planificación. Cada miembro mantiene su propia ventana de contexto completa como una instancia independiente de Claude.
Referencia de base
- Uso medio de Claude Code: ~6 $/desarrollador/día
- Equipos de agentes: el consumo adicional sobre la base es aproximadamente proporcional al tamaño del equipo
7. Resumen de la arquitectura
| Dimensión | Agent Teams de Claude Code |
|---|---|
| Base de coordinación | Archivos planos (~/.claude/tasks/, ~/.claude/teams/) |
| Formato de las tareas | Un archivo JSON por tarea + .lock para reservarlas |
| Mensajería | Archivos JSON de bandeja de entrada (añadir al final + sondear) |
| Ciclo de vida de los agentes | Procesos de CLI que se gestionan por sí mismos |
| Aislamiento del trabajo | Directorio de trabajo compartido |
| Estrategia de fusión | Ninguna (los agentes editan los archivos directamente) |
| Reintentos/escalado | Manual (el líder decide o interviene el usuario) |
| Topología | Líder + miembros al mismo nivel, mensajería entre pares |
| Planificación | Reserva autónoma (los miembros toman la siguiente tarea) |
| Persistencia del estado | Solo archivos; sin reanudación de miembros dentro del proceso |
| Controles de calidad | Hooks de shell (TeammateIdle, TaskCompleted) |
| Seguimiento de tokens | Solo por sesión, sin agregación entre agentes |
| Detección de bloqueos | Manual (el usuario se da cuenta de que un miembro se ha detenido) |
| Control de concurrencia | Implícito (tamaño del equipo = número de miembros) |
| Modelo de dependencias | blocks/blockedBy en los archivos de tareas |
Fuentes
Documentación oficial
- Equipos de sesiones de Claude Code: arquitectura
- Modo interactivo — Lista de tareas
- Equipos de agentes — Asignar y reservar tareas
- Equipos de agentes — Contexto y comunicación
- Equipos de agentes — Cerrar miembros del equipo
- Equipos de agentes — Limpiar el equipo
- Equipos de agentes — Permisos
- Equipos de agentes — Limitaciones
- Equipos de agentes — Buenas prácticas
- Hooks
- Costes — Costes de tokens de los equipos de agentes
- Configuración
- Subagentes
Artefactos en disco (Claude Code v2.1.47)
Observados en /Users/tau/.claude/:
- Directorios de equipos con archivos
config.jsonyinboxes/{agent-name}.json - Directorios de tareas con
.lock,.highwatermarky archivos JSON individuales de tareas - Mensaje de ejemplo de asignación de tarea de
team-leadacp-agent, con la marca de tiempo2026-02-18T02:37:16.890Z
Análisis del binario
Binario de Claude Code v2.1.47. Funciones internas identificadas mediante el análisis de cadenas: getTeamName, getAgentName, getAgentId, isTeammate, isTeamLead, waitForTeammatesToBecomeIdle, getTeammateContext, setDynamicTeamContext, createTeammateContext. Campos del contexto de AsyncLocalStorage: agentId, agentName, teamName, parentSessionId, color, planModeRequired.