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
TypedDictthrough nodes; each node returns a partial update that's merged. Communication is implicit: node B reads what node A wrote. The group-trip suggester'sGroupTripStatecarriesdestination_context,request_type,suggestionbetween nodes. - Blackboard / scratchpad — a mutable store agents post results to and poll. The clinical RAG system's per-request
SupervisorRunStateis exactly this: it caches each specialist'spapersandtext, 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 GroupTripState — classify_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
| Mechanism | Use when | Watch out for |
|---|---|---|
| Shared state / blackboard | In-process, agents don't address each other | Concurrent-write clobbering; unbounded growth |
| Message passing | Decoupled/swarm, cross-process | You own routing/ordering/delivery |
| Handoff | Transfer control + context to a specialist | Boundary context loss — pass minimal, structured |
| Structured protocol (A2A) | Cross-process/org, need discovery + typed contracts | More 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.
Summary + related articles
- 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