메모리 시스템 설계
인간의 기억 구조를 모방하다
섹션 제목: “인간의 기억 구조를 모방하다”AI 에이전트의 메모리 시스템은 인지과학의 기억 모델에서 영감을 받았습니다. 인간이 모든 것을 의식적으로 기억하지 않듯, 에이전트도 무엇을 어디에 저장하는가를 설계해야 합니다.
메모리 계층 구조
섹션 제목: “메모리 계층 구조”┌─────────────────────────────────────────────┐│ 컨텍스트 윈도우 (RAM) ││ ┌─────────────────┐ ┌──────────────────┐ ││ │ 워킹 메모리 │ │ 에피소딕 메모리 │ ││ │ (현재 태스크) │ │ (현재 세션) │ ││ └─────────────────┘ └──────────────────┘ │└─────────────────────────────────────────────┘ ↕ 검색/저장┌─────────────────────────────────────────────┐│ 외부 저장소 (디스크) ││ ┌─────────────────┐ ┌──────────────────┐ ││ │ 장기 메모리 │ │ 파일 시스템 │ ││ │ (벡터 DB) │ │ (확장 메모리) │ ││ └─────────────────┘ └──────────────────┘ │└─────────────────────────────────────────────┘워킹 메모리 (Working Memory)
섹션 제목: “워킹 메모리 (Working Memory)”현재 진행 중인 태스크에 직접 필요한 정보입니다. 컨텍스트 윈도우 안에 상주하며, 태스크가 끝나면 사라집니다.
- 현재 사용자 요청
- 진행 중인 계획과 단계
- 최근 도구 실행 결과
- 임시 변수와 중간 계산값
에피소딕 메모리 (Episodic Memory)
섹션 제목: “에피소딕 메모리 (Episodic Memory)”현재 세션 내의 대화 히스토리입니다. “방금 전에 무슨 말을 했는가”를 기억합니다. 세션이 끝나면 장기 메모리로 저장하거나 폐기합니다.
interface EpisodicMemory { sessionId: string; startTime: Date; messages: Message[]; toolCalls: ToolCallRecord[]; decisions: Decision[]; // 주요 결정 사항}장기 메모리 (Long-term Memory)
섹션 제목: “장기 메모리 (Long-term Memory)”세션을 넘어 지속되는 지식입니다. 벡터 데이터베이스나 지식 그래프에 저장되며, 새 세션 시작 시 관련 내용을 검색해 컨텍스트에 주입합니다.
interface LongTermMemory { type: 'fact' | 'preference' | 'procedure' | 'experience'; content: string; embedding: number[]; // 의미 검색용 createdAt: Date; lastAccessedAt: Date; accessCount: number; confidence: number; // 0.0 ~ 1.0}이중 메모리 아키텍처 (Dual Memory)
섹션 제목: “이중 메모리 아키텍처 (Dual Memory)”OpenDev에서 제안한 이중 메모리 아키텍처는 인-컨텍스트 메모리와 외부 메모리를 명시적으로 분리합니다.
| 구분 | 인-컨텍스트 메모리 | 외부 메모리 |
|---|---|---|
| 위치 | 컨텍스트 윈도우 내 | 벡터 DB, 파일 시스템 |
| 접근 속도 | 즉각적 | 검색 지연 존재 |
| 용량 | 제한적 (토큰 한계) | 무제한 |
| 비용 | 토큰 비용 발생 | 저장소 비용 |
| 갱신 방식 | 직접 편집 | 임베딩 재생성 |
이중 메모리의 핵심은 선택적 로딩입니다. 태스크 시작 시 외부 메모리에서 관련 항목만 검색하여 인-컨텍스트 메모리에 로드합니다.
class DualMemorySystem { private vectorDb: VectorDatabase; private inContext: Map<string, Memory> = new Map();
async loadRelevantMemories( taskDescription: string, maxItems: number = 5 ): Promise<Memory[]> { const embedding = await embed(taskDescription); const relevant = await this.vectorDb.search(embedding, maxItems);
// 외부 메모리 → 인-컨텍스트 메모리로 로드 for (const mem of relevant) { this.inContext.set(mem.id, mem); }
return relevant; }
async saveToLongTerm(memory: Memory): Promise<void> { const embedding = await embed(memory.content); await this.vectorDb.upsert({ ...memory, embedding }); // 인-컨텍스트에서는 제거하여 공간 확보 this.inContext.delete(memory.id); }}파일 시스템을 확장 메모리로 활용
섹션 제목: “파일 시스템을 확장 메모리로 활용”Manus의 핵심 패턴 중 하나는 파일 시스템을 무제한 외부 메모리로 활용하는 것입니다. 컨텍스트에 직접 담기엔 너무 큰 정보는 파일에 쓰고, 컨텍스트에는 파일 경로만 유지합니다.
todo.md 패턴
섹션 제목: “todo.md 패턴”# 현재 태스크: 사용자 인증 시스템 구현
## 완료- [x] 요구사항 분석 (상세: ./notes/requirements.md)- [x] DB 스키마 설계 (스키마: ./notes/schema.sql)
## 진행 중- [ ] JWT 발급 로직 구현 - 현재: src/auth/token.ts 작성 중 - 결정 사항: RS256 알고리즘 사용
## 대기- [ ] 리프레시 토큰 구현- [ ] 통합 테스트 작성
## 주요 결정- bcrypt rounds: 12 (성능/보안 트레이드오프 고려)- 토큰 만료: access 15분, refresh 7일이 파일은 에이전트가 매 스텝 시작 시 읽고 완료 항목을 업데이트합니다. 상세 내용은 별도 파일에 저장되므로 todo.md 자체는 항상 짧게 유지됩니다.
메모리 검색 전략
섹션 제목: “메모리 검색 전략”외부 메모리 검색은 세 가지 방식을 조합합니다.
| 방식 | 적합한 경우 | 예시 |
|---|---|---|
| 임베딩 유사도 | 의미적 관련성 검색 | ”인증 관련 과거 결정” |
| 키워드 매칭 | 정확한 용어 검색 | 특정 함수명, 파일명 |
| 그래프 탐색 | 관계 기반 검색 | ”이 모듈에 영향받는 모든 모듈” |
에이전트의 메모리 시스템은 워킹 메모리(즉각적 태스크 정보), 에피소딕 메모리(세션 히스토리), 장기 메모리(세션 초월 지식)의 세 계층으로 설계합니다. 이중 메모리 아키텍처는 외부 저장소에서 관련 항목만 선택적으로 로드하여 컨텍스트를 효율적으로 사용합니다. Manus의 파일 시스템 패턴은 컨텍스트 폭발 없이 복잡한 장기 태스크를 처리하는 실용적인 해법입니다.