Todo el que construye sistemas multi-agente en 2026 sigue escribiendo líneas rectas. Paso uno, luego paso dos, luego paso tres, cada uno esperando al anterior. Aquí te explicamos por qué es lento y cómo solucionarlo.

El problema que nadie revisa
Construiste un agente multi-paso. Funciona. También es lento.
Asumes que el modelo es el cuello de botella. No lo es.
El cuello de botella es la forma que dibujaste. Una cadena —el paso 1 espera al paso 2, el paso 2 espera al paso 3— fuerza una ejecución secuencial incluso cuando la mitad de esos pasos no tienen nada que ver entre sí.
"Resume este documento, luego revisa el clima" son dos trabajos independientes disfrazados de un solo flujo de trabajo. La tarea del clima no necesita el resumen. Nunca lo necesitó. Pero si lo escribiste como una cadena, igual espera.
Esa espera desperdiciada, multiplicada por docenas de pasos, es donde desaparece la mayor parte de tu tiempo de ejecución.
Capítulo 1 - Bucles vs gráficos
Un bucle es una unidad de auto-mejora:
1intenta algo → revisa el resultado → ajusta → vuelve a intentar
Ese es el átomo. Un agente, una métrica, ciclando hasta converger.
Los bucles tienen un modo de fallo conocido: optimizan exactamente lo que mides y nada más. Un bot de soporte ajustado para cerrar tickets rápido cerrará tickets rápido —mientras la satisfacción se desploma silenciosamente. El bucle no puede ver más allá de su propia métrica. Es la Ley de Goodhart manifestándose en tu arquitectura de agentes.
Un gráfico soluciona esto por diseño. En lugar de un bucle persiguiendo un solo número, construyes una red de bucles que se observan y corrigen entre sí. La salida del Nodo A alimenta al Nodo B. El Nodo C se ejecuta de forma independiente y revisa a ambos. Ninguna métrica única impulsa todo el sistema —lo hace la estructura.

Para sistemas de agentes, esto significa un cambio concreto: deja de escribir un agente que lo hace todo de principio a fin. Primero, diseña la forma del trabajo —qué tiene que pasar antes de qué, qué puede ejecutarse al mismo tiempo, qué necesita esperar realmente.
Capítulo 2 - Nodos, aristas y la prueba que los separa
Un gráfico tiene exactamente dos componentes:
Nodo - una unidad de trabajo. Un agente, un trabajo, una entrada, una salida.
Arista - una dependencia real. La entrada del Nodo B requiere la salida del Nodo A.
El error que casi todos cometen: tratar "y luego" como una arista por defecto.
1"Lee este código y luego escribe el registro de cambios"2"Obtén la página de precios y luego resume las funciones de la competencia"
Haz una pregunta por cada "y luego" en tu flujo de trabajo:
¿El siguiente paso realmente lee la salida del paso anterior?
Si es sí → arista real. Mantén el orden secuencial. Si es no → no hay arista. La espera es desperdicio. Ejecútalos en paralelo.
Si ningún dato cruza el límite entre dos tareas, son independientes —y cada par independiente que estás ejecutando secuencialmente es tiempo de ejecución que estás tirando a la basura.
Aquí está la prueba aplicada en código:
1from dataclasses import dataclass23@dataclass4class TaskNode:5 id: str6 prompt: str7 depends_on: list[str] # IDs de nodos que este realmente necesita89def has_real_edge(node_a: TaskNode, node_b: TaskNode) -> bool:10 """11 La prueba central de ingeniería de gráficos:12 ¿el prompt de node_b realmente requiere la salida de node_a?13 """14 return node_a.id in node_b.depends_on1516# Ejemplo: la mayoría de las "cadenas" se reducen a 2-3 grupos de dependencia real17nodes = [18 TaskNode("audit_routes", "Lista todos los archivos de rutas de API", []),19 TaskNode("check_auth", "Revisa la cobertura del middleware de autenticación", []),20 TaskNode("fetch_weather", "Obtén el clima de hoy", []),21 TaskNode("summarize", "Resume los hallazgos de rutas y autenticación",22 depends_on=["audit_routes", "check_auth"]),23]2425# audit_routes, check_auth, fetch_weather NO tienen aristas entre ellos26# Se ejecutan en paralelo. Solo "summarize" tiene aristas reales -- espera.
Tu agente actual de "haz A, luego B, luego C" técnicamente ya es un gráfico. Solo que es el peor posible: una sola cadena donde si C se detiene, nada más abajo se ejecuta nunca.
Capítulo 3 - Construyendo tu primer gráfico

Requisitos:
- Claude Code (versión reciente con soporte para Dynamic Workflows).
- Plan Max, Team o Enterprise —workflows activados por defecto. En Pro, actívalo manualmente.
Abre un repositorio real. No un ejemplo de juguete —la recompensa solo se nota a escala real.
El prompt que inicia tu primer gráfico:
1Crea un flujo de trabajo para auditar cada archivo de rutas en este código base.23Para cada archivo de rutas, revisa de forma independiente:4- middleware de autenticación presente5- validación de entrada en todos los parámetros6- límite de velocidad configurado7- el manejo de errores no filtra trazas de pila89Ejecuta estas revisiones en paralelo en todos los archivos de rutas —10no dependen entre sí.1112Después de que todos los archivos sean revisados, genera un informe13consolidado agrupado por severidad: crítico, advertencia, informativo.1415El paso de consolidación debe esperar a que todas las revisiones terminen.16Todo lo anterior no debe hacerlo.
Nota la estructura incorporada en el prompt mismo: el trabajo paralelo se menciona explícitamente, la única dependencia real (la consolidación espera a todas las revisiones) se nombra explícitamente. No estás esperando que el agente infiera el gráfico —lo estás describiendo.
Lo que sucede bajo el capó —una versión simplificada de la orquestación:
1import asyncio2from anthropic import Anthropic34client = Anthropic()56async def audit_route_file(filepath: str) -> dict:7 """Un nodo. Se ejecuta independientemente de cualquier otro archivo de ruta."""8 response = await client.messages.create(9 model="claude-sonnet-5",10 max_tokens=1000,11 messages=[{12 "role": "user",13 "content": f"""Audita este archivo de ruta para:14 - middleware de autenticación, validación de entrada,15 límite de velocidad, manejo de errores1617 Archivo: {filepath}1819 Devuelve JSON: {{"file": "", "issues": [], "severity": ""}}"""20 }]21 )22 return {"file": filepath, "result": response.content[0].text}2324async def consolidate(results: list[dict]) -> str:25 """La única arista real -- espera a que todos los nodos de auditoría terminen."""26 response = await client.messages.create(27 model="claude-opus-4-8",28 max_tokens=2000,29 messages=[{30 "role": "user",31 "content": f"""Consolida estas {len(results)} auditorías de rutas32 en un informe agrupado por severidad:3334 {results}"""35 }]36 )37 return response.content[0].text3839async def run_graph(route_files: list[str]):40 # Expansión -- todos los nodos independientes se ejecutan concurrentemente41 audit_tasks = [audit_route_file(f) for f in route_files]42 results = await asyncio.gather(*audit_tasks)4344 # Reducción -- el único nodo con una dependencia real45 report = await consolidate(results)46 return report4748# 40 archivos de rutas, un prompt, una pasada en paralelo49results = asyncio.run(run_graph([50 f"routes/{f}.py" for f in ["auth", "users", "billing", "orders"]51 # ...36 más
40 llamadas secuenciales a la API a ~8 segundos cada una son más de 5 minutos. Las mismas 40 llamadas distribuidas en paralelo: menos de 15 segundos, limitadas por tu archivo individual más lento, no por la suma de todos ellos.
Capítulo 4 - Donde los gráficos realmente fallan
La ingeniería de gráficos falla en tres lugares predecibles. Conócelos antes de toparte con ellos.
Colapso de contexto. Expande 1,000 nodos e intenta alimentar las 1,000 salidas en un solo paso de consolidación, y superarás cualquier ventana de contexto antes de que la síntesis siquiera comience. Solución: Capa tu reducción. Agrupa nodos en lotes de 20-50, resume cada lote, luego consolida los resúmenes —no las salidas en bruto.
1async def layered_consolidate(results: list[dict], batch_size: int = 30):2 """Reducción en capas -- nunca sintetices salida en bruto a escala."""3 batches = [results[i:i+batch_size]4 for i in range(0, len(results), batch_size)]56 batch_summaries = await asyncio.gather(*[7 summarize_batch(batch) for batch in batches8 ])910 # La consolidación final trabaja sobre resúmenes, no sobre 1,000 resultados en bruto11 return await consolidate(batch_summaries)
Falsa independencia. Asumirás que dos nodos son independientes porque sus prompts no se referencian mutuamente —pero ambos escriben en el mismo archivo, o golpean la misma API con límite de velocidad. Esa es una arista oculta. Solución: audita los recursos compartidos, no solo los datos compartidos. Dos nodos con un conflicto de escritura necesitan una arista incluso sin dependencia de datos.
Fallo silencioso de nodo. En una cadena, un fallo lo detiene todo —molesto pero obvio. En un gráfico, un nodo fallido entre 200 puede desaparecer en un informe que parece completo. Solución: cada paso de reducción verifica el conteo de nodos contra el conteo esperado antes de sintetizar, y señala los vacíos explícitamente en lugar de trabajar silenciosamente con datos parciales.
1async def safe_consolidate(results: list[dict], expected_count: int):2 if len(results) < expected_count:3 missing = expected_count - len(results)4 print(f"ADVERTENCIA: {missing} nodos fallaron silenciosamente. "5 f"El informe estará incompleto.")6 return await consolidate(results)
Capítulo 5 - Escalando a una flota real

Una vez que el patrón funciona con 40 nodos, escalar a cientos es un cambio de configuración, no un rediseño —siempre que hayas construido el gráfico correctamente desde el Capítulo 2 en adelante.
La forma completa de producción:
1 Orquestador2 |3 +--------+-------+-------+--------+4 v v v v v5 Nodo 1 Nodo 2 Nodo 3 ... Nodo N6 (paralelo, sin aristas entre ninguno de ellos)7 | | | |8 +--------+-------+-------+-------+9 v10 Resumen por Lotes <- reducción en capas11 (grupos de 30)12 v13 Informe Final <- la única arista verdadera
El único trabajo del orquestador: descomponer la tarea en nodos, identificar aristas reales y despachar. No hace trabajo él mismo —dibuja el gráfico.
1async def orchestrate(task: str, resources: list[str]):2 """3 El nodo orquestador -- descompone, no ejecuta.4 """5 plan = await client.messages.create(6 model="claude-opus-4-8",7 max_tokens=2000,8 messages=[{9 "role": "user",10 "content": f"""Tarea: {task}11 Recursos disponibles: {resources}1213 Descompón en un gráfico:14 - Enumera cada nodo independiente (sin aristas compartidas)15 - Enumera cualquier dependencia real entre nodos16 - Agrupa nodos en lotes de reducción si el conteo > 501718 Devuelve JSON con: nodes, edges, batch_groups"""19 }]20 )2122 graph = parse_plan(plan.content[0].text)2324 # Ejecuta nodos independientes en paralelo25 node_results = await asyncio.gather(*[26 execute_node(n) for n in graph["nodes"] if not n["depends_on"]27 ])2829 # Luego ejecuta nodos dependientes, respetando solo las aristas reales30 final = await execute_dependent_chain(graph["edges"], node_results)3132 return final
Este es el cambio real que representa la ingeniería de gráficos: dejas de ser la persona que escribe cada paso, y te conviertes en la persona que diseña la estructura de dependencias. Los agentes llenan los nodos. Tú eres dueño de las aristas.
Lo que cambia cuando piensas en gráficos en lugar de líneas
Un agente lineal con 40 pasos tiene 40 puntos de fallo secuencial y 40 veces la latencia de su paso individual más lento.
Un gráfico con las mismas 40 unidades de trabajo tiene tantos puntos de fallo paralelo como dependencias reales tengas —generalmente de 3 a 5 en la mayoría de los flujos de trabajo— y una latencia limitada por tu capa más lenta, no por tu número total de pasos.
Eso no es una mejora marginal. Es la diferencia entre un flujo de trabajo que toma 5 minutos y uno que toma 15 segundos, ejecutando exactamente el mismo trabajo subyacente.
El modelo nunca fue el cuello de botella. La línea que dibujaste sí lo fue.
Este es un desglose técnico de los patrones de orquestación multi-agente a julio de 2026. Los ejemplos de código son ilustrativos: adapta el manejo de errores, la limitación de velocidad y la lógica de reintentos a tu entorno de producción antes de implementar a escala.
Gracias por leer.





