Saltar a contenido

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.