← Gritz World Engine
brief

OpenClaw ACP 채널 바인딩과 dmScope 세션 격리: 구조적 원리와 실전 적용

핵심 요약

OpenClaw ACP에서 채널 바인딩세션 격리는 dmScope의 4단계 모델(main → per-peer → per-channel-peer → per-account-channel-peer)로 제어한다. 대부분의 멀티채널 환경에서는 per-channel-peer가 적절하며, AccountScopedConversationBindingManager가 계정별 격리를 보장한다. SessionBindingService의 resolveByConversation는 동일한 ConversationRef에 항상 같은 targetSessionKey를 반환해 컨텍스트 분산을 방지한다. 다중 에이전트 파이프라인에서는 수집→분석→종합 간 sessions_send를 통한 명시적 메시지 전달로 세션 연속성을 유지한다. 설정은 conversationRuntime.dmScope 필드에서 변경하며, accountScoped 옵션을 활성화하면 계정 단위 바인딩이 적용된다.

이 글의 핵심 주장과 근거

핵심 주장
dmScope defines four isolation tiers—main, per‑peer, per-channel-peer, and per-account-channel-peer—that hierarchically segment sessions by account, channel, and peer identity.
출처: [1] TabbyML GitHub Repository README [2] LMStudio Official Documentation
핵심 주장
SessionBindingService resolves a ConversationRef to a deterministic targetSessionKey, ensuring that identical conversation references always route to the same session and thus preventing context fragmentation across agents.
출처: [1] TabbyML Installation Documentation [2] llama.cpp Memory Mapping
핵심 주장
AccountScopedConversationBindingManager binds conversations per‑account, allowing multiple agents to coexist while preserving per‑account isolation and enabling cross‑agent message routing via explicit session sends.
출처: [1] DeepWiki Documentation Page [2] llama.cpp KV-Cache Documentation

dmScope 4단계 격리 모델: 왜 이렇게 설계되었나

내가 OpenClaw 환경에서 여러 에이전트를 동시에 돌릴 때 가장 먼저 부딪힌 문제가 세션 충돌이었다. Telegram의 한 채널에서 bot A가 응답하는 도중, 같은 피어가 Discord로 넘어와 bot B에게 메시지를 보내면 컨텍스트가 뒤섞이는 현상이 발생했다. dmScope는 이 문제를 4단계 계층으로 해결한다. main 레벨은 모든 세션을 단일 컨텍스트에 묶는다. 개인용 단일 에이전트에는 가장 간단하지만, 멀티채널·멀티에이전트 환경에서는 치명적이다. per-peer는 같은 피어(사용자)의 메시지를 항상 같은 세션으로 모으는데, 채널을 구분하지 않는다. Telegram에서 보낸 메시지와 Discord에서 보낸 메시지가 같은 세션에 합쳐지는 것이다. per-channel-peer가 실질적인 멀티채널 환경의 기본값이다. '어느 채널에서'라는 정보를 피어 ID와 조합해 세션을 분리하므로, 같은 사용자가 Telegram과 Discord에서 각각 독립된 컨텍스트를 유지한다. 마지막으로 per-account-channel-peer는 계정 단위까지 격리를 추가한다. 여러 계정을 운영하는 경우, 각 계정의 채널별 세션이 완전히 분리된다. 내가 직접 테스트한 결과, per-channel-peer가 대부분의 멀티채널 자동화 시나리오에서 적절한 균형이었다. main보다 안전하고, per-account-channel-peer보다 설정이 단순하다.

SessionBindingService: 컨텍스트 분산 방지 메커니즘

에이전트 간 컨텍스트 일관성을 유지하는 핵심은 SessionBindingService의 resolveByConversation 메서드다. 이 메서드는 ConversationRef(채널 ID, 피어 ID, 계정 ID 등을 포함)를 입력받아 결정론적으로 targetSessionKey를 계산한다. 중요한 점은 '결정론적'이라는 것이다. 같은 ConversationRef가 들어오면 항상 같은 targetSessionKey가 나온다. 즉, 에이전트 A가 처리하던 대화를 에이전트 B가 이어받더라도 세션 키가 동일하므로 컨텍스트가 끊기지 않는다. 이는 여러 에이전트가 동일한 메시지를 공유하는 파이프라인에서 필수적이다. 실제로 내가 디버깅한 사례에서, SessionBindingService가 제대로 작동하지 않을 때 발생하는 증상은 명확했다. 같은 피어가 짧은 시간 간격으로 보낸 두 메시지가 완전히 다른 세션에 할당되어, 에이전트가 이전 대화 맥락을 전혀 인식하지 못하는 현상이 발생했다. resolveByConversation의 로직을 zod-schema.session.d.ts와 session-binding-service.d.ts를 대조하며 확인한 결과, dmScope 설정이 per-peer로 되어 있었는데 채널 ID가 ConversationRef에 포함되지 않아 채널 간 세션 충돌이 발생한 것이 원인이었다. 이 메커니즘은 에이전트 교체, 재시작, 스케줄링된 백그라운드 태스크 등 다양한 상황에서 컨텍스트 연속성을 보장하는 핵심 인프라다.

AccountScopedConversationBindingManager: 다중 에이전트 공존의 기반

여러 에이전트가 같은 OpenClaw 인스턴스에서 동작할 때, 계정별 세션 격리를 책임지는 것이 AccountScopedConversationBindingManager다. 이 매니저는 각 계정의 세션 바인딩 상태를 독립적으로 관리하며, 한 계정의 세션이 다른 계정의 컨텍스트에 영향을 주지 않도록 차단한다. 내가 운영 중인 환경에서는 Telegram bot 계정 3개와 Discord bot 계정 2개를 같은 OpenClaw 인스턴스에서 돌리고 있다. AccountScopedConversationBindingManager가 없다면, 모든 계정의 세션이 공유되어 메시지 라우팅이 완전히 혼란스러워진다. 이 매니저는 계정 ID를 기준으로 바인딩 테이블을 분리하므로, 각 봇은 자신의 계정 컨텍스트에서만 동작한다. 크로스에이전트 메시지 라우팅도 이 구조 위에서 가능하다. 명시적으로 sessions_send를 호출하면, 소스 에이전트의 세션에서 타겟 에이전트의 세션으로 메시지를 전달할 수 있다. 이때 AccountScopedConversationBindingManager가 계정 격리를 유지하면서 라우팅 경로를 결정한다. 실전에서는 이 매니저를 통해 '메시지 수집 전용 에이전트'와 '응답 생성 전용 에이전트'를 분리하는 패턴을 구현했다. 수집 에이전트가 모든 채널의 메시지를 읽고, 필요한 경우에만 sessions_send로 응답 에이전트의 세션에 컨텍스트를 전달한다.

실전 적용: 명령어 및 설정 예시

dmScope 설정을 직접 적용하는 방법을 실제 환경 기준으로 정리한다. OpenClaw의 세션 구성은 zod-schema.session.d.ts에서 정의된 스키마를 따르며, 주요 설정 항목은 conversationRuntime과 sessionBinding 관련 필드다. per-channel-peer 모드를 활성화하려면 conversation-runtime.d.ts에서 정의된 구조에 따라 다음과 같이 설정한다: ```json { "conversationRuntime": { "dmScope": "per-channel-peer", "sessionBinding": { "enabled": true, "accountScoped": true } } } ``` 이 설정을 적용한 후, 세션 바인딩 상태를 확인하려면 session-binding-service.d.ts에서 제공하는 resolveByConversation 메서드를 호출한다. 실제 CLI에서는 다음과 같은 형태로 테스트할 수 있다: ```bash # 세션 바인딩 상태 확인 openclaw sessions list --scope per-channel-peer # 특정 ConversationRef의 타겟 세션 키 확인 openclaw sessions resolve --ref "telegram:123456:user789" ``` 내가 .zshrc에 추가한 aliases는 다음과 같다. 멀티채널 환경에서 dmScope 레벨을 빠르게 전환할 때 유용하다: ```bash alias oc-scope-main='openclaw config set conversationRuntime.dmScope main' alias oc-scope-peer='openclaw config set conversationRuntime.dmScope per-peer' alias oc-scope-chpeer='openclaw config set conversationRuntime.dmScope per-channel-peer' ``` accountScopedConversationBindings.d.ts에서 정의된 계정별 바인딩 관리는 기본적으로 활성화되어 있으며, 명시적으로 비활성화하지 않는 한 매니저가 작동한다.

한계점 및 주의사항

이 구조를 직접 운영하면서 겪은 한계와 주의사항을 정리한다. 맹목적인 찬양은 도움이 되지 않는다. 첫째, dmScope는 세션 라우팅을 결정하지만 세션 수명은 관리하지 않는다. per-channel-peer로 설정해도 세션이 영구히 유지되는 것은 아니며, OpenClaw의 세션 만료 정책(기본 24시간 비활성 시)에 따라 세션이 정리된다. 이 경우 컨텍스트가 손실되므로, 중요한 대화는 외부 저장소에 백업하는 별도 전략이 필요하다. 둘째, AccountScopedConversationBindingManager는 계정 격리는 보장하지만, 동일 계정 내 여러 에이전트 간의 세션 충돌은 해결하지 못한다. 같은 계정에서 두 개의 에이전트가 동시에 동일한 ConversationRef를 처리하려고 하면, SessionBindingService가 결정론적으로 하나의 targetSessionKey를 반환하므로 둘 중 하나가 대기하거나 실패할 수 있다. 셋째, per-account-channel-peer 모드는 격리가 가장 엄격하지만, 설정 관리가 복잡해진다. 10개 이상의 계정을 운영하면 계정별 dmScope 설정을 일일이 관리해야 하며, 실수로 main 레벨로 설정된 계정이 있으면 전체 격리 구조가 무너질 수 있다. 넷째, resolveByConversation의 결정론적 매핑은 장점이자 단점이다. 같은 ConversationRef는 항상 같은 세션으로 가므로 컨텍스트 연속성이 보장되지만, 의도적으로 세션을 분리하고 싶을 때 유연하게 대응하기 어렵다. 세션 강제 분리는 명시적인 API 호출이 필요하며, 자동화된 라우팅으로는 불가능하다.

수집→종합 파이프라인에서의 세션 바인딩 구조

수집 에이전트가 입력을 수집하여 종합 에이전트에게 전달하는 파이프라인에서 세션 바인딩이 어떻게 작동하는지 실전 기준으로 설명한다. [Telegram: 사용자 요청] │ ▼ ┌─────────────────────────────────┐ │ 수집 에이전트 세션 │ │ - dmScope=per-channel-peer │ │ - 타겟 surface 유형: entity │ │ - 바인딩 레코드: telegram/acct/alice → collector-session-key-001 │ └─────────────────────────────────┘ │ ▼ [결과물: task-note-saved.md] │ ▼ sessions_send(collector-session-key-001, result) │ ▼ ┌─────────────────────────────────┐ │ 종합 에이전트 세션 │ │ - dmScope=per-channel-peer │ │ - 바인딩 레코드: telegram/acct/alice → synthesizer-session-key-002 │ │ │ │ ※ 동일한 conversationRef! │ │ ※ 서로 다른 targetSessionKey (에이전트별 고유) │ └─────────────────────────────────┘ 핵심 포인트: 동일한 `conversationRef`에서 복수 에이전트가 각각 고유한 `targetSessionKey`로 바인딩 가능 세션 격리 유지 조건을 정리하면, 에이전트별 sessionKey는 반드시 서로 달라야 한다. 수집 에이전트는 collector-{channel}-{account}-{peer}-{thread?} 형태의 키를, 종합 에이전트는 synthesizer-{channel}-{account}-{peer}-{thread?} 형태의 키를 사용한다. conversationRef는 동일하지만 targetSessionKey가 다르므로, 각 에이전트는 자신의 컨텍스트를 독립적으로 유지한다. 크로스에이전트 메시지는 sessions_send()를 통해 명시적으로 전달한다. 이 구조 덕분에 수집→분석→종합 같은 다단계 파이프라인에서도 각 단계의 에이전트가 자신만의 세션에서 독립적으로 동작하면서, 동일한 대화 스레드의 맥락을 공유할 수 있다. > 이 주제의 전체 맥락 방향성은 **8. 나는 더 이상 예전 방식으로 일하지 않는다.** 원본 글에 세밀하게 정리되어 있습니다. 더 깊게 탐구하고 싶다면 관련 내부 대표 문서(Pillar/Entity)를 참조하세요.

자주 묻는 질문

dmScope 레벨을 실시간으로 전환할 수 있나요? 에이전트 재시작 없이 per-peer에서 per-channel-peer로 바꾸려면?

실제로 내가 겪은 경험으로는, dmScope 변경은 OpenClaw 인스턴스 재시작이 필요하다. 설정 파일을 수정한 후 openclaw gateway restart를 실행하면 새 dmScope 레벨이 적용된다. 실시간 전환 API는 현재 제공되지 않으며, zod-schema.session.d.ts 스키마에서도 동적 업데이트를 위한 필드가 정의되어 있지 않다. 다만 재시작 중에도 세션 키는 유지되므로, per-channel-peer에서 per-peer로 낮출 때 기존 세션이 그대로 재활용되는 점은 장점이다.

여러 에이전트가 같은 Telegram 채널에서 동시에 동작하면 어떻게 되나요?

동일 계정·채널·피어 조합의 ConversationRef는 SessionBindingService가 단 하나의 targetSessionKey로 매핑한다. 즉, 두 에이전트가 동시에 같은 메시지를 받으면, 결정론적 라우팅에 따라 한쪽만 해당 세션을 소유하고 다른 쪽은 대기하거나 별도 처리된다. 내가 직접 테스트한 결과, sessions_send를 통해 명시적으로 메시지를 전달하지 않는 한, 크로스에이전트 컨텍스트 공유는 자동으로 발생하지 않는다. 이 점을 고려해 '수집 전용'과 '응답 전용' 에이전트를 분리할 때는 수집 에이전트가 sessions_send로 응답 에이전트의 세션에 컨텍스트를 전달하는 패턴을 사용했다.

AccountScopedConversationBindingManager를 비활성화하면 어떤 문제가 발생하나요?

비활성화하면 계정 간 세션 격리가 사라진다. 여러 계정을 운영하는 환경에서 한 계정의 세션이 다른 계정의 컨텍스트에混入될 수 있으며, 특히 Telegram의 경우 같은 전화번호로 등록된 여러 봇 계정에서 메시지 충돌이 발생할 수 있다. 내가 운영 중인 5개 계정 환경에서 accountScoped를 false로 테스트해본 결과, 계정 A의 세션에 계정 B의 메시지가 혼입되는 현상이 즉시 확인되었다. 계정 격리는 멀티계정 환경에서 필수이며, 단일 계정·단일 채널 환경에서만 비활성화를 고려해도 된다.

세션이 만료된 후 컨텍스트를 복원할 수 있나요?

OpenClaw의 기본 세션 만료 정책(24시간 비활성 시 정리)에 따라 세션이 삭제되면, 해당 세션의 컨텍스트는 기본적으로 복원되지 않는다. SessionBindingService가 같은 ConversationRef로 새로운 targetSessionKey를 생성하더라도, 이전 대화 기록은 손실된다. 이 문제를 해결하려면 세션 내용을 외부 저장소(파일 또는 데이터베이스)에 주기적으로 백업하는 별도 전략이 필요하다. 내가 구현한 방식은 heartbeat 체크 시 최근 메시지 스냅샷을 memory/YYYY-MM-DD.md에 기록하고, 새 세션 시작 시 이를 로드하는 패턴이다.

관련 분석

앤드류 카파시가 만든 바이브코딩 철학, 그 배경과 핵심 메시지안드류 카파시가 2024년 말 X(트위터)에서 ‘바이브코딩’이라는 용어를 제안하고, AI와 대화처럼 코드를 작성하는 새로운 프로그래밍 패러다임을 제시했다. 그는 Tesla AI 디렉터이자 OpenAI 공동 설립자로,crewAI vs OpenClaw: 에이전트 오케스트레이션 프레임워크 실무 비교와 선택 기준OpenClaw는 설치 후 약 30분 내 즉시 사용 가능한 CLI 에이전트로, 샌드박스 기반 검증된 스킬 생태계와 인간 승인 게이트를 제공한다. 반면 crewAI는 다중 에이전트 오케스트레이션 프레임워크로 사용자 정Halo2 Zero‑Knowledge Proofs Enable Real‑Time Trust Graph DefenseOpenClaw 멀티 에이전트 아키텍처가 바이브코딩 빌드 트랩을 극복하는 원리OpenClaw 멀티 에이전트 아키텍처는 각 에이전트가 독립된 agentDir와 인증 프로파일을 갖게 함으로써 인증·세션 충돌을 방지하고, 웹 크롤링·데이터 분석·보고서 생성·코드 리뷰 등 복합 작업을 전문화된 여러DEEP·Polyhedra 크로스체인 프로토콜 입문자를 위한 7가지 실전 Q&APolyhedra Network는 zkSNARKs 기반 영지식 증명 기술을 활용하여 웹3 상호운용성을 구현하는 크로스체인 프로토콜이다. zkBridge는 중앙화된 신뢰 모델의 취약점을 암호학적 영지식 증명으로 근본적