Multi-Agent Communication Protocols and Message Passing

Multi-Agent Communication Protocols and Message Passing

When multiple AI agents need to collaborate, the communication layer becomes critical. Unlike single-agent systems where reasoning happens in one context, multi-agent systems must coordinate across different agents with different knowledge, goals, and capabilities. This article explores the foundations of agent communication and the protocols that make effective collaboration possible.

Concept Introduction: Why Communication Protocols Matter

Simple Explanation

Imagine you’re working on a team where everyone speaks different languages and has different expertise. Without a common way to ask questions, share information, and coordinate tasks, chaos ensues. AI agents face the same challenge. A communication protocol is the “language” and “etiquette” agents use to work together.

Technical Detail

Multi-agent communication protocols define the syntax (message format), semantics (meaning), and pragmatics (when and how to use messages) for agent interactions. These protocols must handle:

The protocol choice dramatically affects system complexity, flexibility, and robustness.

Historical & Theoretical Context

Origin of Agent Communication Languages

Agent communication research emerged in the 1970s from distributed artificial intelligence. Early work by Carl Hewitt on the Actor Model (1973) established message passing as a fundamental primitive for concurrent computation.

In the 1990s, DARPA’s Knowledge Sharing Effort led to two major agent communication languages:

KQML (Knowledge Query and Manipulation Language): Developed in 1993, KQML defined performatives (speech acts) like “tell,” “ask,” “reply,” and “subscribe.” It separated content language (what you’re saying) from communication language (how you’re saying it).

FIPA ACL (Foundation for Intelligent Physical Agents Agent Communication Language): Released in 1997, FIPA ACL built on KQML but provided more rigorous semantics based on speech act theory from philosophy of language.

Theoretical Foundation: Speech Act Theory

Philosopher J.L. Austin and later John Searle developed speech act theory, which recognizes that language doesn’t just describe the world—it performs actions. When an agent says “I promise to deliver this result,” it’s not just transmitting information; it’s making a commitment.

FIPA ACL’s 20+ performatives (INFORM, REQUEST, PROPOSE, ACCEPT-PROPOSAL, etc.) map directly to speech act categories: assertives (stating facts), directives (requesting actions), commissives (making commitments), and declaratives (changing world state).

Design Patterns & Architectures

Pattern 1: Request-Reply (Client-Server)

The simplest pattern: one agent requests information or action, another replies.

Agent A → REQUEST(action) → Agent B
Agent B → INFORM(result) → Agent A

Use case: Simple task delegation, information retrieval

Pattern 2: Contract Net Protocol

A manager agent broadcasts a task announcement. Multiple contractor agents bid with proposals. The manager evaluates bids and awards the contract.

Manager → CALL_FOR_PROPOSALS(task) → [Contractors]
Contractors → PROPOSE(bid) → Manager
Manager → ACCEPT_PROPOSAL → Selected Contractor
Manager → REJECT_PROPOSAL → Other Contractors
Contractor → INFORM(result) → Manager

Use case: Task allocation in multi-agent systems, resource optimization

Pattern 3: Publish-Subscribe

Agents subscribe to topics. Publishers send updates to all subscribers without knowing who they are.

Agent B → SUBSCRIBE(topic) → Broker
Agent C → SUBSCRIBE(topic) → Broker
Agent A → PUBLISH(topic, data) → Broker
Broker → INFORM(data) → Agent B, Agent C

Use case: Event-driven architectures, monitoring systems, distributed coordination

Pattern 4: Negotiation Protocols

Agents exchange proposals and counter-proposals until reaching agreement or deadlock.

Agent A → PROPOSE(offer_1) → Agent B
Agent B → REJECT_PROPOSAL + PROPOSE(counter_1) → Agent A
Agent A → PROPOSE(offer_2) → Agent B
Agent B → ACCEPT_PROPOSAL → Agent A
Both agents → COMMIT

Use case: Resource allocation, conflict resolution, collaborative planning

Practical Application: Building a Multi-Agent Task System

Let’s implement a simple multi-agent system using message passing in Python. We’ll build a research team with specialized agents that communicate via a message broker.

from dataclasses import dataclass
from typing import Literal, Any, Callable
from queue import Queue
import json

@dataclass
class Message:
    """FIPA ACL-inspired message structure"""
    performative: Literal['REQUEST', 'INFORM', 'PROPOSE', 'ACCEPT', 'REJECT']
    sender: str
    receiver: str
    content: dict[str, Any]
    reply_to: str | None = None
    conversation_id: str | None = None

class MessageBroker:
    """Central message router for agents"""
    def __init__(self):
        self.agents: dict[str, Queue] = {}
    
    def register(self, agent_id: str) -> Queue:
        """Register an agent and return its message queue"""
        self.agents[agent_id] = Queue()
        return self.agents[agent_id]
    
    def send(self, message: Message):
        """Route message to recipient"""
        if message.receiver in self.agents:
            self.agents[message.receiver].put(message)
        elif message.receiver == "broadcast":
            for agent_id, queue in self.agents.items():
                if agent_id != message.sender:
                    queue.put(message)

class Agent:
    """Base agent with communication capabilities"""
    def __init__(self, agent_id: str, broker: MessageBroker):
        self.agent_id = agent_id
        self.broker = broker
        self.inbox = broker.register(agent_id)
        self.handlers: dict[str, Callable] = {}
    
    def send(self, performative: str, receiver: str, content: dict):
        """Send a message"""
        msg = Message(
            performative=performative,
            sender=self.agent_id,
            receiver=receiver,
            content=content
        )
        self.broker.send(msg)
        print(f"[{self.agent_id}] → [{receiver}]: {performative} - {content}")
    
    def receive(self) -> Message | None:
        """Check for new messages"""
        return None if self.inbox.empty() else self.inbox.get()
    
    def process_messages(self):
        """Process all pending messages"""
        while msg := self.receive():
            self.handle_message(msg)
    
    def handle_message(self, msg: Message):
        """Override to implement agent-specific behavior"""
        print(f"[{self.agent_id}] ← [{msg.sender}]: {msg.performative}")

class ResearcherAgent(Agent):
    """Agent that can conduct research on topics"""
    def __init__(self, agent_id: str, broker: MessageBroker, specialty: str):
        super().__init__(agent_id, broker)
        self.specialty = specialty
    
    def handle_message(self, msg: Message):
        if msg.performative == 'REQUEST' and msg.content.get('action') == 'research':
            topic = msg.content['topic']
            # Simulate research
            result = f"Research on {topic} (specialized in {self.specialty}): " \
                     f"Found 3 key insights..."
            self.send('INFORM', msg.sender, {'result': result})

class CoordinatorAgent(Agent):
    """Agent that coordinates multi-step research tasks"""
    def __init__(self, agent_id: str, broker: MessageBroker):
        super().__init__(agent_id, broker)
        self.pending_tasks: dict[str, list] = {}
    
    def request_research(self, topic: str, researchers: list[str]):
        """Delegate research to multiple specialists"""
        task_id = f"task_{topic}"
        self.pending_tasks[task_id] = []
        
        for researcher in researchers:
            self.send('REQUEST', researcher, {
                'action': 'research',
                'topic': topic,
                'task_id': task_id
            })
    
    def handle_message(self, msg: Message):
        if msg.performative == 'INFORM':
            result = msg.content.get('result')
            print(f"[{self.agent_id}] Received result: {result[:50]}...")

# Example usage
def main():
    broker = MessageBroker()
    
    # Create agents
    coordinator = CoordinatorAgent("coordinator", broker)
    ai_researcher = ResearcherAgent("ai_researcher", broker, "AI/ML")
    systems_researcher = ResearcherAgent("systems_researcher", broker, "Distributed Systems")
    
    # Coordinator requests research from specialists
    coordinator.request_research("LLM Agent Architectures", 
                                ["ai_researcher", "systems_researcher"])
    
    # Process messages
    ai_researcher.process_messages()
    systems_researcher.process_messages()
    coordinator.process_messages()

if __name__ == "__main__":
    main()

Integration with Modern Frameworks

Modern agent frameworks like LangGraph, CrewAI, and AutoGen abstract away low-level message passing but use similar patterns internally.

LangGraph uses a state graph where edges represent message flows between agent nodes. The framework handles message routing automatically.

CrewAI implements role-based agents with built-in communication patterns. Agents have delegation and collaboration methods that use request-reply under the hood.

AutoGen uses a conversation-oriented approach where agents exchange messages in structured chat formats, with built-in support for group chat (broadcast) and two-way conversations.

Comparisons & Tradeoffs

Synchronous vs Asynchronous Communication

Synchronous (Request-Reply): Simpler to reason about, but agents block waiting for responses. Good for simple workflows.

Asynchronous (Message Queue): More complex but enables parallelism. Agents continue working while waiting for responses. Essential for scalable systems.

Centralized vs Decentralized Routing

Centralized Broker: Single point of failure, but easier to implement features like logging, replay, and debugging.

Peer-to-Peer: More robust to failures, but harder to monitor and debug. Requires discovery mechanisms.

Structured vs Unstructured Content

Structured (FIPA ACL, JSON Schema): Enables automatic validation and tool integration, but requires upfront schema design.

Unstructured (Natural Language): Flexible and works with LLMs directly, but harder to ensure correctness. Requires LLM to interpret.

Latest Developments & Research

LLM-Based Agent Communication (2023-2025)

Recent work explores using LLMs to generate and interpret agent messages in natural language rather than formal protocols. Papers like “Communicative Agents for Software Development” (2024) show that LLM agents can coordinate complex tasks through natural language dialogue.

Advantage: Flexibility—agents can handle unexpected situations and explain their reasoning.

Challenge: Ambiguity—natural language lacks the precision of formal protocols, leading to misunderstandings.

Semantic Protocols and Ontologies

The Semantic Web community is reviving interest in shared ontologies for agent communication. Using standards like RDF and OWL, agents can communicate with shared understanding of domain concepts.

OpenAI’s Function Calling effectively creates a semantic protocol where the function schema defines both syntax and semantics. This hybrid approach (structured function calls + natural language) is becoming the de facto standard for LLM agents.

Multi-Agent Benchmarks

New benchmarks like AgentBench and CommunicativeAgents (2024-2025) evaluate how well agents coordinate through communication. Key metrics: task completion rate, number of messages, communication efficiency.

Research shows that explicit communication protocols outperform implicit coordination for complex tasks, but the gap narrows with more capable base models.

Cross-Disciplinary Insight: From Human Organizations

Multi-agent communication patterns mirror organizational structures:

Organizational theory suggests that the optimal structure depends on task complexity and environment stability. The same holds for agent systems: use hierarchical for stable, well-defined tasks; use market-based for dynamic task allocation; use team-based for creative, unpredictable work.

Daily Challenge: Build a Multi-Agent Research System

Exercise: Extend the code example above to implement a complete research pipeline:

  1. Add a WriterAgent that receives research from multiple researchers and synthesizes a report
  2. Implement a timeout mechanism—if a researcher doesn’t respond in time, the coordinator should request help from another agent
  3. Add a PROPOSE performative where researchers can proactively suggest related research topics
  4. Implement conversation_id tracking so multiple concurrent research tasks don’t interfere

Thought Exercise: How would this system change if agents used natural language instead of structured messages? What would you gain? What would you lose?

References & Further Reading

Foundational Papers:

Recent Work:

Tools & Frameworks:

Historical Context: