Building a Team of AI Agents with OpenClaw
Most people start with one AI agent. That's fine — one agent handling your Slack, your tasks, your coding is already a force multiplier. But at some point you hit a wall. One agent can't be everywhere at once, and one personality doesn't fit every context.
OpenClaw solves this with multi-agent routing: multiple fully isolated agents running inside a single gateway process, each with its own workspace, persona, channel accounts, and session history. From the outside they look like a coordinated team. Under the hood they're cleanly separated — no cross-talk unless you explicitly enable it.
This guide walks you through how to build that team — from the concepts to real config examples to spawning ACP coding sessions.
What "one agent" actually means
In OpenClaw, an agent is a fully scoped runtime with its own:
- Workspace — the directory where its files live:
SOUL.md,AGENTS.md,USER.md, memory files, and any workspace-level skills - State directory (
agentDir) — auth profiles, model registry, per-agent config - Session store — chat history under
~/.openclaw/agents/<agentId>/sessions/
Each agent reads from its own ~/.openclaw/agents/<agentId>/agent/auth-profiles.json. Credentials are not shared automatically. If two agents need the same credentials, you copy the file explicitly.
The default setup is single-agent: one agent with id main, workspace at ~/.openclaw/workspace. When you add more agents, they sit alongside main — same process, separate brains.
Why you'd want multiple agents
A few patterns that push people toward multi-agent setups:
- Different channels, different personalities. Your work agent on Telegram should feel different from your personal agent on WhatsApp. Different
SOUL.md, different model, different tool access. - Different people, same server. Multiple people can share one OpenClaw host while keeping their AI data fully isolated — each person gets their own agent, their own session history, their own credentials.
- Specialization. One fast Sonnet agent for quick replies, one Opus agent for deep work. Route by channel or by specific contacts.
- Security isolation. A sandboxed agent for untrusted channels (family groups, public bots) alongside an unsandboxed agent for your personal use.
Adding a second agent
The quickest path is the wizard:
openclaw agents add coding
openclaw agents add social
Each command creates a workspace with the standard bootstrap files, a dedicated agentDir, and a session store. Then verify:
openclaw agents list --bindings How routing works
Messages are routed to agents via bindings. A binding matches an inbound message by channel, account, and peer (DM or group id), then sends it to the specified agentId.
Bindings are deterministic — most-specific match wins:
- Exact peer match (specific DM or group id)
- Parent peer match (thread inheritance)
- Guild + roles (Discord role routing)
- Guild-level (Discord server)
- Team-level (Slack team)
- Account-level (specific account id for a channel)
- Channel-wide fallback (
accountId: "*") - Default agent (first in list, or marked
default: true)
If multiple bindings match at the same specificity, the first one in config order wins. Multiple match fields in a single binding use AND semantics — all specified fields must match.
Example: fast agent on WhatsApp, deep-work agent on Telegram
A clean two-agent split by channel:
{
agents: {
list: [
{
id: "chat",
name: "Everyday",
workspace: "~/.openclaw/workspace-chat",
model: "anthropic/claude-sonnet-4-5",
},
{
id: "opus",
name: "Deep Work",
workspace: "~/.openclaw/workspace-opus",
model: "anthropic/claude-opus-4-6",
},
],
},
bindings: [
{ agentId: "chat", match: { channel: "whatsapp" } },
{ agentId: "opus", match: { channel: "telegram" } },
],
}
Every WhatsApp message hits the Sonnet agent. Every Telegram message hits Opus. Each has its own workspace, so they get different SOUL.md files — different personalities, different operating instructions.
Example: routing one DM to a different agent
Keep WhatsApp on the fast agent, but route one specific contact to Opus:
{
bindings: [
{
agentId: "opus",
match: {
channel: "whatsapp",
peer: { kind: "direct", id: "+15551234567" },
},
},
{ agentId: "chat", match: { channel: "whatsapp" } },
],
} Peer bindings always win over channel-wide rules, so order them first in the config.
Example: multiple Discord bots, multiple agents
Each Discord bot account maps to a unique accountId. Bind each to a different agent:
{
agents: {
list: [
{ id: "main", workspace: "~/.openclaw/workspace-main" },
{ id: "coding", workspace: "~/.openclaw/workspace-coding" },
],
},
bindings: [
{ agentId: "main", match: { channel: "discord", accountId: "default" } },
{ agentId: "coding", match: { channel: "discord", accountId: "coding" } },
],
channels: {
discord: {
accounts: {
default: {
token: "DISCORD_BOT_TOKEN_MAIN",
guilds: {
"111111111111111111": {
channels: { "222222222222222222": { allow: true, requireMention: false } },
},
},
},
coding: {
token: "DISCORD_BOT_TOKEN_CODING",
guilds: {
"111111111111111111": {
channels: { "333333333333333333": { allow: true, requireMention: false } },
},
},
},
},
},
},
}
Two bot tokens, two agent brains, one gateway. The coding agent can have its SOUL.md tuned for technical work while the main agent handles general conversation.
Per-agent sandbox and tool restrictions
You can lock down specific agents without affecting others. This is useful for untrusted contexts — a public-facing bot or a family group chat shouldn't have the same tool access as your personal agent:
{
agents: {
list: [
{
id: "personal",
workspace: "~/.openclaw/workspace-personal",
sandbox: { mode: "off" },
// No tool restrictions
},
{
id: "family",
workspace: "~/.openclaw/workspace-family",
sandbox: {
mode: "all",
scope: "agent",
docker: {
setupCommand: "apt-get update && apt-get install -y git curl",
},
},
tools: {
allow: ["read", "sessions_list", "sessions_history"],
deny: ["exec", "write", "edit", "browser"],
},
},
],
},
} The family agent runs in a Docker sandbox, can only read files and list sessions — no exec, no writes, no browser access. The personal agent runs unrestricted on the host.
ACP coding sessions: agents that write code
OpenClaw supports ACP (Agent Client Protocol) — a way to run external coding harnesses like Claude Code, Codex, Gemini CLI, and Pi through your agent. ACP sessions are a different tier from the regular sub-agent runtime.
The practical difference:
- Sub-agents: OpenClaw-native delegated runs. Use for general task delegation.
- ACP sessions: External harness runtimes (Codex, Claude Code, etc.). Use when you need a full coding agent with file access and shell execution.
Starting an ACP session from chat
/acp spawn codex --mode persistent --thread auto This creates a persistent Codex session and binds it to the current thread. Follow-up messages in that thread route to Codex until you close or unfocus it.
Spawning an ACP session programmatically
From inside an agent turn (e.g., a cron job or another agent):
{
task: "Open the repo and fix the failing tests",
runtime: "acp",
agentId: "codex",
thread: true,
mode: "session"
}
Set runtime: "acp" explicitly — it defaults to "subagent" otherwise.
Supported harnesses
The acpx backend (installed separately) supports these harness aliases:
piclaude(Claude Code)codex(OpenAI Codex)opencodegemini(Gemini CLI)kimi
Install the backend plugin first:
openclaw plugins install acpx
openclaw config set plugins.entries.acpx.enabled true
Then verify with /acp doctor.
Permission modes for ACP sessions
ACP sessions are non-interactive — there's no TTY to approve prompts. Set permissionMode appropriately:
openclaw config set plugins.entries.acpx.config.permissionMode approve-all
openclaw config set plugins.entries.acpx.config.nonInteractivePermissions fail
The default (approve-reads) blocks write operations in ACP sessions. For coding agents that need to write files, set approve-all. Restart the gateway after changing these.
Persistent ACP bindings for channels
You can bind a Discord channel or Telegram topic permanently to an ACP session in your config:
{
bindings: [
{
type: "acp",
agentId: "codex",
match: {
channel: "discord",
accountId: "default",
peer: { kind: "channel", id: "222222222222222222" },
},
acp: { label: "codex-main", mode: "persistent" },
},
],
channels: {
discord: {
threadBindings: {
enabled: true,
spawnAcpSessions: true,
},
},
},
} Messages in that Discord channel route directly to the persistent Codex session. It's like having a dedicated coding channel that always knows the full context of what you're building.
Agent-to-agent messaging
By default, agents cannot message each other — this is intentional for isolation. To enable it explicitly:
{
tools: {
agentToAgent: {
enabled: true,
allow: ["main", "coding"],
},
},
}
Only agents listed in allow can communicate. This lets you build orchestration patterns — your main agent spawning a coding agent to handle a task, then receiving the result — while keeping the blast radius controlled.
Putting it together: a real team setup
Here's a representative setup — a main personal agent, a coding agent via ACP, and a sandboxed family agent:
{
agents: {
list: [
{
id: "main",
default: true,
workspace: "~/.openclaw/workspace",
model: "anthropic/claude-opus-4-6",
},
{
id: "coding",
workspace: "~/.openclaw/workspace-coding",
model: "anthropic/claude-sonnet-4-5",
runtime: {
type: "acp",
acp: { agent: "codex", mode: "persistent", cwd: "/workspace/repos" },
},
},
{
id: "family",
workspace: "~/.openclaw/workspace-family",
sandbox: { mode: "all", scope: "agent" },
tools: {
deny: ["exec", "write", "edit", "browser"],
},
},
],
},
bindings: [
{ agentId: "main", match: { channel: "telegram" } },
{
agentId: "coding",
match: {
channel: "discord",
peer: { kind: "channel", id: "333333333333333333" },
},
},
{
agentId: "family",
match: {
channel: "whatsapp",
peer: { kind: "group", id: "120363999999999999@g.us" },
},
},
{ agentId: "main", match: { channel: "whatsapp" } },
],
tools: {
agentToAgent: {
enabled: true,
allow: ["main", "coding"],
},
},
}
Three brains. Three workspaces. One gateway. Restart with openclaw gateway restart and you're running a team.
One thing people get wrong
Auth profiles are per-agent. If you add a second agent and wonder why it can't access your Slack or GitHub — it's because credentials aren't shared. Copy ~/.openclaw/agents/main/agent/auth-profiles.json to the new agent's agentDir, or re-run the relevant auth setup for that agent explicitly.
Similarly: never share the same agentDir between two agents. It causes session and auth collisions that are annoying to debug.
Final thoughts
The jump from one agent to a team isn't that big — it's mostly config. What changes is the mental model. You stop thinking about "my AI agent" as a single thing and start thinking about it as a staffing problem: who handles what, with what access, on what channel.
Once you've been running two or three agents for a week, going back to one feels like losing a team member.
Want the complete setup guide with config templates and workflow examples? Get The OpenClaw Playbook — $9.99