← Back to list

How Agents Talk to Each Other — and to the World

Message schemas, tool calling, handoff design, and the communication topologies that determine whether your multi-agent system coordinates…

Suresh Kumar Ariya Gowder in Think in AI Agents · 2026-05-27 02:37 · 0 claps · 10.0 min read paywalled
#ai-engineering #ai-agent #artificial-intelligence #software-architecture #langgraph
Open on Medium ↗
Wiki topics: AGT · AI Agents AI · AI · General 🏛️ · Architecture

How Agents Talk to Each Other — and to the World

Message schemas, tool calling, handoff design, and the communication topologies that determine whether your multi-agent system coordinates cleanly — or turns into a game of telephone at scale.

This is Part 4 of a 6-part deep-dive series on multi-agent orchestration patterns. New here? Start with Part 1 — it covers what agents and orchestration actually are before we go deep on patterns.

Full series:

Picture a well-run kitchen during dinner service. The head chef doesn’t cook every dish — they call out orders, coordinate stations, and make sure the right information reaches the right person at the right moment. The saucier doesn’t need to know what the pastry section is doing. The grill cook doesn’t need the full menu. Everyone gets exactly what they need, in the right format, at the right time.

Multi-agent orchestration has the exact same coordination challenge — except the “kitchen” runs at the speed of LLM inference, the “orders” are structured data packets, and the consequences of a miscommunication aren’t a delayed entrée but a cascading failure that poisons the entire pipeline.

In the last article, we covered how agents remember. Now we cover how they communicate — with each other and with the external world through tools. This is the invisible layer that determines whether your system is a well-oiled machine or a chaotic mess of string-passing and hope.

We’ll cover four things: message schemas and structured handoffs, how tool calling actually works, the three communication topologies every agent system falls into, and human-in-the-loop design — building approval gates that let humans intercept agent decisions before they cause real-world consequences.

The Communication Problem Nobody Designs For

Here’s the most common mistake developers make when they first wire agents together: they pass free-form strings between them.

Agent A finishes its research, returns a 2,000-word text block, and Agent B is told “here’s the research, now write the article.” It works in the demo. It breaks in production.

Why? Because unstructured text handoffs are ambiguous by design. Agent B has to infer what’s a finding, what’s a source, what’s a confidence level, and what’s a caveat — from natural language. Every inference is an opportunity for drift. By the time the output reaches Agent D, the original signal has been interpreted, re-interpreted, and partially hallucinated three times over.

The fix isn’t more clever prompting. It’s typed, structured communication — treating agent-to-agent messages the same way you’d treat an API contract between two services. Define the schema. Validate against it. Reject malformed outputs before they propagate.

The key mindset shift: Agents aren’t talking to each other in natural language — they’re exchanging structured data payloads that happen to be generated by language models. Design the communication layer like an engineer, not like a prompt writer.

Section 1: Message Schemas and Structured Handoffs

A message schema is a formal definition of what one agent promises to produce and what the next agent expects to receive. It’s the contract at the boundary between two agents — and like any good API contract, it should be explicit, versioned, and validated.

The real-world example

Imagine a Research Agent handing off to a Writing Agent. Without a schema, the handoff looks like this:

# ❌ Unstructured handoff — fragile and ambiguous
research_output = research_agent.run(task)
writing_agent.run(
    task="Write an article based on this research",
    context=research_output  # a raw string blob
)

With a schema, it looks like this:

# ✅ Structured handoff — typed, validated, unambiguous
from pydantic import BaseModel
from typing import List

class ResearchHandoff(BaseModel):
    goal:          str           # the original research goal
    key_findings:  List[str]    # top 5 validated findings
    sources:       List[str]    # URLs / citations used
    data_points:   List[str]    # specific stats and numbers
    gaps:          List[str]    # things we couldn't verify
    confidence:    float        # 0.0 – 1.0 overall confidence
class ResearchAgent:
    def run(self, task: str) -> ResearchHandoff:
        raw = self.llm.run(
            task=task,
            output_format="Return valid JSON matching ResearchHandoff schema"
        )
        return ResearchHandoff.model_validate_json(raw) # validates on parse

# Writing Agent receives a typed object, not a string
handoff: ResearchHandoff = research_agent.run(task)
writing_agent.run(
    task=f"Write a 1500-word article based on these findings: {handoff.key_findings}",
    sources=handoff.sources,
    gaps_to_acknowledge=handoff.gaps
)

Now the Writing Agent knows exactly what it’s receiving. No inference. No guessing. If the Research Agent returns a malformed output — missing a required field, wrong data type — the validation fails immediately and loudly, not silently and three steps later.

This pattern — schema-first handoff design — is one of the highest-leverage habits in agent engineering. Define the contract between agents before writing either agent’s logic. It forces clarity, catches errors early, and makes the whole pipeline dramatically more debuggable.

Section 2: How Tool Calling Actually Works

Agents communicate not just with each other but with the external world — through tools. A tool is any capability an agent can invoke: a web search, a code executor, a database query, an API call, a file read or write. Tools are what turn an LLM from a text generator into a system that can actually do things.

But most developers treat tool calling as magic — they register the tool, the model calls it, and they move on. Understanding what actually happens under the hood makes you dramatically better at designing tool interfaces, debugging tool failures, and knowing why certain tool designs work and others don’t.

The mechanics: what happens inside a tool call

Here’s the actual sequence when an agent decides to call a tool:

There’s a critical insight in Step 2 above: the model doesn’t execute the tool — it just describes what to call and with what arguments. Your orchestration code does the actual execution. This means tool safety, error handling, and result formatting are entirely your responsibility — the model just tells you what it wants.

Designing good tools for agents

Most developers focus on getting tools to work. Fewer think about what makes a tool easy for an agent to use reliably. Here are the principles that matter:

  • Single responsibility. A tool should do one thing. A web_search tool that also summarises and caches results is three tools pretending to be one. Agents struggle with multi-purpose tools because they can't predict what will happen.
  • Explicit return formats. Return structured data, not prose. If a tool returns “The search found several results about BYD,” the agent has to parse natural language. If it returns {"results": [{"title": "...", "url": "...", "snippet": "..."}]}, the agent can work with it precisely.
  • Descriptive names and doc strings. The tool’s name and description are what the model reads when deciding whether to use it. search_web(query: str) is clearer than do_search(q: str). The description tells the model when to use the tool, not just what it does.
  • Graceful error returns. Never let a tool throw an unhandled exception into the agent’s context. Catch all errors and return structured error objects: {"error": "timeout", "message": "Search service unavailable", "retry": true}. The agent can then decide whether to retry, use an alternative tool, or surface the error.
# Well-designed tool for an agent
def web_search(query: str, max_results: int = 5) -> dict:
    """
    Search the web for current information.
    Use this when you need facts, recent data, or
    information that may have changed recently.
    Returns structured results with title, url, snippet.
    """
    try:
        results = serper_client.search(query, num=max_results)
        return {
            "success": True,
            "results": [
                {"title": r.title, "url": r.url, "snippet": r.snippet}
                for r in results
            ]
        }
    except Exception as e:
        return {          # structured error, not a raw exception
            "success": False,
            "error"  : str(e),
            "retry"  : True
        }

Section 3: The 3 Communication Topologies

Beyond individual message schemas, there’s a higher-level question: what is the shape of communication across your whole agent system? Who talks to whom, and how?

There are three topologies that cover almost every multi-agent system in existence. Choosing the right one isn’t an aesthetic decision — it has real consequences for latency, debuggability, and how your system scales.

Star topology is the default for most orchestrated systems. All communication flows through a central orchestrator. Easy to reason about, easy to debug — every message has a known origin and destination. The downside is the orchestrator becomes a bottleneck at scale, and its failure takes down the whole system.

Mesh topology lets agents communicate directly with each other without routing through a central node. This is faster and eliminates the orchestrator bottleneck, but it’s significantly harder to debug. When something goes wrong, tracing which agent sent what to whom requires distributed tracing infrastructure that most teams don’t invest in early enough.

Pub-Sub topology is the most decoupled of the three. Agents publish events to named channels; other agents subscribe to channels they care about. The producer doesn’t know who’s consuming — it just says “research is done.” Any agent that’s subscribed to research.done reacts. This scales beautifully but introduces complexity around event ordering, deduplication, and the fact that you lose the ability to trace a single request end-to-end without dedicated observability tooling.

When to use which: Start with star — it’s the easiest to build, debug, and reason about. Graduate to pub-sub when you need to decouple producers from consumers at scale, or when multiple agents legitimately need to react to the same events independently. Use mesh sparingly — only when latency is a hard constraint and you’re willing to invest in the observability infrastructure it demands.

Section 4: Human-in-the-Loop Communication

The most consequential communication in any agent system isn’t agent-to-agent. It’s agent-to-human — and most developers design it as an afterthought, if they design it at all.

Human-in-the-loop (HITL) means deliberately building points in your pipeline where execution pauses and a human can review, approve, reject, or redirect before the agent continues. Not as a failure recovery mechanism — as a first-class architectural feature.

Why does this matter? Because agents can be wrong. Confidently, fluently, impressively wrong. When an agent with access to the production database, the company email account, or the payment API acts on bad information, the consequences aren’t a slightly off response — they’re real-world damage that may be difficult or impossible to undo.

The diagram above shows the three outcomes a human gate should support:

  • Approve — the agent’s output looks good. Continue to the high-stakes action.
  • Reject — the output is wrong or unacceptable. Return to an earlier stage with explicit feedback so the agent can try again with correction.
  • Edit — the output is mostly right but needs adjustment. The human modifies the output directly and the pipeline continues from the corrected version.

Implementing this in practice means building a pause-and-notify mechanism into your orchestration. When the pipeline reaches a gate, it writes the pending output to a review queue, sends a notification (Slack message, email, dashboard alert), and waits. The orchestrator resumes only when a human submits a decision.

# Human-in-the-loop gate implementation
import time

def human_approval_gate(
    content: str,
    action_description: str,
    timeout_seconds: int = 3600   # wait up to 1 hour
) -> dict:
    # Write to review queue and notify
    review_id = review_queue.submit({
        "content"     : content,
        "action"      : action_description,
        "status"      : "pending",
    })
    notifier.send_slack(
        channel="#ai-reviews",
        message=f"Agent needs approval for: {action_description}",
        review_url=f"https://dashboard/reviews/{review_id}"
    )
    # Poll until a human responds or timeout
    elapsed = 0
    while elapsed < timeout_seconds:
        decision = review_queue.get_decision(review_id)
        if decision:
            return decision  # {status: approve/reject/edit, notes, edited_content}
        time.sleep(10)
        elapsed += 10
    return {"status": "timeout", "action": "abort"}

Design principle: Make the default behaviour safe. If a human gate times out — nobody reviewed it — the agent should not proceed by default. Silence is not approval. Build a fallback that aborts the high-stakes action and escalates, rather than assuming consent.

Practical Takeaways

✅ WHAT TO TAKE INTO YOUR NEXT BUILD

  • Define message schemas before writing agent logic. The contract between agents is as important as the agents themselves. Use Pydantic models and validate on every handoff — never pass raw string blobs between agents.
  • Treat tools as APIs, not afterthoughts. Write a clear docstring for every tool (this is what the model reads), return structured responses always, and handle all errors gracefully with structured error objects — never raw exceptions.
  • Start with star topology. It’s the most debuggable. Move to pub-sub only when you genuinely need decoupled producers and consumers at scale. Avoid mesh unless you have the observability infrastructure to handle it.
  • Build human gates before you go to production. Identify every action in your pipeline that is irreversible, consequential, or compliance-sensitive. Put a human gate before each one. The default on timeout should always be abort, not proceed.
  • The “too many tools” problem is real. Agents presented with 20+ tools make worse decisions about which to use. Keep tool sets focused — fewer, well-named, well-documented tools consistently outperform large, loosely-defined registries.

Communication Is the Architecture

There’s a principle in distributed systems: the communication design is the architecture. The topology you choose, the schemas you define, the protocols you enforce — these aren’t implementation details that come after you figure out the “real” design. They are the design.

The same is true for multi-agent systems. Two pipelines built with the same agents, the same models, and the same tools can produce radically different results based solely on how those agents communicate. One passes structured validated schemas at every boundary and uses a star topology with human gates before high-stakes actions. The other passes free-form strings, uses a mesh no one fully understands, and runs fully autonomously. The first is debuggable, reliable, and trustworthy. The second is a demo waiting to embarrass you in production.

You now have the vocabulary and the patterns to build the first kind.

Next week: Part 5 — What Happens When Your AI Agent Goes Wrong. Retries, fallbacks, infinite loop prevention, trust boundaries, and the reliability engineering that separates demos from systems that actually hold up at scale.

Level up your skills with my Amazon eBooks

Get the The AI Agent Builder’s Playbook : Why AI Agent Projects Die in Production on Amazon.


메타데이터
post_id
944dabb47427
slug
how-agents-talk-to-each-other-and-to-the-world-944dabb47427
url
https://medium.com/system-design-mastery-series/how-agents-talk-to-each-other-and-to-the-world-944dabb47427
canonical_url
https://medium.com/system-design-mastery-series/how-agents-talk-to-each-other-and-to-the-world-944dabb47427
author_url
https://medium.com/@sureshdotariya
status
ok
fetched_at
2026-06-15 20:49:13