DEV Community

김이더
김이더

Posted on

AI한테 기억을 가르치려면, 잊는 법부터 가르쳐야 한다

코드는 GitHub에, 논문은 arXiv에서 볼 수 있다.
더 많은 글은 radarlog.kr에서.


ChatGPT한테 3일 전 대화 내용을 물어본 적 있다. 기억 못 한다. Claude한테도 물어봤다. 역시 못 한다. 둘 다 대화가 끝나면 기억이 사라진다.

근데 사람은 3일 전 대화를 기억한다. 정확히는, 중요한 대화만 기억한다. 어제 점심 뭐 먹었는지는 까먹어도, 친구가 이직한다고 했던 건 기억난다. 자주 떠올린 기억은 오래 남고, 안 꺼낸 기억은 자연스럽게 사라진다.

MemoryBank는 이 원리를 그대로 LLM에 이식한 프로젝트다.

MemoryBank가 뭔데

중국 중산대학교의 Wanjun Zhong 등이 만든 LLM용 장기 기억 메커니즘이다. AAAI 2024에 정식 논문으로 채택됐고, GitHub에 코드까지 공개돼 있다. 스타 419개. MIT 라이선스.

핵심 아이디어는 간단하다. 1885년 독일 심리학자 에빙하우스가 발견한 망각 곡선을 AI 메모리 시스템에 적용한 거다.

R = e^(−t/S)
Enter fullscreen mode Exit fullscreen mode

R은 기억 보유율, t는 경과 시간, S는 기억 강도다. 처음 배운 건 S가 1이다. 시간이 지나면 R이 급격히 떨어진다. 20분 만에 42%를 잊고, 하루 지나면 67%가 날아간다. 에빙하우스가 자기 자신한테 실험해서 알아낸 숫자다.

근데 여기서 핵심 포인트가 있다.

어떤 기억을 한 번이라도 회상(recall)하면, S가 1 증가하고 t가 0으로 리셋된다. 그 기억은 더 오래 살아남는다. 자주 꺼낸 기억은 점점 더 잊기 어려워지고, 한 번도 안 꺼낸 기억은 빠르게 소멸한다.

게임 서버로 치면 세션 타임아웃이랑 비슷하다. 접속 안 하면 세션이 만료되는데, 접속할 때마다 타이머가 리셋되는 것과 같은 원리다. 다만 MemoryBank는 리셋만 하는 게 아니라 타임아웃 시간 자체를 늘려버린다. 접속할 때마다 세션이 점점 더 오래 유지되는 셈이다.

세 개의 기둥

MemoryBank의 아키텍처는 세 부분으로 나뉜다. Memory Storage, Memory Retrieval, Memory Updating. 게임 개발자라면 ECS 패턴이 떠오를 수도 있다. 데이터를 저장하는 컴포넌트, 데이터를 읽는 시스템, 데이터를 갱신하는 시스템. 역할이 깔끔하게 분리돼 있다.

Memory Storage는 대화 원문을 타임스탬프와 함께 저장하는 계층이다. 단순히 대화를 쌓기만 하는 게 아니라 LLM을 이용해 일별 이벤트 요약, 전체 이벤트 요약, 사용자 성격 프로파일까지 계층적으로 관리한다. "지난주 월요일에 사용자가 이직 고민을 털어놨다"를 통째로 저장하면서, 동시에 "사용자는 내성적이고 성장 지향적"이라는 상위 요약도 만든다.

UE5로 치면 SaveGame 시스템에 가깝다. 원본 데이터는 그대로 두면서, 핵심 상태만 별도로 직렬화해두는 패턴. 나중에 전체 데이터를 다 불러오지 않아도, 요약된 상태만으로 빠르게 맥락을 파악할 수 있다.

Memory Retrieval은 FAISS 기반 벡터 검색이다. 모든 대화와 이벤트 요약을 인코더(영어는 MiniLM, 중국어는 Text2vec)로 임베딩해서 벡터로 만들어둔다. 새 대화가 들어오면 현재 대화 맥락을 같은 인코더로 벡터화하고, FAISS로 가장 관련 높은 기억을 찾아온다. LangChain을 써서 이 파이프라인을 구성했다.

# 대화가 들어오면
query_vector = encoder.encode(current_context)

# FAISS에서 관련 기억을 검색
relevant_memories = faiss_index.search(query_vector, top_k=5)

# 검색된 기억 + 사용자 프로파일 + 이벤트 요약을 프롬프트에 주입
prompt = build_prompt(relevant_memories, user_portrait, event_summary)
response = llm.generate(prompt)
Enter fullscreen mode Exit fullscreen mode

이 구조가 좋은 이유는, 대화 전체를 컨텍스트 윈도우에 밀어넣지 않아도 된다는 거다. Claude의 200K 토큰 컨텍스트도 길게 대화하면 금방 찬다. MemoryBank는 필요한 기억만 골라서 프롬프트에 넣으니까 토큰 효율이 훨씬 낫다.

Memory Updating은 이 글의 핵심이다. 망각 곡선 공식을 적용해서, 각 기억 조각마다 보유율 R을 계산한다. R이 특정 임계값 아래로 떨어지면 그 기억을 제거하거나 약화시킨다. 대화 중 회상된 기억은 S가 올라가고 t가 리셋되니까 살아남는다.

import math

def calculate_retention(t, S):
    """기억 보유율 계산"""
    return math.exp(-t / S)

def update_memory(memory_item, recalled=False):
    """기억 업데이트"""
    if recalled:
        memory_item['S'] += 1   # 기억 강도 증가
        memory_item['t'] = 0    # 경과 시간 리셋

    R = calculate_retention(memory_item['t'], memory_item['S'])

    if R < THRESHOLD:  # 보유율이 임계값 이하면
        memory_item['status'] = 'forgotten'

    return memory_item
Enter fullscreen mode Exit fullscreen mode

단순하다. 근데 이 단순함이 포인트다. 논문 저자들도 "이건 탐색적이고 극도로 단순화된 모델"이라고 명시했다. 실제 인간의 기억은 훨씬 복잡하지만, LLM 메모리에 적용하기엔 이 정도면 충분히 효과적이라는 거다.

SiliconFriend — 기억이 공감의 전제 조건이다

MemoryBank를 탑재한 AI 챗봇이 SiliconFriend다. 그냥 기억만 붙인 게 아니라, 38k건의 심리 상담 대화 데이터로 LoRA 파인튜닝까지 했다. LoRA rank 16, A100 한 장으로 3 에폭 학습.

왜 심리 상담 데이터를 썼을까. 기억과 공감은 분리할 수 없기 때문이다. "지난번에 이직 고민 얘기했잖아, 그 후로 어떻게 됐어?"라고 물어보려면 두 가지가 필요하다. 이직 고민을 기억하는 것, 그리고 그 기억을 꺼내서 자연스럽게 공감하는 것. MemoryBank가 전자를, 심리 상담 파인튜닝이 후자를 담당한다.

실험 결과를 보면 이게 확실히 드러난다. 베이스 ChatGLM한테 "요즘 힘들어"라고 하면 교과서적인 위로가 나온다. SiliconFriend한테 같은 말을 하면, 이전 대화에서 파악한 사용자 성격에 맞춰서 반응한다. 내성적인 사용자한테는 조심스럽게, 외향적인 사용자한테는 적극적으로.

평가도 꽤 체계적이다. ChatGPT가 15명의 가상 사용자 역할을 맡아서 10일치 대화를 생성했다. 각 사용자는 서로 다른 성격을 가진다. 이 대화 이력을 기반으로 194개의 "기억 탐침 질문"을 만들어서 SiliconFriend가 얼마나 정확하게 과거를 회상하는지 측정했다.

ChatGPT 기반 SiliconFriend가 가장 높은 성능을 보였고, 오픈소스 모델(ChatGLM, BELLE)도 기억 검색 정확도에서는 충분히 경쟁력 있었다. 다만 응답의 자연스러움과 일관성에서는 베이스 모델 성능 차이가 그대로 나타났다.

지금 쓰는 AI 메모리와 뭐가 다른가

Claude, ChatGPT, Claude Code. 셋 다 나름의 메모리 시스템을 갖고 있다. 근데 접근 방식이 완전히 다르다.

ChatGPT는 대화 요약을 미리 계산해서 매 대화에 주입한다. 자동이다. 사용자가 신경 쓸 필요가 없다. 대신 요약 과정에서 디테일이 사라진다. 뉘앙스가 압축된다.

Claude는 반대다. 기억 도구를 온디맨드로 사용한다. 과거 대화를 검색할 수 있지만, Claude가 "지금 검색해야겠다"고 판단해야만 작동한다. 판단을 안 하면 맥락이 묻힌다. 대신 검색할 때는 원본 대화에서 직접 가져오니까 깊이가 있다.

Claude Code는 CLAUDE.md 파일 기반이다. 마크다운 파일에 프로젝트 맥락을 적어두면 매 세션 시작 시 자동으로 로드된다. 투명하고 편집 가능하지만, 파일이 커지면 성능이 떨어진다. 200줄 인덱스 제한도 있다.

셋 다 망각 메커니즘이 없다. 이게 MemoryBank와의 결정적 차이다.

ChatGPT는 요약을 무한정 누적한다. Claude는 대화 이력이 계속 쌓인다. Claude Code의 CLAUDE.md는 사용자가 직접 정리하지 않으면 점점 비대해진다. 아무것도 잊지 않으면, 아이러니하게도 기억이 무용해진다. 모든 게 동등한 중요도로 쌓여 있으면, 정말 중요한 기억을 빠르게 찾기 어렵다.

MemoryBank는 여기에 "잊기"를 도입했다. 오래되고 한 번도 안 꺼낸 기억은 자연스럽게 사라진다. 자주 회상된 기억만 강화된다. 결과적으로 메모리 저장소에는 정말 중요한 기억만 남는다. 이건 성능 최적화이기도 하다. FAISS 인덱스가 작아지고, 검색 정확도가 올라간다.

내 프로젝트에 어떻게 붙이나

MemoryBank를 직접 가져다 쓸 수도 있고, 핵심 아이디어만 빌려올 수도 있다. 두 가지 경로를 짚는다.

경로 1: 레포를 그대로 쓴다. GitHub에서 클론하고, pip install -r requirement.txt 돌리고, OpenAI API 키를 세팅한다. ChatGPT 기반 SiliconFriend가 가장 간단하다. SiliconFriend-ChatGPT/launch.sh에 API 키 넣고 실행하면 CLI에서 바로 대화할 수 있다. 영어는 --language=en, 중국어는 --language=cn.

오픈소스 모델을 쓰고 싶으면 ChatGLM이나 BELLE 베이스 모델을 먼저 세팅하고, LoRA 체크포인트를 다운받아야 한다. A100 80GB 환경이 필요하다. 개인 프로젝트로 돌리기엔 좀 무겁다.

경로 2: 핵심 메커니즘만 내 코드에 이식한다. 이쪽이 현실적이다. 필요한 건 세 가지다.

첫째, 대화를 타임스탬프와 함께 저장하는 스토리지. JSON이면 충분하다. 대화가 끝날 때마다 LLM API를 호출해서 일별 요약과 사용자 성격 요약을 생성한다.

memory_storage = {
    "conversations": [
        {
            "timestamp": "2026-04-13T14:30:00",
            "role": "user",
            "content": "요즘 UE5 슬레이트 위젯 작업하는데 막히는 부분이 있어",
            "S": 1,  # 기억 강도 (초기값)
            "t": 0   # 경과 시간
        }
    ],
    "daily_summaries": {},
    "user_portrait": "게임 개발자, UE5 C++ 전문, 내성적, 문제 해결 지향적"
}
Enter fullscreen mode Exit fullscreen mode

둘째, 임베딩 + 벡터 검색. sentence-transformers로 임베딩하고, FAISS로 인덱싱한다. LangChain을 쓰면 이 파이프라인이 몇 줄로 끝난다.

셋째, 망각 곡선 기반 업데이트. 하루에 한 번, 또는 대화 시작 전에 전체 메모리를 순회하면서 R을 계산한다. 임계값(예: 0.1) 아래인 기억은 제거한다. 대화 중 검색된 기억은 S를 올리고 t를 리셋한다.

이 세 가지를 조합하면, 기존 챗봇이나 AI 에이전트에 장기 기억을 붙일 수 있다. 특히 반복적으로 사용자와 대화하는 서비스에 효과적이다. AI 튜터, AI 코치, 고객 응대 봇, 게임 NPC 같은 곳.

게임 NPC에 붙이는 시나리오를 상상해보면 재밌다. 플레이어가 NPC한테 자기 모험담을 여러 번 얘기하면, NPC가 그 이야기를 점점 더 오래 기억한다. 한 번만 스쳐 지나간 대화는 NPC도 잊어버린다. 꽤 자연스럽다.

한계와 주의점

MemoryBank는 AAAI 2024에 채택된 검증된 연구지만, 한계가 분명하다.

일단 S값을 단순히 정수로 올리는 건 현실과 거리가 있다. 사람의 기억 강도는 감정적 중요도, 수면, 스트레스 같은 변수에 영향받는다. MemoryBank는 이걸 전부 무시하고 "회상 횟수"만으로 S를 결정한다. 논문 저자들도 이 점을 명시적으로 인정했다.

또 하나. 메모리 요약과 성격 프로파일링에 LLM API 호출이 필요하다. 대화가 끝날 때마다 요약 API를 호출하면, 대화가 많아질수록 비용이 누적된다. 실서비스에 붙이려면 요약 주기를 조절하거나, 로컬 경량 모델로 요약 레이어를 분리하는 설계가 필요하다.

마지막으로, 기본 레포가 2023년 5월에 공개된 이후 큰 업데이트가 없다. ChatGLM 6B, BELLE 7B 기반이라 지금 시점에서 베이스 모델이 좀 오래됐다. 하지만 아키텍처 자체는 모델에 독립적이다. Claude, GPT-4o, Gemma, Llama 어디에든 붙일 수 있다. 핵심은 모델이 아니라 메모리 메커니즘이다.


다음 편에서는 R = e^(−t/S) 수식 자체를 수학적으로 분해한다. S가 1에서 5로 올라갈 때 곡선이 어떻게 변하는지, 임계값을 어디에 잡아야 하는지, 실제 시뮬레이션으로 돌려본다.

"완벽한 기억은 기억이 아니다. 잊을 줄 아는 기억만이 진짜 기억이다."

Top comments (0)