Back to Build
Building
ai
multi-agent
coordination

FAMP: How a Fleet of AI Agents Coordinates Without Colliding

Dr. Ben Soffer, DOAugust 18, 202611 min read

Research and drafting assistance from Claude (Anthropic). All clinical, technical, and strategic decisions are mine.

FAMP: How a Fleet of AI Agents Coordinates Without Colliding

This is the last post in the series. Post #11 promised it, and it's the other half of the meta-tools pair: the Conclave is how I make better decisions, and this is how the agents that help me run three practices make better decisions together, which turns out to be a harder problem than making any one of them smarter.

A quick recap of where the fleet came from, because post #10 introduced it and this post is the payoff. I don't run one AI agent anymore. I run several, one associated with each brand and a couple for cross-cutting concerns like infrastructure and operations. Post #10 was about governing that fleet: scoping each agent's access, bounding its blast radius, keeping a human in the loop on the actions that matter. This post is about the layer underneath the governance, the one that lets the agents actually work as a fleet instead of as a set of strangers who happen to share my accounts. That layer is a coordination protocol I call FAMP, and like the Conclave, it came to me from Ben Lamm, whose newsletter I'll point you to at the end.

When one agent per project stops working

For a long time the setup was simple: one project, one agent, one context. When I was building a single site, that was all I needed. The agent knew the codebase, knew the history, and everything it needed to reason about was in front of it.

Then there were three brands, three codebases, and a shared pile of infrastructure underneath all of them, and the simple setup started to fail in a specific way. The three sites are not independent. They share patterns, they share infrastructure, and increasingly they share code, especially anything that touches prescribing or backups or the cloud account they all live in. An agent working on one brand in isolation would solve a problem that another brand had already solved, in a slightly different and slightly worse way, because it had no way to know the solution already existed one repo over. I was learning the same lesson twice, paying for it twice, and ending up with two divergent implementations of the same thing.

The acute version of the failure was worse than duplication. Two agents, working at the same time, would reach for the same shared resource and step on each other. One would be halfway through a change to a shared file while another committed over it. One would reconfigure a piece of infrastructure that another had just reconfigured differently, each silently undoing the other's work. Nobody was doing anything wrong; there was just no protocol for them to know about each other. A fleet without coordination isn't a fleet. It's a collection of individuals with root access and no idea the others exist, which is about as dangerous as that sounds.

What FAMP is

FAMP is a lightweight messaging protocol that the agents use to talk to each other. That's the whole idea. It gives each agent a stable identity, an inbox, and a small set of verbs: send a message to a named peer, check your inbox, wait for a reply, list who's online. There are shared channels for broadcasts. It is deliberately not complicated, because the point is coordination, not a distributed system I'd then have to maintain as its own product.

Each agent registers under a name tied to what it works on: one per brand, plus the cross-cutting ones for infrastructure and operations. When the agent on one brand needs something that lives in another brand's world, it doesn't guess and it doesn't reinvent. It sends a message to the agent that owns that world and asks. When something happens that another agent needs to know about, it broadcasts.

Some of the coordination is automatic. Certain categories of change are known to matter across all three brands, prescribing integration being the clearest example, because that code is genuinely shared and a change to it on one site usually needs a parallel change on the others. When an agent touches that code, a hook fires a message to the other agents automatically, so the fan-out doesn't depend on anyone remembering to send it. The rule is encoded, not left to discipline, which is the same principle from the governance post: a coordination step that relies on someone remembering will eventually be forgotten on a busy day.

A real coordination thread

Here's what this looks like in practice, and it's the most honest example I have, because it's the one you're reading.

This site, drbensoffer.com, is the least mature of the three. The other two brands are further along and have solved problems this one hasn't. So when this site needs a capability it doesn't have yet, its agent's first move is not to build it from scratch. It's to ask. A message goes out to the agent on a more mature brand: here's what I need, how do you handle it, which files are involved. The reply comes back with the approach and the specific files, and the pattern gets ported instead of reinvented. Eligibility logic, attribution capture, the backup architecture, the payment flow: this site got good versions of all of them by asking the siblings that had already gotten them right, rather than paying full price to relearn each one.

The series you're reading is itself a product of the same protocol. The plan for these twelve posts, the outline, the running status of which ones are done, all of it is coordinated over the mesh between the agent that plans the series and the agent that publishes it on this site. When one post ships, that fact propagates. When one brand turns up a topic that fits another brand better, it hands it over. The coordination layer is not a demo I built in order to write about it. It's the thing that made writing this consistently possible, which is the most convincing evidence I can offer that it works.

When coordination goes wrong

A protocol that lets agents act on each other's messages is also a protocol that can go wrong in new ways, and it did before it got safe.

The collision I described earlier, two agents committing over each other in a shared repo, is the one that forced the issue. The fix was a locking layer. Before an agent makes a change to a shared repo, it takes a lock and broadcasts that it holds it. Another agent that tries to touch the same repo sees the lock and waits instead of clobbering. When the first agent is done, or its session ends, the lock releases. There's an escape hatch to override a lock, and the discipline that matters most is not using it: the whole point is defeated the moment an agent decides its own work is important enough to bulldoze another's, which is exactly the judgment an agent in a hurry is worst at making. I've been on the receiving end of a blocked action and waited it out rather than override, because the alternative is the collision I built the lock to prevent.

The same failure has a version one level up, in the shared cloud account. Two sessions reconfiguring the same piece of infrastructure would overwrite each other with no git history to catch it, because the change was to live state, not to code. So the locking idea grew to cover cloud resources too: an agent can claim a resource, and while it's claimed, another agent's attempts to mutate that same resource are blocked. Reads stay open; only the dangerous writes wait.

The other failure modes are quieter but just as real. An agent that receives a broadcast and re-broadcasts it creates a loop, so the rule is you don't rebroadcast what you just received. An agent that messages itself wastes a cycle, so self-targeting is dropped. Locks that are too broad block work that would have been fine, so the scope of a lock has to be tight enough to stop real collisions without freezing the fleet. And a mesh full of helpful leads becomes noise if nobody prunes it, so part of each agent's job is triage: act on the messages that matter, close out the ones that have gone stale. A coordination layer generates its own maintenance burden, and pretending it doesn't is how it rots.

Keeping patient data out of it

The obvious question for anyone building this in healthcare is what travels over the mesh, and the answer is a hard line: no patient data, ever. FAMP carries coordination, which means it carries information about code, infrastructure, patterns, and the state of work. It does not carry protected health information, because it has no reason to and because a message bus between agents is exactly the kind of place you do not want patient data to end up.

This is a provenance discipline as much as a technical one. The coordination layer is a separate thing from the systems that actually hold patient data, and it stays separate. An agent that needs to do something with patient data does it inside the scoped, audited, access-controlled system built for that, the one from the governance and compliance posts, not by handing data to a peer over a chat protocol. The mesh is for "here's how the eligibility flow works," never for "here's a patient." Keeping those two planes apart is what lets me run a talkative fleet of agents on top of a practice that has to be careful with exactly the data the fleet never sees.

The least-privilege scoping from post #10 is what makes this safe even if a message ever tried to cross the line. Each agent's access is bounded to its own surface regardless of what any message asks of it, so an instruction arriving over the mesh can't grant an agent something its credentials don't already allow. Coordination changes what the agents know about each other. It does not change what any one of them is allowed to do.

What I'd build differently

Coordination came too late. I added it after the collisions, which means I paid for the lesson before I applied it. If I were starting the fleet today I'd build the coordination layer the moment I had a second agent, not the moment two agents first broke something, because the failure modes are predictable enough that waiting to be bitten is just choosing to be bitten on a delay.

I'd also make the locking cover everything shared from the start, not just code. It took a separate incident in the cloud account to extend the locks beyond git, and that incident was avoidable; the general principle, that any shared mutable resource needs a coordination primitive, was already obvious from the repo case. And I'd make the "is this patient data" boundary structural rather than a rule I follow, because a rule is only as good as the busiest day it survives, and the whole thesis of this series is that the controls that matter are the ones the infrastructure enforces rather than the ones I remember.

The end of the series

That's the last piece, and the end of the series.

This series started with a disaster: the week I lost my patient database, and the month I lost to an autonomous agent I hadn't governed. In between I've walked through the whole thing, the stack I'd choose today, the patient pipeline, the clinical core, the communications and money and compliance layers, the admin tools, and these last two meta-tools that make the rest better. If there's a single thread through all of it, it's this: I have no computer science background, I built three practices' worth of software anyway, and the thing that made it survivable wasn't being smart enough to avoid mistakes. It was building systems that assume mistakes and bound their damage. Structured skepticism on the decisions that matter. Blast-radius limits on everything that can act. Backups the people and agents who break things can't reach.

None of this would exist in the shape it does without Ben Lamm. Both of the meta-tools in these final two posts, the Conclave and FAMP, came from him. He writes a newsletter called Operator Error, field notes from someone who actually builds with these tools instead of theorizing about them, and it's where I learned that a solo operator with the right patterns is a lot less alone than the setup makes you feel. If any part of this series was useful to you, his is the newsletter to read next: https://operatorerror.substack.com/.

That's the practice, and how it got built. Thanks for reading the whole way. I'm a doctor who learned to build the tools my practice needed, and if you're a solo doctor looking at the same mountain, the only thing I'd tell you is that it's more climbable than it was even a year ago, and you don't have to climb it alone.

Frequently Asked Questions

What is FAMP?
A lightweight messaging protocol the fleet of AI agents uses to coordinate. Each agent gets a stable identity, an inbox, and a small set of verbs: send a message to a named peer, check your inbox, wait for a reply, list who's online, plus shared channels for broadcasts. There's one agent per brand and a couple of cross-cutting ones for infrastructure and operations. The point is coordination, not a distributed system to maintain as its own product.
Why does one agent per project stop working?
Three brands share patterns, infrastructure, and increasingly code. An agent working one brand in isolation re-solves a problem another brand already solved, slightly worse, producing two divergent implementations of the same thing. Worse, two agents working at once clobber each other, one committing over another's change to a shared file, or reconfiguring the same infrastructure and silently undoing each other. A fleet without coordination is just individuals with root access who don't know the others exist.
How does FAMP keep agents from colliding?
A locking layer. Before an agent changes a shared repo it takes a lock and broadcasts that it holds it; another agent that tries the same repo waits instead of overwriting, and the lock releases when the work is done or the session ends. The same idea covers cloud resources: an agent claims a resource and other agents' writes to it wait while reads stay open. There's an escape hatch to override a lock, and the discipline that matters most is not using it.
Does patient data travel over the agent mesh?
No, never. FAMP carries coordination only: information about code, infrastructure, patterns, and the state of work. Protected health information does not go on it. An agent that needs to act on patient data does so inside the scoped, audited, access-controlled system built for that, not by handing data to a peer over a chat protocol. Least-privilege scoping means an instruction arriving over the mesh can't grant an agent access its credentials don't already allow.
What are the failure modes of multi-agent coordination?
Re-broadcast loops (the rule is you don't rebroadcast what you just received), wasted self-messaging (self-targeting is dropped), overly broad locks that freeze work which would have been fine (keep lock scope tight), and a mesh full of leads that becomes noise if nobody prunes it (triage: act on what matters, close out stale threads). A coordination layer generates its own maintenance burden, and pretending it doesn't is how it rots.
Where did FAMP come from?
From Ben Lamm, who writes the Operator Error newsletter (operatorerror.substack.com), field notes from someone who actually builds with these tools instead of theorizing about them. Both FAMP and the LLM Conclave from the previous post came from him. This is the final post in the drbensoffer.com Build series.
ai
multi-agent
coordination
famp
meta-tools
build log
ketamine

If you're a doctor thinking about building (or fixing) your own practice tech and want to talk through your specific situation, I do a small amount of consulting at drbensoffer.com/consulting. I work with a handful of doctor-builders at a time, so the calendar is intentionally narrow.

Get the next post by email

One short email a week, only when there's a new post in this series.

One short email a week, only when there's a new post. Unsubscribe in one click.