Empezar aquí
Patrón · workflow

Orquestador y trabajadores

Un modelo central decide en cada caso en qué subtareas dividir el trabajo, las delega a trabajadores y sintetiza sus resultados en una respuesta.

Coste
3 de 3
Latencia
2 de 3
Cuándo usarlo
Cuando la tarea se divide en partes, pero cuáles y cuántas depende de cada entrada concreta.
Cuándo no
Si las subtareas son siempre las mismas o están tan acopladas que los trabajadores necesitan verse entre sí.
Publicado 3 min de lectura

En el patrón orquestador y trabajadores, un LLM central (el orquestador) recibe la tarea, decide en qué subtareas dividirla, las reparte entre trabajadores y combina sus resultados. Se parece a la paralelización, con una diferencia clave que subraya Building effective agents (Anthropic, 2024): las subtareas no están predefinidas, las decide el orquestador según la entrada.

Qué es

flowchart TB
  E[Tarea] --> O[Orquestador: planifica]
  O --> T1[Trabajador 1]
  O --> T2[Trabajador 2]
  O --> T3[Trabajador N]
  T1 --> S[Orquestador: sintetiza]
  T2 --> S
  T3 --> S
  S --> R[Resultado]

Hay tres momentos con responsabilidades distintas:

  1. Planificar: el orquestador produce una lista de subtareas, cada una con objetivo, contexto necesario y formato de respuesta esperado.
  2. Ejecutar: tu código lanza los trabajadores, en paralelo si son independientes. Cada trabajador puede ser una llamada simple, una llamada aumentada o un agente con herramientas propias (un subagente).
  3. Sintetizar: el orquestador recibe los resultados y produce la salida final. A veces decide lanzar otra ronda.

Sigue siendo un workflow porque tu código controla la estructura: un plan, una ejecución, una síntesis. Cuando el orquestador puede lanzar trabajadores cuando quiera y en bucle, te acercas a un sistema multiagente con un agente al mando.

Cuándo usarlo

  • No puedes predecir las subtareas. El artículo pone dos ejemplos: productos de código que tienen que tocar varios archivos (cuántos y cuáles depende del cambio pedido) y tareas de búsqueda que reúnen información de varias fuentes.
  • Las subtareas son independientes entre sí una vez definidas, así que se pueden ejecutar sin que los trabajadores se coordinen.
  • Cada subtarea gana con un contexto limpio. Un trabajador que solo ve su parte no se distrae con el resto, y su contexto no se llena con lo que hacen los demás.

Cuándo no

  • Las subtareas son siempre las mismas: usa seccionado (Paralelización), que es más barato y predecible.
  • Los trabajadores necesitan compartir mucho contexto o decisiones que se toman a medio camino. Si tienes que pasar todo el estado a cada uno, pierdes la ventaja y multiplicas tokens. Lo argumenta Cuándo NO usar multiagente.
  • La tarea es pequeña. Planificar y sintetizar cuestan dos llamadas aunque solo haya una subtarea.

Ejemplo concreto

Un usuario pide: «Compara las condiciones de teletrabajo de los convenios de estas tres empresas». El orquestador planifica una subtarea por empresa, cada una con la instrucción de extraer días permitidos, compensaciones y requisitos, y citar el artículo del convenio. Tres trabajadores leen cada convenio en paralelo. El orquestador recibe tres fichas con el mismo formato y redacta la comparativa en tabla. Si el usuario hubiera mencionado cinco empresas, el plan tendría cinco subtareas sin cambiar una línea de código.

Esqueleto de código

import asyncio
import json
import anthropic

MODEL = "claude-sonnet-5-5"
client = anthropic.AsyncAnthropic()

PLAN = {
    "type": "object",
    "properties": {
        "subtareas": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "objetivo": {"type": "string"},
                    "contexto": {"type": "string"},
                    "formato_respuesta": {"type": "string"},
                },
                "required": ["objetivo", "contexto", "formato_respuesta"],
                "additionalProperties": False,
            },
        }
    },
    "required": ["subtareas"],
    "additionalProperties": False,
}

async def texto(prompt: str, system: str, max_tokens: int = 2048, **extra) -> str:
    resp = await client.messages.create(model=MODEL, max_tokens=max_tokens, system=system,
                                        messages=[{"role": "user", "content": prompt}], **extra)
    if resp.stop_reason == "max_tokens":
        raise RuntimeError("Salida truncada")
    return "".join(b.text for b in resp.content if b.type == "text")

async def planificar(tarea: str, max_subtareas: int = 6) -> list[dict]:
    salida = await texto(
        f"<tarea>\n{tarea}\n</tarea>",
        system=(f"Divide la tarea en como mucho {max_subtareas} subtareas independientes. "
                "Cada una debe poder resolverse sin ver las demás. Indica qué contexto necesita "
                "y en qué formato debe responder."),
        output_config={"format": {"type": "json_schema", "schema": PLAN}},
    )
    return json.loads(salida)["subtareas"][:max_subtareas]   # el límite lo impone tu código

async def trabajar(sub: dict) -> str:
    return await texto(
        f"Objetivo: {sub['objetivo']}\n<contexto>\n{sub['contexto']}\n</contexto>\n"
        f"Formato de respuesta: {sub['formato_respuesta']}",
        system="Resuelve solo esta subtarea. Si te falta información, dilo en lugar de suponer.",
    )

async def orquestar(tarea: str) -> str:
    subtareas = await planificar(tarea)
    resultados = await asyncio.gather(*[trabajar(s) for s in subtareas], return_exceptions=True)
    informe = "\n\n".join(
        f"<resultado objetivo='{s['objetivo']}'>\n"
        f"{r if not isinstance(r, Exception) else f'FALLÓ: {r}'}\n</resultado>"
        for s, r in zip(subtareas, resultados)
    )
    return await texto(
        f"<tarea>\n{tarea}\n</tarea>\n{informe}",
        system="Sintetiza los resultados en una respuesta final. Señala qué subtareas fallaron.",
        max_tokens=4096,
    )

return_exceptions=True evita que un trabajador caído tire toda la ejecución: el orquestador recibe el fallo y puede decirlo en la síntesis.

Coste y latencia

Es de los patrones más caros: una llamada de plan, una por trabajador y una de síntesis, y la síntesis recibe todos los resultados como entrada. Si los trabajadores son agentes con herramientas, el gasto sube bastante más. La latencia es plan + trabajador más lento + síntesis, mejor que hacerlo todo en serie. Pon un tope de subtareas en código y mide si la calidad compensa frente a una sola llamada con más contexto; lo detalla El coste de multiplicar agentes.

Patrones relacionados