Claude Code, Cursor, Gemini CLI 등 모든 MCP 클라이언트에 영구 기억을 붙여주는 로컬 메모리 서버.
왜 이런 메모리 서버가 지금 필요한가.
2024~2026년 사이 Claude Code, Cursor, Codex, Gemini CLI, Cline 같은 AI 코딩 에이전트가 폭발적으로 늘었다. 그런데 공통 약점이 있다 — 세션이 끝나면 모든 맥락을 잊는다. 매번 새 채팅을 열 때마다 "우리 프로젝트는 Next.js이고 인증은 jose로 하고 테스트는 Vitest야…" 같은 설명을 반복해야 한다.
빌트인 메모리(CLAUDE.md, .cursorrules)도 있지만 200줄 한계가 있고 모든 내용이 한 번에 컨텍스트에 로드되어 토큰을 잡아먹는다. agentmemory는 BM25 + 벡터 + 지식 그래프로 검색되는 진짜 데이터베이스를 두고, 새 세션이 시작될 때 관련 기억만 자동으로 꺼내서 끼워주는 미들웨어다.
score = Σ 1/(k + rank), k=60이 표준. 단순하지만 강력해서 BM25 + 벡터 + 그래프 하이브리드 검색의 디폴트로 굳어졌다.한 문장으로 줄이면.
단순한 텍스트 파일 수준의 메모리가 아니라 BM25 + 벡터 + 지식 그래프로 검색되는 진짜 데이터베이스를 만들어 두고, 새 세션이 시작될 때 관련 기억만 top-K로 자동 주입한다.
Karpathy가 트윗한 "LLM Wiki 패턴"을 신뢰도 점수·라이프사이클·지식 그래프·하이브리드 검색으로 확장한 구현체. 설계 문서가 GitHub Gist에 먼저 올라가 1.2k 스타를 받고 바이럴된 후 본격적으로 코드가 공개됐다.
숫자가 강력하다 + 경쟁 우위가 명확하다.
오늘날 AI 코딩 에이전트의 가장 큰 약점은 "건망증"이다. 사용자는 매 세션마다 반복 설명해야 한다.
빌트인 CLAUDE.md는 200줄 한계 + 매 호출마다 전체 로딩으로 토큰을 잡아먹는다.
| 지표 | agentmemory | 경쟁 비교 |
|---|---|---|
| 검색 정확도 R@5 | 95.2% (LongMemEval) | mem0: 68.5% · Letta: 83.2% |
| 토큰 사용량/년 | ~170K tokens | 풀 컨텍스트 붙여넣기: 19.5M+ (사실상 불가능) |
| 비용/년 | ~$10 (로컬 임베딩이면 $0) | LLM 요약 방식: ~$500 |
| 외부 DB 의존성 | 0개 (SQLite + iii-engine 내장) | mem0: Qdrant/pgvector 필요 |
| 자동 캡처 | 12개 훅으로 자동 | mem0: 수동 add() 호출 |
add() 수동 호출. agentmemory는 12 hooks로 자동 캡처설계 문서가 GitHub Gist에 먼저 올라가 1,200 스타 / 172 포크를 받으며 화제가 됐다. Karpathy의 "LLM Wiki 패턴" 트윗에서 영감을 받았지만, 거기에 신뢰도 점수 · 라이프사이클 · 지식 그래프 · 하이브리드 검색까지 얹은 완성도가 어필했다.
런타임 · 검색 · 임베딩 · 인프라.
| 레이어 | 기술 | 역할 |
|---|---|---|
| 언어 | TypeScript / Node.js ≥ 20 | 전체 코드베이스 (118 source files, ~21,800 LOC) |
| 엔진 | iii-engine v0.11.2 (Rust) | HTTP/WS/KV/Stream/OTEL을 한 번에 제공. Postgres+Redis+Express+pm2+Prometheus 통합 대체 |
| 스토리지 | SQLite + iii KV State | 외부 DB 없음 — 모든 데이터 로컬 |
| 벡터 인덱스 | in-memory 자체 구현 | pgvector/Qdrant 같은 외부 의존 제거 |
| 패키지 배포 | npm @agentmemory/agentmemory | npx로 즉시 실행 가능 |
iii worker add 한 줄로 pubsub/cron/queue/sandbox 능력을 추가.┌────────── 검색 쿼리 ──────────┐
│ │
▼ ▼
[BM25] [Vector] [Graph]
키워드 임베딩 유사도 엔티티
└─── RRF 융합 (k=60) ───┘
세션 다양화 (session당 최대 3개)
▼
Top-K 결과 반환
| 스트림 | 역할 | 활성화 조건 |
|---|---|---|
| BM25 | 형태소 분석 + 동의어 확장된 키워드 매칭. 그리스/키릴/히브리/아랍/한·중·일까지 토큰화 지원 | 항상 |
| Vector | 임베딩 코사인 유사도. all-MiniLM-L6-v2 로컬 모델이 기본 (무료/오프라인) | 임베딩 제공자 설정 시 |
| Graph | 지식 그래프 BFS 탐색 — 엔티티 추출 후 관계 따라 확장 | 쿼리에 엔티티 감지 시 |
@xenova/transformers + all-MiniLM-L6-v2. 오프라인, 무료, API 키 불필요gemini-embedding-001 (무료 티어 제공)text-embedding-3-small ($0.02/1M)voyage-code-3 (코드 특화)embed-english-v3.0기본값은 no-op — LLM 호출 없이도 BM25 기반 요약/검색이 동작한다. 필요하면 Anthropic / Gemini / OpenAI / MiniMax / OpenRouter 키를 환경변수에 넣으면 자동 감지된다.
npm i -g @agentmemory/agentmemory 또는 npx 한 방으로 실행docker-compose.yml + iiidev/iii:0.11.2 이미지iii worker add iii-pubsub — write 팬아웃, search 유니온전체 구조도 → 메모리 파이프라인 → 4-Tier 통합.
┌──────────────────────────────────────────────────────────────┐
│ AI 코딩 에이전트들 │
│ Claude Code · Cursor · Codex · Gemini CLI · Cline · … │
└──────────┬──────────────────┬──────────────────┬─────────────┘
│ Hooks │ MCP (stdio) │ REST
▼ ▼ ▼
┌──────────────────────────────────────────────────────────────┐
│ @agentmemory/agentmemory (Node + TS) │
│ ┌─────────────┐ ┌──────────────┐ ┌─────────────────────┐ │
│ │ Capture │ │ Search │ │ Lifecycle │ │
│ │ - 12 hooks │ │ - BM25 │ │ - 4-tier 통합 │ │
│ │ - 중복제거 │ │ - Vector │ │ - 에빙하우스 망각 │ │
│ │ - 프라이버시│ │ - Graph │ │ - 자동 evict │ │
│ └─────────────┘ └──────────────┘ └─────────────────────┘ │
│ │
│ ↓↑ iii functions (mem::remember 등) │
└──────────────────────────────────────────────────────────────┘
↓ ↓ ↓
┌──────────────────────────────────────────────────────────────┐
│ iii-engine (Rust, 외부 DB 0개) │
│ HTTP :3111 │ WS :3112 │ Viewer :3113 │ Console :3114 │
│ KV State (SQLite) │ Streams (WebSocket) │ OTEL traces │
└──────────────────────────────────────────────────────────────┘
PostToolUse 훅 발생 (에이전트가 도구 사용 끝낼 때마다) └─→ SHA-256 중복제거 (5분 윈도우) └─→ 프라이버시 필터 (API키, 시크릿, <private> 태그 제거) └─→ Raw observation 저장 └─→ LLM 압축 → 구조화된 facts + concepts + narrative └─→ Vector 임베딩 생성 (6개 제공자 + 로컬) └─→ BM25 + Vector 인덱스에 등록 Stop / SessionEnd 훅 발생 (세션 끝) └─→ 세션 요약 └─→ 지식 그래프 추출 (옵션: GRAPH_EXTRACTION_ENABLED) └─→ Slot reflection (옵션: SLOT_REFLECT_ENABLED) SessionStart 훅 발생 (새 세션 시작) └─→ 프로젝트 프로파일 로드 (top 컨셉, 파일, 패턴) └─→ 하이브리드 검색 실행 └─→ Token budget 적용 (기본 2000 토큰) └─→ 대화 컨텍스트에 주입
사람의 뇌가 단기/장기 기억을 분리해서 잠자는 동안 통합(consolidation)하는 것에서 영감을 얻었다. Working → Episodic → Semantic → Procedural 4단계로 압축·정착시킨다.
| 티어 | 저장 내용 | 일상 비유 |
|---|---|---|
| Working | 도구 사용에서 나온 raw observation | "방금 본 것" (단기 기억) |
| Episodic | 압축된 세션 요약 | "무슨 일이 있었나" |
| Semantic | 추출된 사실 / 패턴 | "내가 아는 것" |
| Procedural | 워크플로 / 의사결정 패턴 | "어떻게 하는지" |
자주 접근되는 기억은 강화되고, 안 쓰는 건 시간에 따라 decay된다. 모순되는 기억은 자동 감지되어 새 버전이 옛 버전을 supersede한다. 그래서 메모리가 무한정 부풀지 않는다 — 사람이 잠자며 기억을 통합하는 것과 같은 원리.
| 훅 이름 | 저장되는 내용 |
|---|---|
SessionStart | 프로젝트 경로, 세션 ID |
UserPromptSubmit | 사용자 프롬프트 (프라이버시 필터링) |
PreToolUse | 파일 접근 패턴 + 보강된 컨텍스트 |
PostToolUse | 도구 이름, 입력, 출력 |
PostToolUseFailure | 에러 컨텍스트 |
PreCompact | 컨텍스트 압축 직전 메모리 재주입 |
SubagentStart/Stop | 서브 에이전트 라이프사이클 |
Stop | 세션 종료 요약 |
"많이 기억할수록 좋다"고 생각하면 결국 모든 세션에 수천 토큰의 무관한 기억이 주입되어 LLM 컨텍스트를 채우고 노이즈가 된다. CLAUDE.md 200줄 제한이 사실 의도된 보호장치였던 셈.
agentmemory는 SessionStart에서 "주입할 토큰 예산"(기본 2000)을 정하고, 그 안에서 가장 관련도 높은 top-K만 골라 넣는다. 검색 자체는 무제한이지만 컨텍스트에 들어가는 양은 엄격히 제한.
어디에 뭐가 있는지.
레포 자체는 "118 source files · ~21,800 LOC · 950+ tests · 123 functions · 34 KV scopes"로 꽤 크다. 핵심 폴더만 보면:
agentmemory/ ├── src/ │ ├── cli.ts # `agentmemory` 명령어 진입점 (start/stop/connect/doctor/upgrade) │ ├── triggers/api.ts # 124개 REST 엔드포인트 정의 (port 3111) │ ├── … # 메모리 파이프라인, 훅 핸들러, 검색 로직 │ ├── plugin/ # 각 에이전트별 네이티브 플러그인 │ ├── .claude-plugin/ # → 12 hooks + 8 skills + MCP 자동 와이어링 │ ├── .codex-plugin/ # → 6 hooks (Codex 호환 이벤트만) │ └── opencode/ # → 22 auto-capture hooks (OpenCode 가장 깊은 통합) │ ├── integrations/ # 비-네이티브 통합 가이드 │ ├── openclaw/ │ ├── hermes/ # ~/.hermes/config.yaml memory.provider │ └── pi/ │ ├── benchmark/ # 성능 측정 자료 │ ├── LONGMEMEVAL.md # ICLR 2025 벤치 (500 Q) │ ├── QUALITY.md │ ├── SCALE.md │ └── COMPARISON.md # mem0, Letta, Khoj, claude-mem, Hippo와 비교 │ ├── eval/ # 평가 하네스 (adapter-pluggable) ├── docs/benchmarks/ # 공개 스코어카드 (NDJSON) │ ├── deploy/ # 원클릭 배포 템플릿 │ ├── fly/ # fly.io (auto_stop_machines) │ ├── railway/ # Railway Hobby 플랜 │ ├── render/ # Render Blueprint │ └── coolify/ # 셀프호스팅 VPS │ ├── examples/python/ # iii-sdk로 직접 호출하는 파이썬 예제 ├── eval/README.md # 벤치마크 재현 방법 └── docker-compose.yml # iiidev/iii:0.11.2 + agentmemory
처음 코드를 뜯어볼 때는 src/cli.ts → src/triggers/api.ts → plugin/.claude-plugin/ 순서로 보면 "명령어가 어떻게 REST 엔드포인트를 호출하고, 그 엔드포인트가 어떤 훅에서 트리거되는지" 흐름을 따라가기 좋다.
기술 영역마다 무엇을 가져갈지.
~/.npm/_npx에 버전이 고정되는 함정과 -y …@latest 우회법실습: 자신의 npm 패키지를 npm install -g와 npx 양쪽으로 동작하게 만들어보기
실습: 같은 문서 집합에 BM25만, 벡터만, RRF 융합 셋을 각각 돌려 R@5를 비교
tools / resources / prompts / skills 4종 프리미티브의 차이실습: memory_recall만 구현한 미니 MCP 서버를 Claude Desktop에 붙여 본다
@xenova/transformers로 브라우저/Node에서 트랜스포머 모델 직접 실행실습: all-MiniLM-L6-v2를 띄워 본인 문서 100개에 대해 검색 R@5를 측정
실습: 같은 사실의 "구버전/신버전" 두 메모리를 넣고 자동 superseding 동작 관찰
memory_smart_search 한 번이 BM25 scan → embedding lookup → RRF → reranker로 어떻게 워터폴 trace에 찍히는지 보기실습: iii console(:3114)에서 trace를 열어 가장 느린 span을 찾아본다
최소 사양 + OS별 함정.
| 항목 | 요구사항 |
|---|---|
| OS | macOS / Linux / Windows 10·11 |
| Node.js | ≥ 20 |
| 외부 DB | 없음 (SQLite + iii KV 내장) |
| iii-engine | v0.11.2 (네이티브 바이너리 또는 Docker 이미지) |
| 임베딩 (옵션) | 로컬 모델 사용 시 ~100MB 디스크, CPU 추론으로 충분 |
| 포트 | 3111 (REST) · 3112 (WS) · 3113 (Viewer) · 3114 (iii Console) |
| 메모리 | Node 프로세스 기본 + 캐시 — 일반 노트북에서 무리 없음 |
iii-engine은 sh 설치 스크립트만 공식 지원한다. Windows에서는 다음 3가지 옵션 중 하나를 골라야 한다:
① GitHub Release에서 iii-x86_64-pc-windows-msvc.zip 다운로드 후 PATH에 넣기. ② Docker Desktop을 띄우기. ③ MCP만 필요하면 npx -y @agentmemory/mcp로 엔진 없이 쓰기.
난이도별 실습 — Easy → Hard.
1) 터미널 1에서 npx @agentmemory/agentmemory 2) 터미널 2에서 npx @agentmemory/agentmemory demo 3) http://localhost:3113 실시간 뷰어 열기 4) JWT 인증/N+1 쿼리/레이트 리미팅 3개 세션이 시드된다 5) "database performance optimization"으로 검색 → N+1 쿼리 세션이 잡힘
배우는 것: 키워드만으로는 불가능한 시맨틱 매칭의 위력
1) Claude Code: /plugin marketplace add rohitg00/agentmemory → /plugin install agentmemory 2) Cursor: ~/.cursor/mcp.json에 "agentmemory" MCP 서버 등록 (npx -y @agentmemory/mcp) 3) 두 에이전트에서 같은 메모리가 공유되는지 확인 4) curl http://localhost:3111/agentmemory/health로 헬스체크
배우는 것: MCP 서버 와이어링 패턴, 멀티 클라이언트 공유
1) cd agentmemory/eval 2) README에 따라 adapter(grep / vector / agentmemory) 선택 3) LongMemEval-S(500문제) 또는 in-house coding-agent-life-v1 실행 4) 결과는 docs/benchmarks/ 아래 NDJSON으로 저장 5) 세 어댑터의 R@5 옆에 놓고 비교
배우는 것: IR 벤치마크 재현 방법, adapter 패턴 설계
1) 이미 쌓인 Claude Code 대화 로그(~/.claude/projects/) 확인 2) npx @agentmemory/agentmemory import-jsonl 3) Replay 탭에서 키보드 단축키(space, 화살표)로 재생 4) 과거 세션의 컨텍스트가 새 세션에 어떻게 주입되는지 관찰
배우는 것: 기존 대화 자산을 메모리로 변환하는 마이그레이션 패턴
1) iii console --port 3114 --enable-flow 띄우기 2) memory_smart_search 한 번 호출 후 Traces 탭에서 워터폴 확인 3) 가장 느린 span(보통 임베딩 fetch 또는 RRF reranker) 찾기 4) KV browser에서 stuck consolidation timer를 직접 편집 5) fly deploy 후 flyctl ssh console -L 3113:127.0.0.1:3113로 SSH 터널 — 퍼블릭에는 3111만, 뷰어는 로컬에서만
배우는 것: OTel trace 분석, 보안 디폴트(public/private 포트 분리), 클라우드 배포
주차별로 따라가면 전체 스택 마스터.
Andrej Karpathy의 LLM Wiki 패턴 글 읽기 (이 프로젝트의 모태) / Anthropic 공식 MCP 스펙 훑어보기 / agentmemory demo 돌려보고 BM25 vs Vector 차이 체감
BM25 수식 손으로 풀기 (TF, IDF, length normalization) / 코사인 유사도 vs Dot product vs Euclidean / RRF 논문(Cormack et al., 2009) — k=60이 왜 표준인지
sentence-transformers / @xenova/transformers로 직접 임베딩 생성 / HNSW · IVF-PQ 등 ANN 인덱스 비교 / "로컬 임베딩이 충분히 좋은가" — MTEB 리더보드 확인
@modelcontextprotocol/sdk로 최소한의 MCP 서버 작성 / tools / resources / prompts 차이 이해 / Cursor + Claude Desktop 둘 다에 같은 서버 붙여 보기
망각 곡선 모델링: strength = e^(-t/τ) + 접근 시 reset / 모순 감지: 임베딩 + LLM 판정 하이브리드 / 같은 사실의 두 버전을 시간차로 넣어 supersession 트리거 확인
OpenTelemetry trace/span 구조 / fly.io · Railway · Coolify 배포 비용 비교 / 본인 워크플로에 agentmemory를 "상시 켜둔 채" 한 주 사용 — 이게 진짜 학습
자주 마주칠 단어들.
score = Σ 1/(k + rank), k=60이 표준. 하이브리드 검색의 디폴트.공식 · 비교 · 학습.
npx @agentmemory/agentmemory와 … demo를 각각 다른 터미널에서 띄우고 localhost:3113 뷰어 열기. "database performance optimization"으로 검색해 시맨틱 매칭의 느낌 체득./plugin install agentmemory 또는 ~/.cursor/mcp.json 한 줄로 붙이기. 두 에이전트가 같은 메모리를 공유하는지 확인.eval/에서 grep vs vector vs agentmemory의 R@5를 본인 머신에서 재현. 숫자를 직접 만져보면 알고리즘 차이가 와닿는다.memory_smart_search 한 번 호출 후 :3114 콘솔의 Traces 탭에서 워터폴 보기. 가장 느린 span 찾으면 OTel 디버깅이 손에 익는다.