On this page
🏗️ Why multi-agent?
| Reason | How multi-agent helps |
|---|---|
| Security isolation | Personal assistant gets exec access; public Discord bot does not. Per-agent tool allow/deny lists. |
| Cost control | Main agent uses expensive Sonnet; sub-agents use cheaper models for grunt work. |
| Clean sessions | Sub-agent noise stays in its own transcript, keeping your main session clean. |
| Parallel work | Spawn multiple sub-agents to research different topics simultaneously. |
| Context separation | Work agent and personal agent have separate memories — no bleed between contexts. |
📋 Define agents
// ~/.openclaw/openclaw.json
{
"agents": {
"list": [
{
"id": "home",
"default": true,
"workspace": "~/.openclaw/workspace-home"
},
{
"id": "work",
"workspace": "~/.openclaw/workspace-work"
},
{
"id": "coding",
"workspace": "~/.openclaw/workspace-coding",
"model": {
"primary": "anthropic/claude-sonnet-4-5"
}
}
]
}
}
Each agent gets its own workspace directory (SOUL.md, IDENTITY.md, etc.), sessions, and optionally its own model config. All fields from agents.defaults can be overridden per-agent.
💡 CLI shortcut: openclaw agents add walks you through creating a new agent interactively. Verify with openclaw agents list --bindings.
🔀 Route with bindings
Bindings determine which agent handles which channel, account, or peer:
{
"bindings": [
{
"match": { "channel": "telegram" },
"agentId": "home"
},
{
"match": { "channel": "slack" },
"agentId": "work"
},
{
"match": {
"channel": "whatsapp",
"peer": "+40712345678"
},
"agentId": "home"
}
]
}
Match on: channel, accountId, chatType, peer. When no binding matches, the default: true agent handles the message.
🚀 Spawn sub-agents
From chat (slash command)
/subagents spawn main "Summarize the last 7 days of changelog entries"
This is non-blocking — returns a run ID immediately. On completion, the sub-agent sends a summary back to your chat.
From the agent (sessions_spawn tool)
The agent itself can decide to spawn workers. This is the orchestrator pattern:
# Tool call parameters:
{
"message": "Research latest React 19 features",
"model": "google/gemini-2.5-flash",
"runTimeoutSeconds": 120,
"thread": false
}
Key parameters
| Parameter | Default | Purpose |
|---|---|---|
model | Agent default | Override model for cheaper sub-agent runs |
thinking | Agent default | Override thinking level |
runTimeoutSeconds | 0 (none) | Auto-abort after N seconds |
thread | false | Bind sub-agent to a Discord thread |
agentId | Requester | Target a specific named agent |
🎭 Orchestrator pattern
Main agent spawns specialized workers, each returning results:
# Pattern: main → orchestrator → workers
# maxSpawnDepth controls nesting
{
"agents": {
"defaults": {
"subagents": {
"maxSpawnDepth": 2,
"archiveAfterMinutes": 60,
"runTimeoutSeconds": 300
}
}
}
}
- Depth 1 (default): main can spawn sub-agents, but sub-agents cannot spawn their own
- Depth 2: enables main → orchestrator → workers (one level of nesting)
- Sub-agents deeper than depth 2 are not supported
💡 Auto-archive: Sub-agent sessions are archived after archiveAfterMinutes (default 60). Transcripts are renamed to *.deleted.<timestamp> but preserved. Set cleanup: "delete" for immediate archive after completion.
🧵 Thread-bound sessions (Discord)
Discord is currently the only channel supporting persistent thread-bound sub-agent sessions:
# Spawn a sub-agent bound to a Discord thread
/subagents spawn coding "Review this PR" --thread
# The sub-agent stays in that thread
# Follow-up messages in the thread route to it
/session ttl 24h
# Detach manually
/unfocus
Use /agents to list active runs and binding state. Use /focus <target> to bind a thread to a specific agent.
📊 Managing sub-agents
# List active sub-agents
/subagents list
# View output/logs
/subagents log <run-id>
# Stop a running sub-agent
/subagents kill <run-id>
# List available agents for spawning
# (uses agents_list tool internally)
/agents
Allowlist control
{
"agents": {
"list": [
{
"id": "main",
"subagents": {
"allowAgents": ["coding", "research"]
}
}
]
}
}
Set allowAgents: ["*"] to allow spawning any agent. Default: only the requester agent itself.
🔐 Security isolation
- Per-agent tool deny lists — block
execfor public-facing agents while allowing it for trusted ones - Separate workspaces — each agent gets its own SOUL.md, skills, and memory
- Sandbox mode — run sub-agents in Docker sandboxes:
sandbox.mode: "non-main" - Never share skills directories — a skill in a shared dir becomes available to all agents
- Review third-party skills — especially important with multiple agents (more surface area)
⚠️ Security first: Multi-agent increases surface area. Lock down tools, lock down channels, and keep an audit trail. See Security Guide.
💼 Real-world patterns
| Pattern | Setup |
|---|---|
| Home + Work split | Two agents, routed by channel (Telegram → home, Slack → work). Separate memories. |
| Coding specialist | Main spawns "coding" sub-agent with Sonnet for code reviews, using cheaper model for main. |
| Research parallelism | Main spawns 3 sub-agents simultaneously for different research topics, aggregates results. |
| Public + Private | Public Discord bot (no exec, no fs) + private Telegram agent (full access). Same gateway. |
| Family hub | One agent per family member, routed by WhatsApp peer ID. Shared grocery list via hooks. |
🔧 Troubleshooting
| Problem | Fix |
|---|---|
agents_list only shows "main" | Agents must be in agents.list[] array in config. Verify with openclaw agents list. |
agentId is not allowed | Set subagents.allowAgents to include the target agent ID, or ["*"] to allow any. |
| Sub-agent never completes | Set runTimeoutSeconds to auto-abort. Check /subagents log <id> for errors. |
| Context bleed between agents | Ensure separate workspace paths per agent. Shared workspace = shared memory. |
| Sessions lost on restart | Configure session persistence. Sub-agent sessions don't survive gateway restarts by default. |
| Completion announce not delivered | Updated in v2026.3.1+: typed task_completion events replace ad-hoc handoff. Update OpenClaw. |