#AI#AI Agent

Communication Design for Multi-Agent Systems

With multiple Agents, the hardest problem is not individual capability but communication: shared state or message passing, protocols, asynchrony, and errors. This article explains blackboards, message passing, handoffs, serialization and contracts, supervisor/pub-sub/peer-to-peer architectures, plus engineering concerns around serialization, versioning, and

Key Points

  • Topic Focus: AI、AI Agent
  • Suitable for: indie hackers, full-stack engineers, AI application developers
  • Core value: quickly understand background, practical paths, common pitfalls, and engineering advice
Author @admin·Published July 26, 2026·Updated July 26, 2026

A well-built single Agent is, at best, a capable employee. Whether multiple Agents can accomplish anything together depends less on which one is smarter than on how they communicate.

This is counterintuitive. Most teams building multi-Agent systems spend 90% of their effort on “making each Agent stronger”: tuning prompts, adding tools, and switching models. Once the system actually runs, they discover that Agent B did not understand the results Agent A painstakingly researched; Agent C gets stuck on a slow tool and brings down the entire workflow; or one Agent changes a field in state and every downstream component fails. The root cause is always the same: the Agents never agreed on how to communicate.

Communication is the true skeleton of a multi-Agent system. The previous article on LangGraph orchestration covered “who schedules whom.” This article explains what the connection between them actually consists of: communication paradigms, contracts, architectures, and engineering pitfalls.

1. What Makes Communication Difficult

Multi-Agent communication may look like nothing more than “sending a message,” but it is an order of magnitude harder than single-Agent communication across four dimensions.

Asynchrony. A single Agent follows a synchronous loop: think one step, act one step. Multiple Agents cannot work that way. An Agent you invoke might take 30 seconds to respond, and you cannot simply block and wait. Asynchrony introduces message reordering, timeout decisions, and partial-failure handling. A synchronous RPC mental model works for a multi-Agent demo but collapses under production load.

State synchronization. Once multiple Agents read and write the same shared data, every classic concurrency problem returns: races, dirty reads, and lost updates. Agent A may have just set status to reviewing, while Agent B still reads drafting during the same tick and makes a series of decisions from stale state.

Error propagation. When a single Agent fails, the impact is limited to that Agent. In a multi-Agent system, one upstream Agent can emit malformed JSON, causing the next Agent's parser to fail while another downstream Agent continues with empty data. The error snowballs, and by the time the symptom appears, the root cause is far removed from the failure site.

Contracts. This is the easiest issue to overlook and the most dangerous. A “message” passed between two Agents is effectively an interface between two independent programs. Without a schema, versioning, or a backward-compatibility commitment, changing one Agent's prompt can silently alter its output structure and break every downstream consumer, often only in production.

Thinking through these four issues before implementation is ten times more important than rushing to choose a framework.

2. Three Communication Modes

There are fundamentally three ways for multiple Agents to exchange information. Choose the wrong paradigm, and everything built on top of it will feel awkward.

Mode 1: Shared Blackboard

All Agents read and write the same shared state. They do not communicate directly, but collaborate indirectly by changing the “blackboard”: one Agent writes to it, and the others read the update in the next round.

text
         ┌──────────────────────────┐
         │ Shared State (Blackboard)│
         │ { task, drafts,          │
         │   messages, ... }        │
         └──┬────┬────┬────┬────────┘
            │    │    │    │
       ┌────┘    │    │    └────┐
       ▼         ▼    ▼         ▼
  [Retriever] [Writer] [Reviewer] [Publisher]

LangGraph's StateGraph is a representative implementation of this paradigm: state is the memory shared by every node in the graph, and each node receives state and returns an incremental update.

Advantages: It is simple and intuitive, with centralized, observable state; one look at the blackboard reveals overall progress. Disadvantages: The state schema becomes a God object, fields accumulate, and Agents become coupled through implicit field reads. Renaming one field can break many components. This mode suits scenarios with clear task boundaries, a manageable number of Agents, usually fewer than six, and stable state.

Mode 2: Message Passing

Agents do not share state; they only send messages to one another. Each Agent is an independently encapsulated component with private state and interacts externally only through messages, following the classic actor model.

text
   [Agent A] ──msg(topic=search)──▶ [Agent B]
      ▲                                │
      └─────────msg(topic=done)────────┘

The sender does not care who consumes the message or when it is consumed; the receiver processes it at its own pace. This is naturally decoupled and asynchronous.

Advantages: Strong decoupling allows Agents to be deployed and upgraded independently, makes horizontal scaling straightforward, and provides good failure isolation because one Agent can fail without affecting the others. Disadvantages: Debugging is difficult because reconstructing one collaboration requires tracing a chain of messages, while ordering and consistency must be managed explicitly. This mode suits scenarios where Agents run across processes or services, require elastic scaling, or have demanding availability requirements.

Mode 3: Handoff

When one Agent reaches the boundary of its capabilities, it transfers control together with the current context to another Agent. This is not “I finished the research, so here are the results.” It is “Take over this conversation.”

text
   User ──▶ [Support Agent] ──handoff(ctx)──▶ [Technical Specialist] ──▶ User

OpenAI's Swarm and many customer-support systems use this approach. The key to handoff is context compression and transfer. Do not pass the previous Agent's entire message history verbatim to the next Agent; that is expensive and noisy. Summarize it into a concise handoff payload containing the user's intent, confirmed facts, the current blocker, and recommended next steps.

Advantages: It is lightweight, requires no central scheduler, and feels like one continuous conversation to the user. Disadvantages: Responsibility can bounce back and forth, with A handing off to B and B deciding the task belongs to A. There is no global view, and traces become fragmented. Handoff suits conversational tasks with linear escalation, such as support triage and ticket escalation, but not complex tasks that require parallel collaboration among multiple Agents.

A rule of thumb: Use a blackboard when a task completes in one process with only a few Agents. Use message passing when Agents span services and must scale. Use handoff for conversational triage. Forcing every scenario into one paradigm is an architectural disaster.

3. Contracts and Serialization: “Interface Definitions” Between Agents

Regardless of the mode, Agents should not exchange unstructured natural-language text. They should exchange structured messages with schemas. If this foundation is weak, everything built on it will be fragile.

Many teams let Agent A place natural language into state["result"], then ask Agent B to parse it with a prompt. This can run, but it is extremely brittle. A slight wording change from A, or a prefix such as “Sure, here are the results,” can break B. LLM output is already nondeterministic; treating it as an interface contract means building reliability on sand.

The correct approach: Every handoff between Agents must use a structured schema.

ts
// Message contract exchanged between Agents
interface SearchResult {
  query: string;
  hits: { title: string; url: string; snippet: string; score: number }[];
  source: "researcher";
  schema_version: 1;
}

Agent A uses structured output or a tool call to enforce an object that conforms to the schema. Agent B receives deterministically structured data and does not need another prompt to parse it. This provides three immediate benefits: validation, because a validator checks the message on receipt and rejects invalid data; evolution, because schema_version supports backward compatibility; and testability, because the contract is an interface that can be unit-tested.

Two engineering details are often overlooked. First, schemas need version numbers. If Agent A upgrades its output structure before downstream Agent B is updated, the absence of a version number turns the change into a production incident. With schema_version, the consumer can reject an incompatible version instead of silently parsing a new format with old logic. Second, validation failures need a fallback. LLMs occasionally emit noncompliant structures. Do not let that crash the entire pipeline: retry once, then return the error upstream for correction or fall back to a safe default.

A practical principle: Treat communication between Agents like RPC between microservices: use an IDL, versions, validation, and degradation paths. Many teams demand extreme robustness from internal service calls while leaving Agent communication uncontrolled. That is a double standard.

4. Common Architectures: Supervisor / Peer-to-Peer / Pub-Sub

Communication modes determine how information moves. Architecture determines how Agents are organized. Three architectures are common, each with its own use cases.

Supervisor (central coordination). One supervisor Agent decomposes tasks, delegates work, and aggregates results. Specialist Agents communicate only with the supervisor. This is the most common architecture: controllable, observable, and easy to augment with human intervention.

text
            ┌──────────┐
       ┌───▶│ Supervisor│◀───┐
       │    └─────┬────┘    │
  ┌────┴──┐  ┌────┴──┐  ┌───┴───┐
  │Agent A│  │Agent B│  │Agent C│
  └───────┘  └───────┘  └───────┘

The cost is that the supervisor becomes a single point of failure and a bottleneck: if it fails, everything fails; if it slows down, everything waits. This architecture suits scenarios where tasks can be decomposed clearly and require global coordination, covering 80% of multi-Agent needs.

Peer-to-peer collaboration. There is no central supervisor. Agents communicate directly and organize their own collaboration. Each Agent knows whom to contact without needing a dispatcher.

text
  [Agent A]◀──────▶[Agent B]
      │                │
      │    ┌───────┐   │
      └───▶│Agent C│◀──┘
           └───────┘

This is flexible and has no single point of failure, but Agents must maintain knowledge of whom to contact. Loops are easy to create, such as A calling B and B calling A, and traces are difficult to follow. This architecture suits research-oriented scenarios where Agents have distinct specialties and collaboration relationships change dynamically.

Pub-Sub (event-driven). Agents publish events to a bus or topic, and every Agent subscribed to that topic receives them automatically. Publishers do not care who consumes an event, and subscribers do not care who produced it, providing the strongest decoupling.

text
                    ┌─────────────┐
   [Agent A]──────▶│  Event Bus  │
                    │ (topic=*)   │
   [Agent B]◀──────│             │
                    └──────┬──────┘
                           │
                      ┌────▼────┐
                      │[Agent C]│
                      └─────────┘

Adding an Agent requires only subscribing it to a topic, making this the most extensible option. However, debugging event streams is a nightmare, and reordering, duplication, and loss all require separate handling. It suits long-running event-driven workflows where Agents are added and removed dynamically, such as multi-Agent response flows triggered by monitoring alerts.

Selection guidance: Start with a supervisor. Consider peer-to-peer or pub-sub only when the supervisor has genuinely become a bottleneck or single-point risk. Many teams begin by demanding a “decentralized peer-to-peer Agent network” and end up unable to run even one stable workflow. Complex architecture should serve real scalability needs, not showmanship.

5. Engineering Practice: Asynchrony, Error Propagation, and Cross-Agent Tracing

Once the architecture is chosen, the following engineering decisions determine whether it can run in production.

Asynchrony and timeouts. Never wait indefinitely when invoking an Agent. Every call needs a timeout, and timeouts must be layered: 10 seconds for one tool call, 60 seconds for one Agent turn, and 10 minutes for the entire task. Decide in advance what happens after a timeout: retry, degrade, or fail fast. The rule is simple: every place that waits for an Agent needs a timeout, and every timeout needs a fallback.

Error propagation and degradation. In a multi-Agent system, error propagation is more dangerous than the error itself. If an upstream failure is placed into downstream input as “normal output,” downstream Agents may continue reasoning from invalid data and produce results that look correct but are actually wrong. This is the most insidious kind of failure.

The correct approach is to propagate errors explicitly and structurally. Use a Result<T, E> pattern so success and failure travel through separate channels. A failure should include error_code, failed_at, and trace_id. When a downstream Agent receives a failure signal, it must either skip the step or follow a degradation path; it must never reason over error content as if it were normal input.

ts
type AgentResult<T> =
  | { ok: true; data: T; trace_id: string }
  | { ok: false; error: string; code: string; trace_id: string; retryable: boolean };

Cross-Agent tracing. When one request passes through supervisor → A → B → C, you must be able to reconstruct the entire path when something fails. This requires a trace_id / conversation_id that spans the entire workflow, recorded in every Agent's logs and trace events.

text
trace_id: abc-123
├─ supervisor (span 1)
│   ├─ agent-A (span 2, parent=1)
│   │   └─ tool: web_search (span 3, parent=2)
│   └─ agent-B (span 4, parent=1)
│       └─ tool: code_exec (span 5, parent=4)

OpenTelemetry, LangSmith, and Langfuse can all handle this. The essential requirement is to inject trace_id at the entry point and propagate it through every layer. A multi-Agent system without tracing is a black box, and a black box cannot be operated reliably.

State writes must be serialized. When multiple Agents concurrently update state in the blackboard model, use a serialization mechanism such as optimistic locking, version numbers, or a single writer. LLM calls are slow, so concurrent writes may appear harmless, but race conditions at high QPS will be painful. Add a version field to state, use conditional checks on writes, and retry conflicts.

6. Three Anti-Patterns

Finally, here are several anti-patterns that repeatedly appear in production. Any one of them can cost a week of work.

Anti-pattern 1: Passing enormous natural-language contexts between Agents. “Pass every previous Agent's full conversation to the next Agent verbatim.” This causes context growth, token explosions, and noise that buries the signal. Each Agent should receive only the minimum context required for its work, and that context should be a structured summary rather than raw chat history. Tokens are not free, and attention is not unlimited.

Anti-pattern 2: Implicit communication without contracts. Two Agents collaborate on the assumption that “it should understand what I mean,” without a schema or validation. The system happens to work in development, then fails after a model change, prompt edit, or edge case. These systems look most impressive in demos and cause the most pain in production.

Anti-pattern 3: Cyclic calls and deadlock-style handoffs. A decides the task belongs to B, B decides it belongs to A, and they continue handing it back and forth until the budget is exhausted. In a peer-to-peer architecture, A may call B, which calls A, creating infinite recursion. Any system that lets multiple Agents call one another must enforce a global maximum hop count. Without that limit, a multi-Agent system is effectively an infinite loop billed by the token.

Summary

  1. The real difficulty of multi-Agent systems is communication, not capability. Asynchrony, state synchronization, error propagation, and contracts are each several times more complex than in a single-Agent system.
  2. Choose among three communication modes according to the scenario: blackboard for shared state and a small single-process team, message passing for an actor model that scales across services, and handoff for conversational escalation that transfers control.
  3. Every handoff between Agents needs a versioned schema contract. Treat it as an RPC interface with an IDL, validation, degradation, and version numbers. Communication without contracts is a time bomb.
  4. Use a supervisor by default, and adopt peer-to-peer or pub-sub only for genuine scaling needs. Complex architecture must serve real requirements, not showmanship.
  5. Production readiness depends on four things: layered timeouts, explicit structured error propagation, an end-to-end trace_id, and serialized concurrent state writes.

A single Agent competes on model capability; a multi-Agent system competes on communication engineering. How you draw the connections between Agents determines whether they become a coordinated dream team or a disorganized crowd that undermines one another.

Related

Related Content