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.mdSOUL.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를 선택하는 방식)

바인딩은 결정론적이며 가장 구체적인 것이 우선입니다:

  1. peer 매치 (정확한 DM/group/channel id)
  2. guildId (Discord)
  3. teamId (Slack)
  4. channel의 accountId 매치
  5. channel 레벨 매치 (accountId: "*")
  6. 기본 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 거부
        },
      },
    ],
  },
}

참고: setupCommandsandbox.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를 참조하세요.