Memory-Bank 아키텍처 가이드
대화 세션을 384차원 임베딩과 팩트 추출로 관리하는 장기 기억 플러그인의 내부 구조
한눈에 보기
Memory Bank 플러그인이 대화를 어떻게 색인하고, 팩트를 추출·통합하고, 세션 시작 시 컨텍스트를 주입하는지 전체 데이터 흐름을 설명합니다.
- 핵심 질문: 세션이 끝나도 사라지지 않는 장기 기억은 어떤 파이프라인으로 만들어지는가?
- 읽는 대상: 에이전트 메모리 시스템의 내부 구조가 궁금한 개발자
- 연결 문서: Memory & Knowledge Pipeline, Memory & Documentation Policy, Self-Improving Knowledge Loop
개요
Memory Bank는 Claude Code 대화 세션들을 의미론적(semantic) 검색과 팩트(fact) 추출을 통해 관리하는 플러그인이다. 과거 대화 내용을 검색하고, 결정 사항/패턴/선호도 등을 자동 추출하여 장기 기억으로 활용한다.
핵심 기능:
- 의미론적 벡터 검색 (384차원 임베딩)
- 자동 팩트 추출 및 통합 (Fact consolidation)
- 프로젝트별 범위 격리 (Scope isolation)
- MCP 서버를 통한 Claude Code/Desktop 통합
디렉토리 구조
plugins/marketplaces/memory-bank-dev/
├── src/ # TypeScript 소스
│ ├── mcp-server.ts # MCP 서버 진입점 (search, read, search_facts)
│ ├── db.ts # SQLite 스키마 및 초기화
│ ├── fact-db.ts # 팩트 CRUD 및 벡터 검색
│ ├── embeddings.ts # 임베딩 생성 (Xenova/all-MiniLM-L6-v2)
│ ├── indexer.ts # JSONL 파싱 및 인덱싱
│ ├── search.ts # 벡터/텍스트 검색 로직
│ ├── fact-extractor.ts # Haiku LLM으로 팩트 추출
│ ├── consolidator.ts # 팩트 통합 (중복 제거, 모순 해결)
│ ├── parser.ts # JSONL 대화 파일 파싱
│ ├── sync.ts # 파일 동기화 및 아카이빙
│ ├── llm.ts # Anthropic API 래퍼
│ ├── ontology-db.ts # 온톨로지 도메인/카테고리 관리
│ ├── avatar-responder.ts # AI 아바타 응답
│ ├── paths.ts # 설정 경로 해석
│ ├── types.ts # 공유 인터페이스
│ └── constants.ts # 상수 정의
│
├── cli/ # CLI 진입점
│ ├── memory-bank.js # 메인 CLI (sync, search, show, stats, index)
│ ├── mcp-server-wrapper.js # Claude Desktop MCP 래퍼
│ └── mcp-server # MCP 서버 실행
│
├── .claude-plugin/
│ └── plugin.json # 플러그인 매니페스트
│
├── hooks/ # Claude Code 훅
│ ├── hooks.json # SessionStart/SessionEnd/UserPromptSubmit
│ ├── inject-context.sh # 컨텍스트 주입
│ └── capture-decision.sh # 결정 캡처
│
├── scripts/ # 훅 스크립트
│ ├── fact-extract-hook.js # SessionEnd: 팩트 추출
│ ├── fact-consolidate-hook.js # SessionStart: 팩트 통합
│ ├── sync-export-hook.js # SessionEnd: 대화 내보내기
│ ├── sync-import-hook.js # SessionStart: 대화 임포트
│ ├── inject-context.js # 프롬프트에 컨텍스트 주입
│ └── capture-decision.js # 사용자 결정 캡처
│
├── agents/
│ └── search-conversations.md # 대화 검색 에이전트 (Haiku)
│
├── test/ # Vitest 테스트
│ ├── integration.test.ts
│ ├── consolidator.test.ts
│ ├── fact-*.test.ts
│ └── fixtures/
│
└── ui/ # 웹 대시보드 UI
전체 데이터 흐름
Claude Code 세션
↓ (SessionEnd)
[대화 JSONL] → ~/.claude/projects/
↓
[sync-export-hook] → 복사 → ~/.config/superpowers/conversation-archive/
↓
[indexer] → 파싱 + 임베딩 + DB 저장
├─ Parser → 교환(exchange) 추출
├─ Embeddings → 384차원 벡터 생성
└─ DB → exchanges + vec_exchanges 저장
↓
[fact-extractor] → Haiku로 팩트 추출
└─ DB → facts + vec_facts 저장
════════════════════════════════════════════
↓ (SessionStart)
[fact-consolidate-hook]
├─ Consolidator → 새 팩트 통합
│ ├─ 벡터 검색 (유사 팩트 찾기, threshold: 0.85)
│ ├─ Haiku 판정 (DUPLICATE/CONTRADICTION/EVOLUTION/INDEPENDENT)
│ └─ DB 업데이트 + fact_revisions 기록
└─ 상위 10개 팩트 → stdout으로 컨텍스트 주입
════════════════════════════════════════════
↓ (사용자 검색)
[mcp-server] → search / read / search_facts 도구
↓
[search.ts]
├─ Vector: 임베딩 + 코사인 유사도
├─ Text: LIKE 와일드카드
└─ Both: 병합 + 중복 제거
핵심 컴포넌트
MCP 서버 (mcp-server.ts)
Claude Code/Desktop과의 유일한 통신 지점. 3가지 도구를 제공한다.
| 도구명 | 입력 | 설명 |
|---|---|---|
search | query, mode, limit, after, before | 대화 검색 (의미론적/텍스트) |
read | path, startLine?, endLine? | 전체 대화 표시 (페이징) |
search_facts | query, project?, category?, limit | 추출된 팩트 검색 |
데이터베이스 스키마 (db.ts, fact-db.ts)
위치: ~/.config/superpowers/conversation-index/db.sqlite
-- 대화 교환
exchanges (
id TEXT PRIMARY KEY, -- MD5(archivePath:lineStart-lineEnd)
project TEXT, -- 프로젝트명
timestamp TEXT, -- ISO 시간
user_message TEXT, -- 사용자 메시지
assistant_message TEXT, -- 어시스턴트 응답
archive_path TEXT, -- 아카이브 파일 경로
embedding BLOB, -- 384차원 Float32Array
session_id TEXT, -- 세션 ID
cwd TEXT, git_branch TEXT, ...
)
-- 벡터 인덱스 (가상 테이블)
vec_exchanges (
id TEXT PRIMARY KEY,
embedding FLOAT32[384] -- sqlite-vec 벡터 타입
)
-- 추출된 팩트
facts (
id TEXT PRIMARY KEY, -- UUID
fact TEXT, -- 팩트 내용
category TEXT, -- decision|preference|pattern|knowledge|constraint
scope_type TEXT, -- global|project
scope_project TEXT, -- 프로젝트명
source_exchange_ids TEXT, -- JSON 배열
embedding BLOB, -- 384차원
consolidated_count INTEGER, -- 통합 횟수
is_active BOOLEAN,
created_at TEXT, updated_at TEXT
)
-- 팩트 벡터 인덱스
vec_facts (
id TEXT PRIMARY KEY,
embedding FLOAT32[384]
)
-- 팩트 수정 이력
fact_revisions (
id, fact_id, previous_fact, new_fact, reason, source_exchange_id, created_at
)
-- 온톨로지
ontology_domains (id, name, description, created_at)
ontology_categories (id, domain_id, name, description, created_at)
ontology_relations (id, source_fact_id, target_fact_id, relation_type, created_at)
-- 도구 호출 기록
tool_calls (id, exchange_id, tool_name, tool_input, tool_result, is_error, timestamp)
임베딩 (embeddings.ts)
| 항목 | 값 |
|---|---|
| 모델 | Xenova/all-MiniLM-L6-v2 |
| 차원 | 384 |
| 라이브러리 | @xenova/transformers (로컬 실행) |
| 텍스트 제한 | 2000자 truncate |
| 정규화 | 평균 풀링 + L2 정규화 |
검색 (search.ts)
3가지 모드:
- Vector - 코사인 유사도로 의미론적 검색
- Text - LIKE 와일드카드로 정확 매칭
- Both - 두 결과 병합 + 중복 제거
다중 개념 검색 지원 (2-5개 AND 조건, 각 개념별 유사도 평균)
팩트 추출 (fact-extractor.ts)
트리거: SessionEnd 훅
모델: Claude Haiku 4.5
| 카테고리 | 예시 | 범위 |
|---|---|---|
| decision | ”상태 관리로 Riverpod 사용” | project |
| preference | ”named export만 사용” | global |
| pattern | ”버그 수정 3회 재시도” | project |
| knowledge | ”API /api/v2/” | project |
| constraint | ”localStorage 사용 금지” | global |
신뢰도 0.7 이상만 저장 (0.9+ 명시적, 0.7-0.9 추론)
팩트 통합 (consolidator.ts)
트리거: SessionStart 훅
모델: Claude Haiku 4.5
| 관계 | 동작 | 예시 |
|---|---|---|
| DUPLICATE | 기존 팩트 수 증가 | ”Riverpod” + “Riverpod” |
| CONTRADICTION | 기존 비활성화, 새로 활성화 | ”Redux” → “Riverpod” |
| EVOLUTION | 기존 업데이트, 수정 기록 | ”DB 없음” → “SQLite” |
| INDEPENDENT | 둘 다 유지 | ”JWT” vs “WebSocket” |
세션 라이프사이클
SessionEnd (세션 종료)
1. sync-export-hook.js → JSONL을 아카이브로 복사
2. fact-extract-hook.js → exchanges에서 팩트 추출 (Haiku)
SessionStart (세션 시작)
1. sync-import-hook.js → 아카이브에서 대화 인덱싱
2. fact-consolidate-hook.js
├─ 새 팩트 통합 (벡터 검색 + Haiku)
├─ 상위 10개 팩트를 프로젝트 키 팩트로 출력
└─ 마지막 세션 컨텍스트 + 프로젝트 의도 예측 주입
UserPromptSubmit (프롬프트 제출)
inject-context.sh → 관련 팩트/대화 자동 주입
저장 경로
~/.config/superpowers/
├── conversation-archive/ # JSONL 백업
│ ├── project-1/
│ │ ├── uuid-1.jsonl
│ │ └── uuid-2.jsonl
│ └── project-2/
│
└── conversation-index/
├── db.sqlite # 인덱스 DB (exchanges, facts, vec_*)
└── exclude.txt # 제외 프로젝트 목록
기술 스택
| 층 | 기술 | 역할 |
|---|---|---|
| LLM | Claude Haiku 4.5 | 팩트 추출/통합 |
| 벡터 DB | SQLite + sqlite-vec | 임베딩 저장/검색 |
| 임베딩 | Xenova/all-MiniLM-L6-v2 (384d) | 텍스트 벡터화 (로컬) |
| MCP | @modelcontextprotocol/sdk | Claude 도구 통합 |
| 런타임 | Node.js ESM | TypeScript 컴파일 후 실행 |
| 번들러 | esbuild | MCP 서버 번들링 |
| 테스트 | Vitest | 단위/통합 테스트 |
성능 특성
| 작업 | 시간 | 비고 |
|---|---|---|
| 모델 로드 (첫 실행) | 10-30초 | 모델 다운로드 |
| 임베딩 생성 | 50-100ms/대화 | 2000자 truncate 후 |
| 벡터 검색 | 10-50ms | sqlite-vec |
| Haiku 호출 | 0.5-2초 | API 레이턴시 |
| 팩트 통합 | 최대 10회 Haiku | SessionStart 블로킹 |
관련 컴포넌트
self-improve와의 연계
self-improve Phase 1 → memory-bank search로 마찰 패턴 검색
→ Phase 1-C → conversation-index DB 직접 SQL 쿼리
→ facts 테이블에서 constraint/pattern 추출
→ scaffold 규칙 후보로 변환
episodic-memory와의 관계
- Memory Bank = Claude Code 전용
- Episodic Memory = Claude Desktop 전용
DO NOT INDEX마커로 중복 방지- 상호 배제적 운영
CLI 사용법
memory-bank sync # 대화 동기화
memory-bank index # 인덱싱
memory-bank search "React auth" # 의미론적 검색
memory-bank search --mode text "keyword" # 텍스트 검색
memory-bank show path/to/file.jsonl # 대화 조회
memory-bank stats # 통계
설계 원칙
- 범위 격리 - 프로젝트별 팩트 + 글로벌 팩트 공유
- 자동 통합 - Haiku LLM으로 중복/모순/진화 자동 감지
- 로컬 우선 - 벡터 생성 로컬, LLM API만 외부 호출
- 원자적 파일 - temp + rename (race condition 방지)
- 증분 인덱싱 - mtime 확인으로 변경 파일만 처리
- 스키마 마이그레이션 - idempotent ALTER TABLE
요약
Memory Bank는 Claude Code의 장기 기억 시스템이다. 세션 종료 시 자동으로 팩트를 추출하고, 세션 시작 시 통합하여 컨텍스트를 주입한다. self-improve가 이 데이터를 활용하여 scaffold를 자동 진화시킨다.
Source Notes
이 문서는 KeystoneHub에서 운영 중인 Memory Bank 플러그인의 내부 아키텍처 가이드를 공개용으로 정리한 것입니다. 저장 경로는 홈 디렉토리 기준 상대 표기만 유지했습니다. Provenance: keystone-native.