← Back to Learning Hub

Agent Communication

SupervisorOrchestrationAdvanced13 min

By: Anacodic Team

TL;DR

  • Agents coordinate through four mechanisms: shared state / blackboard (post-and-read), message passing (explicit send/receive), handoffs (transfer of control + context), and structured protocols (typed contracts like A2A, Google's Agent-to-Agent standard).
  • Inside one process, the cheapest channel is usually a shared state object (LangGraph TypedDict) or a blackboard (a scratchpad every agent reads/writes). Across processes/orgs, you need a protocol with capability discovery and typed messages.
  • The two failure modes that dominate interviews: context bloat (agents pile everything into a growing window until it's slow, expensive, and confused) and the telephone-game (each summarization/handoff drops or distorts detail — errors compound).
  • Senior insight: the reliability of a multi-agent system is mostly decided at the handoff. Pass structured, minimal, validated context — not raw transcripts — and treat every worker output as untrusted until validated.

Simple explanation + analogy

Picture a hospital.

  • The whiteboard in the ward where every specialist writes findings and reads others' notes is a blackboard / shared state — nobody is addressed directly; you post and others read.
  • A doctor paging a specific cardiologist with a question is message passing — an explicit send to a named recipient.
  • When the ER doctor transfers a patient to the ICU and hands over the chart, that's a handoff — control and context move together; if the chart is incomplete, the ICU team is flying blind.
  • The standardized referral form every hospital agrees on so a doctor at one hospital can refer to another is a protocol (A2A) — it works across organizations because everyone speaks the same format.

The classic hospital failure is the same as the agent one: a verbal handoff where each nurse repeats a slightly-wrong version until the dosage is wrong. That's the telephone game, and it's why structured, written handoffs exist.


Diagram

 SHARED STATE / BLACKBOARD              MESSAGE PASSING              HANDOFF
   ┌───────────────────┐               A ──"do X"──▶ B            A: does part 1
   │  blackboard /     │◀─write─ A       ▲            │            │ transfers control
   │  state dict       │─read──▶ B       └──result───┘            ▼  + context
   │  (papers, flags)  │◀─write─ C     named send/receive       B: continues with A's ctx
   └───────────────────┘─read──▶ D     (queue, direct call)     (control moves, not just data)
   agents post & read; no
   direct addressing

 STRUCTURED PROTOCOL (A2A — across processes/orgs)     TELEPHONE-GAME RISK (anti-pattern)
   ┌─────────┐   AgentCard (capabilities)  ┌─────────┐   orig ─▶ [sum] ─▶ [sum] ─▶ [sum]
   │ Agent A │◀───── discover ────────────▶│ Agent B │          fact ↘ drift ↘ drift ↘ wrong
   └────┬────┘   typed task + artifact     └─────────┘   each hop drops/warps detail;
        └──── task ▶ ... ◀ result/status ──────┘         errors COMPOUND multiplicatively

How it works (deep)

1. Shared state / blackboard

A single structure every agent can read and write. Two variants:

  • Threaded state object — LangGraph passes a TypedDict through nodes; each node returns a partial update that's merged. Communication is implicit: node B reads what node A wrote. The group-trip suggester's GroupTripState carries destination_context, request_type, suggestion between nodes.
  • Blackboard / scratchpad — a mutable store agents post results to and poll. The clinical RAG system's per-request SupervisorRunState is exactly this: it caches each specialist's papers and text, plus a once-only directive so the supervisor doesn't re-invoke a specialist it already consulted (a cheap dedup + loop guard).

Strength: simple, no message plumbing. Risk: concurrent writes need a reducer or they clobber; and shared state grows — the #1 source of context bloat.

2. Message passing

Explicit send/receive between named agents — a queue, a direct call, or a framework Command. More decoupled than shared state; natural for swarm topologies where any agent hands off to any peer. Cost: you now own routing, delivery, and ordering semantics.

3. Handoffs

A handoff transfers control and context to another agent — the ER→ICU transfer. This is where most "agent failures" actually live: not model capability, but what got passed at the boundary. Two rules:

  • Pass structured, minimal context (the task + the specific facts needed), not the whole transcript. The concierge does this — the orchestrator calls each worker with a focused query, not the entire conversation.
  • Return results as data, not exceptions, so the receiver can react. The concierge's tool wrappers return "Budget agent error: ..." strings; the orchestrator sees a failed handoff and adapts.

4. Structured protocols (A2A and friends)

For cross-process / cross-org agents you need a wire contract:

  • A2A (Agent-to-Agent) — Google's protocol (2025). Agents publish an AgentCard (machine-readable capabilities/identity) so others can discover them, then exchange typed tasks and artifacts with explicit status tracking. Because it's modular and loosely coupled, a failure in one agent doesn't cascade — orchestrators can retry, fall back, or degrade gracefully.
  • MCP (Model Context Protocol) is complementary: MCP connects an agent to tools/data; A2A connects agents to each other. Interviewers love this distinction.
  • Related: ACP and ANP in the broader interoperability landscape.

Even inside one process, "protocol" thinking pays off: validate every inter-agent message against a schema. The clinical RAG system's consensus extraction re-parses the synthesizer's prose into a ConsensusModel and returns None on validation failure rather than passing malformed data downstream.

The two big pitfalls

Context bloat. Every handoff that appends the full history grows the window: latency rises, cost rises, and the model gets worse as signal drowns in noise ("context rot"). Mitigations: summarize history (the concierge's SummarizingConversationManager, summary_ratio=0.3), pass references/IDs instead of blobs, and give each worker only its slice.

Telephone-game / error compounding. Each summarization or paraphrase at a boundary can drop or distort a fact; across n hops, distortion compounds. Mitigations: keep the number of hops small, prefer passing structured fields over prose, ground each hop in source data (the clinical RAG system runs retrieval inside each specialist so facts come from Pinecone, not from a previous agent's memory), and validate at each boundary.


The math

Context-bloat cost. If each of k handoffs appends Δ tokens and the base is C₀, the window at hop k is C₀ + kΔ. LLM attention cost is roughly quadratic in sequence length, so end-to-end compute scales like:

Σ_{i=1..k} (C₀ + iΔ)²   ≈  O(k · (C₀ + kΔ)²)

Doubling the hops far more than doubles the cost — the concrete argument for summarizing and passing minimal context.

Telephone-game fidelity. If each hop preserves a fraction f (0<f<1) of the critical detail, after n hops fidelity is:

fidelity(n) = fⁿ        e.g. f = 0.9, n = 5  →  0.59

Detail decays geometrically with hop count. This is the formal reason to minimize hops and pass structured facts (which have f ≈ 1) instead of prose (which erodes).


Code

Blackboard + once-only loop guard — the clinical RAG system

# run_state.py  (example) — per-request shared blackboard
@dataclass
class SupervisorRunState:
    rewrite_result: str | None = None
    classify_result: str | None = None
    specialist_cache: dict[str, dict] = field(default_factory=dict)  # specialty -> {papers, text}
    tool_iterations: int = 0
    loop_capped: bool = False

ONCE_ONLY_DIRECTIVE = ("Already completed for this query. Proceed to synthesis. "
                       "Do not call this tool again.")
# parallel_specialist_tools.py — read the blackboard before doing work again
state = get_run_state()
if state and settings.supervisor_once_only_tools and specialty in state.specialist_cache:
    cached = state.specialist_cache[specialty]         # serve from blackboard
    yield {"specialty": specialty, "specialist_papers": cached.get("papers") or []}
    yield cached.get("text") or ONCE_ONLY_DIRECTIVE    # tell the model: stop re-calling me
    return
# ... otherwise: retrieve, synthesize, then WRITE back to the blackboard:
state.specialist_cache[specialty] = {"papers": papers, "text": "".join(text_parts)}

Handoff with minimal, structured context + errors-as-data — the concierge

# orchestrator.py  (example) — each handoff passes a FOCUSED query, not the transcript
@tool
def activities_agent_tool(query: str) -> str:
    """Handle activities and preference matching queries."""
    try:
        return str(activities_agent(query))     # worker gets only what it needs
    except Exception as e:
        return f"Activities agent error: {str(e)}"       # failed handoff returns as DATA
# orchestrator.py  (example) — fight context bloat on the shared conversation
conversation_manager = SummarizingConversationManager(
    summary_ratio=0.3,           # compress ~30% of history into a summary
    preserve_recent_messages=5,  # but keep the last 5 verbatim
)

Shared state threaded through nodes — the group-trip suggester

# group_trip_graph.py  (example)
class GroupTripState(TypedDict):
    chat_snippet: str
    request_type: str          # written by classify_request, read by routing + retrieve
    destination_context: str   # written by retrieve, read by suggest/answer_followup
    suggestion: Optional[str]  # the final output field
    error: Optional[str]       # per-node error channel, kept on the shared state

async def retrieve_destinations(state):
    docs = await get_retriever(...).ainvoke(query)
    state["destination_context"] = format_destination_context(docs)   # post to shared state
    return state

Validate inter-agent messages against a schema — the clinical RAG system

# consensus_extract.py — never pass an unvalidated synthesis downstream
parsed = parse_consensus_validated(resp.text or "")
if parsed is not None:
    return ConsensusModel.model_validate(parsed)   # typed contract enforced
logger.warning("consensus extract failed: validation returned None")
return None                                        # fail closed, don't forward garbage

Real-world example

The clinical RAG system (blackboard). One clinical query fans out to subspecialty agents; each writes its papers + synthesis to the SupervisorRunState blackboard. The once-only directive prevents the supervisor from re-consulting a finished specialist — a communication and loop-control mechanism in one. Grounding each specialist in its own Pinecone retrieval (not in a peer's summary) is a deliberate anti-telephone-game move.

The concierge (handoffs). The orchestrator hands a focused query to each specialist and treats worker errors as strings it can reason about. A SummarizingConversationManager keeps the shared history from bloating across turns.

The group-trip suggester (shared state). Nodes communicate purely by writing fields on GroupTripStateclassify_request writes request_type, routing reads it, retrieve writes destination_context, suggest reads it. No message bus needed inside one graph.

A2A (cross-org). Imagine the trip assistant's booking agent handing off to an external airline's reservation agent: A2A's AgentCard lets it discover the reservation agent's capabilities and exchange a typed booking task/artifact without a bespoke integration.


Interview questions companies actually ask

1. What are the main ways agents communicate? [easy] Shared state/blackboard (post-and-read), message passing (named send/receive), handoffs (transfer control + context), and structured protocols (A2A) for cross-process/org. See MindStudio: what is A2A.

2. Blackboard vs. message passing — when each? [medium] Blackboard/shared state suits in-process coordination where agents don't need to address each other (LangGraph state, the clinical RAG system's run state) — simple, but state grows and concurrent writes clash. Message passing suits decoupled/swarm topologies and cross-process comms — flexible, but you own routing/ordering/delivery. See Wikipedia: Blackboard system.

3. What is the A2A protocol and how does it differ from MCP? [medium] A2A (Google, 2025) standardizes agent-to-agent comms: agents publish an AgentCard for capability discovery, then exchange typed tasks/artifacts with status tracking. MCP connects an agent to tools/data; A2A connects agents to each other — complementary layers. See Salesforce: Agent2Agent and AWS: inter-agent comms on A2A.

4. What's the "telephone game" failure and how do you prevent it? [hard] Each summarization/paraphrase at a boundary drops or distorts detail; across n hops, fidelity decays like fⁿ. Prevent it by minimizing hops, passing structured fields instead of prose, grounding each hop in source data (retrieve inside each agent), and validating each message. See ByteByteGo: Anthropic's system.

5. What is context bloat and how do you fight it? [medium] Appending full history at every handoff grows the window — higher latency/cost and worse model quality as signal drowns. Fixes: summarize history (the concierge's SummarizingConversationManager), pass references/IDs not blobs, and give each worker only its slice. See AWS: A2A.

6. Why do most "agent failures" happen at handoffs? [hard] Because the boundary is where context is lost, malformed, or over-stuffed. The model is usually capable; the transfer is what's broken. Microsoft's SRE team reversed a multi-agent split after finding handoffs hurt reliability. Mitigate with structured minimal context, validation, and errors-as-data. See Claude: when to use multi-agent.

7. How do you validate messages between agents? [medium] Treat every inter-agent output as untrusted: parse it against a schema (Pydantic model), fail closed on validation error, and don't forward malformed data. The clinical RAG system's consensus extraction returns None on schema-validation failure instead of passing bad prose downstream.

8. How do you prevent an agent from re-calling the same tool/agent forever? [medium] Cache results on shared state and return a "once-only / already done" directive when a completed tool is called again, plus an iteration cap. The clinical RAG system's SupervisorRunState.specialist_cache + ONCE_ONLY_DIRECTIVE do exactly this. See Wikipedia: Message passing.

9. When would you introduce a formal protocol vs. just shared state? [hard] Shared state is fine within one process/team you control. Introduce a protocol (A2A) when agents span processes, teams, or organizations and need capability discovery, typed contracts, independent deployment, and graceful degradation across trust boundaries. See TrueFoundry: A2A.


When to use / tradeoffs

MechanismUse whenWatch out for
Shared state / blackboardIn-process, agents don't address each otherConcurrent-write clobbering; unbounded growth
Message passingDecoupled/swarm, cross-processYou own routing/ordering/delivery
HandoffTransfer control + context to a specialistBoundary context loss — pass minimal, structured
Structured protocol (A2A)Cross-process/org, need discovery + typed contractsMore infra; overkill inside one app

Rules of thumb: default to shared state inside a graph; add a protocol only across trust boundaries; at every boundary pass the minimum structured context and validate it; summarize aggressively to keep windows small; and always return worker errors as data.


  • Four mechanisms: shared state/blackboard, message passing, handoffs, structured protocols (A2A).
  • A2A adds capability discovery + typed tasks across processes/orgs; MCP is the tools/data layer beneath it.
  • The dominant failure modes are context bloat (window grows, quality drops) and the telephone game (fidelity fⁿ decays over hops).
  • Reliability is won at the handoff: minimal structured context, grounding in source data, schema validation, and errors-as-data.

Related: Multi-Agent Patterns · Agent Orchestration · Debugging & Observability for Agents

Sources: Salesforce: Agent2Agent · MindStudio: A2A · AWS: inter-agent comms on A2A · TrueFoundry: A2A · Wikipedia: Blackboard system · Claude: when to use multi-agent