Agent Mentor Learn
De los bucles a los grafos: ingeniería de orquestación para sistemas de agentes · Lección 3 de 6

Lección 3: Paralelización: seccionamiento y votación

Objetivos de aprendizaje:

  • Distinguir entre las dos variantes de la paralelización —seccionamiento y votación—, entender qué resuelve cada una y respetar la frontera que traza «las salidas se agregan programáticamente»
  • Usar Promise.all y un pool de concurrencia propio para implementar el seccionamiento, asegurando que la agregación pase referencias y no cargas útiles
  • Calcular los tres costos del fan-out (los resultados inundando el contexto, los topes de concurrencia de los productos reales, el multiplicador de N× tokens de la votación) y usarlos para decidir si una propuesta debería paralelizarse

Requisitos: Completaste las Lecciones 1 y 2, tienes el envoltorio runAgent() de la Lección 2 | Anterior: << Lección 2: Encadenar y enrutar: encadenamiento y enrutamiento | Siguiente: Lección 4 >>

Doce documentos, una cadena, una hora en la fila

La cadena de la Lección 2 ya funciona: esquema → compuerta → borrador → compuerta → revisar terminología. Cambia los prompts por otros centrados en la revisión —extraer puntos clave, sugerir correcciones, verificar términos— y la forma de la cadena no cambia. Ejecútala sobre un documento: seis o siete minutos.

Entonces el equipo de producto te deja un directorio sobre el escritorio: 12 documentos, cada uno necesita la misma pasada de revisión.

Escribes un bucle for, lo arrancas y te vas a preparar un café. Una hora después vuelves. El registro se detuvo en el documento 9. El documento 10 está extrayendo puntos clave.

Durante esa hora, la máquina pasó la mayor parte del tiempo esperando. Esperando la respuesta de la API del documento 1 antes de enviar la solicitud del documento 2. Esperando a que el documento 2 terminara sus cuatro etapas antes de que le tocara el turno al documento 3. Hazte una pregunta práctica: ¿la conclusión de revisión del documento 3 depende de una sola palabra del resultado del documento 2?

No. Son 12 documentos independientes. A sus informes de revisión no les importa quién termine primero. En el encadenamiento, la espera tiene una razón: la entrada del paso siguiente es la salida del anterior. Aquí no hay tal razón. Estas 12 ejecuciones están en fila únicamente porque un bucle for las puso en fila.

El curso 6 de esta serie ya cubrió los patrones de colaboración de fan-out y agregación y cómo funciona la votación multiperspectiva1. Esta lección lo convierte en código y salda las cuentas: el fan-out no es gratis. La velocidad es real, y el costo también.

Definición: pueden ejecutarse a la vez, con las salidas agregadas programáticamente

Empieza por la formulación original. Los LLM a veces pueden trabajar sobre una tarea de forma simultánea y tener sus salidas agregadas programáticamente. Este flujo de trabajo es la paralelización, con dos variaciones clave1:

  • Seccionamiento (sectioning): Partir una tarea en subtareas independientes ejecutadas en paralelo1. Revisar 12 documentos es seccionamiento.
  • Votación (voting): Ejecutar la misma tarea varias veces para obtener salidas diversas1. Hacer que tres perspectivas evalúen cada una el mismo texto es votación.

Cuándo usarla: cuando las subtareas divididas se pueden paralelizar para ganar velocidad, o cuando hacen falta varias perspectivas o intentos para obtener resultados con mayor confianza1. Hay un añadido fácil de saltarse pero valioso: para tareas complejas con múltiples consideraciones, los LLM en general se desempeñan mejor cuando cada consideración la maneja una llamada al LLM aparte, permitiendo atención enfocada en cada aspecto específico1. Traducción: el seccionamiento y la votación no son solo ahorradores de tiempo. Meter «legal, seguridad, marca» en un solo prompt frente a tener tres llamadas vigilando cada una una cosa produce calidades distintas.

Una media oración más que hay que clavar: las salidas se agregan programáticamente. Después de que vuelven los resultados repartidos en abanico, es tu código el que juzga, filtra y resume; no otra llamada al modelo que lea los 12 informes y escriba un resumen. Que agregue el modelo es un patrón distinto. El orquestador de la Lección 4 hace exactamente ese trabajo. Traza la línea con claridad aquí. La documentación actual de la plataforma Claude enumera Parallelization dentro de la orquestación multiagente: repartir en abanico subtareas independientes a la vez (buscar en varias fuentes, analizar archivos separados) y que el coordinador sintetice los resultados2; nota que en esa versión el coordinador hace la agregación, mientras que esta lección escribe la versión de agregación programática1. La misma palabra: quién hace la agregación son dos cosas distintas.

Seccionamiento: cambiar el bucle for por Promise.all

La versión en serie se ve así, y el tiempo total es la suma de los 12:

La versión de seccionamiento cambia una línea, y el tiempo total se acerca al del más lento:

runAgent() sigue en la misma posición de envoltorio de la Lección 2: un bucle de arnés completo, que internamente se bifurca por stop_reason. Las respuestas simuladas de esta lección se completan todas en un turno (end_turn), así que la versión del script final omite la rama de herramienta como simplificación. Al conectar con un cliente real o si hacen falta herramientas, trae de vuelta sin cambios la versión con despacho de herramientas de la Lección 2. La paralelización no altera ni una línea de este bucle en sí. Solo deja de hacer que estos bucles esperen en fila.

La agregación ocurre en la línea siguiente, hecha por este código:

Estas tres líneas no contienen una segunda llamada al modelo. filter, reduce, una comprobación de umbral: todo código determinista. Los mismos 12 informes entran, la misma conclusión de una línea sale cada vez. Ese es el beneficio de mantener la agregación en código: las 12 llamadas repartidas en abanico son no deterministas, el paso de fusión es determinista. Cuando algo se rompe, sabes de qué lado sospechar.

Promise.all tiene un temperamento que conviene conocer de antemano: si una sola promesa se rechaza, el await entero se rechaza, y aunque las otras 11 hayan terminado, no puedes obtener sus resultados. Revisas 12 documentos, el documento 7 se topa con un 500 y el lote entero se desperdicia: los otros 11 se ejecutaron para nada. Ese costo no es razonable. O te cambias a Promise.allSettled, o como en el ejercicio de esta lección, envuelves cada trabajador en try/catch para recoger las fallas como registros: cada camino del fan-out debería poder fallar de forma independiente.

Por qué la paralelización no es solo cuestión de velocidad

Si la paralelización fuera solo «la misma cosa hecha antes», sería un truco de rendimiento, no algo que merezca su propia lección. La verdadera razón está del lado del contexto.

La retrospectiva de Anthropic sobre su sistema multiagente de investigación es tajante: la esencia de la búsqueda es la compresión —destilar hallazgos de un corpus enorme—. Los subagentes facilitan la compresión al operar en paralelo con sus propias ventanas de contexto, explorando distintos aspectos de la pregunta a la vez antes de condensar los tokens más importantes para el agente investigador principal. Cada subagente además aporta separación de responsabilidades —herramientas, prompts y trayectorias de exploración distintas—, lo que reduce la dependencia del camino y permite investigaciones exhaustivas e independientes3.

Desarma esas dos oraciones. El fan-out compra al menos tres cosas:

  1. Capacidad de ventana. Escribieron este juicio arquitectónico como conclusión: distribuir el trabajo entre agentes con ventanas de contexto separadas añade capacidad para el razonamiento en paralelo3. La Lección 1 cubrió esto: lo que de verdad topa con el techo no es el tamaño de la ventana, es «un solo bucle» como forma. El fan-out rodea los límites de una sola ventana no estirándola sino abriendo varias.
  2. Separación de responsabilidades. Tres subagentes que llevan herramientas y prompts distintos naturalmente no se contaminan entre sí.
  3. Menos dependencia del camino. En un solo bucle, el juicio del paso 3 queda sesgado por la redacción del paso 2. Tres trayectorias independientes no comparten el mismo sesgo.

La documentación actual de la plataforma ofrece la misma dirección: varios agentes pueden actuar en paralelo con su propio contexto aislado, lo que ayuda a mejorar la calidad de la salida y también puede mejorar el tiempo hasta la finalización2. Fíjate en que la calidad va primero.

Del lado de la velocidad dieron un número, con un contexto que hay que copiar junto: sus primeros agentes ejecutaban búsquedas secuenciales, lo que era dolorosamente lento. Por velocidad introdujeron dos clases de paralelización: (1) el agente principal levanta 3 a 5 subagentes en paralelo en lugar de en serie; (2) los subagentes usan 3 o más herramientas en paralelo. Estos cambios recortaron el tiempo de investigación hasta en un 90 % para consultas complejas3.

Este número hay que usarlo con sus tres calificativos: es un número de latencia, no de calidad; está limitado a consultas complejas (las consultas simples no tienen mucho que paralelizar); viene de su propio sistema. Cuánto ahorras tú cambiando for por Promise.all depende de qué parte de tus subtareas es de verdad independiente, de qué tan lento es cada camino y de dónde se atasca la concurrencia; de eso trata el resto de esta lección.

Primer costo: la agregación se come de vuelta el contexto que ahorraste

Al repartir en abanico, todo el mundo mira «cuántos caminos se ejecutan a la vez». Los desastres suelen ocurrir en el camino de vuelta.

La documentación de subagentes de Claude Code pone este costo sobre la mesa: cuando los subagentes terminan, sus resultados vuelven a tu conversación principal. Ejecutar muchos subagentes que devuelven cada uno resultados detallados puede consumir contexto considerable4. Los subagentes existen para proteger el contexto de la conversación principal —manteniendo la exploración y la implementación fuera de tu conversación principal4—, pero en cuanto lo que vuelve pesa demasiado, la protección se invierte.

La misma retrospectiva dio un remedio, y lo dio de forma específica: en lugar de exigir que los subagentes comuniquen todo a través del agente principal, implementa sistemas de artefactos donde los agentes especializados puedan crear salidas que persistan de forma independiente. Los subagentes llaman a herramientas para guardar su trabajo en sistemas externos, y luego pasan referencias ligeras de vuelta al coordinador3.

Traduce eso a un lema para escribir código: pasa referencias, no cargas útiles.

¿Qué tan grande es la diferencia? El script del ejercicio de esta lección te da un número real: 8 salidas en disco suman 2195 bytes, lo que vuelve a la agregación son solo 718 bytes, y esta proporción se ensancha rápidamente a medida que los documentos crecen: con informes diez veces más largos, lo que se pasa de vuelta sigue siendo un resumen de una línea más una ruta. Quien necesite el texto completo, que lo lea desde la ruta.

Este camino compra otras cosas de paso. La documentación de flujos de trabajo menciona que el runtime rastrea el resultado de cada agente a medida que avanza la ejecución, que es lo que hace que una ejecución sea reanudable dentro de la misma sesión. Un flujo de trabajo que reparte el trabajo en abanico entre muchos agentes pequeños preserva por tanto más progreso que un solo agente largo5. Las salidas aterrizan afuera, dejando registro línea por línea; la parte del registro es el mismo principio que la observabilidad del curso 11 de esta serie. La parte de «interrumpido a mitad de ejecución no empieza desde cero» es territorio del curso 9, «que las tareas largas sobrevivan a una interrupción».

Segundo costo: la concurrencia nunca es ilimitada

En el momento en que escribes Promise.all(docs.map(...)), en realidad estás diciendo «concurrencia = longitud del arreglo». El arreglo es 12, bien. El arreglo es 200, otra historia.

Mira los techos de tres productos reales:

  • Claude Code: por defecto, cuando hay 20 subagentes ejecutándose en una sesión, generar otro con la herramienta Agent falla con Concurrent subagent limit reached, y el mensaje de error le dice explícitamente a Claude que no reintente4.
  • El runtime de flujos de trabajo de Claude Code: hasta 16 agentes concurrentes, menos cuando Claude Code dispone de menos CPU (incluido dentro de un contenedor con CPU limitada)5; 1000 agentes en total por ejecución5.
  • Managed Agents: se soporta un máximo de 25 hilos concurrentes. El coordinador puede llamar a varias copias de un mismo agente de la lista, creando varios hilos asociados a un agente2.

Tres equipos distintos, tres implementaciones distintas, todos fijan techos, y los números ni siquiera son grandes. Este hecho por sí mismo es material didáctico: el fan-out ilimitado es un accidente, no una optimización. (La Lección 4 cubrirá un caso real de desastre: los primeros agentes generaban 50 subagentes para una consulta simple3. Para entonces descubrirás que «quién decide cuántos generar» es más peliagudo que «cuál es el techo».)

La regulación más barata es el lote:

Funciona, pero tiene efecto cubeta: cada lote espera a que termine su más lento antes de empezar el siguiente. De tres documentos, uno es especialmente largo, y los otros dos caminos se quedan esperando.

Un pool de concurrencia no tiene este problema: fija N «carriles», y cada carril toma el siguiente elemento de un cursor compartido en cuanto termina su trabajo actual, con siempre N en vuelo:

Unas diez líneas, sin dependencias. cursor++ es seguro en JavaScript de un solo hilo: el código síncrono entre dos await no se puede interrumpir. No hay riesgo de que dos carriles tomen el mismo índice. En el ejercicio le añadirás try/catch para que la falla de un solo camino no arrastre al lote entero.

¿Cuánto debería valer limit? No hay una respuesta universal. Es la intersección de tu cuota de API, la capacidad del servicio aguas abajo y la duración de un solo camino. Pero rellenar un número específico frente a no rellenar ninguno son dos clases distintas de ingeniería.

Tercer costo: la votación paga N× tokens

La definición de la votación es una oración: ejecutar la misma tarea varias veces para obtener salidas diversas1. El código es corto:

La agregación aquí sigue estando hecha por código: filter más un umbral. Cuántos votos fijan el umbral es una decisión de producto, fijada en duro, cambiable en cualquier momento, auditable. No habría que dejársela a la improvisación del modelo. Los escenarios de alto riesgo pueden ajustar el umbral a «un veto», y los de bajo riesgo pueden exigir los tres votos para bloquear.

La contabilidad es directa: vota N veces, paga N× tokens. Pon este dinero junto al multiplicador de la Lección 1: según sus datos, los agentes usan unas 4× los tokens de las interacciones de chat, y los sistemas multiagente unas 15×. Los sistemas multiagente necesitan por tanto tareas lo bastante valiosas como para cubrir el costo de esa mejora de rendimiento3. La etiqueta de precio de la votación de tres perspectivas es ese 4× de un solo agente multiplicado por 3. No multipliques 15× por 3: ese 15× ya incluye la contabilidad del fan-out.

Así que la votación no es «ejecutar unas cuantas veces más para quedarse tranquilo». Tiene que comprar algo concreto. La documentación de flujos de trabajo es más clara: mover el plan al código también le permite a un flujo de trabajo aplicar un patrón de calidad repetible, no solo ejecutar más agentes; puede hacer que agentes independientes revisen de forma adversarial los hallazgos de los demás antes de que se reporten, o redactar un plan desde varios ángulos y sopesarlos entre sí, de modo que obtengas un resultado más confiable que el de una sola pasada5.

«Agentes independientes que se revisan entre sí» es la misma regla que la del curso 10 de esta serie: quien hace el trabajo no juzga su propio trabajo. Si haces que el modelo se autorrevise en el mismo contexto, la mayoría de las veces defenderá lo que acaba de producir. Cámbialo a un camino de contexto independiente, cambia los prompts, y entonces sí podría atrapar problemas. Lo que compran la votación y la revisión entre pares no es «la mayoría», es la independencia.

Nota una frontera: tres llamadas a un mismo modelo no son tres jueces independientes. Comparten los mismos sesgos de entrenamiento. La votación puede filtrar el ruido de muestreo y los huecos de atención de una sola pasada. No puede filtrar sesgos sistemáticos. No la trates como un mecanismo que produce verdad solo por votar.

Medida: la independencia es un prerrequisito, no algo opcional

Todos los beneficios de esta lección descansan sobre un prerrequisito que ha aparecido una y otra vez y vale la pena sacar aparte: las subtareas de verdad no tienen que depender unas de otras.

La documentación de Claude Code, al hablar de varios subagentes investigando a la vez, añade específicamente una oración: cada subagente explora su área de forma independiente, y luego Claude sintetiza los hallazgos. Esto funciona mejor cuando los caminos de investigación no dependen unos de otros4. La condición inversa está escrita en la retrospectiva multiagente: algunos dominios requieren que todos los agentes compartan el mismo contexto o implican muchas dependencias entre agentes, y esos dominios hoy no encajan bien con los sistemas multiagente. Por ejemplo, la mayoría de las tareas de programación implican menos tareas verdaderamente paralelizables que la investigación, y los agentes LLM todavía no son muy buenos coordinándose con otros agentes y delegándoles en tiempo real3.

Para juzgar si una propuesta debería paralelizarse, hazte una pregunta: ¿el camino 2 necesita esperar la conclusión del camino 1 para saber qué hacer?

  • Necesita esperar → esta no es la forma de la paralelización. La salida del paso anterior es la entrada del siguiente: eso es el encadenamiento de la Lección 2.
  • No necesita esperar → seccionamiento.
  • La misma cosa, y quieres varios juicios independientes → votación.

El primer caso es el que más fácilmente se desdibuja: hay una dependencia pero fuerzas el fan-out porque «probablemente salga bien». El resultado son varios caminos de agentes escribiendo cada uno sus propias conclusiones, sin saber de los demás. En la agregación limas las contradicciones a mano: el tiempo ahorrado se va entero en limar, y además pagaste tokens de más.

Frontera: la descomposición de esta lección está predefinida

Clava una palabra: es la entrada a la Lección 4.

En todos los ejemplos de esta lección, ¿quién definió las subtareas? Tú. Doce documentos, los leíste del directorio. Tres perspectivas, las fijaste en duro en un arreglo. Antes de que el código se ejecute, cuántos caminos se reparten en abanico y qué hace cada uno está todo resuelto. A esto se le llama descomposición predefinida.

Su opuesto: el modelo decide en cuántas partes y qué hace cada una. La fuente original trata esta diferencia como la divisoria clave entre dos patrones: en el flujo de trabajo de orquestador-trabajadores, un LLM central descompone tareas dinámicamente, las delega en LLM trabajadores y sintetiza sus resultados1. Aunque es topográficamente similar a la paralelización, la diferencia clave es su flexibilidad: las subtareas no están predefinidas, sino determinadas por el orquestador a partir de la entrada específica1.

Así que la frontera entre las dos no es «cuántos caminos se ejecutan a la vez», sino «quién escribió ese arreglo». Si el arreglo es tuyo, es esta lección. Si el arreglo lo genera el modelo en el momento, es la lección siguiente. El enrutamiento de la lección anterior ya le entregó una decisión al modelo (qué rama). La lección siguiente le entrega un trozo más grande: la descomposición misma.

💻 Ejercicios

Resumen

  • La definición de la paralelización es «los LLM a veces pueden trabajar sobre una tarea de forma simultánea y tener sus salidas agregadas programáticamente», y sus dos variantes son el seccionamiento (partir una tarea en subtareas independientes ejecutadas en paralelo) y la votación (ejecutar la misma tarea varias veces para obtener salidas diversas)1
  • Sus condiciones son que las subtareas se puedan paralelizar para ganar velocidad, o que hagan falta varias perspectivas e intentos para obtener mayor confianza. Para tareas complejas con múltiples consideraciones, manejar cada una con una llamada aparte con atención enfocada en general se desempeña mejor1
  • El fan-out compra más que velocidad: los subagentes operando en paralelo con sus propias ventanas de contexto exploran y comprimen de vuelta los tokens más importantes, y además traen separación de responsabilidades (herramientas, prompts y trayectorias de exploración distintas) y menos dependencia del camino3. Distribuir el trabajo entre agentes con ventanas de contexto separadas añade capacidad para el razonamiento en paralelo3
  • El número de aceleración viene con contexto: su sistema de investigación introdujo dos niveles de paralelización (el principal levanta 3 a 5 subagentes en paralelo, los subagentes usan 3 o más herramientas en paralelo), recortando el tiempo de investigación hasta en un 90 % para consultas complejas3; este es un número de latencia de su propio sistema, no un número de calidad
  • La agregación es el primer costo: los resultados de los subagentes vuelven a la conversación principal, y muchos subagentes devolviendo cada uno resultados detallados consumen contexto considerable4. La solución son los sistemas de artefactos: los subagentes guardan la salida en sistemas externos y pasan solo referencias ligeras de vuelta al coordinador3
  • La concurrencia nunca es ilimitada: Claude Code viene con 20 subagentes concurrentes por defecto y falla con un explícito «no reintentes» al excederse4. El runtime de flujos de trabajo soporta hasta 16 agentes concurrentes (menos cuando la CPU está limitada), con tope de 1000 por ejecución5. Managed Agents llega a 25 hilos concurrentes2
  • El costo de la votación es N× tokens, y hay que ponerlo junto a ese multiplicador: según sus datos, los agentes son unas 4× el chat, y los sistemas multiagente unas 15× el chat. El valor de la tarea tiene que ser lo bastante alto como para cubrirlo3. Lo que debería comprar es un patrón de calidad repetible, como agentes independientes revisando de forma adversarial, o redactar desde varios ángulos y luego sopesar5
  • La independencia es el prerrequisito: varios subagentes investigando a la vez funcionan mejor cuando los caminos de investigación no dependen unos de otros4. Los dominios que requieren contexto compartido o muchas dependencias no encajan bien hoy3; con dependencia, vuelve a la forma del encadenamiento
  • La descomposición de esta lección es toda predefinida (el arreglo lo escribiste tú). Que las subtareas no estén predefinidas sino determinadas por el orquestador a partir de la entrada específica es la diferencia clave entre orquestador-trabajadores y paralelización1, y también el tema de la lección siguiente

>> Lección 4: Orquestador-trabajadores: volver dinámica la descomposición misma

Footnotes

  1. Building Effective AI Agents — Anthropic Engineering — https://www.anthropic.com/engineering/building-effective-agents 2 3 4 5 6 7 8 9 10 11 12 13

  2. Multiagent orchestration — Claude API documentation (Managed Agents) — https://platform.claude.com/docs/en/managed-agents/multiagent-orchestration 2 3 4

  3. How we built our multi-agent research system — Anthropic Engineering — https://www.anthropic.com/engineering/multi-agent-research-system 2 3 4 5 6 7 8 9 10 11 12 13

  4. Create custom subagents — Claude Code official documentation — https://code.claude.com/docs/en/sub-agents 2 3 4 5 6 7

  5. Orchestrate subagents at scale with dynamic workflows — Claude Code official documentation — https://code.claude.com/docs/en/workflows 2 3 4 5 6

Ejercicios

01

Tienes cinco propuestas sobre el escritorio. Dale a cada una una categoría —seccionamiento / votación / no debería paralelizarse— y explica por qué. Para las que juzgues como seccionamiento o votación, añade dos notas de tratamiento: cómo fijar la concurrencia y cómo agregar.

Nivel 1: Cinco propuestas de fan-out, juzgar la forma y encontrar las trampas
  1. Doce documentos de producto independientes, cada uno necesita el mismo proceso de revisión.
  2. Un texto de alto riesgo a punto de salir en la página principal, necesita evaluación desde las perspectivas legal, de seguridad y de marca.
  3. Un script de migración de base de datos que requiere respaldo → alterar el esquema → rellenar datos, tres pasos en orden estricto.
  4. Cuarenta módulos necesitan evaluación de deuda técnica, pero las reglas de evaluación exigen que cada módulo haga referencia a las conclusiones del módulo anterior, convergiendo gradualmente hacia un criterio unificado.
  5. Una entrada de usuario necesita clasificarse como contenido que infringe o no; el costo de clasificar mal es alto y quieres una confianza mayor que la de una sola llamada.
Criterios de finalización · marcado local
02

Escribe un fanout.mjs ejecutable (Node 18+, sin dependencias), con estos requisitos:

Nivel 2: Escribir un fan-out acotado y ejecutarlo tú
  • client simulado incorporado, que finja messages.create, sin conexión, con la duración y la conclusión de cada elemento fijadas en duro, para que dos ejecuciones se puedan comparar carácter por carácter
  • 8 elementos, cada uno manejado por una llamada a runAgent() (reutiliza la forma del envoltorio de la Lección 2)
  • Implementa tu propio pool de concurrencia, con tope en 3; los trabajadores ejecutándose a la vez no pueden exceder 3; la falla de un solo camino no puede arrastrar al lote entero
  • Cada trabajador escribe su salida a un archivo en el directorio out/; la agregación solo recoge rutas de archivo y resúmenes de una línea, no trae de vuelta los cuerpos de los informes
  • Al final imprime una tabla resumen (elemento / duración / conclusión / archivo de resultado), e imprime la comparación de bytes en disco frente a bytes devueltos
  • Camino de dictamen con códigos de salida: todo con éxito exit 0, cualquier falla o archivo de resultado vacío exit 1
  • Después de ejecutar, cambia el tope de concurrencia de 3 a 8 y vuelve a ejecutar (edita el código o usa una variable de entorno), y verifica que el orden de finalización cambió pero el conjunto de resultados no. Para que el orden cambie de verdad, las duraciones fijadas en duro no las hagas crecer según el orden del arreglo ni todas iguales (por ejemplo 120/40/200/60/30/150/80/45), o el orden de finalización con ambos niveles de concurrencia resultará ser el mismo
Criterios de finalización · marcado local