OpenClaw 에이전트 메모리 디버깅에 5일을 쏟았습니다. 그 과정에서 배운 모든 것을 공유합니다.

@code_rams
영어5개월 전 · 2026년 2월 22일
212K
859
78
40
2.7K

TL;DR

OpenClaw AI 에이전트 메모리 최적화를 위한 종합 가이드입니다. 에이전트가 장기 기억을 유지할 수 있도록 컨텍스트 압축 해결, 하이브리드 검색 구현, 쓰기 원칙 수립 방법을 다룹니다.

제 에이전트 이름은 Chiti입니다. Telegram에서 실행되며, 두 개의 SaaS 제품에 대한 고객 지원을 처리하고, 트윗을 초안 작성하며, 인보이스를 관리하고, 시간대가 다른 공동 창업자와의 협업을 조정합니다. 마치 주니어 직원 하나를 둔 것과 가장 가까운 경험입니다.

그런데 몇 주 동안, 계속해서 중요한 것들을 잊어버렸습니다.

미묘한 방식이 아니었어요. 한 시간 동안 매일 실행되는 크론 작업을 설정하고, 모델을 바꾸면, 다음 세션에서 Chiti는 마치 저와 대화한 적이 없는 것처럼 행동했습니다. 이틀 전에 내린 결정을 언급하면 텅 빈 표정을 마주해야 했습니다. 작업을 계속하라고 요청하면 처음부터 다시 시작하곤 했습니다.

그래서 기능 개발을 멈추고, 시간이 날 때마다 5일 동안 메모리 수정에만 집중했습니다. 이 글은 제가 발견한 모든 것, 망가뜨린 모든 것, 그리고 실제로 효과를 본 모든 것에 대한 기록입니다.

1일차: 긴 대화 후 에이전트가 모든 것을 잊어버림

첫 번째 문제는 설명하기는 간단했지만 진단하기는 고통스러웠습니다.

긴 대화 후에 Chiti는 이전 맥락을 잃기 시작했습니다. 점진적으로 사라지는 것이 아니라, 그냥 사라져버렸습니다. 20분 전에 제가 말한 내용들이 사라졌습니다. 세션 초반에 함께 내린 결정들은? 없던 일이 되었습니다.

원인은 압축(compaction)이었습니다. 대화가 컨텍스트 창을 가득 채우면, OpenClaw는 이전 메시지들을 요약본으로 압축하여 새 메시지를 위한 공간을 만듭니다. 이 요약본은 대략적인 내용을 담지만 구체적인 정보는 놓칩니다. 이름, 숫자, 정확한 결정들 - 모두 사라집니다.

이는 설계된 동작입니다. 컨텍스트 창은 유한합니다. 하지만 기본 동작은 모든 것을 동등하게 취급하기 때문에, 메시지 #3의 신중하게 작성된 지시사항이 메시지 #7의 가벼운 잡담과 동일하게 처리됩니다.

제가 한 일:

압축 전에 메모리 플러시(memory flush)를 활성화했습니다. 이는 압축기가 실행되기 전에 에이전트가 중요한 컨텍스트를 디스크에 기록하도록 지시합니다.

json
1{
2 "compaction": {
3 "memoryFlush": {
4 "enabled": true,
5 "softThresholdTokens": 4000
6 }
7 }
8}

세션이 컨텍스트 한도에 가까워지면, OpenClaw는 에이전트에게 지속 가능한 사실을 memory/YYYY-MM-DD.md 파일에 저장하도록 상기시키는 무음 턴(silent turn)을 트리거합니다. 압축이 이 파일들을 지우기 전에 말이죠. 에이전트는 중요한 내용을 기록하고, 압축이 실행되며, 중요한 정보는 컨텍스트 요약이 놓치더라도 디스크에 안전하게 보존됩니다.

배운 점:

압축 자체는 적이 아닙니다. 압축 중 정보 손실이 적입니다. 해결책은 기억할 가치가 있는 모든 것이 압축기가 건드리기 전에 파일에 기록되도록 하는 것입니다. 컨텍스트 창에만 있다면 그것은 임시적입니다. 디스크에 있다면, 정보는 살아남습니다.

2일차: 검색이 쓰레기를 반환함

일일 로그가 쌓이고 MEMORY.md가 커지면서, 에이전트가 실제로 정보를 찾을 수 있어야 했습니다. 내장된 메모리 검색은 관련 없는 결과를 반환하거나 명백한 일치 항목을 놓쳤습니다.

문제는 검색 백엔드였습니다. OpenClaw의 기본 SQLite 기반 검색은 벡터 임베딩(의미적 유사성)을 사용하여 관련 청크를 찾습니다. 광범위한 질의에는 잘 작동하지만 정확한 일치에는 어려움을 겪습니다. 특정 고객 이름을 검색하면 비슷한 언어를 사용한 완전히 다른 주제에 대한 결과가 나왔습니다.

제가 한 일:

메모리 검색 백엔드를 QMD로 전환했습니다. QMD는 BM25(키워드 매칭)를 벡터 임베딩 및 재순위화(reranker)와 결합합니다. 따라서 "Charles payment failure"를 검색하면 해당 정확한 단어를 포함하는 결과와 의미적으로 관련된 결과를 모두 찾은 다음, 관련성에 따라 재순위화합니다.

또한 학습 폴더를 포함하도록 QMD 경로를 구성했습니다:

json
1{
2 "memory": {
3 "qmd": {
4 "paths": [
5 {
6 "path": "/Users/ramya/clawd",
7 "name": "memory-root",
8 "pattern": "MEMORY.md"
9 },
10 {
11 "path": "/Users/ramya/clawd",
12 "name": "memory-alt",
13 "pattern": "memory_alt.md"
14 },
15 {
16 "path": "/Users/ramya/clawd/memory",
17 "name": "memory-dir",
18 "pattern": "**/*.md"
19 },
20 {
21 "path": "/Users/ramya/clawd/learnings",
22 "name": "learnings",
23 "pattern": "**/*.md"
24 }
25 ]
26 }
27 }
28}

배운 점:

순수 의미 검색은 이론적으로는 좋아 보이지만, 고유 명사, 특정 숫자, 정확한 구문에서는 실패합니다. 하이브리드 검색(키워드 + 벡터 + 재순위화)이 실제 에이전트 메모리에 훨씬 더 효과적입니다. 에이전트가 파일에 있다고 알고 있는 정보를 찾을 수 없다면, 병목은 파일 자체가 아니라 검색 백엔드일 가능성이 높습니다.

3일차: 에이전트는 찾지만 사용하지 않음

가장 좌절스러운 날이었습니다. 검색이 작동한다는 것을 확인했습니다. 직접 질의하여 올바른 결과를 얻을 수 있었습니다. 하지만 실제 대화 중에 Chiti는 메모리에 분명히 존재하는 관련 컨텍스트를 검색하지 않았습니다.

문제는 검색이 자동으로 이루어지지 않는다는 점이었습니다. 에이전트가 검색을 결정해야 합니다. 대화가 올바른 신호를 트리거하지 않으면, 에이전트는 정보를 찾지 않습니다.

제가 한 일:

부트 시퀀스에 명시적인 검색 지침을 추가했습니다. 에이전트가 필요할 때 검색하기를 바라는 대신, 언제 검색해야 하는지 명확히 지시했습니다:

markdown

작업을 시작하기 전:

  • 일일 로그에서 관련 컨텍스트 검색
  • LEARNINGS.md에서 이 유형의 작업에 대한 규칙 확인
  • 고객이 언급되면 해당 고객의 이력 검색

또한 검색 테스트를 구축했습니다. 일일 로그에 특정 마커를 심었습니다. 예: "MARKER: 2026-02-20 — 코드를 푸시했다고 주장하기 전에 항상 git 상태를 확인하세요." 그런 다음 기다렸다가 새 세션을 시작하고 물었습니다: "어제 마커가 뭐였지?" 에이전트가 찾으면 검색이 작동하는 것입니다. 찾지 못하면 뭔가 고장난 것입니다.

배운 점:

"정보가 존재한다"는 것과 "에이전트가 정보를 사용한다"는 것은 다릅니다. 둘 다 필요합니다. 검색 인프라는 전자를 처리합니다. 부트 지침과 검색 습관은 후자를 처리합니다. 각각을 따로 테스트하세요.

4일차: 압축으로부터 안전하게 만들기

이제 메모리 플러시, 하이브리드 검색, 검색 지침이 있었습니다. 하지만 특정 시나리오, 즉 압축이 여러 번 실행되는 매우 긴 세션에서 계속 컨텍스트를 잃었습니다.

문제는 메모리 플러시가 압축 주기당 한 번만 트리거된다는 점이었습니다. 세션이 두세 번의 압축을 견딜 만큼 길다면, 첫 번째 압축만 플러시 처리를 받았습니다. 그 이후의 모든 것은 위험에 노출되었습니다.

제가 한 일:

압축과 함께 작동하도록 컨텍스트 정리(context pruning)를 구성했습니다:

json
1{
2 "contextPruning": {
3 "mode": "cache-ttl",
4 "ttl": "6h",
5 "keepLastAssistants": 3
6 }
7}

이 설정은 6시간 후에 오래된 컨텍스트를 적극적으로 정리하면서 마지막 3개의 어시스턴트 응답은 유지합니다. 메모리 플러시와 결합하면, 에이전트가 중요한 정보를 초기에 디스크에 기록하고, 오래된 컨텍스트는 오버플로를 유발하기 전에 정리됩니다.

또한 MARKER 테스트 프로토콜을 추가했습니다: 중요한 구성 변경 후에는 일일 로그에 마커를 심고 압축 경계를 넘어 검색을 테스트합니다. 마커가 살아남으면 변경이 작동한 것입니다. 그렇지 않으면 무언가 고장난 것입니다.

배운 점:

긴 세션이야말로 메모리 시스템이 실제로 테스트되는 순간입니다. 짧은 대화는 거의 압축에 도달하지 않습니다. 컨텍스트를 잃고 이유를 알 수 없는 것은 2시간 깊이 작업 세션입니다. 빠른 채팅뿐만 아니라 부하 상태에서 메모리 시스템을 테스트하세요.

5일차: 시스템 프롬프트가 28% 비대해짐

모든 것이 명확해진 날이었습니다. /context detail을 실행하고 숫자를 응시했습니다.

제 에이전트는 제 메시지를 읽기도 전에 11,887 토큰의 시스템 프롬프트를 로드하고 있었습니다. 51개의 스킬 중 20개는 한 번도 사용하지 않은 것이었습니다. MEMORY.md는 200줄의 회사 위키였으며 매 세션마다 로드되었습니다. 그리고 두 개의 경쟁 부트 시퀀스가 있었습니다. 하나는 BOOT.md(OpenClaw가 인식하지도 않음)에, 다른 하나는 AGENTS.md의 200줄 깊이에 묻혀 있었습니다.

최악은 모델을 바꿀 때마다 Chiti가 모든 것을 잊어버린다는 점이었습니다. 인계 프로토콜도 없었고, 현재 컨텍스트의 기록도 없었습니다. 그냥 사라졌습니다.

근본 원인:

OpenClaw는 새 세션마다 다음 파일들을 자동으로 읽습니다: AGENTS.md, SOUL.md, TOOLS.md, IDENTITY.md, HEARTBEAT.md, MEMORY.md.

그 외의 모든 것(LEARNINGS.md, 일일 로그, 문서, 참조 파일)은 에이전트가 도구를 사용하여 직접 읽어야 합니다. 이러한 파일을 읽으라는 지침이 자동 로드되는 파일(특히 AGENTS.md) 중 하나에 없다면, 에이전트는 절대 그 파일들을 보지 못합니다.

제 BOOT.md에는 전체 부트 시퀀스가 있었습니다. 하지만 OpenClaw는 BOOT.md를 자동 로드하지 않습니다. 그래서 지침은 그냥 읽히지 않은 채로 놓여 있었고, 아무 일도 하지 않았습니다.

제가 한 일:

전체 감사 및 정리를 수행했습니다:

  1. 부트 시퀀스를 AGENTS.md의 맨 위로 이동 (시작 지침을 위한 유일한 신뢰할 수 있는 위치)
  2. BOOT.md 삭제 (OpenClaw가 인식하지 않음)
  3. BOOTSTRAP.md 삭제 (일회성 온보딩 파일, 이미 완료되었으며 매 세션마다 361 토큰 낭비)
  4. MEMORY.md를 200줄에서 90줄로 줄이고 참조 문서를 docs/ 폴더로 이동
  5. 매 세션마다 3,000 토큰을 소모하던 사용하지 않는 마케팅 스킬 20개 제거
  6. 기록 규율 추가: 모든 작업은 결과를 기록하고, 모든 실수는 규칙이 됨
  7. 인계 프로토콜 추가: 모델 전환이나 세션 종료 전에 에이전트가 현재 컨텍스트를 일일 로그에 기록

부트 시퀀스는 이제 다음과 같습니다:

markdown

무엇이든 하기 전:

  1. USER.md 읽기
  2. learnings/LEARNINGS.md 읽기
  3. memory/YYYY-MM-DD.md 읽기 (오늘 + 어제)
  4. MEMORY.md 읽기 (메인 세션에서만, 그룹에서는 절대 안 함)
  5. PROTOCOL_COST_EFFICIENCY.md 읽기
  6. 출력: LOADED: USER | LEARNINGS | DAILY | MEMORY | PROTOCOL

기록 규율:

markdown

모든 작업 후:

  1. 결정 + 결과 기록 → memory/YYYY-MM-DD.md
  2. 실수 시 → learnings/LEARNINGS.md에 추가
  3. 중요한 컨텍스트 시 → MEMORY.md 업데이트 (하트비트 검토 중에만, 작업 중에는 직접 업데이트 금지)

인계 프로토콜:

markdown

세션 종료 또는 모델 전환 전:

memory/YYYY-MM-DD.md에 HANDOVER 섹션 기록:

  • 논의된 내용
  • 결정된 내용
  • 보류 중인 작업과 정확한 세부 사항
  • 남은 다음 단계

결과:

  • 시스템 프롬프트: 11,887 → 8,529 토큰
  • 스킬: 51 → 32
  • 세션 토큰: 18,280 → 14,627
  • 28% 가벼워짐. 같은 에이전트. 같은 모델. 잡음만 줄었습니다.

배운 점:

진정한 해결책은 파일을 더 추가하는 것이 아니었습니다. 아무 일도 하지 않는 파일을 제거하는 것이었습니다. 시스템 프롬프트의 모든 토큰은 에이전트가 모든 메시지에 대해 부담해야 하는 오버헤드입니다. 사용하지 않는 스킬, 비대해진 메모리 파일, 시스템이 읽지도 않는 파일 — 이 모든 것이 조용히 누적됩니다.

1일차에 알았더라면 좋았을 규칙들

5일 동안 이것저것 망가뜨리고 고치면서, OpenClaw 메모리를 설정하는 누구에게나 주고 싶은 규칙들은 다음과 같습니다:

1. 자동 로드되는 파일은 오직 이것뿐입니다: AGENTS.md, SOUL.md, TOOLS.md, IDENTITY.md, USER.md, HEARTBEAT.md, MEMORY.md.

그 외의 모든 것은 AGENTS.md에 명시적인 읽기 지침이 필요합니다. 부트 시퀀스에 없다면 에이전트가 보지 못합니다. BOOT.md는 OpenClaw에서 실제로 존재하는 개념이 아닙니다. 저는 몇 주 동안 가지고 있었지만, 아무 효과가 없었습니다.

2. 부트 시퀀스는 AGENTS.md의 맨 위에 위치해야 합니다.

중간도, 아래도 안 됩니다. 가장 위입니다. 자동 로드된 파일은 시스템 프롬프트에 주입되므로, 부트 지침은 에이전트가 가장 먼저 처리하는 내용이어야 합니다.

3. 기록 규율이 읽기 규율보다 중요합니다.

대부분의 사람들은 에이전트가 읽을 파일을 설정하지만, 기록을 강제하지는 않습니다. 에이전트가 결정, 결과, 실수를 디스크에 기록하지 않으면, 이러한 정보는 컨텍스트 창에만 존재합니다. 그리고 컨텍스트 창은 압축됩니다. 기록(write-back)이 임시 컨텍스트를 영구 메모리로 만드는 방법입니다.

4. 작업 중에는 MEMORY.md에 직접 기록하지 마십시오.

일일 로그는 원시 상태이며 추가 전용입니다. MEMORY.md는 선별된 장기 메모리입니다. 에이전트가 아무거나 MEMORY.md에 덤프하도록 허용하면, 몇 주 안에 200줄짜리 엉망진창이 됩니다. 주기적인 검토(하트비트 또는 크론) 중에 최근 일일 로그에서 통찰을 추출하여 MEMORY.md를 선별하십시오. 저는 MEMORY.md를 쓸모없는 잡음으로 채워 무용지물로 만든 다른 OpenClaw 사용자에게서 배웠습니다.

5. LEARNINGS.md는 가장 과소평가된 파일입니다.

에이전트가 저지르는 모든 실수는 한 줄짜리 규칙이 되어야 합니다. "Git 상태를 확인하지 않고 코드가 푸시되었다고 주장하지 마십시오." "그룹 채팅에서 전체 MEMORY.md를 읽지 마십시오." "스케줄링 전에 항상 사용자의 시간대를 확인하십시오." 이러한 규칙은 누적됩니다. 몇 주 후면, 에이전트는 자신의 실패로부터 구축된 개인 운영 매뉴얼을 가지게 됩니다.

6. 저장뿐만 아니라 검색도 테스트하십시오.

정보를 저장하는 것과 검색하는 것은 다른 문제입니다. 파일이 색인화되고 검색 가능하지만, 에이전트가 그 파일을 찾아야 한다는 것을 몰라서 접근하지 못하는 경우를 경험했습니다. 마커를 심고, 세션 간 테스트하고, 모델 전환 간 테스트하십시오. 에이전트가 어제 저장한 것을 찾을 수 없다면, 저장은 의미가 없습니다.

7. 인계 프로토콜이 모델 전환 문제의 해결책입니다.

OpenClaw 에이전트는 모델을 전환할 때 모든 컨텍스트를 잃습니다. 새 모델은 빈 컨텍스트 창으로 시작합니다. 자동 로드된 파일만 볼 뿐입니다. 전환 전에 현재 상태를 일일 로그에 덤프하는 인계 프로토콜이 없으면, 새 모델은 무슨 일이 일어나고 있었는지 전혀 알 수 없습니다. 이것이 몇 주 동안 제 가장 큰 고통 포인트였습니다.

8. /context detail을 정기적으로 실행하십시오.

이 명령은 정확히 어떤 것이 토큰을 소모하고 있는지 보여줍니다. 설치했는지 잊어버린 스킬, 눈에 띄지 않게 커진 파일, 전혀 사용하지 않는 도구들. 저는 매 세션마다 3,000 토큰을 소모하는 20개의 사용하지 않는 스킬을 발견했습니다. 그것은 제가 한 번도 건드린 적 없는 기능들에 대해 모든 메시지마다 지불하는 3,000 토큰의 오버헤드였습니다.

9. 하이브리드 검색이 순수 의미 검색을 능가합니다.

BM25(키워드) + 벡터(의미) + 재순위화는 벡터 단독보다 훨씬 더 나은 결과를 제공합니다. 고객 이름, 특정 숫자, 정확한 구문 — 의미 검색은 이를 놓칩니다. 키워드 검색이 이를 잡아냅니다. 둘 다 사용하십시오.

10. 압축은 적이 아닙니다. 기록되지 않은 컨텍스트가 적입니다.

저는 압축과 싸우며 며칠을 보냈지만, 해결책이 더 간단하다는 것을 깨달았습니다: 압축이 실행되기 전에 중요한 모든 것이 파일에 기록되도록 하는 것입니다. 메모리 플러시가 이를 자동으로 처리합니다. 디스크에 있으면 압축에서 살아남습니다. 대화에만 있으면 위험합니다.

현재 설정

참고로, 현재 작업 공간은 다음과 같습니다:

workspace/

├── AGENTS.md (부트 시퀀스 + 기록 규율 + 인계 프로토콜)

├── SOUL.md (성격 및 행동)

├── IDENTITY.md (이름, 역할)

├── USER.md (소유자 정보)

├── TOOLS.md (도구 사용 지침)

├── HEARTBEAT.md (자율 체크인 행동)

├── MEMORY.md (선별된 장기 메모리, 약 90줄)

├── PROTOCOL_COST_EFFICIENCY.md

├── learnings/

│ └── LEARNINGS.md (실수로부터 얻은 규칙)

├── memory/ (일일 로그: YYYY-MM-DD.md)

├── docs/ (MEMORY.md에서 이동된 참조 문서)

│ ├── tweetsmash-arch.md

│ ├── knowledge-transfer.md

│ ├── infrastructure.md

│ └── group-chat-rules.md

└── skills/ (32개 스킬, 51개에서 감소)

시스템 프롬프트: 8,529 토큰. 세션 토큰: 200,000 컨텍스트 창 중 14,627 (7.3%). 에이전트는 부팅하고, 필요한 것을 읽고, 배운 것을 기록하며, 모델 전환 전에 컨텍스트를 인계합니다.

여기에 도달하는 데 5일이 걸렸습니다. 대부분은 더 많은 파일이 더 나은 메모리를 의미한다는 가정을 버리는 과정이었습니다. 그렇지 않습니다. 규율이 중요합니다. 제 실험은 계속됩니다.

저는 공동 창업자와 함께 소셜 미디어 북마크 도구인 TweetSmash와 LinkedMash를 만들고 있습니다. 프로덕션에서 OpenClaw 에이전트를 운영하면서 배운 내용을 X에서 공유합니다: @code_rams

YouMind에서 다시 만들기

Turn one viral article into a full content workflow

Collect the source, decode the pattern, create assets, draft the story, and distribute from one AI workspace.

Explore YouMind
크리에이터를 위해

당신의 Markdown을 깔끔한 𝕏 글로

직접 쓴 장문을 올릴 때 이미지, 표, 코드 블록을 𝕏에 맞게 정리하는 일은 번거롭습니다. YouMind는 전체 Markdown 초안을 깔끔하고 바로 게시할 수 있는 𝕏 글로 바꿔 줍니다.

Markdown → 𝕏 사용해 보기

분석할 패턴 더 보기

최근 바이럴 아티클

더 많은 바이럴 아티클 보기