Tech10
Back to blog

Communication Between AI Agents: How to Design It Without Causing Cascading Failures

Communication Between Ai Agents Design Without Cascading Failures
AI AgentsMar 12, 20268 min readDoreid Haddad

The Google AI Overview for "multi-agent architecture" lists communication protocols as one of four core components. The phrasing suggests the problem is solved — pick a protocol, plug agents in, watch them collaborate. The engineering reality is that inter-agent communication is the most common failure surface in multi-agent systems, the place where most cascading bugs originate, and the area where a small amount of upfront design saves a lot of late-stage debugging.

This article is the engineering blueprint. What inter-agent communication actually looks like in production, the cascading-failure pattern that kills multi-agent systems, and how to design contracts that hold up.

What "communication" actually means in practice

When two agents need to coordinate, three things happen at the boundary. Agent A produces output. The output gets transported to Agent B. Agent B parses the output and starts work.

The transport layer is usually boring — function calls in the same process, messages on a queue, HTTP requests, MCP-mediated tool calls. The transport almost never causes problems. The interesting failures live in production and parsing: what shape does Agent A produce, and what shape does Agent B expect, and what happens when those two shapes drift apart.

Anthropic's multi-agent research system retrospective describes the engineering journey of getting this right. The lead agent had to learn to give subagents "an objective, an output format, guidance on the tools and sources to use, and clear task boundaries." Each of those is a contract. The post implies what most multi-agent practitioners learn the hard way: agents that aren't tightly contracted with each other drift.

The cascading-failure pattern

The class of bug that kills multi-agent systems looks like this in slow motion.

A research agent is built with three subagents. Agent A pulls source documents. Agent B extracts claims with citations. Agent C drafts a brief. Six weeks in, an analyst notices a date in the brief that's wrong by 18 months. Nobody can find the source.

The trace shows what happened. Agent A returned its sources correctly. Agent B's output had a citations field, but the field was a string instead of an array on this particular run. Agent C's prompt expected an array. The Python code that handed off between B and C didn't enforce the array shape, so when B returned a single citation as a string, the orchestrator passed it through. Agent C took the string as best it could and confidently wrote a paragraph that cited a date that didn't appear in any source.

That's a contract failure. Not a model failure. The model technically did what it was told. The system didn't catch the shape change at the boundary, so the shape change propagated, so the brief contained an invented fact.

This is the most insidious class of bug in multi-agent systems and the one that good engineering practice prevents. Models are nondeterministic. The shape of what they return is nondeterministic too. Without strict contracts at every handoff, multi-agent systems aren't pipelines. They're long-form games of telephone.

The fix: typed schemas at every boundary

The solution isn't novel. It's older engineering, well-understood, available in every language. Strict typed schemas at every inter-agent handoff. Boring, well-tested, what keeps these systems running.

In Python, Pydantic is the standard tool. Define a class with typed fields. The orchestrator parses agent output through the class. Any deviation raises an exception. The exception is caught and either retried or escalated to human review. Don't pass malformed objects to the next agent.

from pydantic import BaseModel
from typing import List

class Citation(BaseModel):
    source_url: str
    quote: str
    confidence: float

class SubagentOutput(BaseModel):
    findings: str
    citations: List[Citation]
    completion_status: str  # 'complete', 'partial', 'failed'

The orchestrator parses every subagent output through SubagentOutput.model_validate(). If the shape is wrong, the call fails loudly. The downstream agents only see well-formed inputs.

In TypeScript, Zod plays the same role. In Go, encoding/json with strict tags plus libraries like ozzo-validation. The library matters less than the discipline. Whatever your stack supports, use the strictest validator available.

Modern model providers help. Anthropic's tool definitions, OpenAI's structured outputs, and Google's function declarations all let you express the desired output schema in a format the model itself respects. The model is constrained to return JSON that conforms to your schema or to fail trying. Combine this with Pydantic-or-equivalent validation on the receiving side and you get two layers of defense — the model can't easily drift, and even if it does, the orchestrator catches the drift before the next agent sees it.

Patterns that work in production

Beyond the schema discipline, a few patterns show up repeatedly in well-designed multi-agent systems.

Schemas have versions. When you change a schema, increment a version field. Older agents that haven't been updated will fail validation against the new schema and stop running, which is the correct behavior. Better to crash a stale pipeline than to feed it inputs it can't handle.

Schemas are tight, not loose. The temptation when defining a schema is to make every field optional and every type permissive — strings instead of typed enums, dictionaries instead of typed records. Resist it. The point of a schema is to make the model fail fast when something is wrong. A schema that accepts anything catches nothing.

Validation happens twice. Once on output, once on input to the next agent. They sound redundant. They aren't. Outputs are validated against the producing agent's contract; inputs are validated against the consuming agent's expectations. The two contracts can drift apart over time, and the double validation catches the drift at the exact handoff where it matters.

Errors get logged with full context. When validation fails, log the input the agent received, the raw output the agent produced, the schema it was supposed to match, and the specific validation error. This is the difference between "something went wrong in the pipeline" and "Agent B failed to produce a valid Citation array on input X because the model returned a string."

The agent prompt references the schema. Don't write a prose description of the desired output and hope the model gets the shape right. Include the schema in the prompt directly. Modern models follow schema specs better than they follow prose specs.

Retries happen deliberately. When validation fails, the orchestrator can retry once with a hint to the model — "your last output failed validation because X; try again." If the second try also fails, the case escalates. Don't retry indefinitely. Don't pass the malformed output downstream "just in case."

Where MCP fits

Anthropic's Model Context Protocol addresses a related problem: how an agent connects to tools and data sources. MCP defines a client-server design where agents act as clients that communicate with MCP servers exposing tools, resources, and prompts. The arXiv survey on multi-agent orchestration notes MCP as "a client–server design in which agents or orchestrators act as clients."

For agent-to-tool communication, MCP is the right answer. It standardizes integration, reduces glue code, and gives you a clean abstraction over a growing ecosystem of MCP servers (file systems, GitHub, Slack, databases, etc.).

For agent-to-agent communication — the topic of this article — MCP is necessary but not sufficient. You still need application-level schemas for the structured handoffs between your agents. MCP solves the transport and tool-discovery problem. The contract between Agent A's "findings" object and Agent B's input expectation is your responsibility, in your code, with your typed schemas.

A reasonable production setup

Putting it together. A typical production multi-agent system has:

  • Each agent's output type defined as a Pydantic class (or equivalent in your language)
  • Each agent's prompt embedding the JSON Schema generated from that type
  • The orchestrator wrapping each agent call in a function that parses the result into the type or raises
  • A retry-with-feedback path that triggers on validation errors, capped at one retry
  • Failed-validation cases routing to a human queue with full diagnostic context
  • Structured log entries for every call, success or failure, with input, output, and validation status
  • Schema versioning across deployments so older agents fail loudly when contracts change

That's maybe 100-200 lines of glue code on top of the agents themselves. Standard work, well-understood by anyone who's built a typed API before. The systems built this way are dramatically more reliable than the ones built without. The systems built without are the ones whose teams spend their first quarter chasing ghosts in production logs, trying to figure out why the brief contained a date that nobody can source.

The deeper point

Multi-agent design imports the entire body of distributed systems engineering into AI. Schemas. Validation. Retries with backoff. Idempotency. Circuit breakers. Versioned contracts. Structured logging. None of it is novel. None of it is AI-specific. All of it is what makes the difference between a system that runs and a system you can trust.

The teams who get this skip the dramatic failures and ship systems that operate quietly. The teams who don't end up writing post-mortems that begin with "the agent returned an unexpected shape and the orchestrator passed it through."

Type your handoffs. Validate at every boundary. Version your schemas. Log everything. The work is unglamorous. The result is a multi-agent system that does what it's supposed to do, every time, without surprising anyone three months in.

Frequently Asked Questions

What's the most insidious failure mode in multi-agent systems?

Drift via implicit contracts. Agent A starts returning a slightly different shape than Agent B expects. B fills in the missing field with a guess. The system technically runs and produces wrong outputs for weeks before a customer or auditor catches it. Strict typed schemas at every handoff prevent this.

What is the Model Context Protocol (MCP) and does it solve communication?

MCP is Anthropic's open protocol for standardizing how LLMs and agents connect to tools and data sources. It uses a client-server design that simplifies tool integration. It addresses the agent-to-tool communication problem cleanly. Inter-agent communication is a related but distinct problem that still requires application-level schemas.

How do I prevent cascading failures between agents?

Three rules: typed schemas at every handoff (Pydantic, Zod, JSON Schema), validation that fails loudly rather than passing through malformed objects, and explicit error-handling paths that route failed handoffs to human review. Cascading failures happen when malformed output from one agent gets silently consumed by the next; strict validation breaks the chain.

Sources
Doreid Haddad
Written byDoreid Haddad

Founder, Tech10

Doreid Haddad is the founder of Tech10. He has spent over a decade designing AI systems, marketing automation, and digital transformation strategies for global enterprise companies. His work focuses on building systems that actually work in production, not just in demos. Based in Rome.

Read more about Doreid

Keep reading