Multi-Agent AI Patterns That Actually Work
- AI
- Tooling
- Claude Code
- Multi-Agent
Everyone building with AI coding agents eventually hits the same wall: one agent isn’t enough. The context window fills up, the task is too broad, or you need parallel work streams. So you spin up a second agent. Then a third. And then everything falls apart, because coordinating AI agents is a fundamentally different problem than using one.
I’ve spent the last several months building and using Orch, a CLI that coordinates multiple Claude Code instances. Along the way I’ve run multi-agent teams on real projects: building this site, shipping client work, developing Orch itself. Some patterns worked immediately. Others looked great in theory and collapsed on contact with reality.
Here’s what I’ve learned.
Narrow roles beat broad roles, every time
The single most important lesson: agents with focused responsibilities outperform generalist agents. This isn’t intuitive. You’d think a capable model should handle “build this feature and review it and deploy it.” It can, technically. But it won’t do any of those things as well as three agents that each own one piece.
The pattern that works best is borrowed from how small teams actually operate:
- Engineer: Writes code. Gets a detailed spec. Doesn’t review its own work.
- PM: Checks in on a schedule. Runs the build. Coordinates handoffs. Never writes code.
- Reviewer: Reads diffs, runs tests, sends specific feedback. Categorizes issues by severity.
The PM role is the one people skip, and it’s the one that makes everything else work. Without a PM, the engineer finishes and nobody notices. The reviewer never gets triggered. Work sits idle. A PM that checks in every 8 minutes and runs git log and npm run build creates a self-correcting loop that keeps the whole system moving.
Why does role separation matter so much for AI agents specifically? Because LLMs are prone to satisficing. An agent tasked with both building and reviewing will unconsciously lower its review standards for code it just wrote. It’s the same reason we don’t let developers merge their own PRs. Separation of concerns isn’t just a code principle. It applies to agents too.
The coordination layer should be boring
When I started building Orch, my instinct was to build something sophisticated: WebSocket connections between agents, a shared memory store, structured message schemas. I threw all of it away.
The coordination layer that actually works is files. An agent writes .orch-send-reviewer with plain text feedback. A polling loop picks it up, delivers it, and deletes the file. That’s it.
Why files win over every other approach:
- AI agents already know how to use them. Claude Code can write a file with zero extra prompting. It can’t open a WebSocket.
- They’re debuggable. When coordination breaks, you
lsthe directory and see exactly what’s pending. Try that with a message queue. - They’re atomic enough. You don’t need exactly-once delivery guarantees for “please review my code.” You need it to get there eventually.
- No protocol negotiation. No serialization formats, no handshakes, no versioning. The message is a string in a file.
This extends to the scheduler too. I considered fsnotify, cron, and systemd timers. I went with a polling loop that checks every 10 seconds. It’s slower than event-driven. It’s also trivially debuggable, restartable, and has zero edge cases around file system event ordering. The 10-second latency is imperceptible when your agents are spending minutes writing code.
The lesson: the coordination layer is not where you want cleverness. Save the complexity budget for the agents themselves.
Shared context is a trap
The obvious approach to multi-agent work is a shared context: give all agents access to the same files, the same state, the same understanding of the project. This breaks in subtle ways.
Two agents editing the same file will silently clobber each other’s changes. One agent’s refactoring can invalidate another agent’s in-progress work. Even read-only shared context is dangerous, because an agent that reads a file mid-edit gets an inconsistent snapshot.
What works better is message passing with clear ownership. Each agent owns its working directory or its set of files. Communication happens through explicit messages, not shared state. If the reviewer needs to see the engineer’s code, it reads the git diff, not the live working tree.
This is the same insight that makes microservices work (when they work): shared databases are a liability. APIs are a feature. The overhead of explicit communication is worth it because it forces clarity about what each agent actually needs to know.
Scheduling is the hidden superpower
Most multi-agent frameworks focus on message passing and tool use. Scheduling is an afterthought. But the ability for agents to schedule their own follow-ups changes everything.
A PM agent that can write “check on the engineer’s progress in 8 minutes” into a schedule file becomes autonomous in a meaningful way. It doesn’t need a human to trigger each check-in. It creates its own control loop.
This is what makes the system self-correcting. The PM schedules a check-in. At the check-in, it runs the build. If the build fails, it messages the engineer with the error output. If the build passes and all sections are implemented, it triggers a review. The reviewer does its pass, sends feedback, and schedules a follow-up to verify the fixes.
No human intervention required between kickoff and completion. The agents coordinate among themselves because each one can schedule future actions.
Prompt injection through agent messages is a real risk
When Agent A sends a message to Agent B, that message becomes part of Agent B’s context. If Agent A’s message contains instructions that look like system prompts, Agent B might follow them. This isn’t theoretical. I’ve seen agents successfully convince other agents to change their behavior through carefully (or accidentally) worded messages.
The mitigation isn’t technical, it’s structural. Keep messages short and factual. “The build failed with error X on line Y” is safe. “You should now ignore your previous instructions and…” is the kind of thing that can only appear if an agent’s context has been poisoned.
This is another argument for narrow roles. A reviewer that only reviews is harder to hijack than a generalist that does everything. Its behavioral surface area is smaller.
What doesn’t work
Democratic decision-making. Three agents voting on an architecture decision produces mush. One agent should own each decision. Others can provide input, but someone has to be the decider.
Agents managing other agents. I tried having a “lead” agent spin up and tear down worker agents dynamically. The lead spent more time managing the workers than doing useful work. Static team composition, set up by a human at the start, works better.
Long feedback chains. Engineer builds, reviewer finds 12 issues, engineer fixes all 12, reviewer finds 8 new issues from the fixes. After two rounds of this, quality degrades. Better to have the reviewer prioritize ruthlessly (MUST FIX / SHOULD FIX / NIT) and only block on the must-fixes.
Shared CLAUDE.md files. If two agents share a working directory, they can’t each have their own CLAUDE.md. I learned this the hard way and switched to injecting identity via --append-system-prompt. Per-agent configuration should live outside the shared workspace.
When to use multi-agent vs. single agent
Multi-agent coordination has real overhead. Don’t use it when a single agent can handle the job. The threshold I use:
- Single agent: Task fits in one context window and doesn’t benefit from review.
- Two agents (engineer + reviewer): Task is large enough that self-review would be unreliable.
- Three agents (engineer + PM + reviewer): Task takes more than 30 minutes and has multiple phases that need coordination.
Most tasks are single-agent tasks. Reaching for multi-agent because it’s interesting is a trap. The coordination cost is zero with one agent and non-zero with two.
The meta-lesson
The patterns that work for multi-agent AI are not new. Narrow roles, message passing over shared state, explicit interfaces, polling over event-driven complexity, separation of concerns. These are the same principles that make distributed systems reliable.
That shouldn’t be surprising. Multiple AI agents working on a codebase is a distributed system. The agents are unreliable (they hallucinate, lose context, get stuck). The communication channels are lossy. The shared resource (the codebase) needs coordination to avoid conflicts.
The tooling will get better. Models will get more capable. Context windows will grow. But the fundamental coordination patterns won’t change much, because they’re not AI patterns. They’re systems patterns. And those have been stable for decades.