Multi-Agent 라우팅
목표: 하나의 실행 중인 Gateway에서 여러 개의 격리된 agent(별도의 workspace + agentDir + session)와 여러 channel 계정(예: 두 개의 WhatsApp)을 운영합니다. 인바운드 메시지는 바인딩을 통해 agent로 라우팅됩니다.
"하나의 agent"란 무엇인가요?
agent는 다음을 가진 완전히 독립된 두뇌입니다:
- Workspace (파일, AGENTS.md/SOUL.md/USER.md, 로컬 노트, 페르소나 규칙).
- State 디렉토리 (agentDir) - 인증 프로필, model 레지스트리, agent별 config 저장.
- Session 스토어 (채팅 기록 + 라우팅 상태) - ~/.openclaw/agents/<agentId>/sessions 하위에 저장.
인증 프로필은 agent별로 관리됩니다. 각 agent는 자체 프로필을 읽습니다:
~/.openclaw/agents/<agentId>/agent/auth-profiles.json
Main agent 인증 정보는 자동으로 공유되지 않습니다. agent 간에 agentDir을 재사용하지 마세요(인증/session 충돌이 발생합니다). 인증 정보를 공유하려면 auth-profiles.json을 다른 agent의 agentDir로 복사하세요.
Skill은 각 workspace의 skills/ 폴더를 통해 agent별로 관리되며, 공유 skill은 ~/.openclaw/skills에서 사용할 수 있습니다. Skills: per-agent vs shared를 참조하세요.
Gateway는 하나의 agent(기본값) 또는 여러 agent를 동시에 호스팅할 수 있습니다.
Workspace 참고사항: 각 agent의 workspace는 기본 cwd이며, 엄격한 sandbox가 아닙니다. 상대 경로는 workspace 내부에서 해석되지만, 절대 경로는 sandboxing이 활성화되지 않은 한 다른 호스트 위치에 접근할 수 있습니다. Sandboxing을 참조하세요.
경로 (빠른 맵)
- Config: ~/.openclaw/openclaw.json (또는 OPENCLAW_CONFIG_PATH)
- State dir: ~/.openclaw (또는 OPENCLAW_STATE_DIR)
- Workspace: ~/.openclaw/workspace (또는 ~/.openclaw/workspace-<agentId>)
- Agent dir: ~/.openclaw/agents/<agentId>/agent (또는 agents.list[].agentDir)
- Sessions: ~/.openclaw/agents/<agentId>/sessions
Single-agent 모드 (기본값)
아무것도 설정하지 않으면 OpenClaw는 단일 agent를 실행합니다:
- agentId 기본값은 **main**입니다.
- Session은 agent:main:<mainKey>로 키가 지정됩니다.
- Workspace 기본값은 ~/.openclaw/workspace (또는 OPENCLAW_PROFILE이 설정된 경우 ~/.openclaw/workspace-<profile>).
- State 기본값은 ~/.openclaw/agents/main/agent.
Agent helper
Agent 마법사를 사용하여 새로운 격리된 agent를 추가하세요:
openclaw agents add work
그런 다음 인바운드 메시지를 라우팅하기 위해 bindings를 추가하세요(또는 마법사가 자동으로 추가합니다).
다음 명령으로 확인하세요:
openclaw agents list --bindings
여러 agent = 여러 사람, 여러 성격
여러 agent를 사용하면 각 agentId가 완전히 격리된 페르소나가 됩니다:
- 다른 전화번호/계정 (channel별 accountId).
- 다른 성격 (agent별 workspace 파일인 AGENTS.md 및 SOUL.md).
- 별도의 인증 + session (명시적으로 활성화하지 않는 한 상호 간섭 없음).
이를 통해 여러 사람이 하나의 Gateway 서버를 공유하면서도 AI "두뇌"와 데이터를 격리할 수 있습니다.
하나의 WhatsApp 번호, 여러 사람 (DM 분할)
하나의 WhatsApp 계정을 유지하면서 다른 WhatsApp DM을 다른 agent로 라우팅할 수 있습니다. 발신자 E.164(+15551234567와 같은)를 peer.kind: "dm"으로 매칭합니다. 답장은 여전히 같은 WhatsApp 번호에서 전송됩니다(agent별 발신자 identity는 없음).
중요한 세부사항: direct chat은 agent의 main session key로 축약되므로, 진정한 격리를 위해서는 사람당 하나의 agent가 필요합니다.
예시:
{
agents: {
list: [
{ id: "alex", workspace: "~/.openclaw/workspace-alex" },
{ id: "mia", workspace: "~/.openclaw/workspace-mia" }
]
},
bindings: [
{ agentId: "alex", match: { channel: "whatsapp", peer: { kind: "dm", id: "+15551230001" } } },
{ agentId: "mia", match: { channel: "whatsapp", peer: { kind: "dm", id: "+15551230002" } } }
],
channels: {
whatsapp: {
dmPolicy: "allowlist",
allowFrom: ["+15551230001", "+15551230002"]
}
}
}
참고사항:
- DM 접근 제어는 agent별이 아닌 WhatsApp 계정별로 전역입니다(페어링/allowlist).
- 공유 그룹의 경우 그룹을 하나의 agent에 바인딩하거나 Broadcast groups를 사용하세요.
라우팅 규칙 (메시지가 agent를 선택하는 방식)
바인딩은 결정론적이며 가장 구체적인 것이 우선입니다:
- peer 매치 (정확한 DM/group/channel id)
- guildId (Discord)
- teamId (Slack)
- channel의 accountId 매치
- channel 레벨 매치 (accountId: "*")
- 기본 agent로 폴백 (agents.list[].default, 또는 첫 번째 목록 항목, 기본값: main)
여러 계정 / 전화번호
여러 계정을 지원하는 channel(예: WhatsApp)은 accountId를 사용하여 각 로그인을 식별합니다. 각 accountId는 다른 agent로 라우팅될 수 있으므로, 하나의 서버가 session을 혼합하지 않고 여러 전화번호를 호스팅할 수 있습니다.
개념
- agentId: 하나의 "두뇌" (workspace, agent별 인증, agent별 session 스토어).
- accountId: 하나의 channel 계정 인스턴스 (예: WhatsApp 계정 "personal" vs "biz").
- binding: (channel, accountId, peer) 및 선택적으로 guild/team id를 기준으로 인바운드 메시지를 agentId로 라우팅합니다.
- Direct chat은 agent:<agentId>:<mainKey>로 축약됩니다(agent별 "main"; session.mainKey).
예시: 두 개의 WhatsApp → 두 개의 agent
~/.openclaw/openclaw.json (JSON5):
{
agents: {
list: [
{
id: "home",
default: true,
name: "Home",
workspace: "~/.openclaw/workspace-home",
agentDir: "~/.openclaw/agents/home/agent",
},
{
id: "work",
name: "Work",
workspace: "~/.openclaw/workspace-work",
agentDir: "~/.openclaw/agents/work/agent",
},
],
},
// 결정론적 라우팅: 첫 번째 매치가 우선 (가장 구체적인 것부터).
bindings: [
{ agentId: "home", match: { channel: "whatsapp", accountId: "personal" } },
{ agentId: "work", match: { channel: "whatsapp", accountId: "biz" } },
// 선택사항 peer별 재정의 (예시: 특정 그룹을 work agent로 전송).
{
agentId: "work",
match: {
channel: "whatsapp",
accountId: "personal",
peer: { kind: "group", id: "[email protected]" },
},
},
],
// 기본적으로 비활성화됨: agent 간 메시징은 명시적으로 활성화하고 allowlist에 추가해야 합니다.
tools: {
agentToAgent: {
enabled: false,
allow: ["home", "work"],
},
},
channels: {
whatsapp: {
accounts: {
personal: {
// 선택적 재정의. 기본값: ~/.openclaw/credentials/whatsapp/personal
// authDir: "~/.openclaw/credentials/whatsapp/personal",
},
biz: {
// 선택적 재정의. 기본값: ~/.openclaw/credentials/whatsapp/biz
// authDir: "~/.openclaw/credentials/whatsapp/biz",
},
},
},
},
}
예시: WhatsApp 일상 대화 + Telegram 집중 작업
Channel별로 분할: WhatsApp을 빠른 일상 agent로 라우팅하고 Telegram을 Opus agent로 라우팅합니다.
{
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-5"
}
]
},
bindings: [
{ agentId: "chat", match: { channel: "whatsapp" } },
{ agentId: "opus", match: { channel: "telegram" } }
]
}
참고사항:
- Channel에 여러 계정이 있는 경우 바인딩에 accountId를 추가하세요(예: { channel: "whatsapp", accountId: "personal" }).
- 나머지는 chat에 유지하면서 단일 DM/그룹을 Opus로 라우팅하려면 해당 peer에 대한 match.peer 바인딩을 추가하세요. peer 매치는 항상 channel 전체 규칙보다 우선합니다.
예시: 같은 channel, 하나의 peer를 Opus로
WhatsApp을 빠른 agent에 유지하되, 하나의 DM만 Opus로 라우팅합니다:
{
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-5" }
]
},
bindings: [
{ agentId: "opus", match: { channel: "whatsapp", peer: { kind: "dm", id: "+15551234567" } } },
{ agentId: "chat", match: { channel: "whatsapp" } }
]
}
Peer 바인딩은 항상 우선하므로, channel 전체 규칙 위에 배치하세요.
WhatsApp 그룹에 바인딩된 가족 agent
멘션 gating 및 더 엄격한 tool policy를 적용한 전용 가족 agent를 단일 WhatsApp 그룹에 바인딩합니다:
{
agents: {
list: [
{
id: "family",
name: "Family",
workspace: "~/.openclaw/workspace-family",
identity: { name: "Family Bot" },
groupChat: {
mentionPatterns: ["@family", "@familybot", "@Family Bot"]
},
sandbox: {
mode: "all",
scope: "agent"
},
tools: {
allow: ["exec", "read", "sessions_list", "sessions_history", "sessions_send", "sessions_spawn", "session_status"],
deny: ["write", "edit", "apply_patch", "browser", "canvas", "nodes", "cron"]
}
}
]
},
bindings: [
{
agentId: "family",
match: {
channel: "whatsapp",
peer: { kind: "group", id: "[email protected]" }
}
}
]
}
참고사항:
- Tool allow/deny 목록은 tool이며 skill이 아닙니다. Skill이 바이너리를 실행해야 하는 경우 exec가 허용되고 바이너리가 sandbox에 존재하는지 확인하세요.
- 더 엄격한 gating을 위해 agents.list[].groupChat.mentionPatterns를 설정하고 channel에 대한 그룹 allowlist를 활성화하세요.
Agent별 Sandbox 및 Tool 구성
v2026.1.6부터 각 agent는 자체 sandbox 및 tool 제한을 가질 수 있습니다:
{
agents: {
list: [
{
id: "personal",
workspace: "~/.openclaw/workspace-personal",
sandbox: {
mode: "off", // personal agent에 대해 sandbox 없음
},
// tool 제한 없음 - 모든 tool 사용 가능
},
{
id: "family",
workspace: "~/.openclaw/workspace-family",
sandbox: {
mode: "all", // 항상 sandbox 적용
scope: "agent", // agent당 하나의 컨테이너
docker: {
// 컨테이너 생성 후 한 번 실행되는 선택적 설정
setupCommand: "apt-get update && apt-get install -y git curl",
},
},
tools: {
allow: ["read"], // read tool만 허용
deny: ["exec", "write", "edit", "apply_patch"], // 다른 tool 거부
},
},
],
},
}
참고: setupCommand는 sandbox.docker 하위에 있으며 컨테이너 생성 시 한 번 실행됩니다. Agent별 sandbox.docker.* 재정의는 해결된 scope가 "shared"인 경우 무시됩니다.
장점:
- 보안 격리: 신뢰할 수 없는 agent에 대해 tool 제한
- 리소스 제어: 다른 agent는 호스트에 유지하면서 특정 agent만 sandbox 적용
- 유연한 정책: agent별로 다른 권한 부여
참고: tools.elevated는 전역이며 발신자 기반이므로 agent별로 구성할 수 없습니다. Agent별 경계가 필요한 경우 agents.list[].tools를 사용하여 exec를 거부하세요. 그룹 타겟팅의 경우 agents.list[].groupChat.mentionPatterns를 사용하여 @멘션이 의도한 agent에 명확하게 매핑되도록 하세요.
자세한 예시는 Multi-Agent Sandbox & Tools를 참조하세요.