콘텐츠로 이동

OpenClaw 세션 관리 및 압축 가이드: 데이터 최적화 전략

OpenClaw는 세션 상태를 소유하는 단일 Gateway 프로세스를 중심으로 설계되었어요.

  • UI(macOS 앱, 웹 Control UI, TUI)는 세션 목록이나 토큰 수를 확인할 때 Gateway에 쿼리해야 해요.
  • 원격 모드에서는 세션 파일이 원격 호스트에 저장되기 때문에, “로컬 Mac 파일”을 확인해도 Gateway가 실제로 사용하는 데이터를 반영하지 못해요.

OpenClaw는 세션을 두 가지 레이어로 나누어 저장해요.

  1. Session store (sessions.json)

    • sessionKey -> SessionEntry 형태의 Key/value 맵이에요.
    • 크기가 작고 수정이 가능하며, 직접 편집하거나 항목을 삭제해도 안전해요.
    • 현재 세션 ID, 마지막 활동, 토글, 토큰 카운터 같은 세션 메타데이터를 추적해요.
  2. Transcript (<sessionId>.jsonl)

    • 각 항목이 id와 parentId를 가지는 트리 구조의 추가 전용(Append-only) Transcript 파일이에요.
    • 실제 대화 내용, tool calls, Compaction 요약본을 저장해요.
    • 나중에 모델의 컨텍스트를 재구성할 때 사용해요.

Gateway 호스트의 에이전트별 저장 경로는 다음과 같아요.

  • Store: ~/.openclaw/agents/<agentId>/sessions/sessions.json
  • Transcripts: ~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl
    • 텔레그램 토픽 세션: .../<sessionId>-topic-<threadId>.jsonl

OpenClaw는 src/config/sessions.ts를 통해 이 경로들을 확인해요.

저장소 유지 관리 및 디스크 제어

섹션 제목: “저장소 유지 관리 및 디스크 제어”

세션 영속성 관리를 위해 sessions.json과 Transcript 파일에 대한 자동 유지 관리 제어 기능(session.maintenance)을 제공해요.

  • mode: warn(기본값) 또는 enforce
  • pruneAfter: 오래된 항목의 삭제 기준 시간 (기본값 30d)
  • maxEntries: sessions.json에 저장될 최대 항목 수 제한 (기본값 500)
  • rotateBytes: sessions.json 파일이 너무 커지면 로테이션 실행 (기본값 10mb)
  • resetArchiveRetention: *.reset.<timestamp> Transcript 아카이브 파일의 보관 기간 (기본값은 pruneAfter와 동일하며, false로 설정하면 정리를 비활성화해요)
  • maxDiskBytes: 선택 사항으로, 세션 디렉토리의 전체 용량 예산을 설정해요.
  • highWaterBytes: 정리 후 목표로 하는 용량 (기본값은 maxDiskBytes의 80%)

mode: "enforce"일 때 디스크 용량 정리는 다음 순서로 진행돼요.

  1. 가장 오래된 아카이브 파일이나 고립된 Transcript 파일을 먼저 제거해요.
  2. 그래도 목표 용량보다 크다면, 가장 오래된 세션 항목과 해당 Transcript 파일을 삭제해요.
  3. 사용량이 highWaterBytes 이하가 될 때까지 이 과정을 반복해요.

mode: "warn" 모드에서는 삭제될 가능성이 있는 항목을 보고만 하고, 실제 저장소나 파일을 수정하지는 않아요.

필요할 때 직접 유지 관리를 실행할 수도 있어요.

Terminal window
openclaw sessions cleanup --dry-run
openclaw sessions cleanup --enforce

격리된 Cron 실행도 세션 항목과 기록(transcripts)을 생성해요. 이를 관리하기 위한 전용 보관 설정이 마련되어 있어요.

  • cron.sessionRetention (기본값 24h): 세션 저장소에서 오래된 격리된 Cron 실행 세션을 삭제해요. false로 설정하면 이 기능을 꺼둘 수 있어요.
  • cron.runLog.maxBytes + cron.runLog.keepLines: ~/.openclaw/cron/runs/<jobId>.jsonl 파일의 크기를 제한해요. (기본값: 2_000_000 바이트 및 2000 라인)

sessionKey는 여러분이 현재 어떤 대화 버킷에 있는지를 식별해요. 이를 통해 라우팅과 격리가 이루어지죠.

자주 사용되는 패턴은 다음과 같아요:

  • 메인/직접 채팅 (에이전트별): agent:<agentId>:<mainKey> (기본값 main)
  • 그룹: agent:<agentId>:<channel>:group:<id>
  • 룸/채널 (Discord/Slack): agent:<agentId>:<channel>:channel:<id> 또는 ...:room:<id>
  • Cron: cron:<job.id>
  • Webhook: hook:<uuid> (재정의하지 않은 경우)

표준 규칙에 대한 자세한 내용은 /concepts/session 문서에 잘 정리되어 있어요.


각 sessionKey는 현재의 sessionId를 가리켜요. 이는 대화가 계속 이어지는 실제 트랜스크립트 파일을 의미해요.

기억해두면 좋은 규칙들이에요:

  • 리셋 (/new, /reset): 해당 sessionKey에 대해 새로운 sessionId를 생성해요.
  • 일일 리셋: 기본적으로 Gateway 호스트의 현지 시간 기준 오전 4시에 리셋돼요. 리셋 시간 이후 첫 메시지가 오면 새로운 sessionId가 만들어져요.
  • 유휴 만료 (session.reset.idleMinutes 또는 기존 session.idleMinutes): 설정된 유휴 시간 이후에 메시지가 도착하면 새로운 sessionId를 생성해요. 일일 리셋과 유휴 만료가 모두 설정된 경우, 먼저 조건이 충족되는 쪽이 적용돼요.
  • 스레드 부모 포크 가드 (session.parentForkMaxTokens, 기본값 100000): 부모 세션이 이미 너무 크면 부모 트랜스크립트 포크를 건너뛰고, 새 스레드를 깨끗하게 시작해요. 0으로 설정하면 이 기능을 끌 수 있어요.

구현 세부 사항이 궁금하다면 src/auto-reply/reply/session.ts 파일의 initSessionState() 함수를 살펴보세요.


세션 저장소 스키마 (sessions.json)

섹션 제목: “세션 저장소 스키마 (sessions.json)”

저장소의 값 타입은 src/config/sessions.ts에 정의된 SessionEntry를 사용해요.

주요 필드는 다음과 같아요 (전체 목록은 아니에요):

  • sessionId: 현재 트랜스크립트 ID (sessionFile이 설정되지 않았다면 이 값을 바탕으로 파일명이 결정돼요)
  • updatedAt: 마지막 활동 타임스탬프
  • sessionFile: 선택 사항으로, 트랜스크립트 경로를 직접 지정할 때 사용해요
  • chatType: direct | group | room (UI 및 전송 정책 결정에 도움을 줘요)
  • provider, subject, room, space, displayName: 그룹이나 채널 라벨링을 위한 메타데이터
  • 토글 설정:
    • thinkingLevel, verboseLevel, reasoningLevel, elevatedLevel
    • sendPolicy (세션별 재정의)
  • 모델 선택:
    • providerOverride, modelOverride, authProfileOverride
  • 토큰 카운터 (최선을 다해 계산하지만 provider에 따라 다를 수 있어요):
    • inputTokens, outputTokens, totalTokens, contextTokens
  • compactionCount: 이 세션 키에 대해 자동 압축(auto-compaction)이 완료된 횟수
  • memoryFlushAt: 마지막 사전 압축 메모리 플러시 타임스탬프
  • memoryFlushCompactionCount: 마지막 플러시가 실행되었을 때의 압축 횟수

이 저장소는 직접 수정해도 안전하지만, Gateway가 최종 권한을 가져요. 세션이 실행됨에 따라 항목을 다시 쓰거나 데이터를 복구할 수 있다는 점을 참고하세요.

트랜스크립트는 @mariozechner/pi-coding-agent의 SessionManager에 의해 관리돼요.

파일은 JSONL 형식으로 구성되어 있어요:

  • 첫 번째 줄: 세션 헤더 (type: "session", id, cwd, timestamp, 선택 사항인 parentSession 포함)
  • 그 이후: id와 parentId를 가진 세션 엔트리들 (트리 구조)

주요 엔트리 타입은 다음과 같아요:

  • message: user/assistant/toolResult 메시지
  • custom_message: 모델 context에 포함되는 extension 주입 메시지 (UI에서는 숨길 수 있어요)
  • custom: 모델 context에 들어가지 않는 extension 상태
  • compaction: firstKeptEntryId와 tokensBefore가 포함된 저장된 압축 요약
  • branch_summary: 트리 브랜치를 탐색할 때 저장되는 요약

OpenClaw는 의도적으로 트랜스크립트를 직접 “수정”하지 않아요. Gateway가 SessionManager를 사용해서 트랜스크립트를 읽고 쓸 뿐이죠.


Context window와 추적된 토큰의 차이

섹션 제목: “Context window와 추적된 토큰의 차이”

여기서 두 가지 서로 다른 개념이 중요해요:

  1. Model context window: 모델당 하드 제한 (모델이 볼 수 있는 토큰 수)
  2. Session store counters: sessions.json에 기록되는 누적 통계 (/status 및 대시보드에 사용돼요)

제한 수치를 튜닝하고 있다면 다음 내용을 참고하세요:

  • Context window는 모델 카탈로그에서 가져오며, config를 통해 덮어쓸 수 있어요.
  • 저장소의 contextTokens는 런타임 추정치이자 보고용 값이에요. 이걸 엄격한 보증값으로 취급해서는 안 돼요.

더 자세한 내용은 /token-use에서 확인할 수 있어요.

Compaction은 오래된 대화 내용을 요약해서 transcript에 compaction 항목으로 저장하고, 최근 메시지들은 그대로 유지하는 기능이에요.

Compaction이 진행된 후, 이후의 대화(turns)에서는 다음 내용들을 보게 돼요:

  • Compaction 요약본
  • firstKeptEntryId 이후의 메시지들

Compaction은 session pruning과 달리 **영구적(persistent)**으로 유지되는 특징이 있어요. 자세한 내용은 /concepts/session-pruning을 참고해 보세요.


Auto-compaction이 발생하는 시점 (Pi runtime)

섹션 제목: “Auto-compaction이 발생하는 시점 (Pi runtime)”

임베디드된 Pi agent에서는 다음 두 가지 상황에서 auto-compaction이 트리거돼요:

  1. Overflow recovery: 모델이 context overflow 에러를 반환하면, compact를 실행한 후 다시 시도해요.
  2. Threshold maintenance: 대화가 성공적으로 완료된 시점에 아래 조건을 만족하면 실행돼요:

contextTokens > contextWindow - reserveTokens

여기서 각 항목은 다음과 같아요:

  • contextWindow: 모델의 context window 크기
  • reserveTokens: 프롬프트와 다음 모델 출력을 위해 확보해둔 여유 공간(headroom)

이것은 Pi runtime의 동작 로직이에요. OpenClaw는 발생하는 이벤트를 수신하지만, 실제 compact 시점은 Pi가 결정하게 돼요.

압축 설정 (reserveTokens, keepRecentTokens)

섹션 제목: “압축 설정 (reserveTokens, keepRecentTokens)”

Pi의 압축(Compaction) 설정은 Pi settings에서 관리해요.

{
compaction: {
enabled: true,
reserveTokens: 16384,
keepRecentTokens: 20000,
},
}

OpenClaw는 embedded 실행 시 안전 하한선(safety floor)을 적용해요.

  • compaction.reserveTokens가 reserveTokensFloor보다 작으면 OpenClaw가 이 값을 높여요.
  • 기본 하한선은 20000 tokens예요.
  • 하한선을 비활성화하려면 agents.defaults.compaction.reserveTokensFloor: 0으로 설정하면 돼요.
  • 설정값이 이미 하한선보다 높다면 OpenClaw는 값을 수정하지 않아요.

이렇게 하는 이유는 압축이 꼭 필요한 상황이 오기 전에, 메모리 쓰기 같은 멀티턴 “housekeeping” 작업을 처리할 수 있는 충분한 여유 공간(headroom)을 확보하기 위해서예요.

구현 코드: src/agents/pi-settings.ts 파일의 ensurePiCompactionReserveTokens() 함수 (src/agents/pi-embedded-runner.ts에서 호출돼요).

압축 상태와 세션 상태는 다음 위치에서 확인할 수 있어요.

  • /status (모든 채팅 세션 내)
  • openclaw status (CLI 명령)
  • openclaw sessions 또는 sessions --json
  • Verbose 모드: 🧹 Auto-compaction complete 메시지와 함께 압축 횟수가 표시돼요.

OpenClaw는 사용자가 중간 과정을 볼 필요 없는 백그라운드 작업을 위해 “사일런트(silent)” 턴을 지원해요.

사용 규칙은 다음과 같아요:

  • 어시스턴트가 출력을 시작할 때 NO_REPLY를 넣으면 “사용자에게 응답을 전달하지 마세요”라는 신호로 인식해요.
  • OpenClaw는 전달 레이어에서 이 부분을 제거하거나 숨겨줘요.

2026.1.10 버전부터는 부분적인 청크가 NO_REPLY로 시작할 때 드래프트/타이핑 스트리밍도 함께 숨겨줘요. 덕분에 사일런트 작업 중에 중간 내용이 사용자에게 노출되지 않아요.

컴팩션 전 “메모리 플러시” (구현됨)

섹션 제목: “컴팩션 전 “메모리 플러시” (구현됨)”

이 기능의 목표는 자동 컴팩션(auto-compaction)이 일어나기 전에, 중요한 컨텍스트가 유실되지 않도록 에이전트 워크스페이스의 디스크(예: memory/YYYY-MM-DD.md)에 상태를 기록하는 사일런트 에이전트 턴을 실행하는 것이에요.

OpenClaw는 임계값 전 플러시(pre-threshold flush) 방식을 사용해요:

  1. 세션의 컨텍스트 사용량을 실시간으로 모니터링해요.
  2. 사용량이 “소프트 임계값”(Pi의 컴팩션 임계값보다 낮은 지점)을 넘으면, NO_REPLY를 사용해 사용자 몰래 “지금 메모리를 기록하세요”라는 사일런트 명령을 에이전트에게 내려요.

설정 항목 (agents.defaults.compaction.memoryFlush):

  • enabled (기본값: true)
  • softThresholdTokens (기본값: 4000)
  • prompt (플러시 턴을 위해 전달할 사용자 메시지)
  • systemPrompt (플러시 턴에 추가로 붙는 시스템 프롬프트)

참고 사항:

  • 기본 프롬프트와 시스템 프롬프트에는 응답 노출을 막기 위한 NO_REPLY 힌트가 포함되어 있어요.
  • 플러시는 컴팩션 주기당 한 번만 실행되며, 이는 sessions.json에서 추적해요.
  • 이 플러시 로직은 임베디드 Pi 세션에서만 작동하며, CLI 백엔드는 건너뛰어요.
  • 세션 워크스페이스가 읽기 전용(workspaceAccess: "ro" 또는 "none")인 경우 플러시를 실행하지 않아요.
  • 워크스페이스 파일 구조와 기록 패턴에 대해 더 알고 싶다면 Memory 문서를 참고해 보세요.

Pi는 확장 API에서 session_before_compact 훅을 제공하고 있지만, 현재 OpenClaw의 플러시 로직은 Gateway 쪽에서 관리되고 있어요.

  • 세션 키가 잘못되었나요? /concepts/session 문서를 확인하고, /status API에서 sessionKey가 일치하는지 확인해 보세요.
  • Store와 트랜스크립트 내용이 다른가요? Gateway 호스트 설정과 openclaw status 명령어로 확인한 store 경로가 맞는지 체크해 보세요.
  • 컴팩션이 너무 자주 발생하나요? 다음 항목을 확인해 보세요:
    • 모델의 컨텍스트 윈도우가 너무 작거나, 컴팩션 설정의 reserveTokens가 모델 윈도우 크기에 비해 너무 높게 잡혀 있는지 확인해 보세요.
    • 도구 결과(tool-result) 데이터가 너무 큰지 확인하고, 세션 프루닝(pruning) 기능을 활성화하거나 세부 설정을 조정해 보세요.
  • 사일런트 턴 내용이 사용자에게 노출되나요? 응답이 정확히 NO_REPLY 토큰으로 시작하는지 확인하고, 스트리밍 억제 수정 사항이 포함된 최신 빌드를 사용 중인지 확인해 보세요.
OpenClaw

OpenClaw Expert

아직 막혀 있나요?

이 문서에서 답을 못 찾았다면 OpenClaw Expert에게 바로 물어보세요.