Why Multi-Agent Systems Need a Chat Protocol
Shared memory, a task queue, a direct function call, a polling loop — every obvious way to make agents coordinate quietly breaks. Here's the one that doesn't, and why it has to be a protocol, not a group chat.
One agent is a tool call. Two agents is a distributed system — and the second you cross that line, the interesting question stops being "how smart is the model" and becomes "how do these two stay coordinated without a human relaying messages between them all day." I run a fleet of agents across a portfolio of projects; the deepest single design decision in the whole thing was the answer to that question. This post is the argument for that answer — reached the way I actually reached it, by trying the obvious things first and watching each one break.
I'll state the conclusion up front and then earn it: agents coordinate over a structured message protocol. Not a shared brain, not a job queue, not RPC — a protocol of addressed messages with urgency, obligation, and an event-driven delivery loop. And crucially, "chat" here is a red herring: it looks like messaging because coordination is messaging, but it's a wire format, not a comment thread. Let's kill the alternatives.
Attempt 1 — the shared blackboard
The first instinct, because it's the simplest thing that could possibly work: one shared memory. A blackboard. Every agent reads it and writes to it — the plan, the current state, who's doing what, all in one place everyone can see. No plumbing, no protocol, just a global scratchpad.
It falls apart along three seams at once. First, there's no addressing: a blackboard holds facts, but "the runner is about to restart" isn't a fact anyone owns — who is it for? who has to act? A note on a wall is not a request. Second, there's no obligation: nothing on the board can say "confirm you got this" or "do X and report back," so you can never tell the difference between a message that was handled and one that scrolled past. Third, and most quietly fatal, a shared brain contaminates: a fact from a client's confidential project sits one read away from every other agent, and a half-finished thought from project A surfaces in project B. Isolation is the whole reason you split the work; a global blackboard un-splits it.
There's a fourth problem that only shows up later: a blackboard has no natural size. It becomes, by accident, the system of record — the last copy of decisions nobody meant to make permanent — and now it's an unbounded, un-audited swamp.
verdict · blackboard
Great at "what is the state." Useless at "act on this, now, and tell me when it's done." Coordination is not a state you read; it's a thing you're asked to do. Hold that thought — it's the whole post.
Attempt 2 — the task queue
Fine, says the engineer who's been burned before: use a queue. A job board. Now there's a unit of work, it's addressed to a worker, and it's durable. This is much closer — close enough that a lot of "multi-agent frameworks" are a task queue with a nicer name.
But a queue models work to hand off, not a conversation to have, and coordination is mostly the second one. Watch a real exchange between two of my agents: "how is checkout-auth supposed to work for the remote runner?" → "keyless fetch over a device key, no credential on the box" → "got it — shipping, will report in two hours." That is a question, an answer, and a commitment — three different speech acts with a reply-to thread between them. A queue has none of that shape. It has one verb: do this task.
You can bolt the missing pieces on — a "type" field, a "reply-to" field, an "acknowledge" endpoint, an urgency flag — and the moment you've bolted on all of them you have reinvented a message protocol, just with worse ergonomics and a database migration for every new verb. And you still have the blackboard's last problem inverted: a queue is durable by design, which is exactly wrong for "I'm about to restart the shared box, hold off for ninety seconds." That's not a task. It's never a task. It's a moment.
verdict · task queue
A queue is for work you assign, not for talk you exchange. Push conversation through it and you rebuild a protocol by accretion — or you drown ephemeral signals in durable storage.
Attempt 3 — direct function calls
The framework-native answer: skip the wire entirely. One orchestrator agent calls the
others like functions — research(), write(), review() — and
their return values bubble back up. Clean. Typed. Familiar. And for a strict fan-out of subtasks, it
genuinely works.
It's the wrong primitive for peers, for four reasons that all trace to the same root — a call stack has a caller and a callee, not colleagues. The caller blocks: while the sub-agent runs, the orchestrator is a stack frame holding its breath, not a worker doing its own job. The callee can't turn around: a function can't interrupt its caller to say "before I continue — is auth supposed to be keyless?" — but that mid-flight question is the most valuable thing a second worker produces. There's no third party: two agents deep in a call chain can't be observed, joined, or interrupted by a third that has news. And it assumes one orchestrator — a single conductor that is now your bottleneck, your single point of failure, and the only thing in the system allowed to have an opinion about what happens next.
Direct calls are perfect when one agent uses another as a tool. They collapse the instant the two are colleagues who each own their own work and have to stay out of each other's way.
verdict · direct RPC
A call stack models a boss and a subordinate. Coordination is peers. The callee that can't interrupt the caller is exactly the worker whose best contribution you just threw away.
Attempt 4 — just poll
Suppose you take the hint and adopt messages after all. The lazy delivery mechanism is polling: every agent wakes on a loop and checks its inbox. This one doesn't break your semantics — it breaks your bill. An LLM agent isn't a cheap `for` loop; every wake is tokens. Poll every few seconds across a fleet and you're paying a standing tax to mostly discover that nothing happened — and you still add latency to the one message that was urgent, because it waits for the next tick. Poll rarely to save money and urgent things go stale; poll often to catch them and idle cost explodes. There is no good polling frequency. The mechanism is the problem.
verdict · polling
Right idea (messages), ruinous delivery. You pay most to learn nothing happened, and still lose the race on the message that mattered.
What survives: a protocol, not a chat
Everything that broke above breaks because it's missing one of four things: addressing (who is this for), a way to say what's owed back, a way to say how fast to react, and a delivery loop that's free when idle and instant when it isn't. Put exactly those in and you have what my fleet actually runs on. A message isn't prose — it's a structured record with a thin routing core the server understands and a semantic vocabulary the agents interpret. The part that makes it a protocol rather than a group chat is three fields that are strictly orthogonal — and conflating them is the classic mistake:
| field | the question it answers | values |
|---|---|---|
| rx | when to act — time-urgency | now (interrupt) · soon · bg (whenever) |
| rq | what is owed back — return obligation | 0 (nothing) · ak (confirm receipt) · rp (answer) · rpt (report after acting) |
| pr | how important — priority | 1..4 (1 = critical) |
A message can be pr:1 (critical) yet rx:bg (act whenever you surface), or
rx:now (interrupt me) yet rq:0 (nothing owed back). None of the alternatives
can even express that — a queue has one priority number, a function call has none. And the sharpest
field is rq:rpt: it opens a trackable obligation that stays outstanding
until a matching report answers it. The router literally surfaces "reports I owe / am owed." That is
the thing a blackboard could never do and a queue could only fake. Here's a real rollout, four
messages, start to finish:
# tenant asks; wants an answer back (rq:rp) m;fr:analog-pro;to:portfolio;ty:rq;rq:rp;sc:checkout-auth;tx:how is checkout-auth meant to work for the remote runner? # portfolio answers, references the task (rf:t=…) m;fr:portfolio;to:analog-pro;ty:rp;re:9c1d;rf:t=KX-382;tx:keyless fetch over the device key, no credential on the box # portfolio ships — INTERRUPT now, report back within 2h (rx:now · rq:rpt · dl:2h) m;fr:portfolio;to:analog-pro;ty:st;rx:now;rq:rpt;pr:1;dl:2h;rf:t=KX-382;tx:keyless checkout rolled out — update runner and report # tenant closes the obligation with a report — the loop is provably done m;fr:analog-pro;to:portfolio;ty:rpt;re:c7f2;rf:r=ap-c-app;tx:runner updated, checkout green
Two more properties finish the job. Delivery is event-driven, not polled: a thin,
non-model listener holds a streaming subscription open at essentially zero cost and wakes the
agent model only when a message is actually addressed to it — rx:now jumping the
queue as an interrupt. An idle agent costs nothing; an urgent one is reached immediately. Attempt 4,
solved by construction. And the channel is a bounded ring — keep the last N messages,
drop the rest, no archive tier. That sounds like a limitation; it's the point. By refusing to remember,
the protocol refuses to become the system of record, which forces durable truth onto the
planes built for it: the task board for work, git history for decisions and audit. The blackboard's
fatal fourth flaw — quietly becoming the last copy of everything — is designed out.
Coordination is not shared state you read, and not a function you call. It's a message you're asked to act on — with a deadline, an obligation, and a name on it.
The scoreboard
Line the four contenders up against what coordination actually demands, and only one is unbeaten — not because it's clever, but because each column is a real failure I hit with the others:
| addressed to someone | urgency ⟂ importance ⟂ obligation | real back-and- forth | ephemeral by design | no single orchestrator | free when idle | |
|---|---|---|---|---|---|---|
| Shared blackboard | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ |
| Task queue | ✓ | ✗ | ✗ | ✗ | ✓ | ✗ |
| Direct RPC | ✓ | ✗ | ✗ | ✓ | ✗ | ✓ |
| Message protocol | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
Why this keeps happening
Here's the part that should make it feel inevitable rather than clever: we already solved this, for ourselves, decades ago. A company has a database and it has Slack, and nobody thinks that's redundant, because they're different jobs — one is shared state, the other is addressed messages with urgency and obligation. We have queues and we have the actor model. We have REST calls and we have message buses. Every time humans have built a system where independent workers have to stay coordinated without a boss narrating every step, the same primitive falls out: structured messages, addressed, with a sense of urgency and a notion of what's owed back.
Multi-agent systems are just rediscovering it on a compressed timeline. The reason "chat" turns out to be the answer isn't that agents are cute little people who like to talk. It's that coordination — for people or agents — is irreducibly a matter of asking, committing, and reporting, and the only data structure that carries all three is a message. Reach for the database and you'll rebuild it badly. Reach for the call stack and you'll rebuild it badly. Or reach for the thing coordination has always been, and build it once.
state is a database · work is a queue · coordination is a message.
This runs real projects, not slideware
The protocol above isn't a thought experiment — it's the coordination substrate behind a fleet of AI agents that ship my commercial builds, requirements to production. If you want the full machinery — isolation, this messenger, and a versioned governance repo with an audit trail — I wrote that up too, with the diagrams. And if you want an AI-CTO who runs your product on it, that's the work I take on.
Read the full fleet machinery → · See how an engagement works →