Handling Inter-Agent Message Passing and Communication in MetaGPT

MetaGPT implements inter-agent message passing through a publish/subscribe pattern where the Environment routes Message objects to Roles using an address-based routing table, supporting broadcast via MESSAGE_ROUTE_TO_ALL and self-routing via MESSAGE_ROUTE_TO_SELF.

MetaGPT is a multi-agent framework where autonomous agents modeled as Roles collaborate within a shared Environment. Effective coordination requires robust inter-agent message passing and communication mechanisms that route information between specialized agents. This guide examines the concrete implementation in the FoundationAgents/MetaGPT repository, covering message routing, address registration, and the publish/subscribe flow.

Architectural Overview

MetaGPT models multi-agent systems using three core abstractions. Roles represent autonomous agents with specific capabilities and goals. The Environment acts as a message broker that maintains a registry of all active roles. Message objects carry content, metadata, and explicit routing instructions via the send_to field.

The concrete implementation resides in metagpt.environment.Environment, which inherits from ExtEnv and BaseEnvironment. This class manages the lifecycle of messages and ensures delivery to appropriate recipients based on address matching.

The Environment Routing Table

The Environment maintains a routing table called member_addrs that maps each Role to its set of valid addresses. This dictionary enables efficient message delivery without broadcasting to every agent in the system.


# metagpt/environment/base_env.py – member_addrs definition

member_addrs: Dict[BaseRole, Set] = Field(default_factory=dict, exclude=True)

When a Role joins the Environment, its addresses are registered in this table. The member_addrs mapping allows the Environment to iterate over all registered roles and determine which ones should receive a specific message based on address intersection with the message's send_to field.

Message Routing Constants and Logic

MetaGPT defines special routing constants in metagpt/const.py to handle common addressing patterns. These constants simplify broadcast and self-referential messaging.


# metagpt/const.py – routing constants

MESSAGE_ROUTE_TO_ALL = "<all>"   # Broadcast to every role

MESSAGE_ROUTE_TO_SELF = "<self>" # Route back to the sender

The routing decision logic resides in is_send_to, a helper function in metagpt/utils/common.py. This function checks if a message's recipients intersect with a role's address set, returning True if the role should receive the message.


# metagpt/utils/common.py – routing predicate

def is_send_to(message: "Message", addresses: set):
    if MESSAGE_ROUTE_TO_ALL in message.send_to:
        return True
    for i in addresses:
        if i in message.send_to:
            return True
    return False

Publishing Messages to Agents

When a Role or external code calls publish_message, the Environment iterates through member_addrs and delivers the message to matching roles via put_message.


# metagpt/environment/base_env.py – publish_message implementation

for role, addrs in self.member_addrs.items():
    if is_send_to(message, addrs):
        role.put_message(message)
        found = True

If no recipient matches the message's routing instructions, the Environment logs a warning but still archives the message in its history buffer for debugging purposes. This ensures that message flows remain traceable even when routing fails.

Role-Side Message Handling

Roles receive messages through put_message, which enqueues them in the role's private message buffer. This buffer acts as an inbox that the role processes during its observation phase.


# metagpt/roles/role.py – put_message implementation

def put_message(self, message):
    if not message:
        return
    self.rc.msg_buffer.push(message)

When a Role needs to emit a message, it uses its own publish_message method. This method resolves special routing tags like MESSAGE_ROUTE_TO_SELF before forwarding to the Environment.


# metagpt/roles/role.py – role-side publish_message

def publish_message(self, msg):
    if MESSAGE_ROUTE_TO_SELF in msg.send_to:
        msg.send_to.add(any_to_str(self))
        msg.send_to.remove(MESSAGE_ROUTE_TO_SELF)
    ...
    self.rc.env.publish_message(msg)

The Role's _observe method later consumes messages from rc.msg_buffer, transferring them to rc.memory and setting rc.news for the current action cycle.

Address Registration Flow

Address registration occurs when a Role joins an Environment. The set_env method establishes the bidirectional link between Role and Environment, triggering address registration.


# metagpt/roles/role.py – environment registration

def set_env(self, env):
    self.rc.env = env
    if env:
        env.set_addresses(self, self.addresses)

The Environment's set_addresses method updates the routing table with the Role's address set, making the Role reachable via any of its registered identifiers.


# metagpt/environment/base_env.py – address registration

def set_addresses(self, obj, addresses):
    self.member_addrs[obj] = addresses

This registration mechanism allows Roles to define multiple addresses (name, profile, custom tags) and receive messages directed to any of them.

Complete Working Example

The following example demonstrates a Product Manager and Engineer exchanging messages within an Environment:

from metagpt.environment import Environment
from metagpt.roles import Role, ProductManager, Engineer
from metagpt.schema import UserMessage
from metagpt.actions import UserRequirement
from metagpt.utils.common import any_to_str

# 1️⃣ Create the environment

env = Environment()

# 2️⃣ Add two roles (they automatically register their addresses)

pm = ProductManager(name="Alice", profile="product manager")
eng = Engineer(name="Bob", profile="engineer")
env.add_roles([pm, eng])

# 3️⃣ Send a user requirement to the product manager only

msg = UserMessage(
    content="We need a search feature powered by LLM",
    cause_by=UserRequirement,
    send_to=any_to_str(pm)   # resolves to the role's address string

)
env.publish_message(msg)

# 4️⃣ Run the environment – each role will observe, think and act.

#    The product manager will receive the message, put it in its buffer,

#    react, and eventually publish a response that the engineer will see.

await env.run(k=2)

Under the hood, the Environment resolves the send_to address using is_send_to, delivers the message to the Product Manager's msg_buffer via put_message, and logs the exchange in history. When env.run() executes, the Product Manager observes the message, processes it through its action cycle, and publishes new messages that the Environment routes to the Engineer.

Key Source Files

File Description Link
metagpt/environment/base_env.py Core Environment implementation, routing table, publish_message, run, address helpers. view
metagpt/roles/role.py Role base class, publish_message, put_message, environment registration. view
metagpt/schema.py Message model, default routing fields (send_to, cause_by, etc.). view
metagpt/utils/common.py Helper is_send_to, address conversion utilities (any_to_str, any_to_str_set). view
metagpt/const.py Routing constants (MESSAGE_ROUTE_TO_ALL, MESSAGE_ROUTE_TO_SELF). view

Summary

  • MetaGPT implements inter-agent communication through a lightweight publish/subscribe pattern managed by the Environment class.
  • The member_addrs routing table in metagpt/environment/base_env.py maps each Role to its address set, enabling targeted message delivery.
  • Special routing constants MESSAGE_ROUTE_TO_ALL and MESSAGE_ROUTE_TO_SELF support broadcast and self-referential messaging without hardcoding addresses.
  • The is_send_to predicate in metagpt/utils/common.py determines message eligibility by checking address intersection or broadcast flags.
  • Roles receive messages via put_message, which pushes to rc.msg_buffer, and emit messages via publish_message, which resolves routing tags before delegating to the Environment.

Frequently Asked Questions

How does MetaGPT route a message to specific agents only?

MetaGPT routes messages using the is_send_to function in metagpt/utils/common.py. This function checks if the intersection between the message's send_to set and the role's registered addresses is non-empty. If the message contains MESSAGE_ROUTE_TO_ALL, it matches every role. Otherwise, only roles with overlapping addresses receive the message via put_message.

What is the difference between MESSAGE_ROUTE_TO_ALL and MESSAGE_ROUTE_TO_SELF?

MESSAGE_ROUTE_TO_ALL (defined as "<all>" in metagpt/const.py) broadcasts the message to every registered role in the Environment. MESSAGE_ROUTE_TO_SELF (defined as "<self>") routes the message back to the sender. When a role calls publish_message with MESSAGE_ROUTE_TO_SELF, the method automatically replaces the constant with the role's own address string using any_to_str(self) before forwarding to the Environment.

How do roles receive and process incoming messages?

Roles receive messages through the put_message method defined in metagpt/roles/role.py. This method pushes the message onto self.rc.msg_buffer, which acts as an inbox. During the role's execution cycle, the _observe method consumes this buffer, transferring messages to rc.memory and setting rc.news for the current action iteration. This decouples message receipt from processing, allowing asynchronous communication.

Where is the routing table stored and how is it populated?

The routing table is stored in the member_addrs attribute of the Environment class, defined in metagpt/environment/base_env.py as Dict[BaseRole, Set]. It is populated when roles are added to the environment via add_role or add_roles. The set_env method in metagpt/roles/role.py establishes the bidirectional link and calls env.set_addresses(self, self.addresses), which updates member_addrs with the role's address set.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →