콘텐츠로 이동

메모리 시스템 설계

AI 에이전트의 메모리 시스템은 인지과학의 기억 모델에서 영감을 받았습니다. 인간이 모든 것을 의식적으로 기억하지 않듯, 에이전트도 무엇을 어디에 저장하는가를 설계해야 합니다.

┌─────────────────────────────────────────────┐
│ 컨텍스트 윈도우 (RAM) │
│ ┌─────────────────┐ ┌──────────────────┐ │
│ │ 워킹 메모리 │ │ 에피소딕 메모리 │ │
│ │ (현재 태스크) │ │ (현재 세션) │ │
│ └─────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────┘
↕ 검색/저장
┌─────────────────────────────────────────────┐
│ 외부 저장소 (디스크) │
│ ┌─────────────────┐ ┌──────────────────┐ │
│ │ 장기 메모리 │ │ 파일 시스템 │ │
│ │ (벡터 DB) │ │ (확장 메모리) │ │
│ └─────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────┘

현재 진행 중인 태스크에 직접 필요한 정보입니다. 컨텍스트 윈도우 안에 상주하며, 태스크가 끝나면 사라집니다.

  • 현재 사용자 요청
  • 진행 중인 계획과 단계
  • 최근 도구 실행 결과
  • 임시 변수와 중간 계산값

현재 세션 내의 대화 히스토리입니다. “방금 전에 무슨 말을 했는가”를 기억합니다. 세션이 끝나면 장기 메모리로 저장하거나 폐기합니다.

interface EpisodicMemory {
sessionId: string;
startTime: Date;
messages: Message[];
toolCalls: ToolCallRecord[];
decisions: Decision[]; // 주요 결정 사항
}

세션을 넘어 지속되는 지식입니다. 벡터 데이터베이스나 지식 그래프에 저장되며, 새 세션 시작 시 관련 내용을 검색해 컨텍스트에 주입합니다.

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의 핵심 패턴 중 하나는 파일 시스템을 무제한 외부 메모리로 활용하는 것입니다. 컨텍스트에 직접 담기엔 너무 큰 정보는 파일에 쓰고, 컨텍스트에는 파일 경로만 유지합니다.

# 현재 태스크: 사용자 인증 시스템 구현
## 완료
- [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의 파일 시스템 패턴은 컨텍스트 폭발 없이 복잡한 장기 태스크를 처리하는 실용적인 해법입니다.