Por qué los flujos de trabajo de agente único están muertos
Si llevas más de un mes pegado a la interfaz de chat de Claude o ChatGPT y no has salido de ahí, tengo una mala noticia. Estás usando un solo agente cuando podrías estar ejecutando un equipo entero.
Vamos a bajar el hype a la tierra. Para los de marketing, un "agente autónomo" es magia que lee la mente. En términos de ingeniería real para los que picamos código, un agente único es un script monolítico inflado. Es meter todo el contexto, reglas, estado y objetivos en un solo prompt gigantesco, cruzar los dedos y rezar para que el modelo no pierda el hilo. Un equipo de agentes, por el contrario, es arquitectura de microservicios. Divides la carga, limitas el contexto por tarea y pasas estado estructurado (normalmente JSON) entre nodos especializados.
El problema de meter todo en el mismo saco es que los modelos de lenguaje diluyen su atención.
Ese 14% que tiras a la basura antes de empezar
Tienes tu proyecto. Tienes un archivo CLAUDE.md o GEMINI.md lleno de reglas globales. Convenciones, directrices, estilo. Lo metes en cada petición.
Antes de que escribas la primera palabra de tu prompt real, ya has quemado un porcentaje nada despreciable de la ventana de contexto. Hablo de un 10% a un 14% de "impuesto de atención". Le pides al modelo que averigüe por qué el recolector de basura de tu app en Go está fallando, pero al mismo tiempo tiene que mantener en memoria que debe "escribir en primera persona y no usar la palabra sinergia".
Es absurdo. Le exiges que sea analista, arquitecto, programador junior y corrector de estilo en el mismo ciclo de reloj.
La solución no son prompts más largos o detallados. El futuro son equipos de agentes.
La arquitectura que separa a los aficionados de los constructores
Los amateurs se dedican al mega-prompting. Escriben biblias de texto intentando cubrir todos los casos extremos.
Los que construyen de verdad asumen algo básico: el modelo va a fallar. Por diseño. Así que construyen arquitecturas basadas en orquestación y tolerancia a fallos. La estructura mínima viable que veo funcionar en producción se divide en cuatro partes:
- El que investiga: Tiene acceso de lectura. Busca en la base de datos vectorial, lee la documentación, revisa el código fuente. No escribe nada. Su output es un reporte puro.
- El que construye: Recibe el reporte. Su única directriz es escribir la implementación técnica. No sabe nada de convenciones de negocio, solo sabe hacer que el código funcione y cumpla el requerimiento.
- El que revisa: Tiene un prompt cínico y destructivo. Recibe el código generado, ejecuta el linter, pasa los tests y busca agujeros de seguridad. Si encuentra un fallo, rechaza el trabajo y se lo devuelve al constructor.
- El que orquesta: El router. Mantiene el estado del flujo, decide cuándo pasar de investigación a construcción y decide cuándo el código revisado está listo para hacer merge.
Las 3 propiedades de un equipo que sobrevive
Si intentas montar esto con un script básico de Python y bucles while, te vas a dar contra un muro. Un equipo de agentes de verdad necesita tres cosas para no colapsar:
- Estado inmutable entre transiciones: El orquestador debe guardar la entrada y salida de cada agente de forma independiente. Si el revisor rechaza un cambio, el constructor necesita ver exactamente qué produjo antes y por qué falló, sin contaminar la memoria general.
- Alcance limitado: El investigador jamás debe tener la herramienta de escribir archivos. El constructor jamás debe tener acceso a las variables de entorno de producción.
- Criterio de parada determinista: Nada de "revisa hasta que te parezca correcto". Las condiciones de salida tienen que ser binarias. Los tests pasan o fallan. El linting devuelve 0 o 1. Si dejas la salida a la interpretación del LLM, acabarás en un bucle infinito gastando tokens.
Ejemplo práctico: Montando un equipo simple
No hace falta irse a frameworks complejos si quieres empezar. Aquí tienes un ejemplo crudo de cómo se ve esta separación en un pipeline de Python puro.
import json
def agente_investigador(query):
# Prompt enfocado solo en buscar información
system_prompt = "Eres un investigador. Devuelve un JSON con los archivos a modificar."
# Llamada a la API del LLM simulada
return {"archivos": ["main.py"], "contexto": "Falta manejar excepciones en DB."}
def agente_constructor(contexto):
# Prompt enfocado solo en picar código
system_prompt = "Eres programador. Usa el contexto para generar el parche."
# Llamada a la API del LLM simulada
return "try:\n db.connect()\nexcept Exception:\n log.error('Fallo de red')"
def agente_revisor(codigo):
# Prompt enfocado en buscar fallos
system_prompt = "Analiza el código. Devuelve 'OK' o el error."
if "log.error" not in codigo:
return "Falta logging"
return "OK"
def orquestador_pipeline(tarea):
print("Iniciando investigación...")
contexto = agente_investigador(tarea)
intentos = 0
while intentos < 3:
print(f"Construyendo (Intento {intentos + 1})...")
codigo = agente_constructor(contexto)
print("Revisando...")
revision = agente_revisor(codigo)
if revision == "OK":
print("Pipeline completado con éxito.")
return codigo
print(f"Fallo en revisión: {revision}. Reintentando.")
contexto["feedback_previo"] = revision
intentos += 1
raise RuntimeError("El equipo falló tras 3 intentos.")
orquestador_pipeline("Añadir manejo de errores en base de datos")
Dejar que un solo modelo cargue con todo el peso es cómodo para empezar, pero inútil para escalar. Abandona la ventana de chat.