volver al blog

Subagentes: no repartes trabajo, repartes contexto

agentesllmarquitectura

Cuarenta pasos y ningún error

Un agente que lleva 40 pasos rara vez se cae con una excepción. Se va volviendo torpe.

La traza lo cuenta bien. Los primeros diez pasos son limpios. En el 25 repite una búsqueda que ya hizo. En el 38 cita un fichero que leyó al principio y que entendió al revés. Nadie lanzó un error. El modelo simplemente dejó de distinguir qué importaba.

La reacción habitual es subir la ventana de contexto. No arregla nada, porque el problema no es cuánto cabe.

Lo que compite dentro del contexto

Cada llamada a herramienta deja residuos. El JSON completo de una respuesta de API. Las 200 líneas de un fichero del que interesaban tres. El stack trace de un intento que salió mal. Todo eso se queda en el historial y compite por la atención con la instrucción original.

Conviene separar dos efectos, porque se arreglan distinto:

Dilución. La señal útil es un porcentaje cada vez menor del prompt. El modelo atiende a todo, y “todo” es cada vez más ruido. Esto se parece a lo que pasa con un chunk mal recortado: no falta información, sobra.

Contaminación. Un paso fallido no desaparece, se convierte en contexto. Si el agente probó una ruta equivocada en el paso 12, esa ruta sigue ahí en el paso 30 con el mismo aspecto de evidencia que el resto. El modelo no tiene forma fácil de saber que aquello ya se descartó.

La compactación ayuda con la primera y poco con la segunda. Resumir un historial contaminado produce un resumen contaminado, más corto.

Orquestador y trabajadores

El patrón es viejo y aquí encaja bien. Un orquestador mantiene el objetivo, el plan y el estado. Cada subagente recibe una tarea acotada, un contexto limpio y devuelve un resultado estructurado. El trabajo sucio ocurre dentro de un contexto que se tira al terminar.

La consecuencia importa más que el diagrama: lo que vuelve al hilo principal no es la traza, es la conclusión. El orquestador nunca ve los 40.000 tokens que el subagente leyó para llegar a ella.

Y de ahí la tesis incómoda: los subagentes no están principalmente para paralelizar. Paralelizar es un efecto secundario agradable. Están para que el coste de contexto de una tarea no lo pague el resto de la sesión.

El contrato importa más que el prompt

Si el subagente devuelve prosa libre, has cambiado ruido por ruido más corto. El valor aparece cuando el resultado tiene forma.

from pydantic import BaseModel, Field

class ResultadoSubagente(BaseModel):
    """Lo único que cruza de vuelta al orquestador."""
    hallazgo: str = Field(max_length=600)          # La conclusión, no el camino.
    evidencia: list[str] = Field(max_length=5)     # Rutas o IDs, no el contenido.
    confianza: float                               # 0–1, declarada por el modelo.
    incompleto: bool                               # Se quedó sin pasos o sin datos.

async def delegar(tarea: str, herramientas: list, max_pasos: int = 12):
    # Contexto nuevo: ni el historial del orquestador ni el de otro subagente.
    hilo = [{"role": "user", "content": tarea}]

    for paso in range(max_pasos):
        respuesta = await modelo.invocar(
            mensajes=hilo,
            herramientas=herramientas,          # Subconjunto mínimo, no el catálogo.
            formato_salida=ResultadoSubagente,
        )
        if respuesta.final:
            return respuesta.parsed
        hilo += await ejecutar_herramientas(respuesta)

    # Sin resultado no se devuelve None: se devuelve el fallo, y con etiqueta.
    return ResultadoSubagente(
        hallazgo="Límite de pasos alcanzado sin conclusión.",
        evidencia=[], confianza=0.0, incompleto=True,
    )

Tres decisiones ahí dentro que se pagan caras si se saltan.

evidencia guarda referencias, no contenido. Si devuelves los fragmentos enteros, el subagente se convierte en un tubo y el contexto vuelve a crecer. El orquestador ya decidirá si necesita abrir alguno.

max_pasos no es una salvaguarda de coste, es parte de la semántica. Un subagente sin límite bloquea al orquestador de forma indefinida y, lo que es peor, lo hace sin avisar.

incompleto existe porque un fallo silencioso se paga más tarde y más caro. Un subagente que devuelve una conclusión pobre con confianza alta empuja al orquestador a decisiones malas, y la traza no enseña dónde empezó el desvío.

Cuándo repartir y cuándo no

SeñalUn solo hiloSubagentes
Contexto que consume la tareaCabe con holguraDecenas de miles de tokens de los que sobreviven cuatro líneas
Relación entre pasosCada paso depende del anteriorRamas explorables por separado
EscriturasModifica estado compartidoSolo lectura, o particiones que no se tocan
Necesidad de la trazaEl resultado se justifica con el caminoBasta con la conclusión y sus referencias
Coste aceptablePresupuesto ajustadoSe admite multiplicar tokens a cambio de acierto

La fila de las escrituras es la que más veces se ignora. Dos subagentes que escriben en el mismo sitio sin saber el uno del otro producen conflictos que ninguno de los dos puede detectar, porque cada uno cree que su contexto es el mundo entero. Si hay efectos secundarios, que los ejecute el orquestador con lo que le devuelven.

Lo que se rompe en producción

El coste se multiplica. Cada subagente arranca con su propio prompt de sistema y su propio catálogo de herramientas. Cinco subagentes no cuestan como cinco pasos, cuestan como cinco sesiones cortas. Si la tarea cabía en un hilo, has pagado de más para nada.

El resumen pierde justo lo que hacía falta. El subagente decide qué es relevante sin conocer la pregunta completa del orquestador. Se mitiga estrechando la tarea: “¿cumple este módulo la política de reintentos?” se resume bien; “mira el módulo y cuéntame” no.

Depurar se complica. Un fallo puede estar en la tarea que se delegó, en la ejecución del subagente o en cómo el orquestador leyó el resultado. Sin la traza de cada hilo guardada por separado y con su identificador, la investigación es a ciegas.

Por dónde empezar

No adoptes el patrón porque salga bien en los diagramas. Adóptalo si tienes el síntoma.

Coge diez trazas de tu agente que hayan terminado mal y mide dos cosas: cuántos tokens había en el contexto en el momento del primer paso claramente erróneo, y qué fracción de esos tokens venía de una sola llamada a herramienta. Si el patrón es “la traza estaba llena de una lectura gigante que ya no hacía falta”, los subagentes te van a mover la aguja. Si el agente se equivocó en el paso 3 con el contexto casi vacío, el problema es el prompt o las herramientas, y repartir solo te dará tres sitios donde equivocarte.

Cuando decidas probarlo, empieza por la tarea más recortada que tengas: lectura pura, resultado corto, sin efectos secundarios. Búsqueda en el código, comprobación de una condición, exploración de una hipótesis. Delega esa, mide acierto y coste frente al hilo único y solo entonces amplía.

Y quédate con la regla general, que sirve para más cosas que para agentes: cuando un modelo rinde por debajo de su ficha técnica, la primera sospecha no es el modelo. Suele ser todo lo que le hemos ido metiendo delante sin quitarlo después.