Fran Rodrigo
SistemasW0829 min de lectura

Sistemas multi-agente

Del agente individual al equipo: división del trabajo, especialización por rol, revisión entre pares. Protocolos de comunicación (paso de mensajes, memoria compartida, blackboard). Patrones de orquestación. Estándares modernos: Agent-to-Agent (A2A) y Agent Communication Protocol (ACP).

Conceptos núcleoEspecialización por rolOrquestaciónA2A / ACP

01Objetivos de aprendizaje

Al finalizar esta clase, los estudiantes serán capaces de:

  1. Explicar la motivación de los sistemas multiagente e identificar escenarios donde múltiples agentes superan a uno solo.
  2. Comparar los sistemas multiagente clásicos (de la IA distribuida) con los sistemas multiagente modernos basados en LLM.
  3. Describir e implementar protocolos de comunicación clave: paso de mensajes, estado compartido y arquitecturas de pizarra.
  4. Diseñar sistemas multiagente usando patrones de colaboración: secuencial, paralelo, jerárquico y basado en debate.
  5. Analizar el framework AutoGen y su enfoque de conversación multiagente.
  6. Implementar patrones de orquestación incluyendo supervisor, round-robin y enrutamiento dinámico.
  7. Identificar y mitigar desafíos cómo la sobrecarga de coordinación, la propagación de errores y la consecución de consenso.
  8. Comparar protocolos modernos de comunicación entre agentes (A2A, ACP, MCP) y explicar cómo habilitan la interoperabilidad.
  9. Evaluar frameworks multiagente modernos (OpenAI Agents SDK, Claude Agent SDK, LangGraph, CrewAI, AutoGen) y seleccionar los apropiados para diferentes casos de uso.
  10. Construir un sistema simple de debate entre dos agentes en Python.

021. De agentes individuales a arquitecturas multiagente

¿Por qué múltiples agentes?

Consideremos cómo funcionan las organizaciones humanas. Ninguna empresa asigna a una sola persona el diseño, la construcción, las pruebas, el marketing y la venta de un producto. En su lugar, forman equipos de especialistas que colaboran, revisan el trabajo de los demás y aportan diferentes perspectivas al problema. Los sistemas de IA multiagente aplican el mismo principio: en lugar de pedir a un único agente que haga todo, creamos equipos de agentes especializados.

Un único agente basado en LLM puede ser extraordinariamente capaz, pero tiene limitaciones inherentes:

  • Saturación de la ventana de contexto: Las tareas complejas requieren un contexto extenso (descripciones de herramientas, documentos, historial de conversación). La ventana de contexto de un único agente puede sobrecargarse.
  • Confusión de roles: Cuando se le pide a un solo agente que sea planificador, programador, revisor y tester simultáneamente, puede tener dificultades para mantener un comportamiento consistente entre roles.
  • Falta de verificación: Un único agente no tiene quien compruebe su trabajo. Puede producir resultados incorrectos con confianza sin ningún mecanismo de autocorrección.
  • Escalabilidad: Algunas tareas son naturalmente paralelizables. Un único agente procesa los pasos de forma secuencial.
  • Especialización: Diferentes tareas pueden beneficiarse de diferentes modelos, prompts o conjuntos de herramientas.

Los sistemas multiagente abordan estas limitaciones distribuyendo el trabajo entre agentes especializados que se comunican y colaboran. La idea clave es que la división del trabajo y la revisión entre pares pueden mejorar tanto la calidad como la eficiencia de las salidas de los agentes.

Idea clave: El valor de los sistemas multiagente proviene no solo del paralelismo o la especialización, sino de la verificación. Cuando un agente revisa el trabajo de otro, detecta errores que un agente trabajando solo pasaría por alto. Es el mismo principio detrás de la revisión de código, la revisión por pares en ciencia y el equilibrio de poderes en los gobiernos.

Cuándo usar sistemas multiagente

Los sistemas multiagente no siempre son la opción correcta. Añaden complejidad, latencia y coste. Úsalos cuando:

Usar multiagente cuandoUsar un solo agente cuando
La tarea requiere múltiples habilidades distintasLa tarea es enfocada y bien definida
La calidad se beneficia de revision/debateLa velocidad es más importante que la exhaustividad
La tarea es naturalmente paralelizableEl procesamiento secuencial es suficiente
Diferentes pasos necesitan diferentes herramientas/modelosUn solo modelo maneja todo bien
La detección de errores es críticaLos errores son fácilmente detectados por el usuario

Breve historia

El concepto de sistemas multiagente precede a los LLM por décadas; es una de las ideas más antiguas de la IA. El campo de la Inteligencia Artificial Distribuida (DAI) surgió en la década de 1980, estudiando cómo múltiples agentes autónomos podían coordinarse para resolver problemas. Lo nuevo no es la idea de la colaboración multiagente, sino el uso de LLM cómo "cerebro" de cada agente. Hitos clave:

  • Década de 1980: Investigación en DAI sobre resolución cooperativa de problemas (Bond & Gasser, 1988).
  • Década de 1990: Estándares de la Foundation for Intelligent Physical Agents (FIPA) para comunicación entre agentes.
  • Década de 2000: Sistemas multiagente en robótica, simulación y teoría de juegos.
  • 2023-presente: Sistemas multiagente potenciados por LLM (AutoGen, CrewAI, LangGraph, MetaGPT).

032. Sistemas multiagente clásicos vs. basados en LLM

Sistemas multiagente clásicos

En los sistemas multiagente clásicos, los agentes son típicamente:

  • Basados en reglas o algorítmicos: Siguen protocolos y estrategias predefinidas.
  • Formalmente especificados: Los protocolos de comunicación, los mecanismos de negociación y las estrategias de coordinación están matemáticamente definidos.
  • Específicos de dominio: Diseñados para aplicaciones concretas (control de tráfico, asignación de recursos, coordinación robótica).
  • Predecibles: Dados los mismos inputs y estado, producen las mismas salidas.

Los frameworks clásicos incluyen el modelo Belief-Desire-Intention (BDI):

text
Agent = (Beliefs, Desires, Intentions)

Beliefs:    What the agent knows about the world
Desires:    What the agent wants to achieve (goals)
Intentions: What the agent has committed to doing (plans)

El modelo BDI proporciona una forma sistemática de razonar sobre el comportamiento del agente, pero requiere la formalización explícita de todo el conocimiento y los objetivos.

Sistemas multiagente basados en LLM

Los sistemas multiagente basados en LLM difieren fundamentalmente de los MÁS clásicos. La diferencia más notable es que los agentes se comunican en lenguaje natural en lugar de protocolos formales. Esto los hace mucho más fáciles de construir (los agentes se definen mediante prompts, no código) pero mucho más difíciles de verificar (el lenguaje natural es ambiguo y no determinista).

Características clave:

  • Comunicación en lenguaje natural: Los agentes se comunican en lenguaje natural en lugar de protocolos formales.
  • Razonamiento flexible: Los LLM pueden manejar ambigüedad, situaciones inesperadas y tareas novedosas sin programación explícita.
  • Comportamiento emergente: Comportamientos complejos pueden emerger de reglas de interacción simples entre agentes LLM.
  • No determinismo: Los mismos inputs pueden producir diferentes outputs debido al muestreo del LLM.
  • Roles definidos por prompt: El comportamiento del agente se moldea mediante prompts de sistema en lugar de código.
python
# Classical agent: behavior defined in code
class ClassicalAgent:
    def decide(self, percepts: dict) -> str:
        if percepts["temperature"] > 30:
            return "turn_on_cooling"
        elif percepts["temperature"] < 18:
            return "turn_on_heating"
        return "do_nothing"

# LLM agent: behavior defined by prompt
LLM_AGENT_PROMPT = """You are a building climate control agent.
Given sensor readings, decide which action to take.
Consider energy efficiency, occupant comfort, and weather forecasts.
Explain your reasoning before stating your action."""

Tabla comparativa

DimensiónMÁS clásicoMÁS basado en LLM
ComunicaciónProtocolos formales (FIPA ACL)Lenguaje natural
RazonamientoLógica, algoritmos de planificaciónInferencia del LLM
FlexibilidadBaja (reglas predefinidas)Alta (propósito general)
PredecibilidadAlta (determinista)Baja (estocástico)
EscalabilidadBien estudiadaInvestigación emergente
Coste por interacciónBajo (computación)Alto (llamadas a API de LLM)
VerificaciónMétodos formales aplicablesDifícil de verificar

043. Protocolos de comunicación

Como sé comunican los agentes es una de las decisiones arquitectónicas más importantes en un sistema multiagente. El protocolo de comunicación determina qué información fluye entre agentes, cómo sé resuelven los conflictos y cómo escala el sistema. Es cómo elegir entre correo electrónico (paso de mensajes), una pizarra compartida (estado compartido) o una reunión estructurada (arquitectura de pizarra).

Paso de mensajes

El patrón de comunicación más común. Los agentes intercambian mensajes directamente, típicamente en lenguaje natural para sistemas basados en LLM. Es el equivalente digital de agentes enviándose correos electrónicos.

python
from dataclasses import dataclass, field
from datetime import datetime


@dataclass
class Message:
    """A message between agents."""

    sender: str
    receiver: str
    content: str
    message_type: str = "inform"  # inform, request, propose, accept, reject
    timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
    metadata: dict = field(default_factory=dict)


class MessageBus:
    """A simple message bus for agent communication."""

    def __init__(self):
        self.messages: list[Message] = []
        self.subscribers: dict[str, list] = {}  # agent_name -> callback list

    def send(self, message: Message) -> None:
        """Send a message to a specific agent."""
        self.messages.append(message)
        if message.receiver in self.subscribers:
            for callback in self.subscribers[message.receiver]:
                callback(message)

    def subscribe(self, agent_name: str, callback) -> None:
        """Subscribe an agent to receive messages."""
        if agent_name not in self.subscribers:
            self.subscribers[agent_name] = []
        self.subscribers[agent_name].append(callback)

    def get_history(
        self, agent_name: str | None = None, limit: int = 50
    ) -> list[Message]:
        """Get message history, optionally filtered by agent."""
        if agent_name:
            msgs = [
                m for m in self.messages
                if m.sender == agent_name or m.receiver == agent_name
            ]
        else:
            msgs = self.messages
        return msgs[-limit:]

Ventajas: Simple, desacoplado, fácil de registrar y depurar. Desventajas: Puede volverse caótico con muchos agentes; no hay visión compartida de la conversación.

Estado compartido

Los agentes leen y escriben en un objeto de estado compartido. Esto proporciona una "visión del mundo" común a la que todos los agentes pueden acceder.

python
import threading
from typing import Any


class SharedState:
    """Thread-safe shared state for multi-agent systems.

    All agents can read and write to the shared state, providing
    a common ground truth for coordination.
    """

    def __init__(self):
        self._state: dict[str, Any] = {}
        self._lock = threading.Lock()
        self._history: list[dict] = []

    def get(self, key: str, default: Any = None) -> Any:
        """Read a value from shared state."""
        with self._lock:
            return self._state.get(key, default)

    def set(self, key: str, value: Any, agent_name: str = "unknown") -> None:
        """Write a value to shared state."""
        with self._lock:
            old_value = self._state.get(key)
            self._state[key] = value
            self._history.append({
                "agent": agent_name,
                "key": key,
                "old_value": old_value,
                "new_value": value,
                "timestamp": datetime.now().isoformat(),
            })

    def get_all(self) -> dict[str, Any]:
        """Get a snapshot of the entire shared state."""
        with self._lock:
            return dict(self._state)

    def get_changes_by(self, agent_name: str) -> list[dict]:
        """Get all state changes made by a specific agent."""
        return [h for h in self._history if h["agent"] == agent_name]

Ventajas: Todos los agentes tienen una visión consistente; no se necesita enrutamiento de mensajes. Desventajas: Problemas de concurrencia; acoplamiento fuerte; más difícil de escalar.

Arquitectura de pizarra

Una pizarra es un enfoque híbrido donde los agentes publican sus contribuciones en una "pizarra" compartida visible para todos los agentes. Un componente de control decide qué agente debe actuar a continuación basándose en el estado actual de la pizarra.

python
class Blackboard:
    """Blackboard architecture for multi-agent collaboration.

    The blackboard is a shared workspace where agents post their
    contributions. A controller decides which agent should act next.
    """

    def __init__(self):
        self.entries: list[dict] = []
        self.agents: dict[str, dict] = {}  # name -> agent config

    def register_agent(self, name: str, expertise: list[str], agent_fn) -> None:
        """Register an agent with its areas of expertise."""
        self.agents[name] = {
            "expertise": expertise,
            "agent_fn": agent_fn,
        }

    def post(self, agent_name: str, content: str, entry_type: str = "contribution") -> None:
        """Post a contribution to the blackboard."""
        self.entries.append({
            "agent": agent_name,
            "content": content,
            "type": entry_type,
            "timestamp": datetime.now().isoformat(),
        })

    def get_entries(self, entry_type: str | None = None) -> list[dict]:
        """Get blackboard entries, optionally filtered by type."""
        if entry_type:
            return [e for e in self.entries if e["type"] == entry_type]
        return list(self.entries)

    def get_current_state(self) -> str:
        """Format the blackboard contents for an agent to read."""
        if not self.entries:
            return "The blackboard is empty."
        lines = ["Current blackboard state:"]
        for entry in self.entries:
            lines.append(f"  [{entry['agent']}] ({entry['type']}): {entry['content']}")
        return "\n".join(lines)

    def select_next_agent(self, task_description: str) -> str | None:
        """Select the most appropriate agent for the current task.

        Simple implementation: match task keywords to agent expertise.
        More sophisticated implementations would use an LLM.
        """
        task_lower = task_description.lower()
        best_match = None
        best_score = 0
        for name, config in self.agents.items():
            score = sum(1 for exp in config["expertise"] if exp.lower() in task_lower)
            if score > best_score:
                best_score = score
                best_match = name
        return best_match

La arquitectura de pizarra fue propuesta originalmente para la comprensión del habla (Erman et al., 1980) y se ha adaptado a muchos escenarios de resolución colaborativa de problemas.


054. Patrones de colaboración

El patrón de colaboración define cómo trabajan juntos los agentes. Elegir el patrón correcto es crucial: usar un patrón de debate para un pipeline de datos simple es excesivo, mientras que usar un pipeline secuencial para una tarea que requiere evaluación crítica es insuficiente.

Idea clave: La elección del patrón de colaboración debe ajustarse a la estructura del problema. Pregúntate: "¿Es esta tarea naturalmente secuencial? ¿Pueden hacerse partes en paralelo? ¿Necesita el resultado revisión por pares?" La respuesta guía la elección del patrón.

Interactive · Patrones de comunicacion multiagente

Simulador de debate

Dos agentes y un orquestador

Los sistemas multi-agente reparten el razonamiento por roles. Aquí dos agentes se especializan en defender y atacar la misma tesis, y un orquestador concluye.

Elige el tema

Agente A favor0
Agente En contra0
Orquestador

Pulsa Empezar para que los agentes intervengan.

0 / 7

Secuencial (Pipeline)

Los agentes procesan el trabajo uno tras otro, cada uno construyendo sobre la salida del agente anterior. Es la cadena de montaje de los sistemas multiagente.

text
Input -> [Agent A] -> [Agent B] -> [Agent C] -> Output

Ejemplo: Un pipeline de creación de contenido donde:

  1. Agente Investigador recopila información sobre un tema.
  2. Agente Escritor redacta el contenido basándose en la investigación.
  3. Agente Editor revisa y pule el borrador.
  4. Agente Verificador de Hechos comprueba todas las afirmaciones.
python
class SequentialPipeline:
    """Execute agents in sequence, passing output from one to the next."""

    def __init__(self):
        self.stages: list[tuple[str, callable]] = []

    def add_stage(self, name: str, agent_fn) -> None:
        """Add a stage to the pipeline."""
        self.stages.append((name, agent_fn))

    def run(self, initial_input: str) -> dict:
        """Run the pipeline from start to finish.

        Returns:
            Dict with the final output and intermediate results.
        """
        current_input = initial_input
        results = {"stages": [], "final_output": ""}

        for name, agent_fn in self.stages:
            print(f"Running stage: {name}")
            output = agent_fn(current_input)
            results["stages"].append({
                "name": name,
                "input_preview": current_input[:200],
                "output_preview": output[:200],
            })
            current_input = output

        results["final_output"] = current_input
        return results

Ventajas: Simple de entender e implementar; flujo de datos claro. Desventajas: Sin paralelismo; los errores se propagan hacia adelante; sin bucles de retroalimentación.

Paralelo (Fan-Out/Fan-In)

Múltiples agentes trabajan en diferentes aspectos de la tarea simultáneamente, y sus resultados se combinan.

text
              -> [Agent A] ->
Input -> Fork -> [Agent B] -> Merge -> Output
              -> [Agent C] ->
python
import asyncio
from typing import Callable


async def parallel_execution(
    task: str,
    agents: dict[str, Callable],
    merger: Callable,
) -> str:
    """Execute multiple agents in parallel and merge their outputs.

    Args:
        task: The shared task description.
        agents: Dict mapping agent names to their async callable functions.
        merger: Function that combines all agent outputs into a final result.

    Returns:
        The merged output from all agents.
    """
    # Run all agents concurrently
    async def run_agent(name: str, fn: Callable) -> tuple[str, str]:
        result = await fn(task)
        return name, result

    tasks = [run_agent(name, fn) for name, fn in agents.items()]
    results = await asyncio.gather(*tasks)

    # Merge results
    agent_outputs = {name: output for name, output in results}
    final = merger(task, agent_outputs)
    return final

Ventajas: Ejecución más rápida; perspectivas diversas. Desventajas: La fusión no es trivial; los agentes pueden producir resultados contradictorios.

Jerárquico

Un agente supervisor delega subtareas a agentes trabajadores y coordina sus esfuerzos.

python
class HierarchicalSystem:
    """A hierarchical multi-agent system with a supervisor and workers."""

    def __init__(self, supervisor_fn, workers: dict[str, callable]):
        self.supervisor_fn = supervisor_fn
        self.workers = workers

    def run(self, task: str, max_rounds: int = 10) -> str:
        """Run the hierarchical system.

        The supervisor decomposes the task, delegates to workers, and
        synthesizes results. Iteration continues until the supervisor
        declares the task complete.
        """
        conversation_history = [f"Task: {task}"]

        for round_num in range(max_rounds):
            # Supervisor decides what to do next
            supervisor_context = "\n".join(conversation_history)
            delegation = self.supervisor_fn(supervisor_context)

            if "TASK_COMPLETE" in delegation:
                # Extract final answer
                return delegation.replace("TASK_COMPLETE:", "").strip()

            # Parse delegation: which worker and what subtask
            worker_name, subtask = self._parse_delegation(delegation)

            if worker_name not in self.workers:
                conversation_history.append(
                    f"Error: No worker named '{worker_name}'. "
                    f"Available: {list(self.workers.keys())}"
                )
                continue

            # Execute the subtask
            worker_result = self.workers[worker_name](subtask)
            conversation_history.append(
                f"Supervisor delegated to {worker_name}: {subtask}"
            )
            conversation_history.append(
                f"{worker_name} result: {worker_result}"
            )

        return "Max rounds reached without completing the task."

    def _parse_delegation(self, delegation: str) -> tuple[str, str]:
        """Parse the supervisor's delegation into worker name and subtask."""
        # Simple format: "DELEGATE worker_name: subtask description"
        if "DELEGATE" in delegation:
            parts = delegation.split("DELEGATE", 1)[1].strip()
            if ":" in parts:
                worker, subtask = parts.split(":", 1)
                return worker.strip(), subtask.strip()
        return "", delegation

Ventajas: Estructura de autoridad clara; el supervisor puede gestionar la complejidad; bueno para tareas heterogéneas. Desventajas: El supervisor es un cuello de botella; punto único de fallo.

Debate y colaboración adversaria

Los agentes adoptan posiciones opuestas y debaten para llegar a una conclusión bien razonada. Este es quizás el patrón de colaboración más fascinante porque aprovecha el desacuerdo como una característica, no como un defecto.

La intuición es simple: si sólo escuchas una perspectiva, no puedes evaluar su calidad. Pero si escuchas dos perspectivas opuestas, las fortalezas y debilidades de cada una se hacen evidentes. Por eso los tribunales tienen fiscales y abogados defensores, los artículos académicos pasan por revisión por pares y las organizaciones tienen "equipos rojos" que intentan encontrar fallos en los planes.

La investigación de Du et al. (2023) demostró que el debate multiagente puede mejorar la precisión factual y la calidad del razonamiento de las salidas de los LLM. Cuando se pide a los agentes que debatan sus respuestas, detectan los errores mutuos y convergen hacia respuestas más precisas.

Idea clave: El debate funciona porque obliga a los agentes a justificar sus posiciones. Un único agente puede afirmar con confianza una respuesta incorrecta. Pero cuando es desafiado por un crítico, debe proporcionar evidencia, y si la evidencia no respalda la afirmación, el error queda al descubierto. La dinámica adversaria transforma la sobreconfianza en razonamiento cuidadoso.

Interactive · El patrón de debate multiagente

Simulador de debate

Dos agentes y un orquestador

Los sistemas multi-agente reparten el razonamiento por roles. Aquí dos agentes se especializan en defender y atacar la misma tesis, y un orquestador concluye.

Elige el tema

Agente A favor0
Agente En contra0
Orquestador

Pulsa Empezar para que los agentes intervengan.

0 / 7

Este patrón se explora en detalle en la Sección 8 (el ejemplo práctico).


065. Especialización de roles

Asignación de personas y capacidades

En los sistemas multiagente basados en LLM, los roles de los agentes se definen principalmente a través de prompts de sistema. Cada agente recibe una personá que moldea su comportamiento:

python
AGENT_PROMPTS = {
    "researcher": """You are a Research Agent. Your role is to gather, analyze,
and synthesize information. You are thorough, evidence-based, and skeptical of
unverified claims. When you find information, always note the source.

Your capabilities:
- Search the web for information
- Read and summarize documents
- Identify gaps in available information
- Assess the reliability of sources

You communicate clearly and distinguish between facts, inferences, and opinions.""",

    "developer": """You are a Software Developer Agent. Your role is to write,
review, and debug code. You follow best practices, write clean and well-documented
code, and consider edge cases.

Your capabilities:
- Write code in Python, JavaScript, and SQL
- Run and test code
- Review code for bugs and improvements
- Design software architecture

You explain your technical decisions and trade-offs clearly.""",

    "critic": """You are a Critical Review Agent. Your role is to identify flaws,
risks, and areas for improvement in proposals, plans, and outputs. You are
constructive but unflinching in your assessment.

Your capabilities:
- Identify logical fallacies and weak arguments
- Spot missing considerations and edge cases
- Evaluate feasibility and risks
- Suggest specific improvements

You always provide actionable feedback, not just criticism.""",
}

Asignación de roles basada en capacidades

Más allá de los prompts, los agentes pueden tener diferentes capacidades a través de diferentes conjuntos de herramientas:

python
class SpecializedAgent:
    """An agent with a specific role and set of tools."""

    def __init__(
        self,
        name: str,
        system_prompt: str,
        tools: list[dict],
        model: str = "gpt-4",
    ):
        self.name = name
        self.system_prompt = system_prompt
        self.tools = tools
        self.model = model
        self.conversation_history: list[dict] = []

    def process(self, message: str) -> str:
        """Process a message and return a response.

        The agent uses its specialized prompt and tools.
        """
        self.conversation_history.append({"role": "user", "content": message})

        # In a real implementation, this calls the LLM API with
        # the system prompt, tools, and conversation history
        response = llm_call(
            model=self.model,
            system=self.system_prompt,
            messages=self.conversation_history,
            tools=self.tools,
        )

        self.conversation_history.append({"role": "assistant", "content": response})
        return response

El enfoque MetaGPT

MetaGPT (Hong et al., 2024) lleva la especialización de roles más allá al codificar flujos de trabajo de desarrollo de software humano en un framework multiagente. Cada agente representa un rol específico en una empresa de software:

  • Product Manager: Analiza los requisitos y escribe documentos de requisitos del producto.
  • Arquitecto: Diseña la arquitectura del sistema y las especificaciones de la API.
  • Project Manager: Crea asignaciones de tareas y cronogramas.
  • Ingenieros: Escriben código basándose en los documentos de diseño.
  • Ingeniero de QA: Revisa el código y escribe pruebas.

La innovación clave es que los agentes se comunican a través de documentos estructurados (como una empresa real) en lugar de conversación libre. Esto reduce la ambigüedad y la propagación de errores.


076. Framework AutoGen

Visión general

AutoGen (Wu et al., 2023) es un framework de Microsoft Research para construir sistemas multiagente. Su abstracción central es el agente conversable: un agente que puede enviar y recibir mensajes de otros agentes.

Conceptos clave

ConversableAgent: La clase base para todos los agentes. Cada agente tiene:

  • Un nombre y mensaje de sistema (prompt)
  • Una configuración de LLM
  • Un modo de entrada humana (NEVER, ALWAYS o TERMINATE)
  • Capacidades de ejecución de código (opcional)

GroupChat: Una conversación entre múltiples agentes con un protocolo gestionado de turnos.

GroupChatManager: Controla el flujo de la conversación en un chat grupal, decidiendo qué agente habla a continuación.

Ejemplo: Conversación entre dos agentes

python
# Conceptual example showing AutoGen's design pattern
# (simplified for educational purposes)

class ConversableAgent:
    """Simplified version of AutoGen's ConversableAgent."""

    def __init__(
        self,
        name: str,
        system_message: str,
        llm_call,
        max_consecutive_auto_reply: int = 10,
    ):
        self.name = name
        self.system_message = system_message
        self.llm_call = llm_call
        self.max_consecutive_auto_reply = max_consecutive_auto_reply
        self.chat_messages: dict[str, list[dict]] = {}

    def generate_reply(self, messages: list[dict]) -> str:
        """Generate a reply based on the conversation history."""
        full_messages = [{"role": "system", "content": self.system_message}]
        full_messages.extend(messages)
        return self.llm_call(messages=full_messages)

    def initiate_chat(self, recipient: "ConversableAgent", message: str) -> list[dict]:
        """Start a conversation with another agent.

        The agents take turns responding until a termination
        condition is met.
        """
        conversation = []
        current_message = message

        for turn in range(self.max_consecutive_auto_reply * 2):
            if turn % 2 == 0:
                # This agent's turn
                sender, receiver = self, recipient
            else:
                # Other agent's turn
                sender, receiver = recipient, self

            conversation.append({
                "role": "user" if sender == self else "assistant",
                "sender": sender.name,
                "content": current_message,
            })

            # Check for termination
            if "TERMINATE" in current_message:
                break

            # Generate reply
            current_message = receiver.generate_reply(
                [{"role": "user", "content": m["content"]} for m in conversation]
            )

        return conversation

Patrón de chat grupal

python
class GroupChat:
    """A group chat between multiple agents.

    Manages turn-taking and message routing.
    """

    def __init__(
        self,
        agents: list[ConversableAgent],
        max_rounds: int = 20,
        speaker_selection: str = "round_robin",  # or "auto" or "random"
    ):
        self.agents = {agent.name: agent for agent in agents}
        self.max_rounds = max_rounds
        self.speaker_selection = speaker_selection
        self.conversation: list[dict] = []

    def select_next_speaker(self, last_speaker: str) -> str:
        """Select the next agent to speak."""
        agent_names = list(self.agents.keys())

        if self.speaker_selection == "round_robin":
            current_idx = agent_names.index(last_speaker)
            next_idx = (current_idx + 1) % len(agent_names)
            return agent_names[next_idx]

        elif self.speaker_selection == "random":
            import random
            candidates = [n for n in agent_names if n != last_speaker]
            return random.choice(candidates)

        else:  # "auto" - use LLM to decide
            return self._llm_select_speaker(last_speaker)

    def _llm_select_speaker(self, last_speaker: str) -> str:
        """Use an LLM to select the most appropriate next speaker."""
        agent_descriptions = "\n".join(
            f"- {name}: {agent.system_message[:100]}..."
            for name, agent in self.agents.items()
        )
        context = "\n".join(
            f"{m['sender']}: {m['content'][:200]}" for m in self.conversation[-5:]
        )
        # In practice, call the LLM to select the best next speaker
        # based on the conversation context and agent descriptions
        return list(self.agents.keys())[0]  # Placeholder

    def run(self, initial_message: str, initiator: str) -> list[dict]:
        """Run the group chat.

        Args:
            initial_message: The starting message.
            initiator: Name of the agent who starts the conversation.

        Returns:
            The complete conversation history.
        """
        self.conversation = [{
            "sender": initiator,
            "content": initial_message,
        }]

        current_speaker = initiator

        for round_num in range(self.max_rounds):
            # Select next speaker
            next_speaker = self.select_next_speaker(current_speaker)
            agent = self.agents[next_speaker]

            # Generate reply
            messages = [
                {"role": "user", "content": f"[{m['sender']}]: {m['content']}"}
                for m in self.conversation
            ]
            reply = agent.generate_reply(messages)

            self.conversation.append({
                "sender": next_speaker,
                "content": reply,
            })

            # Check for termination
            if "TERMINATE" in reply:
                break

            current_speaker = next_speaker

        return self.conversation

087. Patrones de orquestación

Patrón supervisor

Un agente orquestador dedicado gestiona el flujo de trabajo, decidiendo qué agentes invocar y en qué orden.

python
SUPERVISOR_PROMPT = """You are a workflow supervisor managing a team of specialized agents.

Available agents:
{agent_descriptions}

Given the user's request, decide which agent(s) to invoke and in what order.
You can invoke agents sequentially or gather information from multiple agents.

For each step, respond in this format:
INVOKE <agent_name>: <instruction for that agent>

When you have enough information to provide a final answer:
FINAL_ANSWER: <your synthesized response>
"""


class SupervisorOrchestrator:
    """Supervisor-based orchestration for multi-agent systems."""

    def __init__(self, supervisor_llm_call, agents: dict[str, callable]):
        self.supervisor_llm_call = supervisor_llm_call
        self.agents = agents
        self.execution_log: list[dict] = []

    def run(self, task: str, max_steps: int = 10) -> dict:
        """Execute a task using supervisor-guided orchestration."""
        agent_descriptions = "\n".join(
            f"- {name}" for name in self.agents.keys()
        )
        system = SUPERVISOR_PROMPT.format(agent_descriptions=agent_descriptions)
        context = f"User request: {task}\n\nExecution history:\n"

        for step in range(max_steps):
            # Ask supervisor what to do next
            full_context = context + "\n".join(
                f"Step {i+1}: Invoked {log['agent']} -> {log['result'][:200]}"
                for i, log in enumerate(self.execution_log)
            )

            decision = self.supervisor_llm_call(system=system, prompt=full_context)

            if "FINAL_ANSWER:" in decision:
                answer = decision.split("FINAL_ANSWER:", 1)[1].strip()
                return {
                    "answer": answer,
                    "steps": self.execution_log,
                    "num_steps": len(self.execution_log),
                }

            if "INVOKE" in decision:
                agent_name, instruction = self._parse_invocation(decision)
                if agent_name in self.agents:
                    result = self.agents[agent_name](instruction)
                    self.execution_log.append({
                        "agent": agent_name,
                        "instruction": instruction,
                        "result": result,
                    })

        return {
            "answer": "Max steps reached.",
            "steps": self.execution_log,
            "num_steps": len(self.execution_log),
        }

    def _parse_invocation(self, decision: str) -> tuple[str, str]:
        """Parse 'INVOKE agent_name: instruction' from supervisor output."""
        parts = decision.split("INVOKE", 1)[1].strip()
        if ":" in parts:
            agent_name, instruction = parts.split(":", 1)
            return agent_name.strip(), instruction.strip()
        return "", parts

Patrón round-robin

Cada agente toma un turno en un orden fijo. Simple pero asegura que cada agente contribuya.

Enrutamiento dinámico

Un enrutador basado en LLM examina el estado actual y dirige al agente más apropiado. Esto es similar al patrón supervisor pero el enrutador sólo toma decisiones de enrutamiento y no sintetiza resultados.

python
ROUTER_PROMPT = """Given the current task state, select the best agent to handle the next step.

Current state:
{state}

Available agents:
{agents}

Select ONE agent by responding with just the agent name."""


def dynamic_router(
    state: str,
    agents: dict[str, str],  # name -> description
    llm_call,
) -> str:
    """Dynamically route to the best agent for the current state."""
    agents_str = "\n".join(f"- {name}: {desc}" for name, desc in agents.items())
    prompt = ROUTER_PROMPT.format(state=state, agents=agents_str)
    selected = llm_call(prompt=prompt).strip()
    # Validate selection
    if selected in agents:
        return selected
    # Fallback: find closest match
    for name in agents:
        if name.lower() in selected.lower():
            return name
    return list(agents.keys())[0]  # Default to first agent

098. Desafíos en los sistemas multiagente

Los sistemas multiagente son potentes pero no gratuitos. Cada beneficio tiene un coste asociado, y comprender estos compromisos es esencial para construir sistemas que realmente funcionen en producción y no solo en demostraciones.

Sobrecarga de coordinación

Cada interacción entre agentes cuesta tiempo y dinero (llamadas a la API del LLM). Un sistema con N agentes puede requerir O(N^2) interacciones por ronda, haciendo que el escalado sea costoso.

Estrategias de mitigación:

  • Limitar el número de rondas de interacción.
  • Usar modelos más económicos para mensajes de coordinación.
  • Diseñar protocolos de comunicación eficientes (mensajes estructurados, no conversación libre).
  • Usar comunicación asincrona cuando sea posible.

Propagación de errores

Los errores de un agente pueden propagarse en cascada por el sistema. Si un agente investigador recupera información incorrecta, el agente escritor la incorpora, y el agente editor puede no detectar los errores factuales.

text
[Researcher: wrong fact] -> [Writer: uses wrong fact] -> [Editor: fixes grammar but not facts]

Estrategias de mitigación:

  • Incluir agentes de verificación dedicados.
  • Implementar la comprobación de hechos como una etapa separada.
  • Usar agentes adversarios que intenten activamente encontrar errores.
  • Mantener trazabilidad de procedencia para que los errores puedan rastrearse hasta su origen.

Consenso y desacuerdo

Cuando los agentes discrepan, el sistema necesita un mecanismo para alcanzar una resolución:

  • Voto mayoritario: Sí múltiples agentes opinan, se acepta la mayoría.
  • Basado en autoridad: Un agente "senior" designado o un humano toma la decisión final.
  • Basado en evidencia: Los agentes deben citar evidencia; la posición mejor respaldada gana.
  • Refinamiento iterativo: Los agentes debaten hasta converger.
python
def majority_vote(agent_responses: dict[str, str], llm_call) -> str:
    """Resolve disagreement through majority voting.

    Uses an LLM to classify responses into groups and select
    the majority position.
    """
    responses_text = "\n".join(
        f"{name}: {response}" for name, response in agent_responses.items()
    )

    prompt = f"""The following agents have provided different responses.
Group them by their position and identify the majority view.

{responses_text}

Majority position (summarize the view held by most agents):"""

    return llm_call(prompt=prompt)

Bucles infinitos y costes desbordados

Sin condiciones de terminación adecuadas, los agentes pueden entrar en bucles infinitos de conversación de ida y vuelta, consumiendo recursos sin progresar. Este es uno de los modos de fallo más comunes y costosos en sistemas multiagente: dos agentes educados pueden intercambiar "¡Gracias!" y "¡De nada!" para siempre si nadie les dice que paren.

Concepto erróneo común: "Más interacción entre agentes siempre conduce a mejores resultados." En la práctica, hay un punto de rendimientos decrecientes. Tras 3-4 rondas de debate, los agentes a menudo empiezan a repetirse o a hacer refinamientos marginales. Siempre establece límites estrictos en las rondas y monitoriza si se está progresando.

Estrategias de mitigación:

  • Establecer límites estrictos en el número de rondas y tokens totales.
  • Monitorizar si se está progresando (¿están cambiando sustancialmente las salidas entre rondas?).
  • Implementar seguimiento de presupuesto y cortacircuitos.
  • Usar un mecanismo de timeout.
python
class CostTracker:
    """Track and limit costs in a multi-agent system."""

    def __init__(self, max_tokens: int = 100000, max_rounds: int = 50):
        self.max_tokens = max_tokens
        self.max_rounds = max_rounds
        self.tokens_used = 0
        self.rounds_completed = 0

    def record_usage(self, tokens: int) -> None:
        self.tokens_used += tokens
        self.rounds_completed += 1

    def can_continue(self) -> bool:
        return (
            self.tokens_used < self.max_tokens
            and self.rounds_completed < self.max_rounds
        )

    def usage_report(self) -> dict:
        return {
            "tokens_used": self.tokens_used,
            "token_budget_remaining": self.max_tokens - self.tokens_used,
            "rounds_completed": self.rounds_completed,
            "rounds_remaining": self.max_rounds - self.rounds_completed,
        }

109. Ejemplo práctico: Un sistema de debate entre dos agentes

Construyamos un sistema completo de debate entre dos agentes donde un agente Proponente presenta un argumento y un agente Crítico lo cuestiona. Un agente Juez evalúa el debate y emite un veredicto final.

python
"""
A two-agent debate system: Proposer vs Critic, evaluated by a Judge.

This demonstrates:
- Role-based agent specialization
- Structured multi-turn interaction
- Adversarial collaboration for better reasoning
- Final evaluation by a third-party judge

Reference: Du et al. (2023) "Improving Factuality and Reasoning in
Language Models through Multiagent Debate"

Requirements:
    pip install openai  (or any LLM client)
"""

from dataclasses import dataclass, field


@dataclass
class DebateConfig:
    """Configuration for the debate system."""

    max_rounds: int = 3
    min_rounds: int = 2
    proposer_model: str = "gpt-4"
    critic_model: str = "gpt-4"
    judge_model: str = "gpt-4"


PROPOSER_SYSTEM = """You are a Proposer in a structured debate. Your role is to:
1. Present clear, well-reasoned arguments in favor of your position.
2. Support your claims with evidence, examples, and logical reasoning.
3. Respond thoughtfully to the Critic's objections.
4. Acknowledge valid criticisms and refine your position when warranted.

Be persuasive but honest. Do not make claims you cannot support.
If the Critic raises a valid point, concede it and adjust your argument."""

CRITIC_SYSTEM = """You are a Critic in a structured debate. Your role is to:
1. Carefully analyze the Proposer's arguments for logical flaws,
   unsupported claims, missing evidence, and counterexamples.
2. Raise specific, substantive objections (not just vague disagreement).
3. Suggest alternative viewpoints or interpretations.
4. Acknowledge strong arguments while pressing on weak ones.

Be rigorous but fair. Your goal is not to "win" but to strengthen
the overall quality of reasoning through critical examination.
Do not be contrarian for its own sake."""

JUDGE_SYSTEM = """You are the Judge evaluating a debate between a Proposer and a Critic.

Evaluate the debate on these criteria:
1. **Argument Quality**: How well-supported and logically sound were the arguments?
2. **Evidence Use**: Were claims backed by concrete evidence or examples?
3. **Responsiveness**: Did each side address the other's points effectively?
4. **Intellectual Honesty**: Did participants acknowledge valid opposing points?
5. **Final Assessment**: What is the most well-supported conclusion given the debate?

Provide:
- A score for each side (1-10)
- Key strengths and weaknesses of each side
- Your assessment of the most defensible position
- Any important points that neither side addressed"""


class DebateAgent:
    """An agent participating in a debate."""

    def __init__(self, name: str, system_prompt: str, llm_call, model: str = "gpt-4"):
        self.name = name
        self.system_prompt = system_prompt
        self.llm_call = llm_call
        self.model = model

    def respond(self, conversation_history: list[dict]) -> str:
        """Generate a response given the conversation history."""
        messages = [{"role": "system", "content": self.system_prompt}]
        messages.extend(conversation_history)
        return self.llm_call(model=self.model, messages=messages)


class DebateSystem:
    """Orchestrates a structured debate between a Proposer and a Critic."""

    def __init__(self, llm_call, config: DebateConfig | None = None):
        self.config = config or DebateConfig()
        self.llm_call = llm_call

        # Create agents
        self.proposer = DebateAgent(
            "Proposer", PROPOSER_SYSTEM, llm_call, self.config.proposer_model
        )
        self.critic = DebateAgent(
            "Critic", CRITIC_SYSTEM, llm_call, self.config.critic_model
        )
        self.judge = DebateAgent(
            "Judge", JUDGE_SYSTEM, llm_call, self.config.judge_model
        )

    def run_debate(self, topic: str) -> dict:
        """Run a complete debate on a topic.

        Args:
            topic: The debate topic or question.

        Returns:
            Dict with the debate transcript, judge's verdict, and metadata.
        """
        transcript: list[dict] = []
        proposer_history: list[dict] = []
        critic_history: list[dict] = []

        print(f"{'='*60}")
        print(f"DEBATE TOPIC: {topic}")
        print(f"{'='*60}\n")

        # Round 1: Proposer opens
        opening_prompt = f"Present your opening argument on the following topic: {topic}"
        proposer_history.append({"role": "user", "content": opening_prompt})
        opening = self.proposer.respond(proposer_history)
        proposer_history.append({"role": "assistant", "content": opening})

        transcript.append({
            "round": 1,
            "speaker": "Proposer",
            "type": "opening",
            "content": opening,
        })
        print(f"[Round 1 - Proposer Opening]\n{opening}\n")

        # Debate rounds
        for round_num in range(1, self.config.max_rounds + 1):
            # Critic responds
            critic_prompt = (
                f"The Proposer argues:\n\n{opening if round_num == 1 else proposer_response}\n\n"
                f"Provide your critique (Round {round_num})."
            )
            critic_history.append({"role": "user", "content": critic_prompt})
            critique = self.critic.respond(critic_history)
            critic_history.append({"role": "assistant", "content": critique})

            transcript.append({
                "round": round_num,
                "speaker": "Critic",
                "type": "critique",
                "content": critique,
            })
            print(f"[Round {round_num} - Critic]\n{critique}\n")

            # Proposer responds to critique
            rebuttal_prompt = (
                f"The Critic objects:\n\n{critique}\n\n"
                f"Respond to the criticism and refine your argument (Round {round_num})."
            )
            proposer_history.append({"role": "user", "content": rebuttal_prompt})
            proposer_response = self.proposer.respond(proposer_history)
            proposer_history.append({"role": "assistant", "content": proposer_response})

            transcript.append({
                "round": round_num,
                "speaker": "Proposer",
                "type": "rebuttal",
                "content": proposer_response,
            })
            print(f"[Round {round_num} - Proposer Rebuttal]\n{proposer_response}\n")

        # Judge evaluates
        debate_text = self._format_for_judge(transcript)
        judge_prompt = (
            f"Evaluate the following debate on the topic: '{topic}'\n\n{debate_text}"
        )
        verdict = self.judge.respond([{"role": "user", "content": judge_prompt}])

        transcript.append({
            "round": "final",
            "speaker": "Judge",
            "type": "verdict",
            "content": verdict,
        })
        print(f"{'='*60}")
        print(f"[JUDGE'S VERDICT]\n{verdict}")
        print(f"{'='*60}")

        return {
            "topic": topic,
            "transcript": transcript,
            "verdict": verdict,
            "num_rounds": self.config.max_rounds,
            "total_turns": len(transcript),
        }

    def _format_for_judge(self, transcript: list[dict]) -> str:
        """Format the debate transcript for the judge."""
        lines = []
        for entry in transcript:
            if entry["speaker"] != "Judge":
                lines.append(
                    f"[{entry['speaker']} - {entry['type'].title()}]\n{entry['content']}\n"
                )
        return "\n".join(lines)


# -- Usage Example ---------------------------------------------------

def main():
    """Run a sample debate."""

    # Mock LLM call for demonstration
    call_count = {"n": 0}

    def mock_llm_call(model: str = "gpt-4", messages: list[dict] = None, **kwargs) -> str:
        """Simulate LLM responses for the debate."""
        call_count["n"] += 1
        last_msg = messages[-1]["content"] if messages else ""

        if "opening argument" in last_msg.lower():
            return (
                "I argue that open-source AI models are essential for democratic "
                "AI development. First, they enable transparency: researchers and "
                "the public can inspect model weights, training data, and behavior. "
                "Second, they reduce concentration of power: when only a few companies "
                "control AI, they become gatekeepers of a transformative technology. "
                "Third, open-source accelerates innovation through community contributions, "
                "as seen with Linux and the web."
            )
        elif "critique" in last_msg.lower() or "critic" in messages[0].get("content", "").lower():
            return (
                "While transparency is valuable, the Proposer overlooks significant risks. "
                "Open-source AI models can be misused for generating disinformation, "
                "creating deepfakes, or automating cyberattacks. The 'democratization' "
                "argument assumes all actors are benign, which is naive. Furthermore, "
                "truly 'open' AI requires open training data and compute, not just weights. "
                "Most 'open-source' models are still controlled by companies that choose "
                "what to release."
            )
        elif "respond to the criticism" in last_msg.lower():
            return (
                "The Critic raises valid concerns about misuse. However, restricting access "
                "does not prevent misuse; it merely shifts who can misuse. Closed models can "
                "also be jailbroken. The solution is not secrecy but robust safety research, "
                "which itself benefits from open access. On the 'controlled openness' point, "
                "I concede that current open-source AI is imperfect, but the trajectory is "
                "toward greater openness, and this should be encouraged."
            )
        elif "evaluate" in last_msg.lower():
            return (
                "VERDICT:\n\n"
                "Proposer Score: 7/10\n"
                "- Strengths: Clear structure, good examples (Linux, web), acknowledged "
                "valid criticism about 'controlled openness'.\n"
                "- Weaknesses: Insufficient engagement with the misuse argument.\n\n"
                "Critic Score: 8/10\n"
                "- Strengths: Specific counterexamples, distinguished between types of "
                "openness, practical concern about misuse.\n"
                "- Weaknesses: Did not engage with the 'restricting access shifts misuse' "
                "counterargument.\n\n"
                "Assessment: The debate reveals that the question is not binary. The most "
                "defensible position is graduated openness with safety evaluation, combining "
                "the transparency benefits the Proposer champions with the risk mitigation "
                "the Critic demands."
            )
        return "I acknowledge the points raised and will consider them carefully."

    # Run the debate
    config = DebateConfig(max_rounds=2)
    debate = DebateSystem(mock_llm_call, config)
    result = debate.run_debate(
        "Should AI models be open-source? Consider safety, innovation, and power dynamics."
    )

    print(f"\nDebate completed: {result['total_turns']} total turns across {result['num_rounds']} rounds.")


if __name__ == "__main__":
    main()

1110. Protocolos modernos de comunicación entre agentes

A medida que los sistemas multiagente maduran de prototipos de investigación a despliegues en producción, la necesidad de protocolos de comunicación estandarizados se ha vuelto urgente. En 2024-2025, tres protocolos principales emergieron de las principales empresas de IA, cada uno abordando una capa diferente de la pila de interoperabilidad de agentes. Comprender estos protocolos es esencial para construir sistemas multiagente que funcionen entre frameworks, proveedores y límites organizativos.

10.1 Contexto: El problema de la interoperabilidad

Consideremos un escenario empresarial realista: una empresa utiliza un agente de servicio al cliente construido con LangGraph, un agente de análisis de datos construido con el OpenAI Agents SDK y un agente de conocimiento interno construido con un framework personalizado sobre Claude. Actualmente, hacer que estos agentes colaboren requiere código de integración personalizado para cada par de agentes. Con N sistemas de agentes, esto significa O(N^2) integraciones, un problema clásico de interoperabilidad.

Los protocolos estandarizados resuelven esto definiendo un lenguaje común para la interacción de agentes, del mismo modo que HTTP estandarizó la comunicación web y SMTP estandarizó el correo electrónico.

10.2 Protocolo Agent-to-Agent (A2A) de Google

Google anunció el Protocolo Agent-to-Agent (A2A) en abril de 2025 como un protocolo abierto para la interoperabilidad de agentes. A2A permite que agentes construidos en diferentes frameworks, por diferentes proveedores y usando diferentes modelos, se descubran mutuamente y colaboren en tareas.

Principios de diseño fundamentales

A2A se construye en torno a varios principios clave:

  • Agéntico: Los agentes colaboran como iguales. A2A soporta escenarios donde los agentes negocian, delegan y se comunican sin reducir al agente remoto a una mera herramienta.
  • Ejecución opaca: Los agentes no necesitan compartir su razonamiento interno, memoria o uso de herramientas. Cada agente es una caja negra para los demás, exponiendo solo sus capacidades y resultados.
  • Agnóstico respecto a la modalidad: La comunicación puede incluir texto, archivos, datos estructurados, imágenes, audio y vídeo. Esto refleja la naturaleza multimodal de las tareas reales de los agentes.
  • Independiente del framework: A2A funciona independientemente del framework de agentes subyacente (LangGraph, CrewAI, código personalizado, etc.).

Conceptos clave

Agent Card: Un documento de metadatos JSON que describe la identidad y las capacidades de un agente. Es como una tarjeta de presentación para agentes. Cada agente compatible con A2A aloja una Agent Card en una URL conocida (típicamente /.well-known/agent.json).

json
{
  "name": "financial-analysis-agent",
  "description": "Analyzes financial reports, generates summaries, and answers questions about company financials.",
  "url": "https://finance-agent.example.com",
  "version": "1.0.0",
  "capabilities": {
    "streaming": true,
    "pushNotifications": true
  },
  "skills": [
    {
      "id": "financial-report-analysis",
      "name": "Financial Report Analysis",
      "description": "Analyzes 10-K, 10-Q, and annual reports to extract key metrics and trends.",
      "inputModes": ["text/plain", "application/pdf"],
      "outputModes": ["text/plain", "application/json"]
    },
    {
      "id": "earnings-qa",
      "name": "Earnings Q&A",
      "description": "Answers questions about a company's earnings based on public filings.",
      "inputModes": ["text/plain"],
      "outputModes": ["text/plain"]
    }
  ],
  "authentication": {
    "schemes": ["bearer"]
  }
}

Task: La unidad fundamental de trabajo en A2A. Un agente cliente crea una tarea en un agente remoto y monitoriza su progreso. Las tareas tienen un ciclo de vida:

El estado input-required es particularmente importante: permite a los agentes negociar y solicitar información adicional durante la tarea, soportando una genuina colaboración multiturno en lugar de un simple petición-respuesta.

Message: La unidad de comunicación entre agentes. Cada mensaje contiene una o más Parts, que pueden ser:

  • TextPart: Contenido de texto plano o markdown
  • FilePart: Archivos binarios (PDFs, imágenes, hojas de cálculo)
  • DataPart: Datos estructurados en JSON

Artifact: Una salida producida por un agente durante la ejecución de la tarea. Mientras los mensajes son conversacionales (ida y vuelta), los artifacts representan entregables: el informe generado, los resultados del análisis, el conjunto de datos transformado.

Cómo funciona A2A: Un recorrido

Interactive · Protocolo A2A: Descubrimiento de agentes y flujo de tareas

Sobre A2A

Cómo se habla un agente con otro

Cada mensaje viaja como un sobre con campos estandarizados. Mira en vivo qué significa cada uno.

version

Versión del protocolo. Permite que servidores antiguos rechacen mensajes incompatibles.

A

EnvelopeA → B
versiona2a/1.0
fromagent.research
toagent.summariser
thread_idth_8c1...e3
intentsummarise
payload{ doc_id, max_words: 250 }
deadline2026-04-26T17:00Z

B

Capa de transporte

A2A utiliza HTTP como transporte con JSON-RPC 2.0 como formato de mensaje. Esta es una elección pragmática: HTTP es universalmente soportado, los firewalls están configurados para él, y todos los lenguajes de programación tienen librerías cliente HTTP.

Para actualizaciones en tiempo real, A2A soporta Server-Sent Events (SSE), que permiten al agente remoto transmitir actualizaciones de estado y resultados parciales al cliente sin polling.

Ejemplo conceptual de código: Descubrimiento de agentes y delegación de tareas A2A

python
"""
Conceptual example of A2A agent discovery and task delegation.

This illustrates how a client agent discovers a remote agent via its
Agent Card and delegates a task using the A2A protocol.
"""

import httpx


class A2AClient:
    """A client for interacting with A2A-compatible agents."""

    def __init__(self, base_url: str, auth_token: str | None = None):
        self.base_url = base_url.rstrip("/")
        self.auth_token = auth_token
        self.agent_card: dict | None = None

    def _headers(self) -> dict:
        headers = {"Content-Type": "application/json"}
        if self.auth_token:
            headers["Authorization"] = f"Bearer {self.auth_token}"
        return headers

    async def discover(self) -> dict:
        """Discover the remote agent by fetching its Agent Card."""
        async with httpx.AsyncClient() as client:
            response = await client.get(
                f"{self.base_url}/.well-known/agent.json",
                headers=self._headers(),
            )
            response.raise_for_status()
            self.agent_card = response.json()
            return self.agent_card

    def has_skill(self, skill_id: str) -> bool:
        """Check if the remote agent has a specific skill."""
        if not self.agent_card:
            raise RuntimeError("Call discover() first to fetch the Agent Card.")
        return any(
            s["id"] == skill_id for s in self.agent_card.get("skills", [])
        )

    async def send_task(self, message_text: str, task_id: str | None = None) -> dict:
        """Send a task to the remote agent."""
        payload = {
            "jsonrpc": "2.0",
            "method": "tasks/send",
            "params": {
                "message": {
                    "role": "user",
                    "parts": [{"type": "text", "text": message_text}],
                },
            },
            "id": task_id or "auto",
        }

        async with httpx.AsyncClient() as client:
            response = await client.post(
                f"{self.base_url}/a2a",
                json=payload,
                headers=self._headers(),
            )
            response.raise_for_status()
            return response.json()["result"]

    async def get_task_status(self, task_id: str) -> dict:
        """Poll the status of a previously submitted task."""
        payload = {
            "jsonrpc": "2.0",
            "method": "tasks/get",
            "params": {"id": task_id},
            "id": "status-check",
        }

        async with httpx.AsyncClient() as client:
            response = await client.post(
                f"{self.base_url}/a2a",
                json=payload,
                headers=self._headers(),
            )
            response.raise_for_status()
            return response.json()["result"]


async def orchestrate_financial_analysis():
    """Example: A supervisor agent discovers and delegates to a financial agent."""

    # Step 1: Discover the remote agent
    finance_agent = A2AClient(
        base_url="https://finance-agent.example.com",
        auth_token="secret-token",
    )
    card = await finance_agent.discover()
    print(f"Discovered agent: {card['name']}")
    print(f"Skills: {[s['name'] for s in card['skills']]}")

    # Step 2: Check if the agent has the skill we need
    if not finance_agent.has_skill("financial-report-analysis"):
        print("Agent does not have the required skill. Searching for another agent...")
        return

    # Step 3: Send the task
    task = await finance_agent.send_task(
        "Analyze the Q3 2025 earnings report for Acme Corp. "
        "Focus on revenue growth, profit margins, and forward guidance."
    )
    print(f"Task created: {task['id']}, status: {task['status']}")

    # Step 4: Poll for completion (in production, use SSE streaming)
    import asyncio
    while task["status"] in ("submitted", "working"):
        await asyncio.sleep(2)
        task = await finance_agent.get_task_status(task["id"])
        print(f"Task status: {task['status']}")

    # Step 5: Process results
    if task["status"] == "completed":
        for artifact in task.get("artifacts", []):
            print(f"Received artifact: {artifact.get('name', 'unnamed')}")
            for part in artifact.get("parts", []):
                if "text" in part:
                    print(f"  Content: {part['text'][:200]}...")
    elif task["status"] == "input-required":
        print("Agent needs more information:")
        last_message = task.get("messages", [])[-1]
        print(f"  {last_message}")

A2A vs MCP: Complementarios, no competidores

Una fuente común de confusión es la relación entre A2A de Google y el Model Context Protocol (MCP) de Anthropic. Operan en capas diferentes y están diseñados para ser complementarios:

  • MCP (Model Context Protocol): Conecta un modelo/agente a herramientas y fuentes de datos. Es un protocolo de integración vertical. Es cómo dar manos a un agente: la capacidad de leer archivos, consultar bases de datos, llamar a APIs.
  • A2A (Agent-to-Agent Protocol): Conecta agentes con otros agentes. Es un protocolo de interoperabilidad horizontal. Es cómo dar a los agentes la capacidad de colaborar con otros agentes, independientemente de cómo estén construidos internamente.

Un agente podría usar MCP para acceder a sus propias herramientas (leer una base de datos, llamar a una API) y A2A para delegar subtareas a agentes especializados construidos por otros equipos o proveedores.

10.3 Protocolo de Comunicación de Agentes (ACP) de IBM

IBM introdujo el Agent Communication Protocol (ACP) a principios de 2025, dirigido a sistemas multiagente empresariales donde los agentes necesitan colaborar dentro de los límites organizativos. ACP se desarrolla como parte del proyecto de código abierto BeeAI.

Filosofía de diseño

Mientras A2A se centra en la interoperabilidad entre proveedores, ACP está diseñado para la orquestación empresarial, donde todos los agentes se despliegan dentro de un entorno controlado y necesitan una comunicación fiable y auditable. Decisiones de diseño clave:

  • Asíncrono por defecto: Toda la comunicación es asíncrona por defecto. Los agentes envían solicitudes y reciben respuestas vía callbacks o polling. Esto refleja la realidad de que los agentes empresariales a menudo realizan tareas de larga duración (generar informes, ejecutar análisis) que no pueden completarse de forma síncrona.
  • Mensajes multimodales: Como A2A, ACP soportá texto, archivos y datos estructurados en los mensajes. Esto es esencial para casos de uso empresarial donde los agentes intercambian documentos, hojas de cálculo y visualizaciones.
  • Directorio de agentes: ACP incluye un registro centralizado de agentes donde los agentes publican sus capacidades y otros agentes los descubren. Esto es similar a las Agent Cards de A2A pero usa un directorio centralizado en lugar de descubrimiento distribuido.
  • Comunicación basada en eventos: ACP utiliza una arquitectura basada en eventos donde los agentes publican eventos y se suscriben a eventos de otros agentes. Esto desacopla emisores de receptores y soportá flujos de trabajo complejos.

Componentes clave

Registro de agentes: Los agentes se registran en el directorio, declarando sus habilidades, formatos de entrada/salida y estado de disponibilidad.

Estructura de mensajes: Los mensajes ACP llevan un ID de hilo de conversación, permitiendo que las interacciones multiturno se agrupen y rastreen, lo cual es crítico para la auditabilidad en entornos empresariales.

Ciclo de vida de ejecución: Similar al ciclo de vida de tareas de A2A, ACP define una "ejecución" como la unidad de trabajo, con estados como created, in-progress, awaiting, completed, failed y cancelled.

Ejemplo conceptual: Interacción de agentes ACP

python
"""
Conceptual example of ACP agent communication within an enterprise system.
"""


class ACPAgentDirectory:
    """A centralized directory for ACP agent discovery."""

    def __init__(self):
        self.agents: dict[str, dict] = {}

    def register(self, agent_id: str, metadata: dict) -> None:
        """Register an agent in the directory."""
        self.agents[agent_id] = {
            **metadata,
            "status": "available",
        }

    def discover(self, required_skill: str) -> list[dict]:
        """Find agents that have a specific skill."""
        return [
            {"id": agent_id, **info}
            for agent_id, info in self.agents.items()
            if required_skill in info.get("skills", [])
            and info["status"] == "available"
        ]

    def update_status(self, agent_id: str, status: str) -> None:
        """Update an agent's availability status."""
        if agent_id in self.agents:
            self.agents[agent_id]["status"] = status


class ACPMessage:
    """A message in the ACP protocol."""

    def __init__(self, thread_id: str, sender: str, content: list[dict]):
        self.thread_id = thread_id
        self.sender = sender
        self.content = content  # List of parts: text, file, data


class ACPAgent:
    """An agent that communicates via ACP."""

    def __init__(self, agent_id: str, skills: list[str], directory: ACPAgentDirectory):
        self.agent_id = agent_id
        self.skills = skills
        self.directory = directory
        self.inbox: list[ACPMessage] = []

        # Register with the directory on creation
        self.directory.register(agent_id, {
            "skills": skills,
            "description": f"Agent with skills: {', '.join(skills)}",
        })

    async def send_to(self, recipient_id: str, thread_id: str, content: list[dict]) -> None:
        """Send a message to another agent."""
        message = ACPMessage(
            thread_id=thread_id,
            sender=self.agent_id,
            content=content,
        )
        # In a real implementation, this would go through a message broker
        print(f"[ACP] {self.agent_id} -> {recipient_id}: {content}")

    async def request_work(self, skill: str, instruction: str) -> str | None:
        """Find an agent with a skill and request work."""
        candidates = self.directory.discover(skill)
        if not candidates:
            print(f"No available agent found with skill: {skill}")
            return None

        target = candidates[0]
        target_id = target["id"]
        self.directory.update_status(target_id, "busy")

        await self.send_to(
            recipient_id=target_id,
            thread_id=f"thread-{self.agent_id}-{target_id}",
            content=[{"type": "text", "text": instruction}],
        )
        return target_id

ACP vs A2A

DimensiónACP (IBM)A2A (Google)
Enfoque principalOrquestación empresarial internaInteroperabilidad entre proveedores
DescubrimientoDirectorio centralizado de agentesDistribuido (Agent Cards en URLs conocidas)
ComunicaciónBasada en eventos, asíncrona por defectoHTTP + JSON-RPC, soporta streaming SSE
GobernanzaTrazas de auditoría integradas, control de accesoDelegado a la implementación
Escenario idealMalla de agentes internos de una empresaAgentes de diferentes organizaciones colaborando
Código abiertoProyecto BeeAIEspecificación abierta con implementaciones de referencia

En la práctica, una empresa podría usar ACP para la coordinación interna de agentes y A2A para comunicarse con agentes de socios externos.

10.4 El panorama emergente de protocolos

Tres protocolos principales definen ahora la pila de comunicación de agentes, cada uno operando en una capa diferente:

Interactive · El panorama de protocolos de agentes

Protocolos agénticos

MCP, A2A, ACP

Tres estándares que cubren capas distintas: tools, agentes pares, y comunicación general. Pulsa una columna para abrirla.

MCP

Model Context Protocol

Estándar de Anthropic para conectar agentes a herramientas y fuentes de datos.

Mantenido por Anthropic

CaracterísticaMCPA2AACP
Invocación de herramientas
Descubrimiento de agentes
Sesión con estado
Negociación de schemas
Autenticación

Tabla comparativa de protocolos

ProtocoloCreadorPropósitoTransportePrimitiva claveEstado (2025)
MCPAnthropicModelo/Agente a herramientasJSON-RPC sobre stdio/HTTP+SSETool, Resource, PromptAmpliamente adoptado; soportado por IDEs y frameworks principales
A2AGoogleAgente a agente (entre proveedores)HTTP + JSON-RPC 2.0, SSEAgent Card, Task, ArtifactEspecificación abierta publicada en abril 2025; adopción creciente
ACPIBMAgente a agente (empresarial)HTTP REST, eventos asíncronosAgent Directory, Run, MessageCódigo abierto vía proyecto BeeAI; enfocado a empresas

¿Convergerá el ecosistema?

La historia de los estándares tecnológicos sugiere tres resultados posibles:

  1. Convergencia en un único protocolo: Improbable a corto plazo. Cada protocolo aborda un caso de uso genuinamente diferente (acceso a herramientas vs. comunicación entre agentes de diferentes proveedores vs. orquestación empresarial).

  2. Coexistencia con puentes: Lo más probable. Los agentes implementarán múltiples protocolos o usarán capas de puente/adaptador. Un agente empresarial podría usar MCP para acceder a sus herramientas, ACP para coordinarse con agentes internos y A2A para colaborar con agentes externos.

  3. Fragmentación: Posible si los protocolos evolucionan en direcciones incompatibles. Esto crearía una carga de integración y ralentizaría la adopción.

El modelo mental más productivo es la analogía TCP/IP: del mismo modo que Internet tiene múltiples protocolos en diferentes capas (Ethernet, IP, TCP, HTTP), el ecosistema de agentes está desarrollando protocolos en diferentes capas de abstracción. MCP es la "capa de acceso a herramientas", A2A es la "capa de interoperabilidad entre agentes" y ACP es la "capa de orquestación empresarial". No compiten; se complementan.

Un factor clave a observar es si las principales empresas de IA (Google, Anthropic, OpenAI, Microsoft, IBM, Meta) convergen en soportar estos protocolos en sus SDKs. Las señales tempranas son positivas: Google ha expresado soporte para MCP junto con A2A, y Anthropic ha reconocido la naturaleza complementaria de A2A.


1211. Frameworks multiagente modernos

El panorama de protocolos descrito arriba define cómo se comunican los agentes. Pero los desarrolladores también necesitan frameworks para construir los propios agentes. El ecosistema de frameworks multiagente ha madurado significativamente en 2024-2025. Aquí revisamos los más importantes y discutimos cuándo usar cada uno.

11.1 OpenAI Agents SDK

OpenAI lanzó su Agents SDK (anteriormente llamado Swarm) como un framework ligero para construir sistemas multiagente. La abstracción central es el handoff: la capacidad de un agente para transferir el control a otro agente en mitád de una conversación.

Conceptos clave:

  • Agent: Definido por un nombre, instrucciones (prompt de sistema), herramientas y handoffs opcionales a otros agentes.
  • Handoff: Una herramienta especial que transfiere la conversación a otro agente. El nuevo agente toma el control completamente, con acceso al historial completo de la conversación.
  • Runner: Ejecuta el bucle del agente: llamando al modelo, ejecutando herramientas, gestionándo handoffs, hasta producir una respuesta final.
  • Guardrails: Validación de entrada y salida que puede interrumpir la ejecución si se violan condiciones de seguridad.
python
# Conceptual example of OpenAI Agents SDK pattern
# (simplified for educational purposes)

class Agent:
    def __init__(self, name: str, instructions: str, tools: list = None, handoffs: list = None):
        self.name = name
        self.instructions = instructions
        self.tools = tools or []
        self.handoffs = handoffs or []  # List of other Agents

class HandoffExample:
    """Demonstrates the handoff pattern."""

    def build_triage_system(self):
        """Build a customer service triage system with handoffs."""

        billing_agent = Agent(
            name="Billing Specialist",
            instructions="You handle billing questions: invoices, payments, refunds.",
            tools=[lookup_invoice, process_refund],
        )

        technical_agent = Agent(
            name="Technical Support",
            instructions="You handle technical issues: bugs, configuration, integration.",
            tools=[search_docs, create_ticket],
        )

        triage_agent = Agent(
            name="Triage Agent",
            instructions=(
                "You are the first point of contact. Determine whether the customer "
                "needs billing help or technical support, then hand off to the "
                "appropriate specialist."
            ),
            handoffs=[billing_agent, technical_agent],
        )

        return triage_agent

Cuándo usarlo: Cuándo ya se está en el ecosistema OpenAI y se necesita un sistema multiagente simple y ligero con patrones claros de handoff. Bueno para enrutamiento de servicio al cliente, sistemas de soporte escalonado y flujos de trabajo secuenciales.

11.2 Claude Agent SDK (Anthropic)

El Claude Agent SDK de Anthropic proporciona herramientas para construir agentes potenciados por modelos Claude. Enfatiza la seguridad, el pensamiento extendido y el uso de herramientas como conceptos de primera clase.

Conceptos clave:

  • Bucle agéntico: El SDK gestiona el bucle central de pensar, actuar (uso de herramientas) y observar (resultados de herramientas).
  • Pensamiento extendido: Claude puede "pensar" antes de actuar, produciendo una cadena de pensamiento visible para el desarrollador pero no enviada al usuario.
  • Integración MCP: Soporte nativo para servidores MCP, permitiendo a los agentes conectarse a herramientas y fuentes de datos vía el protocolo MCP.
  • Uso de ordenador: Los agentes pueden interactuar con interfaces gráficas, tomando capturas de pantalla y realizando acciones de ratón y teclado.
python
# Conceptual example of Claude Agent SDK pattern
# (simplified for educational purposes)

import anthropic


def build_research_agent():
    """Build a research agent using Claude Agent SDK patterns."""
    client = anthropic.Anthropic()

    tools = [
        {
            "name": "search_papers",
            "description": "Search academic papers by query.",
            "input_schema": {
                "type": "object",
                "properties": {
                    "query": {"type": "string", "description": "Search query"},
                    "max_results": {"type": "integer", "default": 5},
                },
                "required": ["query"],
            },
        },
        {
            "name": "read_pdf",
            "description": "Extract text from a PDF document.",
            "input_schema": {
                "type": "object",
                "properties": {
                    "url": {"type": "string", "description": "URL of the PDF"},
                },
                "required": ["url"],
            },
        },
    ]

    # The agentic loop: send a message, process tool calls, repeat
    messages = [{"role": "user", "content": "Find recent papers on multi-agent debate."}]

    while True:
        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=4096,
            system="You are a research assistant. Use tools to find and analyze papers.",
            tools=tools,
            messages=messages,
        )

        # Check if the model wants to use a tool
        if response.stop_reason == "tool_use":
            # Execute tool calls and feed results back
            tool_results = execute_tools(response.content)
            messages.append({"role": "assistant", "content": response.content})
            messages.append({"role": "user", "content": tool_results})
        else:
            # Model produced a final response
            return response.content

Cuándo usarlo: Cuándo se necesitan agentes con fuertes capacidades de razonamiento, pensamiento extendido o cuando el sistema está construido en torno al ecosistema MCP. El énfasis de Claude en la seguridad lo convierte en una buena opción para aplicaciones de alto riesgo.

11.3 LangGraph (Ecosistema LangChain)

LangGraph adopta un enfoque fundamentalmente diferente: modela los flujos de trabajo multiagente como grafos dirigidos donde los nodos son agentes o funciones y las aristas son transiciones entre ellos.

Conceptos clave:

  • StateGraph: Un grafo donde los nodos leen y escriben en un objeto de estado compartido. Las aristas definen el flujo entre nodos.
  • Nodos: Pasos individuales de procesamiento (llamadas a agentes, ejecuciones de herramientas, lógica condicional).
  • Aristas: Transiciones entre nodos. Pueden ser incondicionales (siempre seguir esta ruta) o condicionales (elegir ruta basándose en el estado).
  • Checkpointing: LangGraph puede guardar y restaurar el estado en cualquier punto, habilitando flujos de trabajo con intervención humana, recuperación de errores y procesos de larga duración.
  • Subgrafos: Los grafos pueden contener otros grafos, permitiendo composición jerárquica.
python
# Conceptual example of LangGraph pattern
# (simplified for educational purposes)

from typing import TypedDict


class ResearchState(TypedDict):
    query: str
    papers: list[dict]
    analysis: str
    needs_more_research: bool
    final_report: str


def search_node(state: ResearchState) -> dict:
    """Search for relevant papers."""
    papers = search_papers(state["query"])
    return {"papers": papers}


def analyze_node(state: ResearchState) -> dict:
    """Analyze the collected papers."""
    analysis = analyze_papers(state["papers"])
    needs_more = len(state["papers"]) < 5
    return {"analysis": analysis, "needs_more_research": needs_more}


def report_node(state: ResearchState) -> dict:
    """Generate the final report."""
    report = generate_report(state["analysis"], state["papers"])
    return {"final_report": report}


def should_continue(state: ResearchState) -> str:
    """Decide whether to search more or write the report."""
    if state["needs_more_research"]:
        return "search"  # Loop back to search
    return "report"  # Proceed to report generation


# Build the graph
# graph = StateGraph(ResearchState)
# graph.add_node("search", search_node)
# graph.add_node("analyze", analyze_node)
# graph.add_node("report", report_node)
# graph.set_entry_point("search")
# graph.add_edge("search", "analyze")
# graph.add_conditional_edges("analyze", should_continue, {"search": "search", "report": "report"})
# app = graph.compile()

Cuando usarlo: Cuando tu flujo de trabajo multiagente tiene un flujo de control complejo con bucles, ramas y lógica condicional. Particularmente fuerte para flujos de trabajo que necesitan intervención humana, checkpointing o procesos de larga duración. La abstracción de grafos facilita la visualización y el razonamiento sobre el flujo de trabajo.

11.4 CrewAI

CrewAI se inspira en la dinámica de equipos humanos, usando un enfoque basado en roles donde los agentes se definen por su rol, objetivo e historia de fondo. Los agentes forman una "tripulación" que colabora en tareas.

Conceptos clave:

  • Agent: Definido por un rol (por ejemplo, "Analista de Datos Senior"), un objetivo y una historia de fondo que moldea su persona.
  • Task: Una pieza específica de trabajo asignada a un agente, con una descripción y una salida esperada.
  • Crew: Un equipo de agentes que colaboran para completar un conjunto de tareas.
  • Process: La estrategia de colaboración: secuencial (tareas ejecutadas en orden) o jerárquica (un agente manager delega a trabajadores).
python
# Conceptual example of CrewAI pattern
# (simplified for educational purposes)

class CrewAgent:
    def __init__(self, role: str, goal: str, backstory: str, tools: list = None):
        self.role = role
        self.goal = goal
        self.backstory = backstory
        self.tools = tools or []

class Task:
    def __init__(self, description: str, expected_output: str, agent: CrewAgent):
        self.description = description
        self.expected_output = expected_output
        self.agent = agent

# Define agents
researcher = CrewAgent(
    role="Senior Research Analyst",
    goal="Discover and analyze the latest trends in agentic AI.",
    backstory="A veteran AI researcher with 15 years of experience in multi-agent systems.",
)

writer = CrewAgent(
    role="Technical Writer",
    goal="Transform research findings into clear, engaging content.",
    backstory="A science communicator skilled at making complex topics accessible.",
)

# Define tasks
research_task = Task(
    description="Research the latest developments in agent communication protocols (A2A, ACP, MCP).",
    expected_output="A comprehensive summary with key findings and comparisons.",
    agent=researcher,
)

writing_task = Task(
    description="Write a blog post based on the research findings.",
    expected_output="A 1500-word blog post suitable for a technical audience.",
    agent=writer,
)

# The crew would execute: research_task -> writing_task

Cuando usarlo: Cuando se desea una abstracción de alto nivel e intuitiva para la colaboración multiagente. CrewAI es particularmente bueno para pipelines de creación de contenido, flujos de investigación y escenarios donde la colaboración basada en roles se mapea naturalmente al problema. Su simplicidad lo convierte en un buen punto de partida para equipos nuevos en sistemas multiagente.

11.5 AutoGen (Microsoft)

AutoGen fue cubierto en detalle en la Sección 6. Desde su lanzamiento inicial, ha evolucionado a AutoGen 0.4+ con una arquitectura rediseñadaque incluye:

  • Comunicación asíncrona basada en eventos entre agentes.
  • Runtime de agentes: Un entorno gestionado que maneja el enrutamiento de mensajes, el ciclo de vida de agentes y la ejecución distribuida.
  • Tipos de agentes extensibles: Más allá de los agentes conversables, AutoGen ahora soporta agentes de uso de herramientas, agentes de ejecución de código y tipos de agentes personalizados.
  • Soporte multilenguaje: Los agentes pueden implementarse en Python o .NET.

Cuándo usarlo: Cuándo se necesita un framework maduro y bien probado con fuerte soporte para patrones conversacionales multiagente, ejecución de código y dinámicas de chat grupal.

11.6 Comparación de frameworks

FrameworkCreadorAbstracción centralMejor paraCurva de aprendizaje
OpenAI Agents SDKOpenAIHandoffs entre agentesEnrutamiento simple y delegaciónBaja
Claude Agent SDKAnthropicBucle agéntico + herramientas MCPTareas críticas de seguridad, razonamiento intensivoBaja-Media
LangGraphLangChainGrafo de estados con nodos/aristasFlujos de trabajo complejos con bucles y ramasMedia-Alta
CrewAICrewAI Inc.Tripulaciones basadas en roles y tareasPipelines de contenido, flujos de investigaciónBaja
AutoGenMicrosoftAgentes conversables, chat grupalConversaciones multiagente, ejecución de códigoMedia

11.7 Elección de un framework

No existe un único "mejor" framework. La elección depende de tus requisitos:

  1. Si necesitas simplicidad: Empieza con CrewAI u OpenAI Agents SDK. Ambos tienen curvas de aprendizaje suaves y manejan patrones comunes bien.
  2. Si necesitas un flujo de control complejo: Usa LangGraph. Su abstracción de grafos maneja bucles, ramas e intervención humana de forma natural.
  3. Si necesitas un razonamiento fuerte y seguridad: Usa el Claude Agent SDK con pensamiento extendido.
  4. Si necesitas conversaciones multiagente: Usa AutoGen, que fue diseñado específicamente para patrones conversacionales multiagente.
  5. Si necesitas interoperabilidad entre frameworks: Implementa A2A sobre cualquier framework que uses, para que tus agentes puedan colaborar con agentes construidos en otros frameworks.

Los frameworks están convergiendo: la mayoría ahora soporta patrones similares (uso de herramientas, handoffs, conversaciones multiturno). Los diferenciadores son cada vez más la integración del ecosistema, el soporte de modelos y la experiencia del desarrollador, más que las capacidades fundamentales.


1312. Direcciones emergentes adicionales

Leyes de escalado para sistemas multiagente

La investigación está comenzando a explorar cómo escala el rendimiento de los sistemas multiagente con el número de agentes. Los hallazgos iniciales sugieren que añadir más agentes ayuda hasta cierto punto, tras el cual la sobrecarga de coordinación domina y el rendimiento se estanca o degrada (Li et al., 2024).

Sistemas multiagente autoorganizados

En lugar de predefinir roles y patrones de comunicación, algunas investigaciones exploran sistemas donde los agentes se organizan autónomamente, formando equipos, asignando roles y estableciendo canales de comunicación según sea necesario. Esto está inspirado en la teoría organizacional y la inteligencia de enjambre.

Equipos humano-agente

Los sistemas multiagente incluyen cada vez más participantes humanos. Los humanos pueden servir como supervisores, revisores o colaboradores dentro de un flujo de trabajo multiagente. Diseñar interfaces humano-agente efectivas que aprovechen las fortalezas de ambos es un área de investigación activa.


14Preguntas de discusión

  1. Tamaño óptimo del equipo: En las organizaciones humanas, la efectividad del equipo a menudo se degrada más allá de 7-8 miembros. ¿Existe un límite similar para los sistemas multiagente basados en LLM? ¿Qué factores determinan el número óptimo de agentes?

  2. Confianza entre agentes: ¿Deberían los agentes confiar en las salidas de los demás o deberían verificar siempre? ¿Cuál es el equilibrio correcto entre confianza (para eficiencia) y verificación (para precisión)?

  3. Jerarquías emergentes: Si no se les dan roles predefinidos, ¿surgirán jerarquías de forma natural? ¿Qué nos dice esto sobre la naturaleza de la organización?

  4. Rentabilidad: Los sistemas multiagente multiplican los costes de LLM. ¿En qué circunstancias justifica la mejora de calidad el coste adicional?

  5. Responsabilidad: Cuando un sistema multiagente comete un error, ¿qué agente es "responsable"? ¿Cómo debemos diseñar mecanismos de rendición de cuentas?

  6. Robustez adversaria: Si un agente en un sistema multiagente es comprometido (por ejemplo, mediante inyección de prompt), ¿cómo afecta esto al sistema completo? ¿Cómo podemos diseñar arquitecturas multiagente resilientes?

  7. Adopción de protocolos: ¿Coexistirán A2A, ACP y MCP como capas complementarias, o el ecosistema se consolidará en torno a menos protocolos? ¿Qué precedentes históricos (por ejemplo, las "guerras de navegadores", REST vs. SOAP) pueden informar nuestras predicciones?

  8. Dependencia del framework: ¿Cuánto restringe la elección del framework multiagente tu arquitectura? ¿Qué estrategias puedes usar para permanecer agnóstico respecto al framework mientras te beneficias de sus características específicas?


15Resumen y conclusiones clave

  1. Los sistemas multiagente extienden las capacidades de los agentes individuales a través de la especialización, la verificación y el procesamiento paralelo. Son más valiosos para tareas complejas que se benefician de perspectivas diversas.

  2. Los protocolos de comunicación moldean el comportamiento del sistema. El paso de mensajes ofrece flexibilidad, el estado compartido asegura consistencia y las arquitecturas de pizarra proporcionan colaboración estructurada.

  3. Los patrones de colaboración deben ajustarse a la tarea. Los pipelines secuenciales son adecuados para flujos de trabajo lineales, la ejecución paralela para subtareas independientes, las estructuras jerárquicas para descomposición compleja y el debate para tareas de razonamiento.

  4. Tres protocolos complementarios están definiendo la pila de comunicación de agentes: MCP (modelo a herramientas, vertical), A2A (agente a agente, entre proveedores) y ACP (agente a agente, empresarial). Comprender cuándo y cómo usar cada uno es esencial para construir sistemas multiagente en producción.

  5. La orquestación es el desafío clave. Los patrones de supervisor, round-robin y enrutamiento dinámico tienen cada uno compromisos entre control, flexibilidad y sobrecarga.

  6. Los frameworks modernos (OpenAI Agents SDK, Claude Agent SDK, LangGraph, CrewAI, AutoGen) ofrecen diferentes abstracciones para construir sistemas multiagente. La elección depende de la complejidad del flujo de trabajo, los requisitos de razonamiento y las restricciones del ecosistema.

  7. Las preocupaciones prácticas dominan los despliegues reales: la sobrecarga de coordinación, la propagación de errores, la gestión de costés y las condiciones de terminación requieren ingeniería cuidadosa.

  8. La colaboración adversaria (debate) puede mejorar la calidad del razonamiento al obligar a los agentes a defender sus posiciones y abordar contraargumentos.

  9. El ecosistema está convergiendo: los frameworks están adoptando patrones similares mientras los protocolos habilitan la interoperabilidad entre frameworks. Diseñar agentes agnósticos respecto al framework con soporte de protocolos es una estrategia sólida a largo plazo.


16Referencias

  1. Wu, Q., Bansal, G., Zhang, J., Wu, Y., Li, B., Zhu, E., Jiang, L., Zhang, X., Zhang, S., Liu, J., Awadallah, A. H., White, R. W., Burger, D., & Wang, C. (2023). AutoGen: Enabling Next-Gen LLM Applications vía Multi-Agent Conversation. arXiv preprint arXiv:2308.08155.

  2. Du, Y., Li, S., Torralba, A., Tenenbaum, J. B., & Mordatch, I. (2023). Improving Factuality and Reasoning in Language Models through Multiagent Debate. arXiv preprint arXiv:2305.14325.

  3. Hong, S., Zhuge, M., Chen, J., Zheng, X., Cheng, Y., Zhang, C., Wang, J., Wang, Z., Yau, S. K. S., Lin, Z., Zhou, L., Ran, C., Xiao, L., Wu, C., & Schmidhuber, J. (2024). MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework. International Conference on Learning Representations (ICLR).

  4. Bond, A. H., & Gasser, L. (1988). Readings in Distributed Artificial Intelligence. Morgan Kaufmann.

  5. Li, G., Hammoud, H. A. A. K., Itani, H., Khizbullin, D., & Ghanem, B. (2024). CAMEL: Communicative Agents for "Mind" Exploration of Large Language Model Society. Advances in Neural Information Processing Systems (NeurIPS), 36.

  6. Park, J. S., O'Brien, J. C., Caí, C. J., Morris, M. R., Liang, P., & Bernstein, M. S. (2023). Generative Agents: Interactive Simulácra of Human Behavior. Proceedings of the 36th Annual ACM Symposium on User Interface Software and Technology (UIST).

  7. Erman, L. D., Hayes-Roth, F., Lesser, V. R., & Reddy, D. R. (1980). The Hearsay-II Speech-Understanding System: Integrating Knowledge to Resolve Uncertainty. ACM Computing Surveys, 12(2), 213-253.

  8. Chan, C., Chen, W., Su, Y., Yu, J., Xue, W., Zhang, S., Fu, J., & Liu, Z. (2024). ChatEval: Towards Better LLM-based Evaluators through Multi-Agent Debate. International Conference on Learning Representations (ICLR).

  9. Google. (2025). Agent2Agent Protocol (A2A): An Open Protocol for Agent Interoperability. https://google.github.io/A2A/

  10. IBM. (2025). Agent Communication Protocol (ACP). BeeAI Open Source Project. https://agentcommunicationprotocol.dev/

  11. Anthropic. (2024). Model Context Protocol (MCP). https://modelcontextprotocol.io/

  12. OpenAI. (2025). Agents SDK Documentation. https://openai.github.io/openai-agents-python/

  13. LangChain. (2024). LangGraph: Build Stateful Multi-Agent Applications. https://www.langchain.com/langgraph

  14. Moura, J. (2024). CrewAI: Framework for Orchestrating Role-Playing, Autonomous AI Agents. https://www.crewai.com/


Parte de "IA Agentica: Fundamentos, Arquitecturas y Aplicaciones" (CC BY-SA 4.0).