🐾 claw-stack
· Orange & Qiushi Wu memory architecture context-window AI agents

메모리 시스템 v2: 컨텍스트 팽창 문제 해결

두 가지 문제가 우리 AI 에이전트를 괴롭혔습니다. 도구 출력으로 인한 컨텍스트 윈도우 폭증(컨텍스트의 82.5%가 도구 결과) 및 /new 실행 시 단기 메모리 인수 없이 모든 작업 상태 초기화. 우리는 공격적인 컨텍스트 가지치기를 구현하고, MEMORY.md를 97% 압축하며, 각 세션 재설정 전에 메모리 파일을 자동으로 업데이트하는 세션 인수 훅을 작성했습니다.

지속형 메모리 시스템 구축에 관한 지난 게시물에서 우리는 MEMORY.md 팽창 문제를 설명했습니다. 6주 후 파일이 700줄을 넘게 증가했고, 인라인 콘텐츠에서 포인터 기반 항목으로 전환하여 문제를 해결했습니다. 수정이 효과를 거뒀습니다. MEMORY.md가 간결해지고, 세션 시작이 개선되었으며, 모든 것이 잘 작동했습니다.

그 다음 다시 팽창했습니다.

4주 후 MEMORY.md는 다시 92,000자이고 790줄로 돌아왔습니다. 정리자 파이프라인이 계속 새로운 사실을 인라인으로 작성했고, 주제별 파일로 미루지 않았습니다. 바이트 크기 제한이 일관되게 적용되지 않았습니다. 원래 수정은 증상을 패치했지만 원인을 해결하지 못했습니다.

더 걱정스러운 것은 MEMORY.md가 통제 범위 내에 있을 때도 세션이 작업 중간에 컨텍스트 제한에 도달한다는 점이었습니다. 에이전트는 몇 개의 파일을 읽고, 검색을 실행한 다음 중단되었습니다. 메모리가 부족해서가 아니라 컨텍스트 윈도우가 같은 세션 초반의 도구 출력으로 가득 찼기 때문입니다.

그리고 우리가 계속 참아온 세 번째 문제가 있었습니다. 새로운 세션을 시작하려고 /new를 실행할 때마다 에이전트는 방금 하고 있던 일에 대한 모든 인식을 잃었습니다. 우리의 장기 메모리 시스템(v1)은 사실, 선호도 및 프로젝트 지식을 잘 처리했습니다. 하지만 단기 작업 상태(진행 중인 작업, 방금 내린 결정, 다음 단계)는 완전히 사라졌습니다. 사용자는 재설정 전에 에이전트에게 메모리 파일을 업데이트하도록 수동으로 지시하거나 컨텍스트 손실을 감수해야 했습니다.

세 가지 문제, 하나의 주제: 어떤 시간 척도에서도 컨텍스트에 대한 체계적인 라이프사이클이 없었습니다.

수정 전에 측정

뭔가를 변경하기 전에 우리는 session-stats.py를 작성하여 지난 15개 세션을 분석하고 컨텍스트가 실제로 어디로 가는지 이해했습니다. 결과는 명확했습니다.

Session context breakdown (15 sessions, chars):
┌──────────────────┬───────────┬───────────┬────────────┐
│ Category         │ Total     │ % of ctx  │ Avg/session│
├──────────────────┼───────────┼───────────┼────────────┤
│ Tool results     │ 1,842,300 │   82.5%   │   122,820  │
│ System prompt    │   268,100 │   12.0%   │    17,873  │
│ Assistant text   │    64,700 │    2.9%   │     4,313  │
│ User input       │    55,900 │    2.5%   │     3,727  │
└──────────────────┴───────────┴───────────┴────────────┘

가장 극단적인 세션: 159,000자의 도구 결과, 합쳐서 1,500자의 사용자 입력과 어시스턴트 텍스트. 실제 대화는 자신의 컨텍스트 윈도우에서 거의 보이지 않았습니다.

시스템 프롬프트는 평균 세션당 17K자였습니다. 우리는 MEMORY.md가 시작 시 로드된다는 것을 알고 있었지만, 메모리와 관련된 것이 아무것도 일어나지 않은 세션을 포함하여 모든 세션에서 전체 컨텍스트의 12%를 차지한다는 것을 보니 숫자가 구체적이었습니다. 에이전트는 자신이 무엇을 하고 있든 모든 세션에서 17K자의 컨텍스트 세금을 지불하고 있었습니다.

두 가지 문제가 이제 측정 가능했습니다. 세션 내 도구 결과 팽창과 세션 간 MEMORY.md 팽창. 둘 다 해결 가능했고, 솔루션을 평가할 수치를 가지고 있었습니다.

솔루션 1: 컨텍스트 가지치기

세션 내 문제는 도구 출력이 누적된다는 것입니다. 에이전트는 파일을 읽습니다. 그것은 8K자의 컨텍스트입니다. 검색을 실행합니다. 또 다른 4K입니다. 파일을 편집하고 diff를 봅니다. 2K입니다. 테스트 출력을 읽습니다. 6K입니다. 적당히 복잡한 작업 후에 컨텍스트는 대부분 에이전트가 더 이상 참조할 필요가 없는 이전 단계의 도구 출력입니다.

OpenClaw의 contextPruning 기능은 TTL 기반 접근 방식으로 이를 처리합니다. 구성 가능한 시간 윈도우 후에 가장 최근 턴을 넘는 도구 출력은 자리 표시자로 바뀝니다. 콘텐츠는 활성 컨텍스트에서 사라지지만 에이전트는 무언가 일어났다는 것을 볼 수 있습니다.

우리의 구성:

contextPruning:
  mode: cache-ttl
  ttl: 30
  minPrunableToolChars: 100
  hardClearRatio: 0

ttl: 30을 사용하면 30초보다 오래된 도구 결과는 다음 턴에서 가지치기할 수 있습니다. minPrunableToolChars: 100은 거의 비용이 들지 않는 작은 도구 출력을 바꾸지 못하게 합니다. hardClearRatio: 0은 전체 초기화를 절대 수행하지 않음을 의미합니다. 가장 최근 턴은 그대로 유지됩니다.

효과는 에이전트가 누적된 전체 히스토리가 아니라 최근 도구 컨텍스트의 슬라이딩 윈도우로 작동한다는 것입니다. 반복된 파일 읽기나 검색 반복 루프를 포함하는 작업의 경우 이것은 8단계에서 컨텍스트 제한에 도달하는 것과 작업을 완료하는 것의 차이입니다.

우리가 가졌던 한 가지 우려: 가지치기가 에이전트의 이전 작업을 참조하는 능력을 깨뜨리지 않을까요? 실제로는 그렇지 않습니다. 대부분의 작업의 경우 에이전트는 가장 최근 도구 호출의 출력이 필요하거나 메모리에 있어야 하는 일반적인 사실이 필요합니다. 일시적 도구 결과에 캐시되어 있지 않습니다. 에이전트가 이미 처리한 파일을 다시 읽어야 한다면 그것은 보통 해당 사실이 메모리에 기록되었어야 한다는 신호입니다.

솔루션 2: MEMORY.md 구조 압축

92K → 간결 마이그레이션은 우리가 처음에 피했던 설계 질문과 마주해야 했습니다. MEMORY.md가 정확히 무엇을 포함해야 할까요?

우리의 v1 답변은 “최근 활동, 활성 프로젝트, 주요 연락처 및 인프라 메모”였으며, 이를 관리 가능하게 유지하기 위한 바이트 크기 상한선이 있었습니다. 이것은 잘못되었습니다. 바이트 크기 상한선은 콘텐츠를 압축하는 인센티브이지만 누적을 방지하지는 못합니다. 각 항목을 더 짧게 만들기만 하고 공간이 부족해지기 전에 규칙을 구부리기 시작합니다.

올바른 답변은 MEMORY.md가 콘텐츠가 아니라 포인터를 포함해야 한다는 것입니다. “이 파일이 무엇을 위한 것인가?”라는 질문에 “X를 포함한다”는 답변으로 대답할 수 있다면, MEMORY.md는 X를 포함하지 않아야 합니다. memory/X.md에서 X를 보라는 포인터를 포함해야 합니다. MEMORY.md는 에이전트가 어디를 봐야 하는지 알려주는 인덱스이지, 에이전트가 무엇을 알고 있는지 포함하는 문서가 아닙니다.

그 정의를 통해 대상 구조가 명백해졌습니다:

## Users
| handle | role | notes |
| --- | --- | --- |
| @orange | owner | ... |

## Projects
| name | status | detail file |
| --- | --- | --- |
| claw-stack | active | memory/entities/project-claw-stack.md |
| info-pipeline | active | memory/entities/project-info-pipeline.md |

## Infrastructure
| service | notes | detail file |
| --- | --- | --- |
| CF Workers | edge compute | memory/infra/cloudflare.md |

## Behavior rules
See AGENTS.md for current rules.

## Recent (last 5)
- 2026-03-09: ...

구조화된 사실(사용자, 프로젝트, 인프라)에 대한 테이블. 다른 모든 것에 대한 포인터. 5개 항목으로 제한된 최근 활동, 롤링. 총 목표: 5,000자 미만.

마이그레이션 후 MEMORY.md는 92,000자에서 2,900자로 줄었습니다. 97% 감소입니다. 세션 시작이 MEMORY.md 컨텍스트의 약 23K 토큰에서 약 700 토큰으로 개선되었습니다. MEMORY.md에 있던 모든 것은 여전히 QMD 벡터 검색을 통해 검색 가능합니다. 인라인이 아니라 주제별 파일에 있을 뿐입니다.

마이그레이션 스크립트 자체는 약 150줄의 Python이었습니다. 현재 MEMORY.md를 읽고, Claude Haiku를 사용하여 범주별로 사실을 추출하고, 사실을 적절한 주제별 파일에 작성하고, 새로운 포인터 기반 MEMORY.md를 생성합니다. 실행하는 데 20초가 걸렸습니다.

솔루션 3: 세션 인수 훅

컨텍스트 가지치기와 MEMORY.md 압축은 기술적 팽창 문제를 해결했습니다. 우리가 계속 참아온 세 번째 문제가 있었습니다. /new를 실행하여 새로운 세션을 시작할 때 현재 세션의 모든 작업 컨텍스트를 잃습니다. 어떤 파일을 편집하고 있었나요? 다음 단계는 무엇이었나요? 디버깅 중인 버그에 대해 방금 무엇을 파악했나요?

일반적인 응답은 “더 나은 메모를 작성하세요”입니다. 우리는 이를 자동화하고 싶었습니다.

OpenClaw는 특정 명령에서 실행되는 훅을 지원합니다. 우리는 새로운 세션이 시작되기 전에 세션 요약 파이프라인을 실행하는 command:new 훅을 작성했습니다:

# Triggered on /new
def session_handoff(transcript):
    summary = claude_haiku(
        system=open("MANIFEST.md").read(),  # file map for the memory system
        prompt=f"Summarize this session. Extract: current work state, "
               f"decisions made, lessons learned, entities updated. "
               f"Format as structured updates for memory files.\n\n{transcript}"
    )
    apply_memory_updates(summary)  # updates MEMORY.md, TODO.md, entities, etc.

훅은 20초 제한 시간과 함께 동기적으로 실행되고, 트랜스크립트가 빠르게 처리하기에 너무 길면 비동기로 폴백합니다. 실제로 대부분의 세션은 8~12초 내에 처리됩니다.

핵심 부분은 MANIFEST.md로, 메모리 시스템의 구조를 설명하는 파일입니다. 어떤 파일이 존재하고, 각각이 무엇을 포함하며, 어떤 종류의 업데이트가 어디로 가야 하는지. 없으면 Haiku는 프로젝트 업데이트가 MEMORY.md 직접이 아니라 memory/entities/project-X.md로 가야 한다는 것을 알 수 없습니다. MANIFEST은 메모리를 유지하는 에이전트에 대한 스키마 문서입니다.

인수 훅 후에 /new는 여전히 새로운 컨텍스트를 시작하지만, MEMORY.md는 이제 현재 세션의 결과를 반영합니다. 다음 세션은 어디서 멈췄는지 알면서 시작됩니다.

decay 방지 규칙

시스템을 두 번 재구축한 후, 우리는 같은 문제가 반복되는 것을 방지하기 위해 AGENTS.md에 명시적 규칙을 작성했습니다.

하드 제한:

  • MEMORY.md는 5,000자 미만으로 유지되어야 합니다. 업데이트가 이를 초과하면 주제별 파일에 작성하고 포인터를 추가합니다.
  • 커밋 해시, 코드 스니펫 또는 원본 오류 메시지를 MEMORY.md에 작성하지 않습니다. 이는 임시적이거나(커밋 해시, 오류) 주제별 파일에 속합니다(코드).

금지된 콘텐츠:

  • 5개 이상의 항목 목록(주제별 파일 사용)
  • 다른 메모리 파일에 이미 있는 사실(중복 없음)
  • “임시” 메모(MEMORY.md가 아니라 TODO 파일에 작성)

정기적 유지보수:

  • 3개 이상의 파일을 건드린 세션 후, 주제별 파일을 업데이트해야 하는지 확인
  • 프로젝트 상태가 변경되면, MEMORY.md 테이블이 아니라 엔터티 파일 업데이트

AGENTS.md에 작성된 규칙은 시스템 프롬프트의 일부가 되므로, 정리자 파이프라인과 인수 훅 모두 이를 봅니다. 코드로 적용되지 않지만 컨텍스트의 명시적 규칙은 비공식적 관례보다 훨씬 낫습니다.

측정된 결과

v2 변경을 배포한 후의 즉각적인 결과:

메트릭이전이후
MEMORY.md 크기~92K자(~23K 토큰)~2.9K자(~700 토큰)
세션 시작 컨텍스트 오버헤드~23K 토큰~700 토큰
컨텍스트의 도구 결과 비중82.5%30초 후 가지치기
/new 전체에서 보존된 작업 상태아니오예(자동화)

MEMORY.md 감소는 97% 감소입니다. 모든 새로운 세션은 이제 22K 토큰 적게 오버헤드로 시작하므로 실제 작업을 위한 더 많은 공간이 있습니다. 컨텍스트 가지치기 구성은 30초보다 오래된 도구 결과가 자리 표시자로 바뀌도록 하여 다단계 작업에서 중단을 야기하던 세션 내 누적을 방지합니다.

인수 훅이 일관되게 올바른 메모리 업데이트를 생성하는지 여부는 몇 주의 사용 후에 알 수 있을 것입니다. 아키텍처는 맞습니다. 질문은 Haiku의 업데이트 대상에 대한 판단이 규모 면에서 유지되는가 하는 것입니다. 다시 보고하겠습니다.

메모리에 대해 배운 것

v1 블로그 게시물은 팽창 문제를 기술적 문제로 기술적 수정과 함께 표현했습니다. 바이트 크기 제한을 적용하고, 인라인 콘텐츠 대신 포인터를 사용합니다. 그 표현은 정확하지만 불완전했습니다.

실제 문제는 메모리 관리가 저장소 문제가 아니라 정보 아키텍처 문제라는 것입니다. “이 사실은 나중에 관련이 있을 수 있으므로 MEMORY.md에 넣자”라고 말할 때마다 우리는 나쁜 인덱싱 결정을 내렸습니다. MEMORY.md는 특정 아키텍처 계층이 아닌 포괄적인 항목으로 사용되고 있었습니다.

v2 시스템은 더 나은 강제 메커니즘이 있기 때문이 아닙니다(TTL 가지치기 및 크기 제한이 도움이 되기는 함) 각 계층이 무엇을 위한 것인지에 대해 더 명확하기 때문에 작동합니다:

  • 활성 컨텍스트: 현재 세션의 작업 상태. 임시적. 공격적으로 가지치기.
  • MEMORY.md: 세션 방향. 세션을 시작하는 데 필요한 최소 컨텍스트. 포인터만.
  • 주제별 파일: 특정 주제에 대한 깊이. 요청 시 로드. 콘텐츠가 있는 곳.
  • 벡터 검색: 모든 메모리 전체의 폴백 검색. 어디를 봐야 할지 모르는 쿼리의 경우.

새로운 사실이 도착했을 때, 질문은 “이것을 기억해야 할까?”가 아닙니다. “이것이 어느 계층에 속하는가?”입니다. 대부분의 사실은 MEMORY.md에 속하지 않습니다. 아키텍처를 올바르게 파악하는 것이 팽창을 방지하는 것입니다.

에이전트 개발자를 위한 실용적인 조언

유사한 것을 구축하는 경우, 우리가 두 번 한 실수는 알 가치가 있습니다.

인덱스/콘텐츠 분리를 쓰기 시간에 적용하고, 소급적으로 적용하지 마십시오. MEMORY.md에 대한 바이트 크기 제한은 팽창을 방지하지 않습니다. 초과하기 전에 팽창을 작게 만들 뿐입니다. 진정한 제약은: 인덱스에는 콘텐츠가 없고 포인터만. 모든 쓰기에서 이를 확인합니다.

최적화하기 전에 컨텍스트 분포를 측정하십시오. 우리는 MEMORY.md가 주요 문제라고 가정했습니다. 그것은 문제였습니다. 도구 결과는 더 큰 문제였습니다. session-stats를 실행하는 데 하루가 걸렸고 즉시 더 큰 문제를 드러냈습니다. 측정 우선.

TTL 기반 컨텍스트 가지치기는 위험이 낮고 보상이 높습니다. 우리는 에이전트 동작을 깨뜨릴까봐 걱정했습니다. 그렇지 않았습니다. 대부분의 작업의 경우 오래된 도구 결과는 신호가 아니라 노이즈입니다. 가지치기하십시오.

인수 훅은 완벽한 메모 작성보다 더 가치가 있습니다. 인간(또는 에이전트)에게 세션 말미 메모를 안정적으로 작성하도록 요청하는 것은 패전 전략입니다. 자동화합니다. 10초 걸리는 대략적인 추출도 작성되지 않는 수동 메모보다 낫습니다.

메모리 시스템의 스키마를 사용하는 에이전트에 문서화합니다. MANIFEST.md 패턴(사물이 어디로 가는지 설명하는 파일)은 자동화된 메모리 업데이트가 실제로 올바른 위치에 사물을 배치하게 하는 것입니다. 없으면 모든 업데이트는 파일 배치에 대한 임시 결정이 됩니다.

AI 에이전트를 위한 메모리 시스템은 여전히 정해진 관례가 없을 정도로 어립니다. 이것들은 우리 규모에서 우리를 위해 작동한 패턴입니다. 귀사의 규모, 액세스 패턴 및 에이전트의 작업 분포는 다른 제약을 생성할 것입니다. 하지만 기본 원칙은 유지됩니다. 에이전트 메모리는 정보 아키텍처입니다. 인프라를 구축하기 전에 아키텍처를 올바르게 파악하십시오.