콘텐츠로 이동

메모리 시스템 설계

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