OpenClaw 소스 해부: WhatsApp부터 Discord까지 10개 채널을 하나의 AI가 지배하는 구조
Local-first 개인 AI 비서 OpenClaw의 아키텍처를 5계층으로 분해하고, Gateway 중심 설계의 천재적인 부분과 한계를 낱낱이 분석합니다.
왜 OpenClaw인가
ChatGPT, Claude — 모두 브라우저에서 쓴다. 하지만 내 WhatsApp, Telegram, Slack, Discord를 하나의 AI가 통합 관리한다면? OpenClaw는 이 미친 아이디어를 현실로 만든 오픈소스 프로젝트다.
핵심 철학은 Local-first. 클라우드가 아니라 내 맥북에서 직접 돌린다. 대화 데이터가 외부 서버에 저장되지 않는다. 프라이버시를 포기하지 않으면서 10개 이상의 메시징 플랫폼을 하나의 AI 에이전트로 제어한다.
OpenClaw의 핵심 특징 3가지:
- Local-First: 사용자 디바이스에서 직접 실행. 데이터는
~/.openclaw/에 JSON/JSONL 파일로 저장. 클라우드 DB 없음 - Multi-Channel: WhatsApp, Telegram, Slack, Discord, Signal, iMessage, WebChat, Google, MS Teams, Matrix, Zalo 등 10개 이상 플랫폼 통합
- Gateway 중심 단일 제어 플레인: 모든 채널의 메시지가 하나의 WebSocket 서버를 통과
이게 어떻게 가능한지, 소스를 계층별로 낱낱이 뜯어봤다.
5계층 아키텍처 — 전체 구조 상세 분석
OpenClaw의 전체 시스템은 5개의 명확한 계층으로 분리된다.
┌─────────────────────────────────────────────────────────────────────────┐
│ Messaging Channels (입력) │
├─────────────────────────────────────────────────────────────────────────┤
│ WhatsApp │ Telegram │ Slack │ Discord │ Signal │ iMessage │ WebChat │
│ Google │ MS Teams │ Matrix│ Zalo │BlueBubbles│ (확장) │ (확장) │
└───────────┬─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Gateway (Control Plane) │
│ WebSocket Server (18789) │
├─────────────────────────────────────────────────────────────────────────┤
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ • Channel Management (메시징 채널 관리) │ │
│ │ • Session Management (세션 관리) │ │
│ │ • Routing Logic (메시지 라우팅) │ │
│ │ • Authentication & Pairing (인증 및 페어링) │ │
│ │ • Event System (이벤트 시스템) │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└───────────┬─────────────────────────────────────────────────────────────┘
│
├─────────────────┬──────────────────┬─────────────────┐
▼ ▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Pi Agent │ │ CLI Tools │ │ Control UI │ │ Nodes │
│ Runtime │ │ │ │ (Web) │ │ (iOS/macOS │
│ (pi-mono 기반)│ │ │ │ │ │ /Android) │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Agent Workspace │
│ • AGENTS.md (operating instructions) │
│ • SOUL.md (persona, tone) │
│ • TOOLS.md (tool notes) │
│ • Skills (bundled/managed/workspace) │
│ • Session logs (~/.openclaw/agents/<id>/sessions/) │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ AI Model Providers │
│ • Anthropic (Claude Pro/Max + Opus 4.6) │
│ • OpenAI (ChatGPT/Codex) │
│ • Other providers (via configuration) │
└──────────────────────────────────────────────────────────┘
각 계층을 하나씩 살펴보자.
Layer 1: Messaging Channels (입력 계층)
사용자가 실제로 메시지를 보내는 플랫폼들이다. OpenClaw는 각 플랫폼의 네이티브 SDK를 직접 통합한다:
- WhatsApp: Baileys (비공식 Web API 래퍼)
- Telegram: grammY (Telegram Bot Framework)
- Discord: discord.js (공식 JS 라이브러리)
- Slack: Bolt (Slack 공식 프레임워크)
- Signal: signal-cli (Signal CLI 래퍼)
- iMessage / BlueBubbles: macOS 전용 브릿지
- WebChat: 내장 웹 인터페이스
각 SDK의 특성이 다르기 때문에 (WebSocket, Long Polling, Webhook 등) Channel Manager가 이 차이를 추상화해서 Gateway에 통일된 인터페이스를 제공한다. extensions/ 디렉토리를 통해 MS Teams, Matrix, Zalo 같은 새 플랫폼도 추가 가능하다.
Layer 2: Gateway (제어 플레인)
이 시스템의 심장. WebSocket 서버(기본 포트 18789)로, 모든 채널의 메시지가 반드시 이곳을 통과한다. Channel Management, Session Management, Routing, Auth, Event System 등 핵심 로직이 모두 여기에 집중되어 있다. 아래 섹션에서 내부 구조를 상세히 분석한다.
Layer 3: Agent Runtime (실행 엔진)
Anthropic의 pi-mono를 임베디드로 사용하는 AI 실행 엔진이다. Gateway에서 라우팅된 메시지를 받아 실제 AI 대화를 처리한다. Workspace에서 persona(SOUL.md), 운영 지침(AGENTS.md), 도구 노트(TOOLS.md)를 로드하고, Skills 시스템으로 확장 기능을 추가한다.
Gateway 하위에는 Pi Agent Runtime 외에도 CLI Tools, Control UI(Web), Nodes(iOS/macOS/Android) 등 다양한 클라이언트가 동일한 WebSocket 프로토콜로 연결된다. 이건 Gateway가 단순히 "메시지 프록시"가 아니라 진짜 "제어 플레인"이라는 뜻이다.
Layer 4: AI Model Providers (외부 AI 서비스)
실제 추론을 수행하는 외부 서비스. Anthropic(Claude), OpenAI(GPT) 등을 설정으로 선택 가능하고, Primary model + fallback 구성을 지원한다. Auth profile 관리로 API 키를 사용자/계정별로 분리할 수 있다.
Layer 5: 응답 전달
생성된 응답이 원래 채널을 통해 사용자에게 전달된다. sendPolicy(immediate/batch/silent)에 따라 전달 방식이 달라진다.
Gateway 내부 구조 상세 분석
Gateway는 단순한 프록시가 아니다. 4개의 핵심 서브시스템과 6개의 보조 서비스로 구성된 복합 제어 플레인이다. 소스 파일 단위로 들여다보자.
┌───────────────────────────────────────────────────────────────────────┐
│ Gateway Server │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ WebSocket Handler (server-ws-runtime.ts) │ │
│ │ • Connection handshake │ │
│ │ • Device pairing & authentication │ │
│ │ • Request/Response dispatcher │ │
│ │ • Event broadcasting │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Channel Manager (server-channels.ts) │ │
│ │ • WhatsApp (Baileys) │ │
│ │ • Telegram (grammY) │ │
│ │ • Discord (discord.js) │ │
│ │ • Slack (Bolt) │ │
│ │ • Signal (signal-cli) │ │
│ │ • + Extension Channels │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Routing Engine (resolve-route.ts) │ │
│ │ • Agent binding resolution │ │
│ │ • Session key generation │ │
│ │ • DM scope handling (main/per-peer/per-channel) │ │
│ │ • Identity linking │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Agent Event Handler (server-chat.ts) │ │
│ │ • Queue management (steer/followup/collect) │ │
│ │ • Message injection & steering │ │
│ │ • Block streaming coordination │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Supporting Services │ │
│ │ • Cron Service (server-cron.ts) │ │
│ │ • Node Registry (node-registry.ts) │ │
│ │ • Health Monitor (health-state.ts) │ │
│ │ • Config Reloader (config-reload.ts) │ │
│ │ • Canvas Host Server (canvas-host/) │ │
│ │ • Browser Control (browser/) │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────────┘
1. WebSocket Handler (server-ws-runtime.ts)
Gateway의 입구. 모든 클라이언트(메시징 채널, CLI, Web UI, iOS/Android Node)가 WebSocket으로 연결된다. 연결 과정은 다음과 같다:
- 클라이언트가 WS 연결을 열고
connect프레임을 전송 (deviceId, role, auth 포함) - Gateway가 인증을 확인하고 페어링 상태를 체크
- 성공하면
hello-okpayload를 보내고 이벤트 구독을 시작 - 이후
req/res패턴으로 요청을 주고받고,event로 서버 이벤트를 브로드캐스트
role은 operator(관리자)와 node(디바이스)로 구분된다. 동일한 WS 프로토콜을 사용하기 때문에 macOS 앱, CLI, 웹 UI가 모두 같은 방식으로 Gateway에 접근한다.
2. Channel Manager (server-channels.ts)
각 메시징 플랫폼의 SDK를 통합 관리하는 계층이다. 플랫폼마다 프로토콜이 다르다:
- WhatsApp(Baileys): WebSocket 기반 비공식 API. QR 코드로 세션 인증
- Telegram(grammY): Long Polling 또는 Webhook 모드
- Discord(discord.js): WebSocket Gateway로 실시간 이벤트 수신
- Slack(Bolt): Socket Mode 또는 HTTP Webhook
- Signal(signal-cli): CLI 프로세스를 래핑한 브릿지
Channel Manager는 이 모든 차이를 추상화해서 Gateway 내부에서는 통일된 메시지 형식으로 처리한다. 새 플랫폼을 추가하려면 extensions/ 디렉토리에 채널 어댑터를 구현하면 된다.
설계 포인트: SDK 직접 통합 방식의 장점은 플랫폼 API 변경에 독립적으로 대응할 수 있다는 것. 단점은 WhatsApp처럼 비공식 API(Baileys)에 의존하면 언제든 깨질 수 있는 리스크가 있다.
3. Routing Engine (resolve-route.ts)
들어온 메시지를 어떤 Agent에게 보낼지, 어떤 세션에 연결할지 결정하는 두뇌다. 메시지에서 channel, accountId, peer(상대방), guildId(Discord), teamId(Slack) 정보를 추출한 뒤, 설정된 binding 규칙과 7단계 우선순위로 매칭한다. 라우팅 시스템은 아래에서 별도 섹션으로 상세히 다룬다.
4. Agent Event Handler (server-chat.ts)
Agent 실행 중 메시지 흐름을 제어하는 핵심 컴포넌트다. 특히 Queue Management가 이 파일의 핵심 역할이다:
- steer 모드: Agent가 Tool을 실행하는 중에도 사용자의 새 메시지를 대화 흐름에 즉시 주입. 나머지 Tool 실행을 건너뛰고 새 지시를 반영
- followup 모드: 현재 Agent 턴이 완전히 끝난 후에 대기 중인 메시지를 처리
- collect 모드: 여러 메시지를 수집한 후 일괄 처리
Block streaming coordination도 여기서 담당한다. Agent의 응답을 실시간으로 클라이언트에 전달하되, 완성된 블록 단위로 전송한다.
5. Supporting Services
6개의 보조 서비스가 Gateway를 뒷받침한다:
| 서비스 | 파일 | 역할 |
|---|---|---|
| Cron Service | server-cron.ts | 예약된 작업 실행 (정기 메시지, 스케줄링) |
| Node Registry | node-registry.ts | iOS/Android/macOS 디바이스 노드 등록 및 관리 |
| Health Monitor | health-state.ts | Gateway 및 채널 연결 상태 모니터링 |
| Config Reloader | config-reload.ts | openclaw.json 변경 감지 및 핫 리로드 |
| Canvas Host | canvas-host/ (포트 18793) | Agent가 제어하는 시각적 워크스페이스 서버 |
| Browser Control | browser/ | Chrome/Chromium을 통한 웹 자동화 |
메시지 처리 8단계 상세 플로우
사용자가 WhatsApp에서 "내일 일정 알려줘"라고 보내면 무슨 일이 일어나는가? 8단계를 하나씩 따라가 보자.
사용자 메시지
│
▼
┌─────────────────────────────────────────────┐
│ 1. Channel Input │
│ (WhatsApp/Telegram/Slack 등) │
└────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 2. Gateway Reception │
│ • Message validation │
│ • DM policy check (pairing required?) │
│ • Allowlist verification │
└────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 3. Routing Layer │
│ • resolveAgentRoute() │
│ • Determine: agentId, sessionKey │
│ • Match bindings (peer/guild/channel) │
└────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 4. Session Resolution │
│ • Load or create session │
│ • Apply session overrides │
│ (thinkingLevel, verboseLevel, model) │
└────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 5. Agent Execution │
│ • Pi Agent Runtime (RPC mode) │
│ • Tool streaming & block streaming │
│ • Queue mode (steer/followup/collect) │
└────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 6. Model Interaction │
│ • Primary model + fallbacks │
│ • Auth profile management │
│ • Token tracking & usage │
└────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 7. Response Generation │
│ • Stream or block output │
│ • Apply formatting/chunking │
└────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 8. Response Delivery │
│ • sendPolicy (immediate/batch/silent) │
│ • Channel-specific formatting │
│ • Delivery to original channel │
└────────────┬────────────────────────────────┘
│
▼
사용자에게 답변
Step 1: Channel Input
WhatsApp(Baileys), Telegram(grammY), Discord(discord.js) 등 각 SDK가 플랫폼으로부터 메시지를 수신한다. 각 SDK의 이벤트 핸들러가 발화되고, Channel Manager가 내부 형식으로 변환한다. 이 시점에서 메시지의 원본 채널, 발신자(peer), 그룹/DM 여부, 첨부파일 등의 메타데이터가 추출된다.
Step 2: Gateway Reception
Gateway에 도달한 메시지는 두 가지 검증을 거친다:
- DM Policy Check: 기본값은
pairing모드. 페어링되지 않은 알 수 없는 발신자의 메시지는 여기서 차단된다. 이건 스팸/피싱 방지에 필수적인 단계 - Allowlist Verification: 채널별로 허용된 연락처 목록을 확인한다. Group allowlist도 지원하며, 와일드카드(
*)로 전체 허용도 가능
검증에 실패하면 메시지는 조용히 드롭된다. 성공하면 다음 단계로 넘어간다.
Step 3: Routing Layer
resolveAgentRoute() 함수가 호출된다. 이 함수는 메시지의 channel, accountId, peer, guildId, teamId 정보를 기반으로 설정된 binding 규칙과 7단계 우선순위로 매칭해서 agentId와 sessionKey를 결정한다. "이 메시지는 어떤 Agent가 처리해야 하는가?"를 결정하는 핵심 단계다.
Step 4: Session Resolution
결정된 sessionKey로 기존 세션을 로드하거나 새 세션을 생성한다. 세션에는 대화 히스토리(JSONL transcript)뿐만 아니라 session overrides도 적용된다:
thinkingLevel: AI의 사고 깊이 설정verboseLevel: 응답 상세도 조절model: 이 세션에서 사용할 AI 모델 오버라이드
이 오버라이드 덕분에 같은 Agent라도 세션별로 다른 모델, 다른 사고 깊이를 사용할 수 있다.
Step 5: Agent Execution
Pi Agent Runtime이 실행된다. 구체적으로:
- Workspace 로드 (
~/.openclaw/workspace/의 AGENTS.md, SOUL.md, TOOLS.md, IDENTITY.md, USER.md) - Skills 로드 (Workspace -> Managed -> Bundled 우선순위)
- 세션 컨텍스트 주입
- RPC 모드로 pi-mono 에이전트 실행 (
runEmbeddedPiAgent()또는runCliAgent())
Tool streaming이 활성화되어 있으면 Tool 실행 결과가 실시간으로 스트리밍된다. Queue mode에 따라 대화 중 메시지 처리 방식이 달라진다.
Step 6: Model Interaction
Pi Agent가 AI 모델 API를 호출한다. resolveConfiguredModelRef() 함수로 모델이 결정되는데:
- Primary model: 설정에서 지정한 기본 모델 (예: Claude Opus 4.6)
- Fallback models: Primary 실패 시 대체 모델
- Auth profile: API 키를 사용자/계정별로 분리 관리
토큰 사용량도 이 단계에서 추적된다.
Step 7: Response Generation
AI 모델의 응답을 가공하는 단계:
- Block streaming: 응답이 완성된 블록(문단, 코드 블록 등) 단위로 실시간 전송
- Coalescing & chunking: 메시징 플랫폼의 글자 수 제한에 맞게 응답을 800-1200자 단위로 분할
- 채널별 포맷팅: Discord는 마크다운, WhatsApp은 제한된 포맷팅 등 플랫폼에 맞게 변환
Step 8: Response Delivery
최종 응답이 원래 채널을 통해 사용자에게 전달된다. sendPolicy에 따라 전달 방식이 달라진다:
- immediate: 생성되는 대로 즉시 전송
- batch: 응답 전체가 완성된 후 한 번에 전송
- silent: 응답을 세션에만 저장하고 전송하지 않음 (백그라운드 처리용)
Agent 실행 흐름 8단계
메시지가 Agent에 도달한 후 내부에서 어떤 일이 일어나는지 더 상세히 들여다보자.
메시지 수신
│
▼
┌────────────────────────────────────────────┐
│ 1. Session Resolution │
│ • resolveSession() │
│ • sessionKey → agentId, sessionId │
└─────────────┬──────────────────────────────┘
│
▼
┌────────────────────────────────────────────┐
│ 2. Workspace Preparation │
│ • Load workspace (~/.openclaw/workspace)│
│ • Inject bootstrap files: │
│ - AGENTS.md, SOUL.md, TOOLS.md │
│ - IDENTITY.md, USER.md │
└─────────────┬──────────────────────────────┘
│
▼
┌────────────────────────────────────────────┐
│ 3. Skills Loading │
│ • Bundled skills │
│ • Managed skills (~/.openclaw/skills) │
│ • Workspace skills (workspace/skills) │
└─────────────┬──────────────────────────────┘
│
▼
┌────────────────────────────────────────────┐
│ 4. Model Configuration │
│ • resolveConfiguredModelRef() │
│ • Primary model + fallbacks │
│ • Auth profile selection │
└─────────────┬──────────────────────────────┘
│
▼
┌────────────────────────────────────────────┐
│ 5. Pi Agent Runtime │
│ • runEmbeddedPiAgent() or runCliAgent() │
│ • RPC mode with tool streaming │
│ • Session context injection │
└─────────────┬──────────────────────────────┘
│
▼
┌────────────────────────────────────────────┐
│ 6. Tool Execution Loop │
│ • Tool call streaming │
│ • Queue check after each tool call │
│ • Steering injection (if queue mode) │
└─────────────┬──────────────────────────────┘
│
▼
┌────────────────────────────────────────────┐
│ 7. Response Assembly │
│ • Block streaming (optional) │
│ • Coalescing & chunking │
│ • Format for target channel │
└─────────────┬──────────────────────────────┘
│
▼
┌────────────────────────────────────────────┐
│ 8. Session Update & Delivery │
│ • Save session transcript (JSONL) │
│ • Update session store metadata │
│ • Deliver via sendPolicy │
└─────────────┬──────────────────────────────┘
│
▼
완료
Step 1: Session Resolution
resolveSession() 함수가 sessionKey를 agentId와 sessionId로 분해한다. 기존 세션이 있으면 로드, 없으면 새로 생성한다. 세션 메타데이터는 ~/.openclaw/sessions.json에, 실제 대화 기록은 ~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl에 저장된다.
Step 2: Workspace Preparation
Agent의 컨텍스트를 구성하는 핵심 단계. ~/.openclaw/workspace/ 디렉토리에서 다음 bootstrap 파일들을 로드한다:
- AGENTS.md: 운영 지침. Claude Code의
CLAUDE.md와 동일한 역할 - SOUL.md: persona와 tone 정의. "당신은 친절한 비서입니다" 같은 성격 설정
- TOOLS.md: 도구 사용법 노트. Agent가 어떤 도구를 어떻게 써야 하는지 가이드
- IDENTITY.md: Agent의 정체성 정보
- USER.md: 사용자 정보 (이름, 선호도 등)
용량이 큰 파일은 자동으로 trim/truncate된다. Multi-Agent 구성에서는 각 Agent가 다른 workspace 디렉토리를 사용할 수 있다 (예: ~/.openclaw/workspace-work/, ~/.openclaw/workspace-personal/).
Step 3: Skills Loading
3단계 우선순위로 Skills를 로드한다:
- Workspace Skills (
workspace/skills/): 프로젝트별 커스텀 스킬. 최우선 적용 - Managed Skills (
~/.openclaw/skills/): 전역 설치된 스킬 - Bundled Skills: OpenClaw에 내장된 기본 스킬
각 스킬은 SKILL.md 파일로 정의되며, Claude Code의 Skills 구조(.claude/skills/<name>/SKILL.md)와 거의 동일하다.
Step 4: Model Configuration
resolveConfiguredModelRef() 함수로 AI 모델을 결정한다:
- Primary model: 설정 파일에서 지정한 기본 모델
- Fallback models: Primary 실패 시 순차적으로 시도할 대체 모델
- Auth profile selection: 사용자/계정별로 다른 API 키를 사용 가능
Session override로 특정 세션에서만 다른 모델을 사용하는 것도 가능하다.
Step 5: Pi Agent Runtime
실제 Agent를 실행하는 단계. 두 가지 실행 모드가 있다:
runEmbeddedPiAgent(): pi-mono를 프로세스 내에서 직접 실행 (기본 모드)runCliAgent(): CLI를 통해 별도 프로세스로 실행
RPC 모드로 동작하며, Tool streaming이 활성화되어 있으면 Tool 호출과 응답이 실시간으로 스트리밍된다. 세션 컨텍스트(이전 대화 히스토리)가 이 시점에 주입된다.
Step 6: Tool Execution Loop
Agent가 Tool(함수 호출)을 실행하는 루프다. 각 Tool 호출 후 Queue check가 발생한다:
- Queue에 새 메시지가 있고 mode가
steer이면: 남은 Tool 실행을 건너뛰고 새 메시지를 주입 - Queue가 비어있거나 mode가
steer가 아니면: 다음 Tool로 계속 진행 - 모든 Tool이 끝나면 Model API를 다시 호출
이 Queue check 메커니즘이 "대화 중 실시간 조향(steering)"을 가능하게 한다. 사용자가 Agent의 응답을 기다리는 중에 "아, 그리고 내일 날씨도 알려줘"라고 추가 메시지를 보내면, 현재 실행 중인 Agent가 이를 즉시 반영할 수 있다.
Step 7: Response Assembly
응답을 조립하는 단계:
- Block streaming (선택적): 완성된 블록(텍스트 문단, 코드 블록 등) 단위로 클라이언트에 실시간 전송
- Coalescing & chunking: 응답을 800-1200자 단위로 분할. 메시징 플랫폼의 글자 수 제한에 대응
- 채널별 포맷팅: 타겟 채널(WhatsApp, Discord 등)에 맞게 마크다운, 이모지, 첨부파일 형식을 변환
Step 8: Session Update & Delivery
마지막 단계:
- 세션 transcript를 JSONL 파일에 저장 (
.jsonl한 줄 = 한 turn) sessions.json의 세션 메타데이터 업데이트 (마지막 활동 시간, 메시지 수 등)sendPolicy에 따라 응답 전달 (immediate/batch/silent)
다중 에이전트 라우팅 시스템
OpenClaw의 Multi-Agent 지원은 단순히 "여러 Agent를 돌릴 수 있다"가 아니다. 7단계 우선순위 매칭으로 메시지가 들어올 때 자동으로 적절한 Agent에게 라우팅된다.
7단계 우선순위 테이블
| 우선순위 | 매칭 대상 | 설명 | 실제 용례 |
|---|---|---|---|
| 1 | binding.peer | 특정 사용자/그룹과 정확히 매칭 | "이 Slack 채널의 메시지는 업무 Agent에게" |
| 2 | binding.peer.parent | 스레드의 부모 메시지 매칭 (상속) | "이 스레드에서 시작된 대화는 같은 Agent가 계속" |
| 3 | binding.guild | Discord 서버(guild) 매칭 | "이 Discord 서버의 모든 메시지는 게임 Agent에게" |
| 4 | binding.team | Slack/Teams 팀 매칭 | "회사 Slack 팀의 메시지는 업무 Agent에게" |
| 5 | binding.account | 특정 계정 매칭 | "개인 WhatsApp 계정은 개인 Agent에게" |
| 6 | binding.channel | 채널 와일드카드 매칭 | "모든 Telegram 메시지는 이 Agent에게" |
| 7 | default | 기본 Agent로 폴백 | 어떤 규칙에도 매칭되지 않으면 기본 Agent |
이 우선순위는 가장 구체적인 것부터 가장 일반적인 것 순서다. Peer(특정 사용자/그룹) 매칭이 가장 높고, Default가 가장 낮다.
Binding 설정 예시
agents:
list:
- id: "default"
default: true
- id: "work-assistant"
- id: "personal-bot"
routing:
bindings:
- agentId: "work-assistant"
match:
channel: "slack"
peer:
kind: "group"
id: "C123WORK"
- agentId: "personal-bot"
match:
channel: "whatsapp"
accountId: "personal"
이 설정의 의미:
- Slack의
C123WORK그룹 채널에서 온 메시지 →work-assistantAgent가 처리 - WhatsApp
personal계정의 메시지 →personal-botAgent가 처리 - 나머지 모든 메시지 →
defaultAgent가 처리
┌─────────────────────────────────────────────────────────────────┐
│ Routing Resolution (resolveAgentRoute) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Priority Order: │
│ 1. binding.peer (exact peer match) │
│ 2. binding.peer.parent (thread parent inheritance) │
│ 3. binding.guild (Discord guild) │
│ 4. binding.team (Teams/Slack team) │
│ 5. binding.account (specific account) │
│ 6. binding.channel (channel wildcard) │
│ 7. default (fallback to default agent) │
│ │
└───────────────────┬─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Session Key Generation │
│ │
│ Format: agent=<agentId>:channel=<channel>:account=<accountId> │
│ :peer=<kind>:<peerId> │
│ │
│ DM Scope modes: │
│ • main: collapse all DMs to main session │
│ • per-peer: separate session per peer │
│ • per-channel-peer: per channel+peer │
│ • per-account-channel-peer: full isolation │
└─────────────────────────────────────────────────────────────────┘
Session Key 형식
Agent가 결정되면 Session Key가 생성된다. 형식은:
agent=<agentId>:channel=<channel>:account=<accountId>:peer=<kind>:<peerId>
예시: agent=work-assistant:channel=slack:account=corp:peer=group:C123WORK
이 키가 세션을 고유하게 식별한다. 같은 Agent라도 채널, 계정, 상대방이 다르면 다른 세션이 된다.
DM Scope 모드
DM(Direct Message)의 세션 격리 수준을 4가지로 설정할 수 있다:
| 모드 | 동작 | 적합한 경우 |
|---|---|---|
main | 모든 DM을 하나의 세션으로 병합 | 모든 대화 컨텍스트를 공유하고 싶을 때 |
per-peer | 상대방별 별도 세션 | 상대방마다 다른 대화 맥락이 필요할 때 |
per-channel-peer | 채널+상대방 조합 | 같은 사람이라도 WhatsApp과 Telegram에서 다른 맥락일 때 |
per-account-channel-peer | 완전 격리 | 최대한 세밀한 세션 분리가 필요할 때 |
Multi-Agent 활용 사례
이 라우팅 시스템 덕분에 실용적인 시나리오들이 가능하다:
- 업무/개인 분리: Slack은 업무용 Agent (격식체, 업무 도구), WhatsApp은 개인용 Agent (반말, 일상 도구)
- 채널별 Agent: Discord의 게임 서버는 게임 봇 Agent, 스터디 서버는 학습 보조 Agent
- 그룹별 Agent: 특정 Slack 팀에만 전용 Agent를 배치
- VIP 대응: 특정 연락처(peer)에 대해 더 강력한 모델을 사용하는 Agent를 배치
보안 및 페어링
Local-first 시스템의 보안은 "누가 내 Gateway에 접근할 수 있는가"로 귀결된다. OpenClaw는 3단계 보안 체계를 사용한다.
3단계 보안 체계
- Gateway Auth: Token 기반 인증. Gateway 시작 시 생성된 토큰으로 접근을 제한
- Device Pairing: 디바이스별 승인. Local은 자동, Remote는 수동 페어링
- DM Policy: 메시징 플랫폼에서 들어오는 메시지의 발신자 검증. 기본값
pairing(미인증 발신자 차단)
페어링 흐름 상세
새 디바이스 연결 시도
│
▼
┌──────────────────────────────────────────┐
│ 1. WebSocket Handshake │
│ • connect frame 전송 │
│ • Device identity 포함 │
│ • Role (operator/node) 명시 │
└────────────┬─────────────────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ 2. Authentication Check │
│ • Gateway auth token 확인 │
│ • Tailscale identity (if enabled) │
└────────────┬─────────────────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ 3. Pairing Status Check │
│ Is device paired? │
└────────────┬─────────────────────────────┘
│
┌────┴────┐
│ │
Yes No
│ │
│ ▼
│ ┌──────────────────────────────┐
│ │ Local connection? │
│ │ (loopback/tailnet) │
│ └────┬──────────────────┬──────┘
│ │ │
│ Yes No
│ │ │
│ ▼ ▼
│ ┌─────────────┐ ┌──────────────────┐
│ │ Auto-approve│ │ Require pairing │
│ │ (local UX) │ │ • Issue code │
│ └─────────────┘ │ • Wait approval │
│ │ • Signature check │
│ └──────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ 4. Device Token Issued │
│ • Store in pairing DB │
│ • Future connects skip pairing │
└────────────┬─────────────────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ 5. Connection Established │
│ • Send hello-ok payload │
│ • Start event subscriptions │
└──────────────────────────────────────────┘
핵심 설계 포인트:
- Local connection (loopback/tailnet)은 자동 승인. 같은 머신이나 Tailscale 네트워크에서의 연결은 이미 물리적/논리적 보안이 확보된 것으로 간주
- Remote connection은 명시적 페어링 필요. 페어링 코드를 발급하고, 사용자가 승인하고, 서명을 검증하는 3단계를 거침
- 한 번 페어링되면 Device Token이 발급되어 이후 연결에서는 페어링을 건너뜀.
~/.openclaw/pairing/에 저장
Allowlist 시스템
메시징 플랫폼에서 들어오는 메시지에 대한 추가 보안 레이어:
- 채널별로 허용된 연락처 목록을 관리
- Group allowlist 지원 (특정 그룹의 모든 멤버 허용)
- 와일드카드(
*) 옵션으로 전체 허용 가능 (주의해서 사용)
Tailscale 통합
Tailscale은 이 시스템에서 두 가지 역할을 한다:
- 원격 접속: VPN 없이도 집 맥북의 Gateway에 접근 가능 (Serve/Funnel 자동 구성)
- 인증: Tailscale의 identity를 Gateway 인증에 활용. Tailscale에 인증된 디바이스는 Gateway에서도 신뢰
데이터 저장 구조
OpenClaw는 DB가 없다. 전부 로컬 파일시스템의 JSON/JSONL 파일이다. Local-first 철학에 충실한 선택.
~/.openclaw/
├── openclaw.json # Main configuration
├── credentials/ # Channel credentials
│ ├── whatsapp-auth/ # Baileys session
│ ├── telegram/
│ └── ...
├── agents/
│ ├── default/ # Agent-specific data
│ │ └── sessions/ # Session transcripts
│ │ ├── main.jsonl
│ │ ├── peer-<id>.jsonl
│ │ └── ...
│ ├── work-assistant/
│ └── ...
├── workspace/ # Agent workspace
│ ├── AGENTS.md
│ ├── SOUL.md
│ ├── TOOLS.md
│ ├── IDENTITY.md
│ ├── USER.md
│ └── skills/ # Workspace skills
│ ├── skill-name/
│ │ └── SKILL.md
│ └── ...
├── skills/ # Managed skills
│ └── ...
├── pairing/ # Device pairing store
├── sessions.json # Session metadata store
└── logs/ # Gateway logs
각 디렉토리의 역할:
| 경로 | 형식 | 역할 |
|---|---|---|
openclaw.json | JSON | 메인 설정 파일. Agent 목록, routing bindings, 채널 설정, 모델 설정 등 |
credentials/ | 디렉토리 | 각 채널의 인증 정보. WhatsApp의 Baileys 세션, Telegram 봇 토큰 등 |
agents/<agentId>/sessions/ | JSONL | 대화 기록. 한 줄 = 한 turn. main.jsonl은 기본 세션, peer-<id>.jsonl은 상대방별 세션 |
workspace/ | Markdown | Agent의 컨텍스트 파일. persona, 지침, 도구 노트, 스킬 정의 |
skills/ | Markdown | 전역 설치된 Managed skills |
pairing/ | DB/JSON | 디바이스 페어링 정보. 한 번 승인된 디바이스는 여기에 기록 |
sessions.json | JSON | 세션 메타데이터 (마지막 활동 시간, 메시지 수, 오버라이드 등) |
logs/ | 텍스트 | Gateway 실행 로그 |
장점: 설치가 단순하고, 백업이 쉽고 (~/.openclaw/ 통째로 복사), 디버깅이 직관적 (파일을 직접 열어보면 됨).
단점: 대화량이 많아지면 JSONL 파일이 비대해져서 성능 이슈가 생길 수 있다. 인덱싱이 없기 때문에 세션 검색도 느려질 수 있다.
확장 포인트 4가지
OpenClaw는 4가지 축으로 확장할 수 있다.
1. Extension Channels
새로운 메시징 플랫폼을 추가하는 확장점. extensions/ 디렉토리에 채널 어댑터를 구현하면 된다.
적용 예시: MS Teams, Matrix, Zalo 등 아직 내장되지 않은 플랫폼을 추가. 채널 어댑터는 메시지 수신/발신, 인증, 포맷 변환을 구현해야 한다.
2. Skills
Agent의 능력을 확장하는 커스텀 도구/기능. 3가지 레벨이 있다:
- Bundled Skills: OpenClaw에 내장. 기본 제공
- Managed Skills (
~/.openclaw/skills/): npm 등을 통해 전역 설치 - Workspace Skills (
workspace/skills/): 프로젝트별 커스텀.SKILL.md파일로 정의
각 스킬은 SKILL.md 파일 하나로 정의되며, Agent가 어떤 도구를 어떻게 사용해야 하는지 지시한다.
3. Plugins
Gateway 자체의 기능을 확장하는 플러그인 시스템. plugins/ 디렉토리에 구현.
Gateway의 미들웨어 파이프라인에 커스텀 로직을 삽입할 수 있다. 예를 들어 메시지 필터링, 로깅 강화, 외부 시스템 연동 등.
4. Nodes
iOS/Android/macOS 앱을 통한 디바이스별 명령 확장. Node는 Gateway에 WebSocket으로 연결되어 디바이스의 고유 기능을 Agent에게 노출한다:
- Canvas: Agent가 제어하는 시각적 워크스페이스 (Canvas Host Server, 포트 18793)
- Camera: 디바이스 카메라 접근
- Screen Recording: 화면 녹화
- 기타 디바이스 고유 기능: 알림, 위치, 연락처 등
Node Registry (node-registry.ts)가 연결된 노드를 관리하고, Agent가 필요할 때 해당 노드의 기능을 호출할 수 있게 한다.
주요 컴포넌트 역할 테이블
전체 시스템의 9개 핵심 컴포넌트를 한눈에 정리한다.
| 컴포넌트 | 소스 파일 | 역할 |
|---|---|---|
| Gateway Server | server-ws-runtime.ts | 모든 연결의 중앙 허브. WebSocket 프로토콜 제공. Connection handshake, 이벤트 broadcasting |
| Channel Manager | server-channels.ts | 메시징 플랫폼 연결 관리. WhatsApp(Baileys), Telegram(grammY), Discord(discord.js), Slack(Bolt), Signal(signal-cli) 통합 |
| Routing Engine | resolve-route.ts | 메시지를 적절한 Agent/Session으로 라우팅. 7단계 우선순위 매칭, Session key 생성 |
| Agent Event Handler | server-chat.ts | Agent 실행 중 메시지 흐름 제어. Queue management (steer/followup/collect), block streaming 조율 |
| Pi Agent Runtime | (pi-mono embedded) | 실제 AI 대화 처리. Workspace 로드, Skills 실행, Model API 호출 |
| Session Store | sessions.json + JSONL files | 대화 히스토리 및 메타데이터 관리. JSONL로 transcript, JSON으로 메타데이터 |
| Workspace | ~/.openclaw/workspace/ | Agent의 동작 지침(AGENTS.md), persona(SOUL.md), 도구 노트(TOOLS.md), 스킬 저장 |
| Node Registry | node-registry.ts | iOS/Android/macOS 디바이스 노드 등록/관리. Agent가 디바이스 기능 호출 가능 |
| Canvas Host | canvas-host/ (포트 18793) | Agent가 제어하는 시각적 워크스페이스. 별도 HTTP 서버로 동작 |
| Browser Control | browser/ | Chrome/Chromium을 통한 웹 자동화. Agent가 웹 브라우저를 직접 조작 |
특이사항 및 제약
Streaming 지원
OpenClaw는 두 가지 스트리밍 메커니즘을 지원한다:
- Tool Streaming: Tool 호출 결과를 실시간으로 전달. Agent가 웹 검색 Tool을 실행하면 결과가 나오는 대로 사용자에게 전송
- Block Streaming: AI 응답을 완성된 블록(문단, 코드 블록 등) 단위로 전송. 전체 응답이 완성될 때까지 기다리지 않음
채널마다 스트리밍 지원 여부가 다르다. WebChat과 Control UI는 실시간 스트리밍이 가능하지만, WhatsApp이나 Telegram은 메시지 수정 API의 제약 때문에 블록 단위 전송이 더 적합하다.
Queue Modes 상세
일반 챗봇에서는 AI가 답변하는 동안 사용자가 보낸 추가 메시지를 무시하거나 별도 대화로 처리한다. OpenClaw는 이걸 Queue Mode로 해결한다:
| 모드 | 동작 | 사용 사례 |
|---|---|---|
steer | Tool 실행 중 새 메시지가 오면 즉시 주입. 남은 Tool 실행을 건너뛰고 새 지시 반영 | "아 잠깐, 그거 말고 이것도 해줘" — 실시간 방향 전환 |
followup | 현재 Agent 턴이 완전히 끝난 후 대기 메시지를 처리 | 차분하게 순차 처리. 대부분의 일반적인 대화에 적합 |
collect | 여러 메시지를 모은 후 일괄 처리 | 사용자가 여러 줄에 걸쳐 지시를 보낼 때. "첫째...", "둘째...", "셋째..." |
steer 모드가 가장 인상적이다. 이건 일반 챗봇에서는 보기 어려운 패턴으로, 사용자가 AI의 작업을 실시간으로 조향할 수 있게 해준다.
단일 Gateway per Host
WhatsApp(Baileys) 세션의 제약 때문에 하나의 호스트에서 하나의 Gateway만 실행할 수 있다. Baileys는 하나의 WhatsApp 세션만 유지할 수 있기 때문이다. 이건 HA(High Availability) 구성을 불가능하게 만드는 구조적 제약이다.
Tailscale 통합
Tailscale이 선택적으로 통합되어 있다:
- Serve/Funnel: 리모트에서 Gateway에 접근할 때 Tailscale의 Serve(내부 네트워크) 또는 Funnel(퍼블릭 인터넷)을 자동 구성
- Identity: Tailscale에 인증된 디바이스의 identity를 Gateway 인증에 활용
VPN을 별도로 설정하지 않아도 집의 맥북에서 돌리는 Gateway에 외출 중에 접속할 수 있다. 영리한 선택이다.
런타임 요구사항
Node.js 22+ 필수. 이전 버전에서는 동작하지 않는다.
천재적인 부분과 한계
천재적인 부분
1. Gateway 단일 제어 플레인
10개 이상의 메시징 채널을 하나의 WebSocket 서버로 통합했다. 채널 추가/제거가 Gateway 재시작 없이 가능하고(Config Reloader), 모든 복잡도가 Gateway 한 곳에 집중되어 있어 디버깅이 상대적으로 쉽다. 각 채널 SDK의 차이를 Channel Manager가 완벽히 추상화한 것도 인상적이다.
2. 7단계 우선순위 라우팅
Multi-Agent 라우팅을 "설정 파일 하나"로 해결한다. binding 규칙만 추가하면 새로운 Agent를 특정 채널/그룹/사용자에 바인딩할 수 있다. 우선순위가 명확해서 충돌이 없고, default fallback이 있어서 매칭 누락도 없다.
3. Queue Mode (steer/followup/collect)
이건 진짜 UX 차별화다. 대화 중 메시지 주입은 일반 챗봇에서는 거의 볼 수 없는 기능이다. 특히 steer 모드는 Agent가 복잡한 작업을 수행하는 중에도 사용자가 실시간으로 방향을 전환할 수 있게 해준다.
4. Workspace 분리 (AGENTS.md + SOUL.md + TOOLS.md)
persona(SOUL.md)와 운영 지침(AGENTS.md)을 분리한 건 깔끔한 설계다. Claude Code에서 CLAUDE.md 하나에 모든 걸 담는 것보다 관리가 편하다. IDENTITY.md와 USER.md까지 분리한 건 역할별 컨텍스트 관리의 모범 사례.
5. Tailscale 통합
원격 접속 문제를 VPN 설정 없이 해결했다. Local-first의 "내 디바이스에서 돌린다" 철학과 "어디서든 접속할 수 있다"는 실용성을 양립시킨 영리한 선택.
한계
1. 단일 Gateway per Host
WhatsApp(Baileys) 세션 제약 때문에 호스트당 하나의 Gateway만 실행 가능하다. HA 구성이 불가능하고, Gateway가 죽으면 모든 채널이 끊긴다. 프로덕션 환경에서는 치명적인 약점.
2. 파일 기반 저장 (No DB)
JSONL 파일로 대화를 저장하는 건 단순하지만, 대규모 사용 시 병목이 된다. 세션이 수백 개가 넘어가면 파일 I/O가 느려지고, 검색/인덱싱이 불가능하다. SQLite라도 사용했으면 나았을 것.
3. WhatsApp 비공식 API(Baileys) 의존
Baileys는 WhatsApp의 공식 API가 아니라 Web API를 리버스 엔지니어링한 라이브러리다. Meta가 프로토콜을 변경하면 언제든 깨질 수 있다. WhatsApp Business API(공식)를 사용하는 옵션이 없는 것도 아쉽다.
4. Node.js 22+ 필수
최신 Node.js 버전을 요구하는 건 설치 장벽이 될 수 있다. 특히 기업 환경에서는 Node.js 버전 관리가 까다로운 경우가 많다.
5. 단일 프로세스 아키텍처
Gateway와 Agent Runtime이 같은 프로세스에서 돌아간다. Agent가 무거운 Tool을 실행하면 Gateway 전체가 느려질 수 있다. 마이크로서비스 분리나 워커 프로세스 분리가 없다.
내 프로젝트에 적용할 패턴
OpenClaw 소스에서 훔쳐올 패턴 5가지:
1. Workspace 분리 패턴
AGENTS.md + SOUL.md + TOOLS.md + IDENTITY.md + USER.md로 Agent 컨텍스트를 분리한 구조. Claude Code 프로젝트에서 CLAUDE.md 하나에 모든 걸 담는 대신, persona/지침/도구노트를 별도 파일로 분리하면 관리가 편해진다.
2. 우선순위 기반 라우팅
7단계 매칭 우선순위는 Multi-Agent 시스템 설계에서 바로 적용 가능하다. "가장 구체적인 규칙 우선" 원칙은 라우팅 충돌을 깔끔하게 해결한다.
3. Queue Mode 패턴
AI가 작업하는 중에 사용자의 추가 지시를 처리하는 steer/followup/collect 패턴. 실시간 대화 시스템에서 UX를 크게 개선할 수 있다. 특히 steer 모드의 "Tool 실행 후 Queue check" 패턴은 어떤 Agent 시스템에서든 적용 가능.
4. Session Key 설계
agent=<id>:channel=<ch>:account=<acc>:peer=<kind>:<id> 형식의 복합 키. 여러 차원의 컨텍스트를 하나의 키로 인코딩하면서도, DM Scope 모드로 격리 수준을 유연하게 조절할 수 있다.
5. Local-first + Tailscale 조합
프라이버시와 접근성의 양립. 데이터는 로컬에 두면서 Tailscale로 원격 접속을 해결하는 패턴은 개인 AI 비서뿐만 아니라 다양한 Local-first 앱에 적용할 수 있다.
Quick Start
# 설치
npm install -g openclaw@latest
# 온보딩 (대화형 설정)
openclaw onboard --install-daemon
# Gateway 실행
openclaw gateway --port 18789 --verbose
# Agent와 대화
openclaw agent --message "Hello!" --to +1234567890소스는 GitHub에서 확인할 수 있다. 문서는 docs.openclaw.ai, 커뮤니티는 Discord에서 만날 수 있다.
# Comments
아직 댓글이 없습니다. 첫 댓글을 남겨보세요.