LLM 에이전트 메모리가 꽉 찼다 — 4계층 구조로 풀기까지
에이전트를 매일 쓰다 보면 어느 날 문득 '상주 메모리'라는 게 눈에 들어옵니다. 매 턴마다 모델이 처음부터 읽어야 하는 2,200자짜리 작은 파일인데, 어느 순간 그 파일이 꽉 차 버렸습니다.
그 파일 하나에 취향과 환경 설정, 경고 문구는 물론이고 시한부 정보까지 전부 몰아넣고 있었습니다. 저장 규칙이 없으니 새 정보는 전부 제일 위에 쌓였고, 한도가 2,200자니 금방입니다. 꽉 차면 뭔가가 밀려나고, 밀려난 게 나중에 꼭 필요해집니다. 잊힘이 생기는 구조였습니다.
2,200자가 90%까지 찼다
실측을 해보니 90%까지 차 있었습니다. 남는 자리가 220자, 그러니까 한두 문장이었고, 그때 새 사실 하나가 들어오면 기존 항목을 밀어내야 했습니다. 어떤 항목이 밀려나는지는 그때그때 달랐습니다 — 정리 규칙 같은 게 없었으니까요.
문제의 핵심은 '사실의 등급'이 없다는 것이었습니다. 모델 벤치 결과, 리모트 서버 주소, 자동화 상태 같은 시한부 정보가 취향·환경 설정과 같은 선반에 섞여 있었습니다. 유통기한이 있는 우유와 신문 스크랩을 같은 냉장고 선반에 넣어둔 꼴이었죠. 자주 쓰는 사실은 저절로 위로 올라가고 낡은 사실은 가라앉는데, 위로 올릴 자리 자체가 이미 없었습니다. 사실이 살 곳이 없던 게 근본 문제였습니다.

브레인으로는 안 풀린다
가장 먼저 든 생각은 '검색을 쓰면 되는 거 아닌가'였습니다. 세컨드 브레인에 시맨틱 RAG를 이미 돌리고 있었으니까요. 그런데 이건 처음부터 실패가 뻔했습니다. 시맨틱 RAG는 깊은 지식 검색용이지, 매 턴 주입되는 상시 기억용이 아니거든요. 검색은 명시적인 질문이 있을 때만 빛을 발합니다. '지금 이걸 기억해야 한다'는 상시 기억은 질문 없이도 떠 있어야 하는데, 검색은 그걸 못 합니다.
Obsidian 복귀도 고민했지만 이것도 오답이었습니다. 노트 정리와 큐레이션 부담을 다시 사람에게 지우는 것이니까요. 정리로 시간을 만드는 게 아니라, 정리할 시간을 새로 만들어야 하는 셈이었습니다.
결국 병목은 저장소가 아니라 '쓰기 규칙'과 '자동화'였습니다. 어디에 뭘 저장할지, 언제 회상할지가 규칙으로 정해져 있지 않으니 아무리 좋은 저장소를 들여와도 같은 삽질을 반복할 뿐이었습니다. 기분이 좀 씁쓸했습니다 — 도구가 아니라 제 절차가 문제였다는 걸 인정해야 했으니까요.
provider 3개를 직접 깨봤다
그래서 Hermes가 지원하는 메모리 provider 3개를 격리 환경에서 동일한 기준으로 직접 실행해 봤습니다. 기준은 두 가지였습니다. 사실을 쓰고 다시 찾아오는 '쓰기→검색 왕복'이 되는지, 그리고 자동 프리페치, 즉 회상 주입이 매 턴 붙는지.
| provider | 도구 수 | 쓰기→검색 왕복 | 자동 프리페치 | 판정 |
|---|---|---|---|---|
| Local Memory | 17개 | 통과 | 259자 | 채택 |
| Mnemosyne | 40개 | 통과 | 훅 경고 | 보류 |
| Holographic | 2개 | 복합어 검색 실패 | 0자 | 탈락 |
결과부터 말하면 Local Memory가 이겼습니다. 로컬 SQLite 기반이고 FTS5 전문 검색이 기본으로 붙어 있었고, 자동 프리페치가 259자로 실제로 주입됐습니다. Mnemosyne는 도구가 40개로 가장 화려했지만 훅 경고가 떠서 보류, Holographic은 복합어 검색이 실패하고 프리페치가 0자라서 바로 탈락이었습니다.
이 과정에서 하나가 분명해졌습니다. 상시 기억과 심층 지식은 저장소가 다르다는 것, 그리고 기본 내장 메모리만으로는 이 문제가 원천적으로 안 풀린다는 것이었습니다. 외부 provider가 기본 메모리 위에 얹히는 방식으로 추가된다는 걸 직접 보게 된 것도 이때였습니다.

4계층으로 나누기
그래서 정한 구조가 4계층입니다. L1 상주 메모리는 취향·환경·경고처럼 매 턴 반드시 필요한 것만, L2 로컬 메모리(이번에 신설)는 시한부·운영 사실을 SQLite+FTS5로 자동 저장·회상, L3 브레인 RAG는 심층 지식을 벡터 검색으로 온디맨드, L4 그래프는 개념 간 연결 관계를 담당합니다.
핵심은 규칙 하나였습니다. "저장 규칙이 없는 사실은 전부 L2로." 시한부 정보가 더 이상 상주 메모리를 위협하지 않게 된 겁니다. 자주 묻는 취향은 L1에 남고, 어제 바뀐 운영 상태는 L2로 내려가고, 논문 한 편의 지식은 L3가, 개념 간 연결은 L4가 책임집니다. 각 층이 할 일이 정해지니까 '이건 어디에 넣지'라는 고민 자체가 사라졌습니다.

40 facts를 SQLite로, 그리고 매일 아침 리포트
이주는 의외로 순조로웠습니다. 기존 상주 메모리의 24개와 운영 레지스트리(AUTOMATION.md)에서 추린 15개를 재구성했습니다. 총 40 facts, 전부 SQLite로 옮겼습니다. 이주는 멱등하게, 즉 몇 번을 다시 돌려도 중복이 생기지 않게 짰고, dry-run → 리뷰 → apply 3단계로 진행했습니다. apply 전에 무엇이 옮겨지는지 사람 눈으로 확인할 수 있게 한 거죠. 백업도 자동으로 걸어두었습니다.

이주 후 운영은 매일 아침 07:30 크론 하나로 요약됩니다. Slack으로 리포트가 옵니다. Phase A는 L2 정리 dry-run, Phase B는 브레인 재동기화입니다. 시간을 새벽이 아니라 아침으로 고른 이유는 밤 부하 때문이었습니다. 모델 사용이 몰리는 밤을 피해서 조용히 돌리는 거죠. 정리 작업용 모델은 로컬 워크스테이션의 qwen이 맡고, 메인 대화용 deepseek(GB10 듀얼)는 건드리지 않습니다.
리포트만 온다는 점이 이 운영의 핵심입니다. apply는 없습니다. 자동 삭제나 자동 정리를 하지 않고, 오늘 정리 후보가 무엇인지만 보여줍니다. 안전 우선이라서요.

아직 반자동입니다
여기까지 쓰니 성공담처럼 보일 텐데, 한계도 또렷합니다. 선택한 provider가 아직 alpha 단계 소프트웨어라 첫 1~2주는 리포트를 눈으로 확인하는 수동 운영 중입니다. 자동 정리(dry-run → apply)도 아직 반자동이라 후보만 제시하고, 적용은 사람이 확인한 뒤에 합니다. 실수로 뭘 날릴까 봐 그러는 건데, 그러다 보니 아침마다 리포트를 읽는 일이 하나 늘었습니다. 별 수 없습니다.
다음 계획은 두 가지입니다. 리포트를 메신저, 예를 들어 텔레그램으로 직접 수신하게 하고, 운영 레지스트리의 변경분이 자동으로 반영되게 하는 것. 둘 다 되면 아침마다 슬랙을 열어 확인하는 일도 사라집니다. 그때까지는 아침 알림을 확인하는 일상을 유지합니다. 씁쓸하지만 이게 지금의 현실이니까요.
'공부 > 인공지능' 카테고리의 다른 글
| DGX Spark 2대, 1M 컨텍스트 도전기 2. (부팅은 정복, 1M은 여전히 벽) (0) | 2026.08.29 |
|---|---|
| H3를 직접 돌려봤다. Wan 2.2와 스프라이트 7모션 비교 (레고 로봇) (0) | 2026.08.29 |
| 게임 스프라이트를 로컬 Wan 2.2로 뽑아봤다. (핑크팬더 5모션) (0) | 2026.08.28 |
| DGX Spark 2대, 1M 컨텍스트 도전기. (Qwen3.8-Flash-Next day-0) (0) | 2026.08.28 |
| [세컨드브레인 개발기] 16. 122B와 27B — 커도, 빠른 것도 다 이긴 건 아니었다 (0) | 2026.08.26 |