메모리 시스템 설계
인간의 기억 구조를 모방하다
섹션 제목: “인간의 기억 구조를 모방하다”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 논문의 dual-memory는 장기 이력의 요약과 최근 메시지 원문을 결합하는 다른 구분입니다. 아래 벡터 DB 예제를 논문의 직접 구현으로 해석하지 않습니다.
| 구분 | 인-컨텍스트 메모리 | 외부 메모리 |
|---|---|---|
| 위치 | 컨텍스트 윈도우 내 | 벡터 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 자체는 항상 짧게 유지됩니다.
메모리의 출처와 유효 기간
섹션 제목: “메모리의 출처와 유효 기간”메모리에는 원문 위치, 관찰 시각, 사용자·프로젝트 범위와 만료 조건을 함께 저장합니다. 예전 모델 ID·가격·배포 상태는 검색됐다는 이유만으로 현재 사실이 되지 않습니다. 변경될 수 있는 사실은 원래 도구나 문서에서 다시 확인합니다.
외부 문서에서 얻은 지시는 권한 설정으로 승격하지 않습니다. 장기 메모리 쓰기·삭제는 사용자별 정책으로 제어하고, 검증되지 않은 에이전트 추론을 확정 사실과 구분합니다.
메모리 검색 전략
섹션 제목: “메모리 검색 전략”외부 메모리 검색은 세 가지 방식을 조합합니다.
| 방식 | 적합한 경우 | 예시 |
|---|---|---|
| 임베딩 유사도 | 의미적 관련성 검색 | ”인증 관련 과거 결정” |
| 키워드 매칭 | 정확한 용어 검색 | 특정 함수명, 파일명 |
| 그래프 탐색 | 관계 기반 검색 | ”이 모듈에 영향받는 모든 모듈” |
에이전트의 메모리 시스템은 워킹 메모리(즉각적 태스크 정보), 에피소딕 메모리(세션 히스토리), 장기 메모리(세션 초월 지식)의 세 계층으로 설계합니다. 이중 메모리 아키텍처는 외부 저장소에서 관련 항목만 선택적으로 로드하여 컨텍스트를 효율적으로 사용합니다. Manus의 파일 시스템 패턴은 컨텍스트 폭발 없이 복잡한 장기 태스크를 처리하는 실용적인 해법입니다.