LLM 에이전트의 '기억 상실'을 막는 아키텍처 설계: 장기 기억(LTM) 구현 심화 가이드
LLM 에이전트 시스템을 구축하는 개발자라면 누구나 한 번쯤 '맥락 상실(Context Loss)'이라는 벽에 부딪힙니다. 아무리 정교하게 프롬프트를 설계하고, 최신 RAG(Retrieval-Augmented Generation) 기법을 적용해도, 대화가 길어지거나 여러 단계를 거치는 복잡한 업무를 처리하게 되면 에이전트는 마치 기억을 잃은 사람처럼 이전의 중요한 맥락이나 사용자의 숨겨진 의도를 놓치기 일쑤입니다.
이는 LLM의 근본적인 한계라기보다는, 우리가 에이전트에게 '기억'을 어떻게 구조화하여 제공할 것인가에 대한 아키텍처적 고민의 영역입니다. 단순히 대화 로그를 벡터 DB에 저장하는 것을 넘어, 시스템 레벨에서 '장기 기억(Long-Term Memory, LTM)'을 설계하는 것이 현재 LLM 에이전트 개발의 핵심 과제입니다.
이 가이드는 단순한 튜토리얼을 넘어, 실제 운영 가능한 고도화된 에이전트 시스템을 위한 아키텍처적 청사진을 제공하는 것을 목표로 합니다.
휘발성 메모리를 넘어: 왜 구조화된 장기 기억이 필수인가?
우리가 흔히 사용하는 LLM의 컨텍스트 윈도우는 본질적으로 휘발성 메모리입니다. 세션이 종료되면 모든 정보는 사라지며, 아무리 큰 컨텍스트 윈도우를 가진 모델이라도 처리할 수 있는 토큰 수에는 명확한 물리적 한계가 존재합니다.
이 한계를 극복하고 에이전트가 '지능적'으로 작동하기 위해서는, 휘발성 메모리(단기 대화 기록)와 영속적인 장기 기억(과거의 지식, 사용자 프로필, 업무 이력)을 분리하고, 이 둘을 유기적으로 연결하는 메커니즘이 필요합니다.
단순히 대화 로그를 임베딩하여 벡터 DB에 저장하는 방식은 '유사도'라는 단일 척도에 의존합니다. 하지만 실제 비즈니스 지식은 단순한 유사성으로 포착되지 않습니다. "사용자 A가 제품 B에 대해 문의했고, 이 과정에서 '가격 민감도'라는 속성을 발견했다"와 같은 관계적 정보가 핵심인데, 이는 구조화된 메모리만이 담아낼 수 있습니다.
💡 메모리 저장 방식 비교: 단순 임베딩 vs. 구조화된 지식
| 특징 | 단순 벡터 DB 저장 (Raw Embedding) | 지식 그래프 (KG) 연동 | 압축/요약 메모리 (Summarization) |
|---|---|---|---|
| 저장 단위 | 원본 텍스트의 임베딩 벡터 | 노드(개체)와 엣지(관계) | 핵심 요약문, 액션 목록 |
| 장점 | 구현 용이성, 빠른 검색 속도 | 관계 추론 가능, 높은 설명력 | 컨텍스트 윈도우 최적화, 정보 밀도 높음 |
| 단점 | 관계 파악 불가, 정보 과부하 위험 | 구축 복잡성 높음, 관계 추출 난이도 | 요약 과정에서 중요한 뉘앙스 손실 가능성 |
| 적합한 상황 | 단순 Q&A, 문서 검색 | 복잡한 의사결정 지원, 관계 분석 | 장시간 대화 요약, 세션 요약 |
이 표에서 보듯, 가장 강력한 에이전트는 이 세 가지 방식을 하이브리드로 결합합니다.
메모리 구조화 심화: 지식 그래프와 압축의 결합
단순히 텍스트를 벡터로 저장하는 것을 넘어, 우리는 에이전트의 상호작용 자체를 데이터베이스의 '객체'로 취급해야 합니다.
1. 지식 그래프(Knowledge Graph, KG)를 통한 관계 모델링
사용자 A가 제품 B에 대해 문의하고, 상담원 C가 '할인 쿠폰'이라는 정보를 제공했다면, 이 세 가지 요소는 독립적인 데이터가 아닙니다. 이들은 다음과 같은 관계를 형성합니다.
- (사용자 A) $\xrightarrow{\text{관심사}}$ (제품 B)
- (상담원 C) $\xrightarrow{\text{제공 정보}}$ (할인 쿠폰)
- (사용자 A) $\xrightarrow{\text{획득 정보}}$ (할인 쿠폰)
KG는 이 관계(Edge)를 명시적으로 저장함으로써, "A가 B에 관심이 있고, C가 제공한 쿠폰이 A의 구매 결정에 영향을 주었다"와 같은 **추론(Inference)**을 가능하게 합니다.
2. 다단계 요약 및 임베딩 (Summarization & Embedding)
매번 긴 대화 로그 전체를 임베딩하는 것은 비효율적입니다. 대신, 대화가 특정 주제로 전환될 때마다, LLM을 활용하여 해당 대화 턴을 **'핵심 요약(Summary)'**으로 추출하고, 이 요약본을 임베딩하여 벡터 DB에 저장하는 방식이 효율적입니다.
🛠️ 실습: 다단계 구조화 과정 (Pseudo-Code)
def process_conversation_turn(conversation_history, current_turn):
# 1. 턴별 요약 및 구조화
summary = llm_call(prompt="이 대화 턴의 핵심 주제와 결론을 1문장으로 요약해줘:", history=conversation_history, current=current_turn)
# 2. 메타데이터 추출 (주체, 객체, 액션)
metadata = llm_call(prompt="이 요약에서 핵심 엔티티(주체, 객체)와 액션(행위)을 추출해줘:", summary=summary)
# 3. 벡터 임베딩 및 저장
embedding = embedding_model.encode(summary)
vector_db.insert(vector=embedding, metadata={"summary": summary, "entities": metadata, "timestamp": current_turn.time})
return summary, metadata🚀 고급 검색 및 추론 (Advanced Retrieval)
단순히 벡터 유사도 검색(Semantic Search)만으로는 부족합니다. 우리는 **'구조화된 검색(Structured Retrieval)'**을 결합해야 합니다.
-
질의 분해 (Query Decomposition): 사용자의 질문("지난주에 할인받았던 그 노트북 모델이 아직 재고가 있나요?")을 받으면, 이를 여러 개의 검색 쿼리로 분해합니다.
- 쿼리 1 (검색): "지난주 할인 정보" (시간 기반 필터링)
- 쿼리 2 (검색): "노트북 모델" (엔티티 기반 필터링)
- 쿼리 3 (검색): "재고 여부" (상태 기반 필터링)
-
필터링 및 재순위화 (Filtering & Re-ranking): 벡터 DB에서 검색된 상위 N개의 문서를 가져온 후, 메타데이터 필터(예:
timestamp가 '지난주')를 적용하여 검색 범위를 좁힙니다. 이후, Reranker 모델을 사용하여 검색된 문서들이 실제로 질문의 의도와 얼마나 관련이 깊은지 재평가하여 최종 순위를 매깁니다.
💡 결론: 시스템 아키텍처의 변화
과거의 RAG(Retrieval-Augmented Generation)가 **'문서 검색'**에 초점을 맞췄다면, 진보된 시스템은 **'지식 그래프 기반의 추론적 검색'**을 수행해야 합니다.
| 단계 | 목표 | 사용 기술 | 결과물 |
|---|---|---|---|
| Ingestion | 비정형 텍스트를 구조화된 지식으로 변환 | LLM (Summarization, NER), Vector DB | 구조화된 벡터 및 메타데이터 |
| Retrieval | 질문 의도에 맞는 여러 관점의 정보를 검색 | Query Decomposition, Vector Search + Metadata Filter | 관련성이 높은, 구조화된 정보 셋 |
| Generation | 검색된 정보들을 종합하여 추론 기반의 답변 생성 | LLM (Contextual Reasoning) | 답변 + 근거(어떤 구조화된 정보가 사용되었는지) |
이러한 다층적 접근 방식을 통해, 시스템은 단순한 정보 검색을 넘어, **'과거의 대화 맥락을 이해하고, 필요한 정보를 조합하여 추론하는 비서'**의 역할을 수행할 수 있게 됩니다.
이 글은 AI 에이전트가 1차 초안을 작성한 뒤, 사람 편집자가 사실관계·출처·톤과 맥락을 검토하여 발행했습니다. 오류나 부정확한 내용이 확인되면 24시간 이내에 정정합니다.
댓글
불러오는 중...