Phase 16 - Lesson 01

¿Por qué Multiagente?

Un solo agente se topa con un muro. La jugada inteligente no es un agente más grande, sino más agentes.

Type: Learn Languages: TypeScript Prerequisites: Phase 14 (Agent Engineering) Time: ~60 minutos

Objetivos de Aprendizaje

  • Identificar el límite del agente único (saturación de contexto, especialidades mixtas, cuello de botella secuencial) y explicar cuándo dividir el trabajo en múltiples agentes es la decisión correcta
  • Comparar patrones de orquestación (pipeline, fan-out paralelo, supervisor, jerárquico) y seleccionar el adecuado para la estructura de una tarea dada
  • Diseñar un sistema multiagente con límites de roles claros, estado compartido y un contrato de comunicación
  • Analizar las ventajas y desventajas de la complejidad multiagente (latencia, costo, dificultad de depuración) frente a la simplicidad del agente único

El Problema

Construiste un solo agente en la Phase 14. Funciona. Puede leer archivos, ejecutar comandos, llamar a APIs y razonar sobre los resultados. Luego lo orientas a una base de código real: 200 archivos, tres lenguajes, pruebas que dependen de la infraestructura y el requisito de investigar APIs externas antes de escribir código.

El agente se ahoga. No porque el LLM sea tonto, sino porque la tarea supera lo que un bucle de agente único puede manejar. La ventana de contexto se llena con el contenido de los archivos. El agente olvida lo que leyó hace 40 llamadas de herramientas. Intenta ser investigador, codificador y revisor a la vez, y hace las tres cosas mal.

Este es el límite del agente único. Te topas con él cada vez que una tarea requiere:

  • Más contexto del que cabe en una ventana - leer 50 archivos supera los 200k tokens
  • Diferentes especialidades en diferentes etapas - la investigación requiere un prompting diferente al de la generación de código
  • Trabajo que puede ocurrir en paralelo - ¿por qué leer tres archivos secuencialmente cuando puedes leerlos simultáneamente?

El Concepto

El Límite del Agente Único

Un solo agente es un bucle, una ventana de contexto, un system prompt. Imagínalo:

┌─────────────────────────────────────────┐
│            SINGLE AGENT                 │
│                                         │
│  ┌───────────────────────────────────┐  │
│  │         Context Window            │  │
│  │                                   │  │
│  │  research notes                   │  │
│  │  + code files                     │  │
│  │  + test output                    │  │
│  │  + review feedback                │  │
│  │  + API docs                       │  │
│  │  + ...                            │  │
│  │                                   │  │
│  │  ██████████████████████ FULL ███  │  │
│  └───────────────────────────────────┘  │
│                                         │
│  One system prompt tries to cover       │
│  research + coding + review + testing   │
│                                         │
│  Result: mediocre at everything         │
└─────────────────────────────────────────┘

Tres cosas fallan:

  1. Saturación de contexto - los resultados de las herramientas se acumulan. Para el turno 30, el agente ha consumido 150k tokens de contenidos de archivos, salidas de comandos y razonamiento previo. Se pierden detalles críticos del turno 5.

  2. Confusión de roles - un system prompt que dice "eres investigador, codificador, revisor y evaluador" produce un agente que investiga a medias, codifica a medias y nunca termina de revisar.

  3. Cuello de botella secuencial - el agente lee el archivo A, luego el archivo B, luego el archivo C. Tres llamadas consecutivas de LLM. Tres ejecuciones de herramientas en serie. Sin paralelismo.

La Solución Multiagente

Divide el trabajo. Dale a cada agente una tarea, una ventana de contexto y un system prompt adaptado para esa tarea:

┌──────────────────────────────────────────────────────────┐
│                    ORCHESTRATOR                          │
│                                                          │
│  "Build a REST API for user management"                  │
│                                                          │
│         ┌──────────┬──────────┬──────────┐               │
│         │          │          │          │               │
│         ▼          ▼          ▼          ▼               │
│   ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐  │
│   │RESEARCHER│ │  CODER   │ │ REVIEWER │ │  TESTER  │  │
│   │          │ │          │ │          │ │          │  │
│   │ Reads    │ │ Writes   │ │ Checks   │ │ Runs     │  │
│   │ docs,    │ │ code     │ │ code     │ │ tests,   │  │
│   │ finds    │ │ based on │ │ quality, │ │ reports  │  │
│   │ patterns │ │ research │ │ finds    │ │ results  │  │
│   │          │ │ + spec   │ │ bugs     │ │          │  │
│   └─────┬────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘  │
│         │           │            │             │         │
│         └───────────┴────────────┴─────────────┘         │
│                          │                               │
│                     Merge results                        │
└──────────────────────────────────────────────────────────┘

Cada agente tiene:

  • Un system prompt enfocado ("Eres un revisor de código. Tu único trabajo es encontrar errores.")
  • Su propia ventana de contexto (no contaminada por el trabajo de otros agentes)
  • Un contrato claro de entrada/salida (recibe notas de investigación, genera código)

Sistemas Reales que Hacen Esto

Subagentes de Claude Code - cuando Claude Code genera un subagente con Task, crea un agente hijo con una tarea delimitada. El padre mantiene limpio su contexto. El hijo realiza el trabajo enfocado y devuelve un resumen.

Devin - ejecuta un agente planificador, un agente codificador y un agente de navegación. El planificador divide el trabajo en pasos. El codificador escribe el código. El navegador investiga la documentación. Cada uno tiene un contexto independiente.

Equipos de desarrollo multiagente (SWE-bench) - los sistemas con mejor rendimiento en SWE-bench utilizan un investigador que lee la base de código, un planificador que diseña la corrección y un codificador que la implementa. Los sistemas de agente único obtienen puntuaciones más bajas.

ChatGPT Deep Research - genera múltiples agentes de búsqueda en paralelo, cada uno explorando una perspectiva diferente, y luego sintetiza los resultados.

El Espectro

Multiagente no es binario. Es un espectro:

SIMPLE ──────────────────────────────────────────── COMPLEX

 Single        Sub-         Pipeline      Team         Swarm
 Agent         agents

 ┌───┐       ┌───┐        ┌───┐───┐    ┌───┐───┐    ┌─┐┌─┐┌─┐
 │ A │       │ A │        │ A │ B │    │ A │ B │    │ ││ ││ │
 └───┘       └─┬─┘        └───┘─┬─┘    └─┬─┘─┬─┘    └┬┘└┬┘└┬┘
               │                │        │   │       ┌┴──┴──┴┐
             ┌─┴─┐          ┌───┘───┐    │   │       │shared │
             │ a │          │ C │ D │  ┌─┴───┴─┐    │ state │
             └───┘          └───┘───┘  │  msg   │    └───────┘
                                       │  bus   │
 1 loop      Parent +      Stage by    │       │    N peers,
 1 context   child tasks   stage       └───────┘    emergent
                                       Explicit      behavior
                                       roles

Agente único (Single agent) - un bucle, un prompt. Adecuado para tareas sencillas.

Subagentes (Subagents) - un padre genera hijos para subtareas enfocadas. El padre mantiene el plan. Los hijos reportan de vuelta. Esto es lo que hace Claude Code.

Pipeline - los agentes se ejecutan en secuencia. La salida del Agente A se convierte en la entrada del Agente B. Adecuado para flujos de trabajo por etapas: investigación -> código -> revisión -> prueba.

Equipo (Team) - los agentes se ejecutan en paralelo con un bus de mensajes compartido. Cada uno tiene un rol. Un orquestador coordina. Adecuado cuando se necesitan diferentes habilidades simultáneamente.

Enjambre (Swarm) - muchos agentes idénticos o casi idénticos con estado compartido. Sin orquestador fijo. Los agentes toman tareas de una cola. Adecuado para tareas paralelas de alto rendimiento.

Los Cuatro Patrones Multiagente

Patrón 1: Pipeline

Input ──▶ Agent A ──▶ Agent B ──▶ Agent C ──▶ Output
          (research)  (code)      (review)

Cada agente transforma los datos y los envía al siguiente. Fácil de analizar. Una falla en una etapa bloquea las demás.

Patrón 2: Fan-out / Fan-in

                ┌──▶ Agent A ──┐
                │              │
Input ──▶ Split ├──▶ Agent B ──├──▶ Merge ──▶ Output
                │              │
                └──▶ Agent C ──┘

Divide el trabajo entre agentes paralelos y luego fusiona los resultados. Adecuado para tareas que se descomponen en subtareas independientes.

Patrón 3: Orquestador-Trabajador (Orchestrator-Worker)

                    ┌──────────┐
                    │  Orch.   │
                    └──┬───┬───┘
                  task │   │ task
                 ┌─────┘   └─────┐
                 ▼               ▼
           ┌──────────┐   ┌──────────┐
           │ Worker A │   │ Worker B │
           └──────────┘   └──────────┘

Un orquestador inteligente decide qué hacer, delega en los trabajadores y sintetiza los resultados. El orquestador es en sí mismo un agente con herramientas para generar trabajadores.

Patrón 4: Enjambre de Pares (Peer Swarm)

          ┌───┐ ◄──── msg ────▶ ┌───┐
          │ A │                  │ B │
          └─┬─┘                  └─┬─┘
            │                      │
       msg  │    ┌───────────┐     │ msg
            └───▶│  Shared   │◄────┘
                 │  State    │
            ┌───▶│  / Queue  │◄────┐
            │    └───────────┘     │
       msg  │                      │ msg
          ┌─┴─┐                  ┌─┴─┐
          │ C │ ◄──── msg ────▶ │ D │
          └───┘                  └───┘

Sin orquestador central. Los agentes se comunican de par a par (peer-to-peer). Las decisiones surgen de la interacción. Más difícil de depurar, pero escala a muchos agentes.

Cuándo NO usar Multiagente

Multiagente añade complejidad. Cada mensaje entre agentes es un punto potencial de falla. La depuración pasa de "leer una conversación" a "rastrear mensajes a través de cinco agentes".

Quédate con un solo agente cuando:

  • La tarea quepa en una ventana de contexto (menos de ~100k tokens de datos de trabajo)
  • No necesites system prompts diferentes para etapas distintas
  • La ejecución secuencial sea lo suficientemente rápida
  • La tarea sea tan simple que dividirla añada más sobrecarga que valor

El costo de la complejidad:

  • Cada límite de agente es un paso de compresión con pérdida: el contexto completo del agente A se resume en un mensaje para el agente B
  • La lógica de coordinación (quién hace qué, cuándo y en qué orden) es su propia fuente de fallos (bugs)
  • La latência aumenta: N agentes significan un mínimo de N llamadas consecutivas de LLM, y más si necesitan comunicarse entre sí de forma interactiva
  • El costo se multiplica: cada agente consume tokens de manera independiente

Regra general: si una tarea toma menos de 20 llamadas a herramientas y cabe en 100k tokens, mantenla con un solo agente.

Constrúyelo

Paso 1: El Agente Único Sobrecargado

Aquí tienes un solo agente intentando hacer todo. Tiene un system prompt masivo y una ventana de contexto que contiene investigación, código y revisiones:

type AgentResult = {
  content: string;
  tokensUsed: number;
  toolCalls: number;
};

async function singleAgentApproach(task: string): Promise<AgentResult> {
  const systemPrompt = `You are a full-stack developer. You must:
1. Research the requirements
2. Write the code
3. Review the code for bugs
4. Write tests
Do ALL of these in a single conversation.`;

  const contextWindow: string[] = [];
  let totalTokens = 0;
  let totalToolCalls = 0;

  const research = await fakeLLMCall(systemPrompt, `Research: ${task}`);
  contextWindow.push(research.output);
  totalTokens += research.tokens;
  totalToolCalls += research.calls;

  const code = await fakeLLMCall(
    systemPrompt,
    `Given this research:\n${contextWindow.join("\n")}\n\nNow write code for: ${task}`
  );
  contextWindow.push(code.output);
  totalTokens += code.tokens;
  totalToolCalls += code.calls;

  const review = await fakeLLMCall(
    systemPrompt,
    `Given all previous context:\n${contextWindow.join("\n")}\n\nReview the code.`
  );
  contextWindow.push(review.output);
  totalTokens += review.tokens;
  totalToolCalls += review.calls;

  return {
    content: contextWindow.join("\n---\n"),
    tokensUsed: totalTokens,
    toolCalls: totalToolCalls,
  };
}

Problemas con este enfoque:

  • La ventana de contexto crece con cada etapa. Para el paso de revisión, contiene notas de investigación Y código Y razonamiento previo.
  • El system prompt es genérico. No se puede ajustar para cada etapa.
  • Nada se ejecuta en paralelo.

Paso 2: Agentes Especialistas

Ahora divídelo. Cada agente tiene una sola tarea:

type SpecialistAgent = {
  name: string;
  systemPrompt: string;
  run: (input: string) => Promise<AgentResult>;
};

function createSpecialist(name: string, systemPrompt: string): SpecialistAgent {
  return {
    name,
    systemPrompt,
    run: async (input: string) => {
      const result = await fakeLLMCall(systemPrompt, input);
      return {
        content: result.output,
        tokensUsed: result.tokens,
        toolCalls: result.calls,
      };
    },
  };
}

const researcher = createSpecialist(
  "researcher",
  "You are a technical researcher. Read documentation, find patterns, and summarize findings. Output only the facts needed for implementation."
);

const coder = createSpecialist(
  "coder",
  "You are a senior TypeScript developer. Given requirements and research notes, write clean, tested code. Nothing else."
);

const reviewer = createSpecialist(
  "reviewer",
  "You are a code reviewer. Find bugs, security issues, and logic errors. Be specific. Cite line numbers."
);

Cada especialista tiene un prompt enfocado. Cada uno obtiene una ventana de contexto limpia con solo la entrada que necesita.

Paso 3: Coordinación a través de Mensajes

Conecta a los especialistas mediante el paso explícito de mensajes:

type AgentMessage = {
  from: string;
  to: string;
  content: string;
  timestamp: number;
};

async function multiAgentApproach(task: string): Promise<AgentResult> {
  const messages: AgentMessage[] = [];
  let totalTokens = 0;
  let totalToolCalls = 0;

  const researchResult = await researcher.run(task);
  messages.push({
    from: "researcher",
    to: "coder",
    content: researchResult.content,
    timestamp: Date.now(),
  });
  totalTokens += researchResult.tokensUsed;
  totalToolCalls += researchResult.toolCalls;

  const coderInput = messages
    .filter((m) => m.to === "coder")
    .map((m) => `[From ${m.from}]: ${m.content}`)
    .join("\n");

  const codeResult = await coder.run(coderInput);
  messages.push({
    from: "coder",
    to: "reviewer",
    content: codeResult.content,
    timestamp: Date.now(),
  });
  totalTokens += codeResult.tokensUsed;
  totalToolCalls += codeResult.toolCalls;

  const reviewerInput = messages
    .filter((m) => m.to === "reviewer")
    .map((m) => `[From ${m.from}]: ${m.content}`)
    .join("\n");

  const reviewResult = await reviewer.run(reviewerInput);
  messages.push({
    from: "reviewer",
    to: "orchestrator",
    content: reviewResult.content,
    timestamp: Date.now(),
  });
  totalTokens += reviewResult.tokensUsed;
  totalToolCalls += reviewResult.toolCalls;

  return {
    content: messages.map((m) => `[${m.from} -> ${m.to}]: ${m.content}`).join("\n\n"),
    tokensUsed: totalTokens,
    toolCalls: totalToolCalls,
  };
}

Cada agente recibe únicamente los mensajes dirigidos a él. Sin contaminación de contexto. La lectura de 50k tokens de documentación del investigador nunca ingresa al contexto del revisor.

Paso 4: Comparación

async function compare() {
  const task = "Build a rate limiter middleware for an Express.js API";

  console.log("=== Single Agent ===");
  const single = await singleAgentApproach(task);
  console.log(`Tokens: ${single.tokensUsed}`);
  console.log(`Tool calls: ${single.toolCalls}`);

  console.log("\n=== Multi-Agent ===");
  const multi = await multiAgentApproach(task);
  console.log(`Tokens: ${multi.tokensUsed}`);
  console.log(`Tool calls: ${multi.toolCalls}`);
}

La versión multiagente utiliza más tokens en total (tres agentes, tres llamadas a LLM independientes), pero el contexto de cada agente se mantiene limpio. La calidad de cada etapa mejora debido a la especialización del system prompt.

Úsalo

Esta lección produce un prompt reutilizable para decidir cuándo optar por un enfoque multiagente. Consulta outputs/prompt-multi-agent-decision.md.

Ejercicios

  1. Agrega un cuarto especialista: un agente "probador" (tester) que reciba el código del codificador y los comentarios de revisión del revisor, y luego escriba pruebas
  2. Modifica el pipeline para que el revisor pueda enviar comentarios de vuelta al codificador para un ciclo de revisión (máximo 2 rondas)
  3. Convierte el pipeline secuencial en un fan-out: ejecuta en paralelo el investigador y un agente de "análisis de requisitos", y luego fusiona sus salidas antes de pasarlas al codificador

Términos Clave

Término Lo que la gente dice Lo que realmente significa
Swarm "Una mente colmena de agentes de IA" Un conjunto de agentes de igual nivel con estado compartido y sin líder fijo. El comportamiento surge de interacciones locales.
Orchestrator "El agente jefe" Un agente cuyas herramientas incluyen la creación y gestión de otros agentes. Planifica y delega, pero puede que no realice el trabajo real.
Coordinator "El policía de tráfico" Un componente que no es un agente (a menudo solo código, no un LLM) que enruta los mensajes entre agentes según reglas predefinidas.
Consensus "Los agentes se ponen de acuerdo" Un protocolo donde múltiples agentes deben llegar a un acuerdo antes de proceder. Se utiliza cuando las salidas en conflicto requieren resolución.
Emergent behavior "Los agentes lo descifraron por sí mismos" Patrones a nivel de sistema que surgen de las interacciones entre agentes, pero que no fueron programados explícitamente. Pueden ser útiles o dañinos.
Fan-out / fan-in "Map-reduce para agentes" División de una tarea entre agentes paralelos (fan-out) y la posterior combinación de sus resultados (fan-in).
Message passing "Los agentes hablan entre sí" El mecanismo de comunicación entre agentes: datos estructurados enviados de un agente a otro, que reemplazan las ventanas de contexto compartidas.

Lecturas Adicionales

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).