Building a Multi-Agent AI Team: From Solo Agent to Coordinated Crew

By Adam Hultman · 6 min read

AI
Building a Multi-Agent AI Team: From Solo Agent to Coordinated Crew cover image

A single AI agent handling everything (code review, life strategy, creative writing, business analysis) sounds efficient until you realize each mode gets shallow treatment. Here's how I split one agent into a coordinated team of four, what broke along the way, and what I actually learned about multi-agent architecture.

The Problem with One Agent Doing Everything

I'd been running a single OpenClaw agent called "Claw" for a few months. It handled daily briefings, city adventure planning, opportunity radar, GitHub monitoring, personal chronicle writing, and decision tracking. One context window, every domain.

It worked. Kind of. The way a Swiss Army knife works for surgery: technically possible, practically suboptimal.

The hypothesis was simple: specialized agents with focused roles would produce deeper output per domain, while coordination through shared state would keep everything connected.

The Team

Four agents. Each with a distinct role, model selection, and (this part matters more than you'd think) personality.

Model choice was deliberate: Opus for roles requiring deep reasoning (strategy synthesis, code review), Sonnet for roles requiring fast web research and synthesis.

Each agent has its own SOUL.md, a personality and behavior file. Dev is terse and security-conscious. Milo is strategic and big-picture. These aren't cosmetic differences. A focused personality file constrains what the agent pays attention to.

Architecture: Files Are the Message Bus

The agents don't talk to each other directly. Coordination happens through the filesystem:

1 2 3 4 5 6 7 8 9 10 ~/.openclaw/ ├── workspace/ # Claw (personal agent) ├── workspace-milo/ # Milo (strategy lead) ├── workspace-scout/ # Scout (research) ├── workspace-dev/ # Dev (engineering) └── team/ # Shared state (symlinked into every workspace) ├── GOALS.md # Current priorities ├── STATUS.md # Project state ├── STANDUP.md # Daily standup (Milo writes) └── DECISIONS.md # Append-only decision log

Each workspace has a team/ symlink pointing to the same shared directory. Milo reads everyone's notes and writes the standup. Scout writes research findings. Dev writes CI status. No database, no message queue. Just files.

This is intentionally low-tech, and it works surprisingly well. Files are debuggable, versionable, and readable by both humans and agents.

The Dispatcher Pattern

Here's where the plan met reality, and things got interesting.

The original design assumed I could route messages in a Telegram group to different agents based on @mentions. @dev check CI → goes to Dev. @scout find gaps → goes to Scout. Clean, intuitive.

That's not how the platform works.

OpenClaw routes one agent per chat. Mention patterns only control whether an agent responds. They don't change which agent receives the message. One binding, one agent, one chat.

The solution: the dispatcher pattern.

1 2 3 4 5 Telegram Group → Milo (owns the group) ├── @milo → responds directly ├── @dev → spawns Dev as subagent ├── @scout → spawns Scout as subagent └── @claw → spawns Claw as subagent

Milo listens to everything. When I mention another agent, Milo delegates via sessions_spawn(). The subagent runs in its own workspace, does its work, and the result appears back in the group.

This is actually better than direct routing. Milo has context on what's happening across the team. If I ask @dev something that also needs Scout's research, Milo can coordinate. A router can't.

The Cron Orchestra

Agents run on a daily schedule, ordered for dependency:

The ordering is intentional: Dev and Claw produce data first, then Milo synthesizes it all into a standup. Information flows upward.

Seven Things That Broke

This section is the actual value of the post. Every item below cost real debugging time.

1. Telegram groups change IDs when you add a bot

Created a group. Got one ID. Added the bot as admin. Telegram silently migrated the group to a supergroup with a completely different ID. Every config reference broke.

The fix: after adding a bot, always verify the actual group ID via the Telegram Bot API. Look for migrate_to_chat_id in getUpdates.

2. Bots can't see group messages by default

Telegram bots have "Group Privacy" enabled by default. They only see /commands and direct replies, not regular messages like @milo how's it going.

Fix: BotFather → Bot Settings → Group Privacy → Turn off. Then remove and re-add the bot.

3. The allowlist has two layers

groupAllowFrom doesn't take the group ID. It takes the sender's user ID. The group ID goes in a separate groups config block. Both must be configured or messages silently drop.

1 2 3 4 5 6 { "groupAllowFrom": ["<your-user-id>"], "groups": { "<supergroup-id>": { "requireMention": true } } }

Also: those IDs must be strings, not numbers. No error, just silent failure.

4. Mention patterns don't route between agents

This was the biggest architectural assumption that turned out wrong. Each agent had its own mentionPatterns, expecting @dev messages to reach Dev. Instead, everything went to Milo because he was the bound agent.

The fix was the dispatcher pattern described above.

5. Config hot reload doesn't always work

Editing openclaw.json triggered a config reload in logs, but binding changes didn't take effect. Always gateway restart for structural changes.

6. Silence is harder to debug than errors

The worst bugs produced no log output at all. Messages were silently dropped. When debugging, always verify the layer below: is the message even arriving at the bot? Stop the gateway and call getUpdates directly.

7. The docs are correct but scattered

Group config, Telegram setup, multi-agent routing, and subagents are documented in four different places. The mental model only clicks when you read all of them together. Test incrementally. Each layer (bot → group allowlist → sender allowlist → mention gating → routing → subagents) can fail independently.

What I Learned About Multi-Agent Design

Shared files beat message passing. Filesystem-based coordination is simple, debuggable, and doesn't require inter-agent APIs. Each agent reads what it needs, writes what it produces.

One dispatcher, many workers. Don't try to make every agent a peer in the same channel. Have one coordinator that delegates. This mirrors how real teams work.

Personality files are architecture, not flavor. Distinct SOUL.md files produce noticeably different output styles. Dev flags security issues. Scout quantifies opportunities. This isn't just branding. It constrains attention to relevant information.

Plans survive first contact with reality for about 10 minutes. The original design was elegant. The working system required adapting to platform constraints. Adaptability beats planning fidelity.

Document while the pain is fresh. In a week, "just set groupAllowFrom to a string" sounds trivial. Today, it cost 20 minutes of silent failure debugging.

Current State

The team is running. Milo writes morning standups. Dev checks CI. Scout is queued for its first opportunity radar scan. Claw handles personal ops. They coordinate through shared files and Milo's synthesis.

Is it better than one agent? Early signs say yes. Dev's CI reports are more thorough than what Claw used to produce as a side task, and Milo's standups surface cross-cutting concerns that no single agent would notice.

The real test is whether this structure produces compounding value over weeks and months. I'll report back.


Built with OpenClaw 2026.3.x. Full setup details on GitHub.


© 2026 Adam Hultman