포스트

Clawdbot의 메모리 관리 - Markdown 기반 하이브리드 메모리 아키텍처

목차

  1. 개요
  2. 컨텍스트와 메모리의 차이
  3. 4계층 컨텍스트 구조
  4. 2계층 메모리 저장소
  5. 메모리 도구
  6. 메모리 인덱싱 파이프라인
  7. 하이브리드 검색
  8. 컴팩션
  9. 메모리 플러시
  10. 가지치기
  11. 세션 생명주기
  12. 다중 에이전트 메모리 격리
  13. 설계 원칙
  14. Reference

개요

Clawdbot은 Peter Steinberger가 만든 오픈소스 개인 AI 어시스턴트이다. 모든 기억을 로컬 환경의 Markdown 파일로 저장하며, Discord, WhatsApp, Telegram 등과 통합된다. MIT 라이선스로 공개되어 있고 GitHub에서 32,600개 이상의 스타를 받았다.

이 글은 Manthan Gupta가 분석한 Clawdbot의 메모리 시스템 아키텍처를 다룬다. 사용자 데이터 소유권과 투명성을 최우선으로 설계된 로컬 기반 메모리 관리 방식이 핵심이다.

컨텍스트와 메모리의 차이

Clawdbot은 컨텍스트와 메모리를 명확히 구분한다.

컨텍스트는 임시적이다. 단일 요청에만 존재하며, 모델 컨텍스트 윈도우(예: 200K 토큰)에 의해 제한된다. API 비용과 응답 속도에 직접적인 영향을 미친다.

메모리는 영구적이다. 디스크에 Markdown 파일로 저장되며 무한정 증가할 수 있다. API 비용이 들지 않고 의미론적 검색이 가능하다.

이 두 개념의 분리가 Clawdbot 메모리 시스템의 기반이 된다.

4계층 컨텍스트 구조

Clawdbot의 컨텍스트는 4개 계층으로 구성된다.

계층 0은 시스템 프롬프트로, 정적 지시사항과 조건부 지시사항을 포함한다.

계층 1은 프로젝트 컨텍스트이다. 부트스트랩 파일들이 여기에 해당한다.

파일명목적
AGENTS.md에이전트 지시사항 및 메모리 가이드라인
SOUL.md성격과 톤 설정
USER.md사용자 정보
TOOLS.md외부 도구 사용 가이드

계층 2는 대화 기록으로, 메시지, 도구 호출, 요약 등이 포함된다.

계층 3은 현재 메시지이다.

2계층 메모리 저장소

메모리는 2개의 계층으로 나뉘어 저장된다.

1
2
3
4
5
6
~/clawd/
├── MEMORY.md              # 계층 2: 장기 큐레이션된 지식
└── memory/
    ├── 2026-01-26.md      # 계층 1: 오늘의 노트
    ├── 2026-01-25.md
    └── ...

계층 1 - 일일 로그

memory/YYYY-MM-DD.md 형식의 추가 전용(append-only) 일일 노트이다. 에이전트가 하루 종일 대화 내용, 결정사항, 사용자 선호도 등을 시간순으로 기록한다.

1
2
3
4
5
6
7
8
9
10
11
# 2026-01-26

## 10:30 AM - API 토론
REST vs GraphQL 논의. 결정: 단순성을 위해 REST 사용.
핵심 엔드포인트: /users, /auth, /projects.

## 2:15 PM - 배포
v2.3.0을 프로덕션에 배포. 문제 없음.

## 4:00 PM - 사용자 선호도
사용자가 JavaScript보다 TypeScript를 선호한다고 언급.

계층 2 - 장기 메모리

MEMORY.md에 큐레이션된 영구 지식이 저장된다. 사용자 선호도, 중요 결정, 주요 연락처 등 반복적으로 참조해야 하는 핵심 정보를 담고 있다.

1
2
3
4
5
6
7
8
9
10
11
# 장기 메모리

## 사용자 선호도
- TypeScript를 JavaScript보다 선호
- 간결한 설명 선호
- "Acme Dashboard" 프로젝트 진행 중

## 중요 결정
- 2026-01-15: PostgreSQL 선택
- 2026-01-20: REST over GraphQL 채택
- 2026-01-26: Tailwind CSS 스타일링 사용

메모리 도구

Clawdbot은 메모리를 읽기 위한 전용 도구 2가지를 제공한다.

memory_search - 의미론적 검색

과거 작업, 결정, 날짜, 인물, 선호도, TODO 등을 MEMORY.md와 memory/*.md에서 의미론적으로 검색한다. 파일 경로, 라인 번호, 유사도 점수(0~1), 코드 스니펫을 반환한다. 임베딩에는 openai/text-embedding-3-small을 사용한다.

1
2
3
4
5
6
7
8
{
  "name": "memory_search",
  "parameters": {
    "query": "API에 대해 무엇을 결정했나?",
    "maxResults": 6,
    "minScore": 0.35
  }
}

memory_get - 특정 내용 읽기

memory_search로 찾은 결과의 특정 라인 범위를 읽는다.

1
2
3
4
5
6
7
8
{
  "name": "memory_get",
  "parameters": {
    "path": "memory/2026-01-20.md",
    "from": 45,
    "lines": 15
  }
}

메모리 쓰기

별도의 메모리 쓰기 도구는 없다. 일반적인 write/edit 도구로 Markdown 파일을 직접 작성한다.

트리거저장 위치
일일 노트, “기억해줘”memory/YYYY-MM-DD.md
영구 사실, 선호도, 결정MEMORY.md
배운 교훈AGENTS.md 또는 TOOLS.md

메모리 인덱싱 파이프라인

Markdown 파일이 저장되면 자동으로 인덱싱 파이프라인이 동작한다.

  1. 파일이 저장된다
  2. Chokidar 파일 감시자가 변경을 감지한다 (1.5초 debounce)
  3. 400 토큰 크기의 청크로 분할된다 (80 토큰 오버랩)
  4. 임베딩 모델이 1536차원 벡터를 생성한다 (OpenAI, Gemini, Local 지원)
  5. SQLite에 저장된다

SQLite 내부에는 네 가지 구조가 사용된다.

  • chunks 테이블 : 원본 텍스트 청크
  • chunks_vec : sqlite-vec 기반 벡터 유사도 검색 인덱스
  • chunks_fts : FTS5 기반 전문 검색 인덱스
  • embedding_cache : 재임베딩 방지용 캐시

sqlite-vec를 사용하므로 외부 벡터 데이터베이스가 필요 없다. 모든 것이 로컬 SQLite 파일 하나에 담긴다.

하이브리드 검색

메모리 검색 시 두 가지 전략이 병렬로 실행된다.

벡터 검색은 의미 기반으로 개념적 유사성을 찾는다. BM25 키워드 검색은 이름, ID, 날짜 등 정확한 용어를 찾는다.

최종 점수는 두 검색 결과를 가중 합산하여 계산한다.

1
최종 점수 = (0.7 × 벡터 점수) + (0.3 × BM25 점수)

벡터 70%, 텍스트 30%의 비율을 사용한다. 유사도 점수가 0.35 이상인 결과만 반환한다.

이 하이브리드 방식 덕분에 “데이터베이스 관련 것”과 같은 개념적 검색과 “POSTGRES_URL”과 같은 정확한 키워드 검색 모두 처리할 수 있다.

컴팩션

긴 대화가 컨텍스트 윈도우 한계에 도달하면 자동으로 요약이 실행된다.

예를 들어 200,000 토큰 중 180,000 토큰을 사용한 150턴 대화가 있을 때, 컴팩션은 다음과 같이 진행된다.

  1. 중요 정보를 MEMORY.md에 저장 (Memory Flush)
  2. 턴 1~140을 요약으로 대체하고 턴 141~150은 원본 보존
  3. 결과적으로 약 45,000 토큰으로 90% 감소

컴팩션은 자동과 수동 두 가지 방식이 있다. 자동 컴팩션은 컨텍스트 한계에 접근하면 자동으로 트리거된다. 수동 컴팩션은 /compact 명령으로 실행하며, 요약 방향을 지정할 수 있다.

1
/compact 결정과 미해결 질문에 중점을 두세요

요약은 JSONL 트랜스크립트에 영구 저장된다.

메모리 플러시

컴팩션이 중요 정보를 요약 과정에서 손실시킬 위험이 있다. 메모리 플러시는 이 문제를 해결하기 위한 사전 보호 장치이다.

  1. 컨텍스트가 75%에 도달한다
  2. 에이전트가 사일런트 턴을 실행하여 중요 정보를 memory/ 디렉토리에 저장한다
  3. 사용자에게는 표시되지 않는 NO_REPLY 응답이 반환된다
  4. 이후 컴팩션이 안전하게 진행된다

설정 파일에서 메모리 플러시의 임계값과 시스템 프롬프트를 커스터마이징할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
  "agents": {
    "defaults": {
      "compaction": {
        "reserveTokensFloor": 20000,
        "memoryFlush": {
          "enabled": true,
          "softThresholdTokens": 4000,
          "systemPrompt": "세션이 컴팩션에 가까움. 영구 메모리를 저장하세요."
        }
      }
    }
  }
}

이 방식으로 컴팩션 전에 반드시 중요 정보가 영구 파일에 기록되므로 정보 손실을 방지할 수 있다.

가지치기

도구 결과(빌드 로그, 실행 출력 등)가 수십만 자에 달할 수 있다. 가지치기는 오래된 도구 출력을 정리하여 토큰을 절약하는 기능이다.

두 가지 방식이 있다.

  • Soft Trim : 처음과 끝을 보존하고 중간을 잘라낸다
  • Hard Clear : 오래된 결과를 완전히 제거한다

캐시-TTL 가지치기

Anthropic의 프롬프트 캐싱은 TTL 5분으로, 캐시된 토큰은 약 90% 할인된다. TTL 만료 후 유휴 세션은 전체 재캐시 비용이 발생한다.

캐시-TTL 가지치기는 캐시 만료 후 오래된 도구 결과를 자동으로 정리한다. 마지막 3개의 어시스턴트 응답은 보존하고, 나머지 도구 결과에 대해 Soft Trim을 적용한다.

1
2
3
4
5
6
7
8
9
10
11
12
{
  "contextPruning": {
    "mode": "cache-ttl",
    "ttl": "600",
    "keepLastAssistants": 3,
    "softTrim": {
      "maxChars": 4000,
      "headChars": 1500,
      "tailChars": 1500
    }
  }
}

세션 생명주기

기본적으로 세션은 매일 리셋된다 (기본 새벽 4시).

모드동작
daily정해진 시간에 리셋
idleN분 비활성 후 리셋
daily+idle먼저 도래한 조건에서 리셋

/new 명령으로 새 세션을 시작하면 세션-메모리 훅이 동작한다.

  1. 종료 세션의 마지막 15개 메시지를 추출한다
  2. LLM으로 설명적 slug를 생성한다
  3. ~/clawd/memory/2026-01-26-api-design.md 형식으로 저장한다
  4. 새 세션이 시작되면 이전 컨텍스트를 memory_search로 검색할 수 있다

이를 통해 세션이 종료되어도 중요한 대화 내용이 메모리에 보존된다.

다중 에이전트 메모리 격리

Clawdbot은 에이전트별 완전한 메모리 격리를 지원한다.

1
2
3
4
5
6
7
8
9
10
11
~/.clawdbot/memory/
├── main.sqlite           # "main" 에이전트 벡터 인덱스
└── work.sqlite           # "work" 에이전트 벡터 인덱스

~/clawd/                  # "main" 워크스페이스
├── MEMORY.md
└── memory/

~/clawd-work/             # "work" 워크스페이스
├── MEMORY.md
└── memory/

각 에이전트는 자신의 워크스페이스와 벡터 인덱스만 접근할 수 있다. 개인용과 업무용 에이전트를 분리하여 운용할 수 있다.

설계 원칙

Clawdbot 메모리 시스템의 성공을 이끄는 4가지 원칙이 있다.

첫 번째는 투명성이다. 메모리가 순수 Markdown이므로 사람이 직접 읽고, 편집하고, 버전 관리할 수 있다.

두 번째는 검색 우선이다. 전체 메모리를 컨텍스트에 넣는 대신 관련 정보만 검색하여 컨텍스트를 집중시키고 비용을 절감한다.

세 번째는 영구성이다. 파일에 저장된 정보는 컴팩션으로 손실되지 않는다.

네 번째는 하이브리드 검색이다. 벡터(의미)와 BM25(키워드)를 결합하여 완전한 검색 경험을 제공한다.

Reference