Coordinación Multi-agente y Evaluación
Cuando intentas que un solo modelo de lenguaje escriba código, consulte la base de datos, mande correos y analice datos financieros todo a la vez, el fracaso está garantizado. La tasa de alucinaciones se dispara y el prompt de sistema se vuelve un monstruo inmanejable. La solución es dividir para vencer.
Para dummies: El fin del empleado orquesta
Si diriges una empresa, no contratas a una sola persona para que sea el contable, el programador, el conserje y el jefe de ventas simultáneamente. Es un desastre esperando a ocurrir. Creas departamentos y pones a un mánager para que coordine.
En IA, esto se llama arquitectura multi-agente. En lugar de un "agente orquesta" sobrecargado, despliegas varios agentes especialistas. Uno sabe buscar información, otro sabe redactar y un agente supervisor decide a quién encargarle qué. Se pasan el estado entre ellos hasta que el supervisor determina que el trabajo está hecho.
Patrones de coordinación
He probado desde estructuras jerárquicas estrictas hasta mercados de agentes donde "pujan" por tareas. Al final, lo que mejor funciona en producción es el patrón de Supervisor-Trabajador (Routing). El usuario habla con el supervisor, el supervisor delega en el trabajador A, recibe el resultado, se lo pasa al trabajador B para que lo revise, y te devuelve la respuesta.
El reto principal aquí es la evaluación. ¿Cómo sabes que el agente "Redactor" no se ha inventado los datos que le pasó el "Investigador"? La respuesta es LLM-as-a-judge o evaluaciones basadas en aserciones rígidas en tu pipeline de CI/CD.
Ejemplo Práctico: Un grafo de Supervisor y Especialistas
Imagina que usamos un motor de grafos para orquestar la delegación. En vez de poner todo el código de fontanería, veamos la estructura de toma de decisiones del supervisor:
from typing import TypedDict, Literal
class MultiAgentState(TypedDict):
task: str
draft: str | None
research_notes: str | None
next_agent: str
def supervisor_node(state: MultiAgentState) -> dict:
"""
El supervisor decide quién tiene que actuar a continuación.
"""
if not state.get("research_notes"):
# No hay datos, mandamos al investigador
return {"next_agent": "Investigador"}
if not state.get("draft"):
# Hay datos pero no hay borrador, mandamos al redactor
return {"next_agent": "Redactor"}
# Si tenemos borrador y notas, asumimos que hemos terminado
# En un sistema real, aquí habría un agente 'Evaluador' que revisa la calidad
return {"next_agent": "FIN"}
def investigador_node(state: MultiAgentState) -> dict:
"""Simula al agente investigador"""
notas = f"Datos encontrados sobre: {state['task']}..."
return {"research_notes": notas, "next_agent": "Supervisor"}
def redactor_node(state: MultiAgentState) -> dict:
"""Simula al agente redactor"""
texto = f"Basado en las notas ({state['research_notes']}), redacto este texto."
return {"draft": texto, "next_agent": "Supervisor"}
# El sistema encolaría estos nodos basándose en el valor de "next_agent".
# Supervisor -> Investigador -> Supervisor -> Redactor -> Supervisor -> FIN
Este diseño te permite cambiar el modelo del "Investigador" por uno más barato o más rápido sin afectar a la calidad del "Redactor". Aísla los problemas, que es exactamente lo que buscas cuando pasas de un prototipo a un sistema robusto y auditable.