claude-memory-layer 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/commands/memory-forget.md +42 -0
- package/.claude-plugin/commands/memory-history.md +34 -0
- package/.claude-plugin/commands/memory-import.md +56 -0
- package/.claude-plugin/commands/memory-list.md +37 -0
- package/.claude-plugin/commands/memory-search.md +36 -0
- package/.claude-plugin/commands/memory-stats.md +34 -0
- package/.claude-plugin/hooks.json +59 -0
- package/.claude-plugin/plugin.json +24 -0
- package/.history/package_20260201112328.json +45 -0
- package/.history/package_20260201113602.json +45 -0
- package/.history/package_20260201113713.json +45 -0
- package/.history/package_20260201114110.json +45 -0
- package/Memo.txt +558 -0
- package/README.md +520 -0
- package/context.md +636 -0
- package/dist/.claude-plugin/commands/memory-forget.md +42 -0
- package/dist/.claude-plugin/commands/memory-history.md +34 -0
- package/dist/.claude-plugin/commands/memory-import.md +56 -0
- package/dist/.claude-plugin/commands/memory-list.md +37 -0
- package/dist/.claude-plugin/commands/memory-search.md +36 -0
- package/dist/.claude-plugin/commands/memory-stats.md +34 -0
- package/dist/.claude-plugin/hooks.json +59 -0
- package/dist/.claude-plugin/plugin.json +24 -0
- package/dist/cli/index.js +3539 -0
- package/dist/cli/index.js.map +7 -0
- package/dist/core/index.js +4408 -0
- package/dist/core/index.js.map +7 -0
- package/dist/hooks/session-end.js +2971 -0
- package/dist/hooks/session-end.js.map +7 -0
- package/dist/hooks/session-start.js +2969 -0
- package/dist/hooks/session-start.js.map +7 -0
- package/dist/hooks/stop.js +3123 -0
- package/dist/hooks/stop.js.map +7 -0
- package/dist/hooks/user-prompt-submit.js +2960 -0
- package/dist/hooks/user-prompt-submit.js.map +7 -0
- package/dist/services/memory-service.js +2931 -0
- package/dist/services/memory-service.js.map +7 -0
- package/package.json +45 -0
- package/plan.md +1642 -0
- package/scripts/build.ts +102 -0
- package/spec.md +624 -0
- package/specs/citations-system/context.md +243 -0
- package/specs/citations-system/plan.md +495 -0
- package/specs/citations-system/spec.md +371 -0
- package/specs/endless-mode/context.md +305 -0
- package/specs/endless-mode/plan.md +620 -0
- package/specs/endless-mode/spec.md +455 -0
- package/specs/entity-edge-model/context.md +401 -0
- package/specs/entity-edge-model/plan.md +459 -0
- package/specs/entity-edge-model/spec.md +391 -0
- package/specs/evidence-aligner-v2/context.md +401 -0
- package/specs/evidence-aligner-v2/plan.md +303 -0
- package/specs/evidence-aligner-v2/spec.md +312 -0
- package/specs/mcp-desktop-integration/context.md +278 -0
- package/specs/mcp-desktop-integration/plan.md +550 -0
- package/specs/mcp-desktop-integration/spec.md +494 -0
- package/specs/post-tool-use-hook/context.md +319 -0
- package/specs/post-tool-use-hook/plan.md +469 -0
- package/specs/post-tool-use-hook/spec.md +364 -0
- package/specs/private-tags/context.md +288 -0
- package/specs/private-tags/plan.md +412 -0
- package/specs/private-tags/spec.md +345 -0
- package/specs/progressive-disclosure/context.md +346 -0
- package/specs/progressive-disclosure/plan.md +663 -0
- package/specs/progressive-disclosure/spec.md +415 -0
- package/specs/task-entity-system/context.md +297 -0
- package/specs/task-entity-system/plan.md +301 -0
- package/specs/task-entity-system/spec.md +314 -0
- package/specs/vector-outbox-v2/context.md +470 -0
- package/specs/vector-outbox-v2/plan.md +562 -0
- package/specs/vector-outbox-v2/spec.md +466 -0
- package/specs/web-viewer-ui/context.md +384 -0
- package/specs/web-viewer-ui/plan.md +797 -0
- package/specs/web-viewer-ui/spec.md +516 -0
- package/src/cli/index.ts +570 -0
- package/src/core/canonical-key.ts +186 -0
- package/src/core/citation-generator.ts +63 -0
- package/src/core/consolidated-store.ts +279 -0
- package/src/core/consolidation-worker.ts +384 -0
- package/src/core/context-formatter.ts +276 -0
- package/src/core/continuity-manager.ts +336 -0
- package/src/core/edge-repo.ts +324 -0
- package/src/core/embedder.ts +124 -0
- package/src/core/entity-repo.ts +342 -0
- package/src/core/event-store.ts +672 -0
- package/src/core/evidence-aligner.ts +635 -0
- package/src/core/graduation.ts +365 -0
- package/src/core/index.ts +32 -0
- package/src/core/matcher.ts +210 -0
- package/src/core/metadata-extractor.ts +203 -0
- package/src/core/privacy/filter.ts +179 -0
- package/src/core/privacy/index.ts +20 -0
- package/src/core/privacy/tag-parser.ts +145 -0
- package/src/core/progressive-retriever.ts +415 -0
- package/src/core/retriever.ts +235 -0
- package/src/core/task/blocker-resolver.ts +325 -0
- package/src/core/task/index.ts +9 -0
- package/src/core/task/task-matcher.ts +238 -0
- package/src/core/task/task-projector.ts +345 -0
- package/src/core/task/task-resolver.ts +414 -0
- package/src/core/types.ts +841 -0
- package/src/core/vector-outbox.ts +295 -0
- package/src/core/vector-store.ts +182 -0
- package/src/core/vector-worker.ts +488 -0
- package/src/core/working-set-store.ts +244 -0
- package/src/hooks/post-tool-use.ts +127 -0
- package/src/hooks/session-end.ts +78 -0
- package/src/hooks/session-start.ts +57 -0
- package/src/hooks/stop.ts +78 -0
- package/src/hooks/user-prompt-submit.ts +54 -0
- package/src/mcp/handlers.ts +212 -0
- package/src/mcp/index.ts +47 -0
- package/src/mcp/tools.ts +78 -0
- package/src/server/api/citations.ts +101 -0
- package/src/server/api/events.ts +101 -0
- package/src/server/api/index.ts +18 -0
- package/src/server/api/search.ts +98 -0
- package/src/server/api/sessions.ts +111 -0
- package/src/server/api/stats.ts +97 -0
- package/src/server/index.ts +91 -0
- package/src/services/memory-service.ts +626 -0
- package/src/services/session-history-importer.ts +367 -0
- package/tests/canonical-key.test.ts +101 -0
- package/tests/evidence-aligner.test.ts +152 -0
- package/tests/matcher.test.ts +112 -0
- package/tsconfig.json +24 -0
- package/vitest.config.ts +15 -0
package/context.md
ADDED
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
# Context: Claude Code Memory Plugin
|
|
2
|
+
|
|
3
|
+
## 1. 프로젝트 배경
|
|
4
|
+
|
|
5
|
+
이 문서는 Claude Code용 Memory Plugin 개발을 위한 배경 연구와 참조 자료를 정리합니다.
|
|
6
|
+
|
|
7
|
+
### 1.1 목표
|
|
8
|
+
|
|
9
|
+
사용자가 Claude Code를 사용할수록 더 똑똑해지는 Agent를 만들기 위한 플러그인 개발:
|
|
10
|
+
- 사용자 prompt와 agent 응답을 지속적으로 기억
|
|
11
|
+
- 새로운 prompt 입력 시 관련된 과거 기억을 검색하여 컨텍스트로 활용
|
|
12
|
+
- 시간이 지남에 따라 개인화된 경험 제공
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 2. Claude Code Plugin System 분석
|
|
17
|
+
|
|
18
|
+
### 2.1 플러그인 구조
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
plugin-name/
|
|
22
|
+
├── .claude-plugin/
|
|
23
|
+
│ └── plugin.json # 플러그인 메타데이터 (필수)
|
|
24
|
+
├── commands/ # 슬래시 명령어 (선택)
|
|
25
|
+
├── agents/ # 전문화된 에이전트 (선택)
|
|
26
|
+
├── skills/ # 에이전트 스킬 (선택)
|
|
27
|
+
├── hooks/ # 이벤트 핸들러 (선택)
|
|
28
|
+
├── .mcp.json # MCP 서버 구성 (선택)
|
|
29
|
+
└── README.md # 문서
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 2.2 사용 가능한 Hook 이벤트
|
|
33
|
+
|
|
34
|
+
| Hook Event | 용도 | Memory Plugin 활용 |
|
|
35
|
+
|------------|------|-------------------|
|
|
36
|
+
| `SessionStart` | 세션 시작 시 실행 | 이전 세션 기억 로드 |
|
|
37
|
+
| `SessionEnd` | 세션 종료 시 실행 | 현재 세션 기억 저장 |
|
|
38
|
+
| `UserPromptSubmit` | 사용자 입력 시 실행 | 관련 기억 검색 및 주입 |
|
|
39
|
+
| `PreToolUse` | 도구 실행 전 | 도구별 과거 사용 패턴 제공 |
|
|
40
|
+
| `PostToolUse` | 도구 실행 후 | 도구 결과 기억 |
|
|
41
|
+
| `Stop` | Agent 응답 완료 시 | 전체 대화 기억 저장 |
|
|
42
|
+
| `PreCompact` | 컨텍스트 압축 전 | 중요 기억 보존 |
|
|
43
|
+
|
|
44
|
+
### 2.3 Hook Input/Output 형식
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
// UserPromptSubmit hook input
|
|
48
|
+
{
|
|
49
|
+
"session_id": "...",
|
|
50
|
+
"prompt": "사용자가 입력한 텍스트",
|
|
51
|
+
"timestamp": "..."
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Hook은 stdout으로 결과 반환
|
|
55
|
+
// - 빈 출력: 변경 없음
|
|
56
|
+
// - JSON 출력: 컨텍스트 주입 또는 수정
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 3. AI Memory System 연구
|
|
62
|
+
|
|
63
|
+
### 3.1 Memory의 종류
|
|
64
|
+
|
|
65
|
+
| 유형 | 설명 | 지속성 |
|
|
66
|
+
|------|------|--------|
|
|
67
|
+
| **Short-term Memory** | 현재 대화 컨텍스트 | 세션 내 |
|
|
68
|
+
| **Long-term Memory** | 사용자 선호도, 과거 인사이트 | 영구적 |
|
|
69
|
+
| **Episodic Memory** | 구체적인 대화/이벤트 기억 | 영구적 |
|
|
70
|
+
| **Semantic Memory** | 추출된 지식과 관계 | 영구적 |
|
|
71
|
+
|
|
72
|
+
### 3.2 주요 Memory 솔루션 비교
|
|
73
|
+
|
|
74
|
+
| 솔루션 | 특징 | 장점 |
|
|
75
|
+
|--------|------|------|
|
|
76
|
+
| **Mem0** | Y Combinator 투자, 그래프 기반 | 복잡한 관계 표현 |
|
|
77
|
+
| **LangChain Memory** | 프레임워크 내장 | 쉬운 통합 |
|
|
78
|
+
| **Zep/Graphiti** | 시간적 지식 그래프 | 시계열 추적 |
|
|
79
|
+
| **AWS AgentCore Memory** | 비동기 파이프라인 | 확장성 |
|
|
80
|
+
| **Google Vertex Memory Bank** | 유사도 검색 | 엔터프라이즈 |
|
|
81
|
+
|
|
82
|
+
### 3.3 Memory vs RAG
|
|
83
|
+
|
|
84
|
+
- **RAG**: 외부 문서에서 정보 검색 (stateless)
|
|
85
|
+
- **Memory**: 과거 상호작용에서 컨텍스트 검색 (stateful)
|
|
86
|
+
- **이 플러그인**: Memory 중심 + 선택적 RAG 통합
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 4. AXIOMMIND Memory System 참조
|
|
91
|
+
|
|
92
|
+
Gist에서 제공된 AXIOMMIND 시스템의 핵심 개념:
|
|
93
|
+
|
|
94
|
+
### 4.1 아키텍처 레이어
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
┌─────────────────────────────────────────────────────────┐
|
|
98
|
+
│ L0 EventStore (Single Source of Truth) │
|
|
99
|
+
│ - Append-only events table │
|
|
100
|
+
│ - Event deduplication via dedupe_key │
|
|
101
|
+
│ - Projection offset tracking │
|
|
102
|
+
└─────────────────────────────────────────────────────────┘
|
|
103
|
+
↓
|
|
104
|
+
┌─────────────────────────────────────────────────────────┐
|
|
105
|
+
│ Extraction/Sorting Layer (LLM Processing) │
|
|
106
|
+
│ - LLM extracts structured JSON from raw input │
|
|
107
|
+
│ - Evidence alignment and validation │
|
|
108
|
+
└─────────────────────────────────────────────────────────┘
|
|
109
|
+
↓
|
|
110
|
+
┌─────────────────────────────────────────────────────────┐
|
|
111
|
+
│ Derived Stores (Rebuildable from Events) │
|
|
112
|
+
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
113
|
+
│ │ DuckDB │ │ LanceDB │ │ Relational │ │
|
|
114
|
+
│ │ (FTS/SQL) │ │ (Vectors) │ │ Views │ │
|
|
115
|
+
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
116
|
+
└─────────────────────────────────────────────────────────┘
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 4.2 핵심 원칙
|
|
120
|
+
|
|
121
|
+
- **Append-only EventStore**: 모든 변경 추적, 파생 저장소에서 언제든 재구성 가능
|
|
122
|
+
- **Canonical Key 정규화**: 동일 개념의 여러 표현을 단일 키로 통합
|
|
123
|
+
- **단일 진실 공급원(SoT)**: events 테이블만 영구 저장, 나머지는 파생
|
|
124
|
+
- **멱등성 보장**: `dedupe_key`로 중복 이벤트 차단
|
|
125
|
+
|
|
126
|
+
### 4.3 Canonical Key 정규화 (핵심 알고리즘)
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
# canonical_key.py - 결정론적 키 생성
|
|
130
|
+
def make_canonical_key(title: str, project: str = None) -> str:
|
|
131
|
+
"""
|
|
132
|
+
동일한 제목은 항상 동일한 키를 생성
|
|
133
|
+
|
|
134
|
+
정규화 단계:
|
|
135
|
+
1. NFKC 유니코드 정규화
|
|
136
|
+
2. 소문자 변환
|
|
137
|
+
3. 구두점 제거
|
|
138
|
+
4. 연속 공백 정리
|
|
139
|
+
5. (선택) 프로젝트/도메인 컨텍스트 추가
|
|
140
|
+
6. 긴 키는 MD5 체크섬으로 truncate
|
|
141
|
+
"""
|
|
142
|
+
import unicodedata
|
|
143
|
+
import re
|
|
144
|
+
import hashlib
|
|
145
|
+
|
|
146
|
+
# Step 1-4: 정규화
|
|
147
|
+
normalized = unicodedata.normalize('NFKC', title)
|
|
148
|
+
normalized = normalized.lower()
|
|
149
|
+
normalized = re.sub(r'[^\w\s]', '', normalized)
|
|
150
|
+
normalized = re.sub(r'\s+', ' ', normalized).strip()
|
|
151
|
+
|
|
152
|
+
# Step 5: 컨텍스트 추가
|
|
153
|
+
if project:
|
|
154
|
+
key = f"{project}::{normalized}"
|
|
155
|
+
else:
|
|
156
|
+
key = normalized
|
|
157
|
+
|
|
158
|
+
# Step 6: 긴 키 처리
|
|
159
|
+
MAX_KEY_LENGTH = 200
|
|
160
|
+
if len(key) > MAX_KEY_LENGTH:
|
|
161
|
+
hash_suffix = hashlib.md5(key.encode()).hexdigest()[:8]
|
|
162
|
+
key = key[:MAX_KEY_LENGTH - 9] + "_" + hash_suffix
|
|
163
|
+
|
|
164
|
+
return key
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Memory Plugin 적용**:
|
|
168
|
+
- 사용자 prompt의 canonical key로 중복 질문 감지
|
|
169
|
+
- 유사한 질문들을 그룹화하여 패턴 추출
|
|
170
|
+
|
|
171
|
+
### 4.4 Matching Thresholds (엄격한 매칭 기준)
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
# task_matcher.py - 매칭 임계값
|
|
175
|
+
MATCH_THRESHOLDS = {
|
|
176
|
+
"min_combined_score": 0.92, # 최소 결합 점수
|
|
177
|
+
"min_gap": 0.03, # 1위와 2위 간 최소 점수 차이
|
|
178
|
+
"suggestion_threshold": 0.75, # 제안 모드 임계값
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
def calculate_weighted_score(result: SearchResult) -> float:
|
|
182
|
+
"""
|
|
183
|
+
가중치 점수 계산 (stage, status, recency)
|
|
184
|
+
"""
|
|
185
|
+
weights = {
|
|
186
|
+
"semantic_similarity": 0.4, # 벡터 유사도
|
|
187
|
+
"fts_score": 0.25, # 전문 검색 점수
|
|
188
|
+
"recency_bonus": 0.2, # 최신성 가산점
|
|
189
|
+
"status_weight": 0.15, # 상태별 가중치
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
score = (
|
|
193
|
+
result.vector_score * weights["semantic_similarity"] +
|
|
194
|
+
result.fts_score * weights["fts_score"] +
|
|
195
|
+
result.recency_score * weights["recency_bonus"] +
|
|
196
|
+
result.status_score * weights["status_weight"]
|
|
197
|
+
)
|
|
198
|
+
return score
|
|
199
|
+
|
|
200
|
+
def match_with_confidence(query: str, candidates: list) -> MatchResult:
|
|
201
|
+
"""
|
|
202
|
+
엄격한 매칭: top-1이 확실히 우세할 때만 확정
|
|
203
|
+
"""
|
|
204
|
+
if len(candidates) == 0:
|
|
205
|
+
return MatchResult(match=None, confidence="none")
|
|
206
|
+
|
|
207
|
+
top = candidates[0]
|
|
208
|
+
|
|
209
|
+
if top.score < MATCH_THRESHOLDS["suggestion_threshold"]:
|
|
210
|
+
return MatchResult(match=None, confidence="none")
|
|
211
|
+
|
|
212
|
+
if top.score >= MATCH_THRESHOLDS["min_combined_score"]:
|
|
213
|
+
if len(candidates) == 1:
|
|
214
|
+
return MatchResult(match=top, confidence="high")
|
|
215
|
+
|
|
216
|
+
gap = top.score - candidates[1].score
|
|
217
|
+
if gap >= MATCH_THRESHOLDS["min_gap"]:
|
|
218
|
+
return MatchResult(match=top, confidence="high")
|
|
219
|
+
|
|
220
|
+
# 점수가 높지만 확실하지 않음 → 제안 모드
|
|
221
|
+
return MatchResult(match=top, confidence="suggested")
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Memory Plugin 적용**:
|
|
225
|
+
- 관련 기억 검색 시 엄격한 임계값 적용
|
|
226
|
+
- 애매한 매칭은 "suggested" 상태로 표시
|
|
227
|
+
|
|
228
|
+
### 4.5 Single-Writer Pattern (벡터 동시성 제어)
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
# vector_worker.py - Outbox 패턴으로 동시성 제어
|
|
232
|
+
|
|
233
|
+
"""
|
|
234
|
+
LanceDB는 동시 쓰기에 취약하므로 Single-Writer 패턴 사용:
|
|
235
|
+
1. 이벤트 저장 시 embedding_outbox 테이블에 작업 추가
|
|
236
|
+
2. 별도 워커가 outbox를 순차적으로 처리
|
|
237
|
+
3. 처리 완료 시 outbox에서 삭제
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
# DuckDB의 outbox 테이블
|
|
241
|
+
CREATE TABLE embedding_outbox (
|
|
242
|
+
id UUID PRIMARY KEY,
|
|
243
|
+
event_id UUID NOT NULL,
|
|
244
|
+
content TEXT NOT NULL,
|
|
245
|
+
status VARCHAR DEFAULT 'pending', -- 'pending' | 'processing' | 'done'
|
|
246
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
247
|
+
processed_at TIMESTAMP
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
# Python 워커 (단일 프로세스)
|
|
251
|
+
class VectorWorker:
|
|
252
|
+
def __init__(self, db: DuckDB, lance: LanceDB, embedder: Embedder):
|
|
253
|
+
self.db = db
|
|
254
|
+
self.lance = lance
|
|
255
|
+
self.embedder = embedder
|
|
256
|
+
|
|
257
|
+
async def process_outbox(self, batch_size: int = 32):
|
|
258
|
+
"""
|
|
259
|
+
Outbox에서 pending 항목을 가져와 순차 처리
|
|
260
|
+
"""
|
|
261
|
+
# 1. Pending 항목 가져오기 (락 획득)
|
|
262
|
+
pending = self.db.execute("""
|
|
263
|
+
UPDATE embedding_outbox
|
|
264
|
+
SET status = 'processing'
|
|
265
|
+
WHERE id IN (
|
|
266
|
+
SELECT id FROM embedding_outbox
|
|
267
|
+
WHERE status = 'pending'
|
|
268
|
+
ORDER BY created_at
|
|
269
|
+
LIMIT ?
|
|
270
|
+
)
|
|
271
|
+
RETURNING *
|
|
272
|
+
""", [batch_size])
|
|
273
|
+
|
|
274
|
+
if not pending:
|
|
275
|
+
return
|
|
276
|
+
|
|
277
|
+
# 2. 배치 임베딩 생성
|
|
278
|
+
contents = [p.content for p in pending]
|
|
279
|
+
vectors = await self.embedder.embed_batch(contents)
|
|
280
|
+
|
|
281
|
+
# 3. LanceDB에 저장 (단일 쓰기)
|
|
282
|
+
records = [
|
|
283
|
+
{"event_id": p.event_id, "content": p.content, "vector": v}
|
|
284
|
+
for p, v in zip(pending, vectors)
|
|
285
|
+
]
|
|
286
|
+
self.lance.add(records)
|
|
287
|
+
|
|
288
|
+
# 4. Outbox 정리
|
|
289
|
+
ids = [p.id for p in pending]
|
|
290
|
+
self.db.execute("""
|
|
291
|
+
DELETE FROM embedding_outbox WHERE id = ANY(?)
|
|
292
|
+
""", [ids])
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**Memory Plugin 적용**:
|
|
296
|
+
- 대화 저장 시 즉시 반환, 임베딩은 비동기 처리
|
|
297
|
+
- 동시성 문제 없이 안정적인 벡터 인덱싱
|
|
298
|
+
|
|
299
|
+
### 4.6 Blocker/Condition 분류 전략
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
# task_resolver.py - 애매한 참조 처리
|
|
303
|
+
|
|
304
|
+
"""
|
|
305
|
+
Blocker 분류 전략:
|
|
306
|
+
1. Artifact: URL, Jira, GitHub 이슈 등 명확한 참조
|
|
307
|
+
2. Task: 엄격한 매칭만 허용 (score >= 0.92)
|
|
308
|
+
3. Condition: 애매한 참조 흡수 (나중에 해결 가능)
|
|
309
|
+
"""
|
|
310
|
+
|
|
311
|
+
class BlockerType(Enum):
|
|
312
|
+
ARTIFACT = "artifact" # 명확한 외부 참조
|
|
313
|
+
TASK = "task" # 확정된 작업 참조
|
|
314
|
+
CONDITION = "condition" # 애매한 조건/참조
|
|
315
|
+
|
|
316
|
+
def classify_blocker(reference: str, match_result: MatchResult) -> BlockerType:
|
|
317
|
+
# URL, 이슈 번호 등은 Artifact
|
|
318
|
+
if is_artifact_reference(reference):
|
|
319
|
+
return BlockerType.ARTIFACT
|
|
320
|
+
|
|
321
|
+
# 높은 신뢰도 매칭은 Task
|
|
322
|
+
if match_result.confidence == "high":
|
|
323
|
+
return BlockerType.TASK
|
|
324
|
+
|
|
325
|
+
# 나머지는 Condition으로 흡수
|
|
326
|
+
return BlockerType.CONDITION
|
|
327
|
+
|
|
328
|
+
# Condition은 나중에 실제 Task로 해결될 수 있음
|
|
329
|
+
# resolves_to edge로 연결
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**Memory Plugin 적용**:
|
|
333
|
+
- 불완전한 컨텍스트도 일단 저장
|
|
334
|
+
- 나중에 추가 정보로 보강 가능
|
|
335
|
+
|
|
336
|
+
### 4.7 Query Patterns (효과적인 뷰 활용)
|
|
337
|
+
|
|
338
|
+
```sql
|
|
339
|
+
-- v_task_blockers_effective: Condition 해결을 반영한 최종 blocker 뷰
|
|
340
|
+
CREATE VIEW v_memory_context_effective AS
|
|
341
|
+
SELECT
|
|
342
|
+
m.id,
|
|
343
|
+
m.session_id,
|
|
344
|
+
m.content,
|
|
345
|
+
m.event_type,
|
|
346
|
+
m.timestamp,
|
|
347
|
+
-- Condition이 해결된 경우 실제 참조로 대체
|
|
348
|
+
COALESCE(r.resolved_content, m.content) as effective_content,
|
|
349
|
+
COALESCE(r.resolved_id, m.id) as effective_id
|
|
350
|
+
FROM memories m
|
|
351
|
+
LEFT JOIN memory_resolutions r ON m.id = r.condition_id
|
|
352
|
+
WHERE r.resolution_type IS NULL OR r.resolution_type = 'confirmed';
|
|
353
|
+
|
|
354
|
+
-- 4가지 주요 쿼리 패턴
|
|
355
|
+
-- 1. 확정된 관련 기억
|
|
356
|
+
SELECT * FROM v_memory_context_effective
|
|
357
|
+
WHERE semantic_score >= 0.92;
|
|
358
|
+
|
|
359
|
+
-- 2. 제안 상태의 기억 (확인 대기)
|
|
360
|
+
SELECT * FROM memories
|
|
361
|
+
WHERE match_confidence = 'suggested';
|
|
362
|
+
|
|
363
|
+
-- 3. 자동 플레이스홀더 감지
|
|
364
|
+
SELECT * FROM memories
|
|
365
|
+
WHERE auto_placeholder = true;
|
|
366
|
+
|
|
367
|
+
-- 4. 해결된 조건 매핑
|
|
368
|
+
SELECT condition_id, resolved_to_id
|
|
369
|
+
FROM memory_resolutions
|
|
370
|
+
WHERE resolution_type = 'confirmed';
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### 4.8 Placeholder 자동 생성
|
|
374
|
+
|
|
375
|
+
```python
|
|
376
|
+
# 정보가 불완전할 때 플레이스홀더 생성
|
|
377
|
+
def create_placeholder_if_needed(event: MemoryEvent) -> Optional[Placeholder]:
|
|
378
|
+
"""
|
|
379
|
+
컨텍스트가 불완전하면 자동 플레이스홀더 생성
|
|
380
|
+
- auto_placeholder=true 플래그 설정
|
|
381
|
+
- 나중에 추가 정보로 해결 가능
|
|
382
|
+
"""
|
|
383
|
+
if is_incomplete_context(event):
|
|
384
|
+
return Placeholder(
|
|
385
|
+
id=generate_uuid(),
|
|
386
|
+
event_id=event.id,
|
|
387
|
+
placeholder_type="unknown_context",
|
|
388
|
+
auto_placeholder=True,
|
|
389
|
+
created_at=datetime.now()
|
|
390
|
+
)
|
|
391
|
+
return None
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### 4.9 주요 모듈 요약
|
|
395
|
+
|
|
396
|
+
| 모듈 | 역할 | Memory Plugin 대응 |
|
|
397
|
+
|------|------|-------------------|
|
|
398
|
+
| `canonical_key.py` | 결정론적 키 정규화 | `normalizer.ts` |
|
|
399
|
+
| `event_store.py` | append-only 이벤트 저장 | `event-store.ts` |
|
|
400
|
+
| `task_matcher.py` | 가중치 기반 매칭 | `matcher.ts` |
|
|
401
|
+
| `task_resolver.py` | 상태 전이 검증 | `resolver.ts` |
|
|
402
|
+
| `projector_task.py` | 이벤트→엔티티 투영 | `projector.ts` |
|
|
403
|
+
| `vector_worker.py` | 단일 쓰기 임베딩 | `vector-worker.ts` |
|
|
404
|
+
|
|
405
|
+
### 4.10 Memory Graduation Pipeline (L0 → L4)
|
|
406
|
+
|
|
407
|
+
AXIOMMIND의 핵심 개념인 **다단계 메모리 승격 파이프라인**:
|
|
408
|
+
|
|
409
|
+
```
|
|
410
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
411
|
+
│ Memory Graduation Pipeline │
|
|
412
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
413
|
+
│ │
|
|
414
|
+
│ L0: EventStore (Raw) │
|
|
415
|
+
│ ├── 원본 채팅 로그, 프롬프트/응답 │
|
|
416
|
+
│ ├── Append-only, 불변 │
|
|
417
|
+
│ └── dedupe_key로 멱등성 보장 │
|
|
418
|
+
│ ↓ │
|
|
419
|
+
│ L1: Structured JSON │
|
|
420
|
+
│ ├── LLM이 추출한 구조화된 데이터 │
|
|
421
|
+
│ ├── 엔티티, 관계, 인사이트 │
|
|
422
|
+
│ └── EvidenceAligner로 증거 스팬 정렬 │
|
|
423
|
+
│ ↓ │
|
|
424
|
+
│ L2: Idris Candidates (검증 대상) │
|
|
425
|
+
│ ├── 타입 안전한 표현으로 변환 │
|
|
426
|
+
│ ├── 의존적 타입으로 불변식 검증 │
|
|
427
|
+
│ └── idris_generator.py가 생성 │
|
|
428
|
+
│ ↓ │
|
|
429
|
+
│ L3: Verified Knowledge │
|
|
430
|
+
│ ├── Idris 타입체커 통과 │
|
|
431
|
+
│ ├── 모순 없음 확인 │
|
|
432
|
+
│ └── 신뢰도 높은 지식 │
|
|
433
|
+
│ ↓ │
|
|
434
|
+
│ L4: Active Memory (검색 가능) │
|
|
435
|
+
│ ├── 벡터 인덱싱 완료 │
|
|
436
|
+
│ ├── 실시간 검색 가능 │
|
|
437
|
+
│ └── 컨텍스트 주입에 사용 │
|
|
438
|
+
│ │
|
|
439
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**Memory Plugin 적용**:
|
|
443
|
+
- L0: 모든 대화를 `events` 테이블에 저장
|
|
444
|
+
- L1: 주기적으로 인사이트 추출 (LLM 기반)
|
|
445
|
+
- L2: TypeScript 강타입으로 검증 (Idris2 개념 적용)
|
|
446
|
+
- L3: 테스트 통과한 검증된 지식
|
|
447
|
+
- L4: LanceDB에 인덱싱되어 검색 가능한 상태
|
|
448
|
+
|
|
449
|
+
### 4.11 AXIOMMIND 7가지 필수 원칙
|
|
450
|
+
|
|
451
|
+
| # | 원칙 | 설명 | Memory Plugin 적용 |
|
|
452
|
+
|---|------|------|-------------------|
|
|
453
|
+
| 1 | **진실의 원천(SoT)은 이벤트 로그** | 파생 테이블은 언제든 재구성 가능 | `events` 테이블만 영구 저장 |
|
|
454
|
+
| 2 | **추가전용 구조** | events에 UPDATE/DELETE 금지 | `append()` 메서드만 제공 |
|
|
455
|
+
| 3 | **멱등성 보장** | `dedupe_key`로 중복 제어 | content_hash + session_id |
|
|
456
|
+
| 4 | **증거 범위는 파이프라인이 확정** | LLM은 인용문만, aligner가 스팬 계산 | `EvidenceAligner` 모듈 |
|
|
457
|
+
| 5 | **Task는 엔티티** | 세션마다 새 항목 아닌 기존 업데이트 | `canonical_key`로 동일성 판단 |
|
|
458
|
+
| 6 | **벡터 저장소 정합성** | DuckDB → outbox → LanceDB 단방향 | Single-Writer Pattern |
|
|
459
|
+
| 7 | **DuckDB JSON 사용** | JSONB 제거, 표준 JSON만 | `metadata JSON` 컬럼 |
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
// 원칙 적용 예시: EventStore 인터페이스
|
|
463
|
+
interface EventStoreInterface {
|
|
464
|
+
// 원칙 2: 추가전용 - append만 허용
|
|
465
|
+
append(event: MemoryEvent): Promise<AppendResult>;
|
|
466
|
+
|
|
467
|
+
// 조회는 자유롭게
|
|
468
|
+
getBySession(sessionId: string): Promise<MemoryEvent[]>;
|
|
469
|
+
getRecent(limit: number): Promise<MemoryEvent[]>;
|
|
470
|
+
|
|
471
|
+
// 원칙 2 위반: UPDATE/DELETE 메서드 없음
|
|
472
|
+
// update(): ❌ 금지
|
|
473
|
+
// delete(): ❌ 금지
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### 4.12 Evidence Aligner (증거 정렬기)
|
|
478
|
+
|
|
479
|
+
```python
|
|
480
|
+
# evidence_aligner.py - LLM 인용문을 정확한 스팬으로 변환
|
|
481
|
+
|
|
482
|
+
class EvidenceAligner:
|
|
483
|
+
"""
|
|
484
|
+
원칙 4: LLM은 인용문만 제공, aligner가 정확한 스팬 계산
|
|
485
|
+
|
|
486
|
+
LLM이 추출한 대략적인 인용문을 원본 텍스트에서
|
|
487
|
+
정확한 (start, end) 위치로 변환
|
|
488
|
+
"""
|
|
489
|
+
|
|
490
|
+
def align(
|
|
491
|
+
self,
|
|
492
|
+
source_text: str,
|
|
493
|
+
llm_quote: str,
|
|
494
|
+
fuzzy_threshold: float = 0.85
|
|
495
|
+
) -> Optional[EvidenceSpan]:
|
|
496
|
+
"""
|
|
497
|
+
1. 정확한 매칭 시도
|
|
498
|
+
2. 실패 시 fuzzy matching (Levenshtein)
|
|
499
|
+
3. 임계값 미달 시 None 반환
|
|
500
|
+
"""
|
|
501
|
+
# 정확한 매칭
|
|
502
|
+
exact_pos = source_text.find(llm_quote)
|
|
503
|
+
if exact_pos >= 0:
|
|
504
|
+
return EvidenceSpan(
|
|
505
|
+
start=exact_pos,
|
|
506
|
+
end=exact_pos + len(llm_quote),
|
|
507
|
+
confidence=1.0,
|
|
508
|
+
match_type="exact"
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
# Fuzzy 매칭
|
|
512
|
+
best_match = self._fuzzy_search(source_text, llm_quote)
|
|
513
|
+
if best_match and best_match.score >= fuzzy_threshold:
|
|
514
|
+
return EvidenceSpan(
|
|
515
|
+
start=best_match.start,
|
|
516
|
+
end=best_match.end,
|
|
517
|
+
confidence=best_match.score,
|
|
518
|
+
match_type="fuzzy"
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
return None
|
|
522
|
+
|
|
523
|
+
def _fuzzy_search(self, text: str, query: str) -> Optional[FuzzyMatch]:
|
|
524
|
+
# 슬라이딩 윈도우 + Levenshtein 거리
|
|
525
|
+
...
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
**Memory Plugin 적용**:
|
|
529
|
+
- 사용자가 "이전에 rate limiting 얘기했잖아"라고 하면
|
|
530
|
+
- LLM이 대략적인 인용문 추출
|
|
531
|
+
- EvidenceAligner가 정확한 원본 위치 찾기
|
|
532
|
+
- 해당 컨텍스트를 정확히 주입
|
|
533
|
+
|
|
534
|
+
### 5.1 Vector Database: LanceDB
|
|
535
|
+
|
|
536
|
+
선택 이유:
|
|
537
|
+
- **Embedded 모드**: SQLite처럼 서버 없이 로컬 실행
|
|
538
|
+
- **Apache Arrow 기반**: 빠른 디스크 접근
|
|
539
|
+
- **다중 모달 지원**: 텍스트, 이미지, 오디오 임베딩
|
|
540
|
+
- **DuckDB 호환**: SQL 쿼리 가능
|
|
541
|
+
|
|
542
|
+
```python
|
|
543
|
+
import lancedb
|
|
544
|
+
|
|
545
|
+
db = lancedb.connect("~/.claude-memory")
|
|
546
|
+
table = db.create_table("conversations", data)
|
|
547
|
+
results = table.search(query_embedding).limit(10).to_list()
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### 5.2 관계형 저장소: DuckDB
|
|
551
|
+
|
|
552
|
+
선택 이유:
|
|
553
|
+
- **임베디드**: 파일 기반, 서버 불필요
|
|
554
|
+
- **분석 최적화**: OLAP 워크로드에 적합
|
|
555
|
+
- **SQL 지원**: 친숙한 쿼리 언어
|
|
556
|
+
- **Lance 포맷 호환**: LanceDB와 통합
|
|
557
|
+
|
|
558
|
+
### 5.3 Embedding Model
|
|
559
|
+
|
|
560
|
+
옵션:
|
|
561
|
+
1. **OpenAI text-embedding-3-small**: 고품질, API 비용
|
|
562
|
+
2. **sentence-transformers**: 로컬 실행, 무료
|
|
563
|
+
3. **Ollama embeddings**: 로컬 LLM 활용
|
|
564
|
+
|
|
565
|
+
권장: sentence-transformers (로컬 우선) + OpenAI fallback
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
## 6. Idris2 활용 고려사항
|
|
570
|
+
|
|
571
|
+
### 6.1 Idris2 개요
|
|
572
|
+
|
|
573
|
+
- **의존적 타입 시스템**: 타입 수준에서 프로그램 검증
|
|
574
|
+
- **Type-Driven Development**: 타입이 프로그램 설계를 가이드
|
|
575
|
+
- **Quantitative Type Theory (QTT)**: 선형 타입 지원
|
|
576
|
+
|
|
577
|
+
### 6.2 적용 가능 영역
|
|
578
|
+
|
|
579
|
+
1. **타입 안전한 이벤트 스키마**
|
|
580
|
+
```idris
|
|
581
|
+
data MemoryEvent : Type where
|
|
582
|
+
UserPrompt : (sessionId : String) -> (content : String) -> MemoryEvent
|
|
583
|
+
AgentResponse : (sessionId : String) -> (content : String) -> MemoryEvent
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
2. **불변성 보장**
|
|
587
|
+
- Append-only EventStore의 불변성을 타입 수준에서 강제
|
|
588
|
+
|
|
589
|
+
3. **정확성 증명**
|
|
590
|
+
- 중복 제거 로직의 정확성 검증
|
|
591
|
+
- 검색 알고리즘의 속성 증명
|
|
592
|
+
|
|
593
|
+
### 6.3 실용적 접근
|
|
594
|
+
|
|
595
|
+
Idris2를 직접 사용하기보다 **개념적 영감**으로 활용:
|
|
596
|
+
- TypeScript의 강타입 시스템 적극 활용
|
|
597
|
+
- Zod/io-ts로 런타임 타입 검증
|
|
598
|
+
- 불변 데이터 구조 (Immutable.js 또는 순수 함수형 패턴)
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
602
|
+
## 7. 참조 링크
|
|
603
|
+
|
|
604
|
+
### Claude Code Plugin 개발
|
|
605
|
+
- [Create plugins - Claude Code Docs](https://code.claude.com/docs/en/plugins)
|
|
606
|
+
- [Claude Code Plugins README](https://github.com/anthropics/claude-code/blob/main/plugins/README.md)
|
|
607
|
+
- [Hook Development SKILL](https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/hook-development/SKILL.md)
|
|
608
|
+
- [Claude Code Plugins Complete Guide](https://jangwook.net/en/blog/en/claude-code-plugins-complete-guide/)
|
|
609
|
+
|
|
610
|
+
### AI Memory Systems
|
|
611
|
+
- [Mem0: Building Production-Ready AI Agents](https://arxiv.org/pdf/2504.19413)
|
|
612
|
+
- [AWS AgentCore Long-term Memory](https://aws.amazon.com/blogs/machine-learning/building-smarter-ai-agents-agentcore-long-term-memory-deep-dive/)
|
|
613
|
+
- [Google Vertex AI Memory Bank](https://docs.cloud.google.com/agent-builder/agent-engine/memory-bank/overview)
|
|
614
|
+
- [LangChain Conversational Memory](https://www.pinecone.io/learn/series/langchain/langchain-conversational-memory/)
|
|
615
|
+
|
|
616
|
+
### Vector Databases
|
|
617
|
+
- [LanceDB](https://lancedb.com/)
|
|
618
|
+
- [Lance Format on GitHub](https://github.com/lance-format/lance)
|
|
619
|
+
|
|
620
|
+
### Idris2
|
|
621
|
+
- [Idris2 Official Site](https://www.idris-lang.org/)
|
|
622
|
+
- [Idris 2: Quantitative Type Theory in Practice](https://arxiv.org/abs/2104.00480)
|
|
623
|
+
- [Idris2 GitHub](https://github.com/idris-lang/Idris2)
|
|
624
|
+
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
## 8. 용어 정의
|
|
628
|
+
|
|
629
|
+
| 용어 | 정의 |
|
|
630
|
+
|------|------|
|
|
631
|
+
| **Memory** | 과거 대화에서 추출/저장된 정보 |
|
|
632
|
+
| **Embedding** | 텍스트를 벡터로 변환한 표현 |
|
|
633
|
+
| **Semantic Search** | 의미 기반 유사도 검색 |
|
|
634
|
+
| **EventStore** | 모든 이벤트를 시간순으로 저장하는 append-only 저장소 |
|
|
635
|
+
| **Hook** | 특정 이벤트 발생 시 실행되는 스크립트 |
|
|
636
|
+
| **MCP** | Model Context Protocol - Claude와 외부 도구 연결 |
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# /memory-forget
|
|
2
|
+
|
|
3
|
+
Remove specific memories from storage.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
/memory-forget <event-id>
|
|
9
|
+
/memory-forget --session <session-id>
|
|
10
|
+
/memory-forget --before <date>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Arguments
|
|
14
|
+
|
|
15
|
+
- `event-id`: Specific event ID to forget
|
|
16
|
+
- `--session <id>`: Forget all events from a session
|
|
17
|
+
- `--before <date>`: Forget events before a date (YYYY-MM-DD)
|
|
18
|
+
- `--confirm`: Skip confirmation prompt
|
|
19
|
+
|
|
20
|
+
## Examples
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
/memory-forget abc123-def456
|
|
24
|
+
/memory-forget --session session_xyz --confirm
|
|
25
|
+
/memory-forget --before 2024-01-01
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Description
|
|
29
|
+
|
|
30
|
+
Removes memories from storage. This operation:
|
|
31
|
+
|
|
32
|
+
1. Marks events as deleted in EventStore (soft delete)
|
|
33
|
+
2. Removes corresponding vectors from LanceDB
|
|
34
|
+
3. Updates memory level statistics
|
|
35
|
+
|
|
36
|
+
⚠️ **Note**: Due to the append-only architecture, deleted events are marked but not physically removed from the event log. Vector embeddings are physically deleted.
|
|
37
|
+
|
|
38
|
+
## Implementation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
node dist/cli/index.js forget $ARGUMENTS
|
|
42
|
+
```
|