본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 27. · 3 Views
Memory Systems 에이전트 메모리 아키텍처 완벽 가이드
AI 에이전트가 정보를 기억하고 활용하는 메모리 시스템의 핵심 아키텍처를 다룹니다. 벡터 스토어의 한계부터 Knowledge Graph, Temporal Knowledge Graph까지 단계별로 이해할 수 있습니다.
목차
- Context-Memory Spectrum
- 벡터_스토어의_구조적_한계
- Knowledge_Graph
- Temporal_Knowledge_Graph
- 메모리_아키텍처_비교
- Neo4j_시간적_지식_그래프_실습
1. Context-Memory Spectrum
어느 날 김개발 씨가 AI 에이전트 프로젝트를 맡게 되었습니다. 사용자와 대화하는 챗봇을 만들었는데, 대화가 길어지니 에이전트가 앞서 나눈 이야기를 까먹어 버립니다.
"도대체 AI 에이전트는 어떻게 정보를 기억하는 거지?" 김개발 씨의 고민이 시작되었습니다.
Context-Memory Spectrum은 AI 에이전트가 정보를 저장하고 접근하는 방식의 연속적인 스펙트럼입니다. 마치 사람이 방금 들은 전화번호를 잠깐 기억하는 것과, 졸업 앨범을 꺼내 옛 친구를 찾는 것이 다르듯이, 에이전트도 즉각적인 컨텍스트부터 영구적인 저장소까지 다양한 메모리 계층을 활용합니다.
이 스펙트럼을 이해하면 상황에 맞는 최적의 메모리 전략을 설계할 수 있습니다.
다음 코드를 살펴봅시다.
# Context-Memory Spectrum 구현 예제
class MemorySpectrum:
def __init__(self):
# 즉각 컨텍스트: 현재 대화 턴에서만 유효
self.immediate_context = []
# 작업 메모리: 현재 세션 동안 유지
self.working_memory = {}
# 단기 메모리: 최근 N개 상호작용 저장
self.short_term = []
# 장기 메모리: 영구 저장소 (DB, Vector Store 등)
self.long_term_store = None
def add_to_context(self, message, memory_level="immediate"):
# 메모리 수준에 따라 적절한 저장소에 추가
if memory_level == "immediate":
self.immediate_context.append(message)
elif memory_level == "working":
self.working_memory[message["id"]] = message
elif memory_level == "short_term":
self.short_term.append(message)
self._enforce_short_term_limit(max_items=100)
김개발 씨는 입사 6개월 차 AI 엔지니어입니다. 오늘도 열심히 챗봇을 개선하던 중, 이상한 문제를 발견했습니다.
분명히 5분 전에 사용자가 "저는 서울에 살아요"라고 말했는데, 에이전트가 "어디 사시나요?"라고 다시 묻는 것이었습니다. 선배 개발자 박시니어 씨가 다가와 상황을 살펴봅니다.
"아, Context-Memory Spectrum을 제대로 설계하지 않아서 생긴 문제네요. 모든 정보를 똑같이 취급하면 안 됩니다." 그렇다면 Context-Memory Spectrum이란 정확히 무엇일까요?
쉽게 비유하자면, 우리 뇌의 기억 시스템과 비슷합니다. 방금 전화로 들은 배달 주소는 주문이 끝나면 잊어버립니다.
하지만 부모님 생신은 평생 기억하지요. 컴퓨터에서도 마찬가지입니다.
CPU 레지스터는 가장 빠르지만 용량이 작고, 하드디스크는 느리지만 영구적입니다. AI 에이전트의 메모리도 이런 계층 구조를 따릅니다.
가장 왼쪽 끝에는 **즉각 컨텍스트(Immediate Context)**가 있습니다. 이것은 현재 대화 턴에서만 유효한 정보입니다.
사용자가 방금 입력한 질문, LLM에 전달되는 프롬프트가 여기에 해당합니다. 접근 속도는 가장 빠르지만, 한 턴이 끝나면 사라집니다.
그 다음은 **작업 메모리(Working Memory)**입니다. 현재 작업 세션 동안 유지되는 정보입니다.
사용자가 쇼핑 중일 때 장바구니에 담은 상품 목록처럼, 세션이 유지되는 동안 계속 참조해야 하는 데이터입니다. 조금 더 오른쪽으로 가면 **단기 메모리(Short-Term Memory)**가 있습니다.
최근 N개의 대화 내역이나 상호작용을 저장합니다. 대화의 맥락을 유지하는 데 핵심적인 역할을 합니다.
하지만 무한히 저장할 수는 없으므로, 오래된 것부터 잊어버립니다. 스펙트럼의 가장 오른쪽에는 **장기 메모리(Long-Term Memory)**가 있습니다.
벡터 데이터베이스, 그래프 데이터베이스, 관계형 데이터베이스 등을 활용하여 영구적으로 저장합니다. 접근 속도는 느리지만, 필요할 때 언제든 꺼내 쓸 수 있습니다.
위의 코드를 살펴보면, MemorySpectrum 클래스가 이 네 가지 계층을 모두 관리합니다. add_to_context 메서드는 memory_level 파라미터에 따라 적절한 저장소에 정보를 추가합니다.
단기 메모리의 경우 _enforce_short_term_limit으로 최대 개수를 제한하여 메모리 폭발을 방지합니다. 실제 현업에서는 어떻게 활용할까요?
고객 상담 챗봇을 예로 들어봅시다. 고객이 "환불 문의드립니다"라고 하면, 이 의도는 즉각 컨텍스트에 저장됩니다.
주문 번호를 물어보고 받으면, 그 정보는 작업 메모리에 저장되어 환불 처리가 끝날 때까지 유지됩니다. 대화 전체 흐름은 단기 메모리에, 이 고객의 과거 구매 이력이나 선호도는 장기 메모리에서 가져옵니다.
주의할 점도 있습니다. 모든 정보를 장기 메모리에 저장하면 검색 비용이 늘어나고, 관련 없는 정보가 노이즈로 작용할 수 있습니다.
반대로 너무 많은 정보를 즉각 컨텍스트에 넣으면 LLM의 토큰 한계에 부딪힙니다. 균형이 중요합니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다.
"아, 사용자 위치 같은 중요한 정보는 단기 메모리에 저장해야 했군요!" 메모리 스펙트럼을 이해하면 에이전트의 기억력 문제를 체계적으로 해결할 수 있습니다.
실전 팁
💡 - 토큰 비용이 민감하다면 즉각 컨텍스트에는 꼭 필요한 정보만 포함하세요
- 단기 메모리의 크기 제한은 프로젝트 특성에 맞게 조절해야 합니다
- 장기 메모리 접근 전에 캐싱 레이어를 두면 성능이 크게 향상됩니다
2. 벡터 스토어의 구조적 한계
김개발 씨는 박시니어 씨의 조언대로 벡터 데이터베이스를 도입했습니다. 문서를 임베딩하고, 유사도 검색으로 관련 정보를 찾아오니 꽤 잘 동작하는 것 같았습니다.
그런데 어느 날 이상한 버그 리포트가 들어왔습니다. "A 회사의 CEO가 누구냐고 물었더니 B 회사 CEO 이름을 알려줘요."
벡터 스토어는 텍스트를 고차원 벡터로 변환하여 의미적 유사도를 기반으로 검색하는 저장소입니다. 매우 강력하지만, 구조적인 한계가 있습니다.
정보를 독립적인 청크로 저장하기 때문에 엔터티 간의 관계 정보가 손실됩니다. 마치 백과사전의 페이지를 낱장으로 흩어놓고 비슷한 단어가 있는 페이지만 찾는 것과 같습니다.
다음 코드를 살펴봅시다.
# 벡터 스토어의 관계 정보 손실 문제 시연
from sentence_transformers import SentenceTransformer
import numpy as np
# 두 개의 서로 다른 관계를 가진 문장
documents = [
"김철수는 A회사의 CEO입니다.",
"박영희는 B회사의 CEO입니다.",
"A회사는 IT 서비스를 제공합니다.",
"B회사는 제조업체입니다."
]
model = SentenceTransformer('all-MiniLM-L6-v2')
embeddings = model.encode(documents)
# 쿼리: "A회사 CEO"
query = "A회사의 대표는 누구인가요?"
query_embedding = model.encode([query])
# 코사인 유사도 계산
similarities = np.dot(embeddings, query_embedding.T).flatten()
# 결과: 두 CEO 문장 모두 높은 유사도를 보임
# 관계 정보(어떤 회사의 CEO인지)가 임베딩에서 희석됨
김개발 씨는 자신 있게 벡터 데이터베이스를 구축했습니다. 회사 정보, 인물 정보, 제품 정보를 모두 청크로 나누어 임베딩하고 저장했습니다.
"이제 뭘 물어봐도 척척 대답할 수 있겠지!" 하지만 현실은 달랐습니다. 고객 지원팀에서 급한 연락이 왔습니다.
"챗봇이 A회사 CEO가 누구냐고 물었는데 박영희라고 대답했어요. 박영희는 B회사 CEO인데요!" 김개발 씨는 당황했습니다.
분명히 데이터에는 정확한 정보가 있는데, 왜 이런 일이 벌어진 걸까요? 박시니어 씨가 설명해 주었습니다.
"벡터 스토어의 구조적 한계 때문이야. 관계 정보가 손실되거든." 벡터 임베딩은 텍스트의 의미를 고차원 공간의 점으로 표현합니다.
"김철수는 A회사의 CEO입니다"와 "박영희는 B회사의 CEO입니다"라는 두 문장을 생각해 보세요. 이 두 문장은 의미적으로 매우 유사합니다.
둘 다 "누군가가 어떤 회사의 CEO"라는 같은 패턴을 가지고 있으니까요. 그래서 "A회사 CEO가 누구인가요?"라고 질문하면, 벡터 유사도 검색은 두 문장 모두를 높은 점수로 반환합니다.
둘 다 "회사"와 "CEO"라는 키워드를 포함하고, 문장 구조도 비슷하니까요. 문제는 어떤 회사의 CEO인지라는 관계 정보가 임베딩 과정에서 희석된다는 것입니다.
이것을 비유하자면, 도서관에서 책을 찾는 상황과 비슷합니다. 사서에게 "경영학 관련 책 주세요"라고 하면 경영학 코너에서 책을 가져다 줍니다.
하지만 "A회사에 대해 다룬 경영학 책 주세요"라고 하면, 사서는 경영학 코너 전체를 뒤져야 합니다. 책들이 "어떤 회사에 대한 책인지"로 분류되어 있지 않기 때문입니다.
코드를 살펴보면, 쿼리 "A회사의 대표는 누구인가요?"에 대해 코사인 유사도를 계산했을 때, 김철수 문장과 박영희 문장 모두 비슷한 점수를 받습니다. 왜냐하면 임베딩 모델은 "CEO", "회사", "대표"라는 의미적 유사성에 집중하지, "A회사"와 "B회사"의 차이를 명확히 구분하지 못하기 때문입니다.
실제 현업에서 이 문제는 심각한 결과를 초래할 수 있습니다. 고객이 "우리 회사 계약 조건이 뭐였지?"라고 물었는데, 다른 회사의 계약 조건을 알려준다면 어떻게 될까요?
법적 문제로까지 번질 수 있습니다. 이 한계를 완화하기 위한 몇 가지 방법이 있습니다.
메타데이터 필터링을 사용하여 검색 범위를 좁히거나, 청크에 더 많은 컨텍스트 정보를 포함시키는 방법입니다. 하지만 이것들은 근본적인 해결책이 아닙니다.
벡터 스토어는 태생적으로 관계 정보를 표현하기 어렵습니다. 김개발 씨는 한숨을 쉬었습니다.
"그럼 어떻게 해야 하나요?" 박시니어 씨가 미소를 지으며 말했습니다. "관계 정보가 중요하다면, Knowledge Graph를 고려해 봐야지."
실전 팁
💡 - 벡터 스토어에 저장할 때 청크에 엔터티 정보를 명시적으로 포함시키세요
- 메타데이터 필터링을 적극 활용하여 검색 범위를 좁히세요
- 관계가 중요한 데이터는 벡터 스토어만으로는 한계가 있음을 인식해야 합니다
3. Knowledge Graph
박시니어 씨의 조언을 듣고 김개발 씨는 Knowledge Graph에 대해 공부하기 시작했습니다. 그래프 데이터베이스라니, 뭔가 복잡해 보였습니다.
하지만 알고 보니 우리가 일상적으로 그리는 관계도와 크게 다르지 않았습니다. "아, 이게 그 유명한 지식 그래프구나!"
Knowledge Graph는 엔터티(노드)와 그들 사이의 관계(엣지)를 명시적으로 저장하는 구조입니다. "김철수 - CEO_OF -> A회사"처럼 주어-관계-목적어 형태의 트리플로 지식을 표현합니다.
마치 인물 관계도나 마인드맵처럼, 정보 간의 연결 고리를 보존하고 이를 따라 추론할 수 있습니다.
다음 코드를 살펴봅시다.
# Knowledge Graph 기본 구조 및 순회 기반 추론
from py2neo import Graph, Node, Relationship
# Neo4j 연결
graph = Graph("bolt://localhost:7687", auth=("neo4j", "password"))
# 노드 생성: 엔터티 정의
kim = Node("Person", name="김철수", role="CEO")
park = Node("Person", name="박영희", role="CEO")
company_a = Node("Company", name="A회사", industry="IT서비스")
company_b = Node("Company", name="B회사", industry="제조업")
# 관계 생성: 트리플 정의
rel1 = Relationship(kim, "CEO_OF", company_a, since=2020)
rel2 = Relationship(park, "CEO_OF", company_b, since=2018)
# 그래프에 저장
graph.create(rel1 | rel2)
# 정확한 관계 기반 쿼리
query = """
MATCH (p:Person)-[:CEO_OF]->(c:Company {name: 'A회사'})
RETURN p.name as ceo_name
"""
result = graph.run(query).data()
# 결과: [{'ceo_name': '김철수'}] - 정확히 A회사 CEO만 반환
김개발 씨는 화이트보드에 동그라미를 그리기 시작했습니다. "김철수"라고 쓰고, 옆에 "A회사"라고 쓴 뒤, 화살표로 연결하며 "CEO"라고 적었습니다.
박시니어 씨가 고개를 끄덕였습니다. "바로 그거야.
그게 Knowledge Graph의 핵심이야." Knowledge Graph는 정보를 노드와 엣지로 표현합니다. 노드는 실체가 있는 것들입니다.
사람, 회사, 제품, 개념 등 우리가 이름을 붙일 수 있는 모든 것이 노드가 될 수 있습니다. 엣지는 이 노드들 사이의 관계입니다.
"~의 CEO다", "~에서 일한다", "~를 소유하다" 같은 것들이죠. 이것을 **트리플(Triple)**이라고 부릅니다.
주어-술어-목적어, 즉 "김철수(주어) - CEO_OF(술어) -> A회사(목적어)" 형태로 지식을 표현합니다. 마치 문장을 쓰듯이 지식을 저장하는 것입니다.
왜 이렇게 하면 벡터 스토어의 문제가 해결될까요? "A회사의 CEO는 누구인가요?"라는 질문을 받았을 때, Knowledge Graph는 완전히 다른 방식으로 답을 찾습니다.
먼저 "A회사"라는 노드를 찾습니다. 그 다음, 이 노드로 들어오는 "CEO_OF" 관계를 가진 엣지를 따라갑니다.
그 엣지의 출발점 노드를 보니 "김철수"입니다. 끝.
이것을 그래프 순회(Graph Traversal) 또는 관계 기반 추론이라고 합니다. 의미적 유사도가 아니라, 명시적으로 정의된 관계를 따라가는 것입니다.
B회사의 CEO인 박영희는 "A회사"와 연결되어 있지 않으므로 절대로 잘못된 결과로 반환되지 않습니다. 코드를 살펴보면, Cypher라는 그래프 쿼리 언어를 사용합니다.
MATCH 절에서 패턴을 지정합니다. "(p:Person)-[:CEO_OF]->(c:Company {name: 'A회사'})"는 "Person 타입의 노드 p가 CEO_OF 관계로 A회사라는 이름의 Company 노드 c와 연결되어 있다"는 패턴입니다.
이 패턴에 맞는 모든 p를 찾아 반환합니다. Knowledge Graph의 또 다른 강력한 기능은 멀티홉 추론입니다.
"A회사 CEO의 출신 학교 동문 중에 B회사에서 일하는 사람은?"이라는 복잡한 질문도 관계를 따라가며 답을 찾을 수 있습니다. 벡터 검색으로는 상상하기 어려운 일이죠.
실제 현업에서 Knowledge Graph는 다양하게 활용됩니다. 구글의 검색 결과 우측에 뜨는 정보 패널, 아마존의 "이 상품을 산 사람들이 함께 본 상품", 넷플릭스의 추천 시스템 등이 모두 Knowledge Graph를 활용합니다.
특히 복잡한 도메인 지식을 다루는 AI 에이전트에서는 필수적인 구성 요소가 되고 있습니다. 물론 단점도 있습니다.
스키마를 미리 정의해야 하고, 새로운 유형의 관계가 추가될 때마다 설계를 고민해야 합니다. 또한 자유로운 텍스트 검색에는 벡터 스토어가 여전히 더 효과적입니다.
김개발 씨가 물었습니다. "그럼 벡터 스토어랑 Knowledge Graph를 같이 쓰면 되겠네요?" 박시니어 씨가 웃으며 대답했습니다.
"정확해. 실제로 많은 시스템이 두 가지를 조합해서 사용하지.
각각의 장점을 취하는 거야."
실전 팁
💡 - 관계가 명확한 데이터는 Knowledge Graph가 적합합니다
- Neo4j, Amazon Neptune, ArangoDB 등 다양한 그래프 데이터베이스를 검토하세요
- 벡터 스토어와 Knowledge Graph를 함께 사용하는 하이브리드 접근법을 고려하세요
4. Temporal Knowledge Graph
김개발 씨는 Knowledge Graph를 성공적으로 구축했습니다. 그런데 며칠 뒤, 또 다른 버그 리포트가 들어왔습니다.
"김철수가 A회사 CEO라고 했는데, 김철수는 작년에 퇴임했어요. 지금 CEO는 이영희입니다." 세상은 변하고, 정보도 변합니다.
이것을 어떻게 처리해야 할까요?
Temporal Knowledge Graph는 일반 Knowledge Graph에 시간 차원을 추가한 것입니다. 모든 관계에 유효 기간(valid_from, valid_to)을 부여하여 "언제 그 관계가 유효했는지"를 추적합니다.
마치 이력서에 경력 기간을 적듯이, 관계의 시작과 끝을 명시합니다. 이를 통해 "2023년 당시 A회사 CEO는 누구였나요?" 같은 시간 여행 쿼리가 가능해집니다.
다음 코드를 살펴봅시다.
# Temporal Knowledge Graph 구현 예제
from datetime import datetime, date
# 시간 정보가 포함된 관계 생성
def create_temporal_relationship(graph, from_node, to_node, rel_type,
valid_from, valid_to=None):
"""시간 정보가 포함된 관계를 생성합니다."""
query = """
MATCH (a), (b)
WHERE id(a) = $from_id AND id(b) = $to_id
CREATE (a)-[r:%s {
valid_from: date($valid_from),
valid_to: date($valid_to),
is_current: $is_current
}]->(b)
RETURN r
""" % rel_type
is_current = valid_to is None
return graph.run(query, from_id=from_node.identity,
to_id=to_node.identity,
valid_from=valid_from, valid_to=valid_to,
is_current=is_current)
# 시간 여행 쿼리: 특정 시점의 CEO 조회
time_travel_query = """
MATCH (p:Person)-[r:CEO_OF]->(c:Company {name: 'A회사'})
WHERE r.valid_from <= date('2023-06-01')
AND (r.valid_to IS NULL OR r.valid_to > date('2023-06-01'))
RETURN p.name as ceo_name, r.valid_from as since
"""
김개발 씨는 난감했습니다. Knowledge Graph에 저장된 "김철수 - CEO_OF -> A회사" 관계는 분명히 맞았습니다.
2020년부터 2023년까지는요. 하지만 지금은 2024년이고, 이영희가 새로운 CEO입니다.
기존 관계를 삭제하고 새 관계를 추가하면 될까요? 박시니어 씨가 고개를 저었습니다.
"그러면 안 돼. 과거 정보도 중요하거든.
누군가 '2022년에 A회사 CEO가 누구였지?'라고 물어보면 뭐라고 대답할 거야?" 이것이 바로 Temporal Knowledge Graph가 필요한 이유입니다. 모든 관계에 시간 정보를 추가하는 것입니다.
일반 Knowledge Graph의 트리플은 "김철수 - CEO_OF -> A회사"입니다. Temporal Knowledge Graph에서는 이것이 "김철수 - CEO_OF -> A회사 [2020-01-01 ~ 2023-12-31]"이 됩니다.
관계가 언제부터 언제까지 유효한지를 명시하는 것이죠. 이렇게 하면 여러 가지 강력한 쿼리가 가능해집니다.
첫째, 현재 시점 쿼리입니다. "지금 A회사 CEO는 누구인가요?"라는 질문에, valid_to가 NULL이거나 오늘 날짜보다 큰 관계만 찾으면 됩니다.
이영희가 2024년 1월 1일부터 CEO이고, valid_to가 NULL(현재 진행형)이므로 이영희가 반환됩니다. 둘째, **시간 여행 쿼리(Time Travel Query)**입니다.
"2022년 6월 1일 기준 A회사 CEO는 누구였나요?"라는 질문에, 해당 날짜가 valid_from과 valid_to 사이에 포함되는 관계를 찾습니다. 김철수의 CEO 기간이 2020-2023이므로, 김철수가 반환됩니다.
셋째, 변화 이력 추적입니다. "A회사의 역대 CEO를 모두 알려주세요"라는 질문에, A회사와 CEO_OF 관계로 연결된 모든 Person을 valid_from 순서로 정렬하여 반환합니다.
코드를 보면, create_temporal_relationship 함수가 관계 생성 시 valid_from, valid_to, is_current 속성을 함께 저장합니다. is_current는 현재 유효한 관계인지를 빠르게 확인하기 위한 플래그입니다.
시간 여행 쿼리에서는 WHERE 절에서 날짜 범위를 체크합니다. 실제 현업에서 Temporal Knowledge Graph는 매우 유용합니다.
금융 분야에서는 규제 준수를 위해 "그 시점에 해당 정책이 적용되었는가"를 확인해야 합니다. 법률 분야에서는 "계약 당시 어떤 조항이 유효했는가"를 추적해야 합니다.
인사 관리에서는 "이 사람이 그 팀에 있을 때 어떤 프로젝트를 했는가"를 알아야 합니다. 주의할 점은 데이터 모델링입니다.
단순히 관계에 날짜 속성을 추가하는 것만으로는 부족할 수 있습니다. 어떤 속성이 시간에 따라 변하는지, 어떤 것은 불변인지를 명확히 구분해야 합니다.
예를 들어, 사람의 이름은 보통 변하지 않지만, 직책은 자주 변합니다. 김개발 씨는 감탄했습니다.
"시간이라는 차원을 추가하니까 훨씬 풍부한 질문에 답할 수 있군요!" 박시니어 씨가 덧붙였습니다. "맞아.
하지만 복잡도도 올라가. 필요한 만큼만 시간 정보를 추가하는 게 중요해."
실전 팁
💡 - 모든 관계에 시간 정보를 추가하면 복잡도가 급증하니, 시간에 따라 변하는 관계만 선별하세요
- is_current 같은 플래그를 활용하면 현재 시점 쿼리 성능이 향상됩니다
- 날짜 범위 쿼리를 위한 인덱스 설정을 잊지 마세요
5. 메모리 아키텍처 비교
김개발 씨는 이제 네 가지 메모리 시스템을 모두 배웠습니다. Working Memory, Short-Term Memory, Knowledge Graph, Temporal Knowledge Graph.
하지만 실제 프로젝트에서 어떤 것을 선택해야 할지 고민이 됩니다. "상황에 따라 다르다고 하는데, 구체적으로 어떻게 다른 거지?"
네 가지 메모리 아키텍처는 각각 다른 목적과 특성을 가집니다. Working Memory는 현재 작업에 필요한 즉각적인 정보를, Short-Term Memory는 최근 대화 맥락을, Knowledge Graph는 엔터티 간 관계를, Temporal KG는 시간에 따른 관계 변화를 다룹니다.
마치 메모장, 다이어리, 인맥 지도, 연대기처럼 각각의 용도가 다릅니다.
다음 코드를 살펴봅시다.
# 4가지 메모리 아키텍처 통합 예제
class HybridMemorySystem:
"""네 가지 메모리 아키텍처를 통합한 시스템"""
def __init__(self):
# Working Memory: 현재 작업 컨텍스트
self.working_memory = {
"current_intent": None,
"active_entities": [],
"pending_actions": []
}
# Short-Term Memory: 최근 대화 기록
self.short_term = []
self.max_short_term = 50
# Knowledge Graph: 엔터티 관계
self.kg_connection = None # Neo4j 연결
# Temporal KG: 시간 기반 관계
self.temporal_enabled = True
def query(self, question, reference_date=None):
"""질문 유형에 따라 적절한 메모리에서 검색"""
# 1. 현재 대화 맥락이 필요한 질문
if self._needs_conversation_context(question):
return self._search_short_term(question)
# 2. 관계 기반 질문
if self._is_relationship_query(question):
if reference_date and self.temporal_enabled:
# 과거 시점 쿼리 -> Temporal KG
return self._query_temporal_kg(question, reference_date)
else:
# 현재 시점 쿼리 -> Knowledge Graph
return self._query_kg(question)
# 3. 일반 정보 검색 -> Vector Store fallback
return self._vector_search(question)
김개발 씨는 화이트보드에 네 개의 상자를 그렸습니다. 각 상자에 이름을 적고, 특징을 정리하기 시작했습니다.
박시니어 씨가 옆에서 지켜보며 조언을 덧붙였습니다. 첫 번째 상자는 Working Memory입니다.
이것은 마치 책상 위 메모장과 같습니다. 지금 당장 하고 있는 일에 필요한 정보만 올려놓습니다.
사용자가 "환불 처리해 주세요"라고 했다면, "현재 의도: 환불", "관련 주문 번호: 12345" 같은 정보가 여기에 저장됩니다. 작업이 끝나면 깨끗이 비웁니다.
특징은 빠른 접근 속도와 작은 용량입니다. LLM의 프롬프트에 직접 포함되므로 토큰 제한을 고려해야 합니다.
구조화된 형태로 저장하면 효율적입니다. 두 번째 상자는 Short-Term Memory입니다.
이것은 다이어리와 비슷합니다. 최근 며칠간의 대화를 기록해 둡니다.
"어제 사용자가 파란색 상품을 찾았었지" 같은 맥락을 기억합니다. 슬라이딩 윈도우 방식으로 오래된 것은 잊고, 새로운 것을 기억합니다.
특징은 순차적인 기록과 제한된 크기입니다. 보통 최근 N개의 대화 턴 또는 M개의 토큰으로 제한합니다.
중요한 정보는 요약해서 장기 메모리로 옮기기도 합니다. 세 번째 상자는 Knowledge Graph입니다.
이것은 인맥 지도와 같습니다. 누가 누구와 어떤 관계인지, 어떤 회사가 어떤 제품을 만드는지 등의 관계 정보를 저장합니다.
관계를 따라가며 추론할 수 있습니다. 특징은 명시적인 관계 표현과 멀티홉 추론 능력입니다.
"A의 친구의 친구 중에 B회사에 다니는 사람"처럼 복잡한 쿼리가 가능합니다. 하지만 스키마 설계가 필요하고, 관계가 없는 자유로운 텍스트 검색에는 적합하지 않습니다.
네 번째 상자는 Temporal Knowledge Graph입니다. 이것은 연대기 또는 역사책과 같습니다.
Knowledge Graph의 모든 기능에 더해, "언제" 그 관계가 유효했는지를 추적합니다. 과거로의 시간 여행이 가능합니다.
특징은 시간 차원의 추가와 변화 이력 추적입니다. "3년 전 그 시점에는 어땠는가"라는 질문에 답할 수 있습니다.
하지만 데이터 모델링이 복잡해지고, 저장 용량과 쿼리 비용이 증가합니다. 코드를 보면, HybridMemorySystem 클래스가 네 가지 메모리를 모두 관리합니다.
query 메서드는 질문의 특성을 분석하여 적절한 메모리에서 정보를 검색합니다. 대화 맥락이 필요하면 Short-Term을, 관계 쿼리면 KG를, 과거 시점 기준이면 Temporal KG를 사용합니다.
실제 프로젝트에서는 어떻게 선택해야 할까요? 단순한 Q&A 챗봇이라면 Working Memory와 Short-Term Memory만으로 충분할 수 있습니다.
복잡한 도메인 지식을 다루는 전문가 시스템이라면 Knowledge Graph가 필수입니다. 규제 준수나 감사 추적이 중요한 금융/법률 분야라면 Temporal KG를 고려해야 합니다.
김개발 씨가 정리했습니다. "결국 하나만 선택하는 게 아니라, 필요에 따라 조합해서 쓰는 거군요." 박시니어 씨가 고개를 끄덕였습니다.
"정확해. 그리고 처음부터 완벽하게 설계하려 하지 말고, 점진적으로 필요한 메모리를 추가해 나가는 게 좋아."
실전 팁
💡 - 프로젝트 초기에는 Working + Short-Term으로 시작하고, 필요시 KG를 추가하세요
- 메모리 계층 간 데이터 이동 전략(승격/강등)을 미리 설계해 두세요
- 각 메모리의 크기와 유지 비용을 모니터링하세요
6. Neo4j 시간적 지식 그래프 실습
이론은 충분히 배웠습니다. 이제 직접 손으로 코드를 작성해 볼 차례입니다.
김개발 씨는 Neo4j를 설치하고, 처음으로 Temporal Knowledge Graph를 구축해 보기로 했습니다. "백문이 불여일타라고 했으니, 직접 만들어 보자!"
이번 실습에서는 Neo4j를 사용하여 Temporal Knowledge Graph를 직접 구현합니다. 회사와 임원 정보를 시간 정보와 함께 저장하고, 현재 시점 쿼리와 시간 여행 쿼리를 모두 수행해 봅니다.
Docker로 Neo4j를 실행하고, Python의 py2neo 라이브러리로 접근합니다.
다음 코드를 살펴봅시다.
# Neo4j Temporal Knowledge Graph 완전 실습
from py2neo import Graph, Node, Relationship
from datetime import date
# 1. Neo4j 연결 (Docker: docker run -p 7687:7687 neo4j)
graph = Graph("bolt://localhost:7687", auth=("neo4j", "your-password"))
# 2. 스키마 초기화 - 기존 데이터 정리 및 인덱스 생성
graph.run("MATCH (n) DETACH DELETE n")
graph.run("CREATE INDEX person_name IF NOT EXISTS FOR (p:Person) ON (p.name)")
graph.run("CREATE INDEX company_name IF NOT EXISTS FOR (c:Company) ON (c.name)")
# 3. 노드 생성
companies = {
"TechCorp": Node("Company", name="TechCorp", industry="IT"),
"DataInc": Node("Company", name="DataInc", industry="Data")
}
people = {
"김철수": Node("Person", name="김철수"),
"이영희": Node("Person", name="이영희"),
"박민수": Node("Person", name="박민수")
}
for node in list(companies.values()) + list(people.values()):
graph.create(node)
# 4. 시간 정보가 포함된 관계 생성
temporal_relations = [
# 김철수: TechCorp CEO (2020-2023)
(people["김철수"], "CEO_OF", companies["TechCorp"],
"2020-01-01", "2023-12-31"),
# 이영희: TechCorp CEO (2024-현재)
(people["이영희"], "CEO_OF", companies["TechCorp"],
"2024-01-01", None),
# 박민수: DataInc CEO (2019-현재)
(people["박민수"], "CEO_OF", companies["DataInc"],
"2019-06-01", None)
]
for person, rel_type, company, valid_from, valid_to in temporal_relations:
props = {"valid_from": valid_from, "is_current": valid_to is None}
if valid_to:
props["valid_to"] = valid_to
rel = Relationship(person, rel_type, company, **props)
graph.create(rel)
# 5. 현재 시점 쿼리
current_ceo_query = """
MATCH (p:Person)-[r:CEO_OF]->(c:Company {name: $company_name})
WHERE r.is_current = true
RETURN p.name as ceo, r.valid_from as since
"""
print("현재 TechCorp CEO:",
graph.run(current_ceo_query, company_name="TechCorp").data())
# 6. 시간 여행 쿼리 - 2022년 6월 기준
time_travel_query = """
MATCH (p:Person)-[r:CEO_OF]->(c:Company {name: $company_name})
WHERE date(r.valid_from) <= date($query_date)
AND (r.valid_to IS NULL OR date(r.valid_to) > date($query_date))
RETURN p.name as ceo, r.valid_from as from, r.valid_to as to
"""
print("2022년 TechCorp CEO:",
graph.run(time_travel_query,
company_name="TechCorp",
query_date="2022-06-01").data())
김개발 씨는 터미널을 열고 Docker 명령어를 입력했습니다. "docker run -p 7687:7687 -e NEO4J_AUTH=neo4j/your-password neo4j:latest" Neo4j가 실행되자, 브라우저에서 http://localhost:7474로 접속하여 웹 인터페이스를 확인했습니다.
이제 Python 코드를 작성할 차례입니다. 먼저 py2neo 라이브러리를 설치합니다.
"pip install py2neo"를 실행하면 됩니다. 코드의 첫 번째 부분은 Neo4j 연결입니다.
bolt 프로토콜을 사용하여 7687 포트로 접속합니다. 인증 정보는 Docker 실행 시 설정한 것과 동일해야 합니다.
두 번째 부분은 스키마 초기화입니다. 기존 데이터를 모두 삭제하고, Person과 Company 노드의 name 속성에 인덱스를 생성합니다.
인덱스가 있으면 이름으로 노드를 찾을 때 훨씬 빠릅니다. 세 번째 부분은 노드 생성입니다.
두 개의 회사(TechCorp, DataInc)와 세 명의 인물(김철수, 이영희, 박민수)을 만듭니다. 각 노드에는 레이블(Person, Company)과 속성(name, industry)이 있습니다.
네 번째 부분이 핵심입니다. 시간 정보가 포함된 관계를 생성합니다.
김철수는 2020년부터 2023년까지 TechCorp의 CEO였습니다. 이영희는 2024년 1월 1일부터 현재까지 TechCorp의 CEO입니다.
valid_to가 None이면 is_current를 True로 설정하여 현재 유효한 관계임을 표시합니다. 다섯 번째 부분은 현재 시점 쿼리입니다.
is_current가 true인 관계만 찾으면 됩니다. 결과는 {"ceo": "이영희", "since": "2024-01-01"}입니다.
여섯 번째 부분이 시간 여행 쿼리입니다. 2022년 6월 1일 기준으로 TechCorp CEO를 찾습니다.
WHERE 절에서 query_date가 valid_from과 valid_to 사이에 있는 관계를 찾습니다. valid_to가 NULL인 경우도 처리해야 하므로 OR 조건을 사용합니다.
결과는 {"ceo": "김철수", "from": "2020-01-01", "to": "2023-12-31"}입니다. 실제로 코드를 실행해 보면, 같은 회사에 대해 시점에 따라 다른 CEO가 반환되는 것을 확인할 수 있습니다.
이것이 Temporal Knowledge Graph의 힘입니다. 조금 더 복잡한 쿼리도 가능합니다.
예를 들어, "2020년부터 현재까지 TechCorp의 모든 CEO를 시간순으로 나열하라"는 쿼리는 다음과 같습니다. 역대 CEO 쿼리는 간단합니다.
해당 회사와 CEO_OF 관계로 연결된 모든 Person을 찾고, valid_from 순서로 정렬하면 됩니다. 이런 식으로 리더십 변화 이력, 조직 개편 기록 등을 추적할 수 있습니다.
김개발 씨는 쿼리 결과를 보며 만족스러운 미소를 지었습니다. "이제 과거에 대한 질문에도 정확하게 답할 수 있겠군요!" 박시니어 씨가 덧붙였습니다.
"실제 프로덕션에서는 더 많은 고려사항이 있어. 대량 데이터 처리, 트랜잭션 관리, 백업과 복구 등.
하지만 기본 원리를 이해했으니, 나머지는 문서를 보면서 익히면 돼."
실전 팁
💡 - Neo4j AuraDB를 사용하면 관리형 서비스로 쉽게 시작할 수 있습니다
- APOC 라이브러리를 설치하면 날짜 처리 등 유틸리티 함수를 활용할 수 있습니다
- 프로덕션 환경에서는 반드시 인덱스와 제약조건을 설정하세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Context Optimization 컨텍스트 최적화 기법
AI 에이전트와 대규모 언어 모델 활용 시 컨텍스트 윈도우를 효율적으로 관리하는 방법을 다룹니다. 토큰 비용 절감부터 캐시 최적화까지, 실무에서 바로 적용할 수 있는 핵심 기법들을 소개합니다.
보안 운영 자동화 완벽 가이드
종합 보안 스캐너부터 SIEM 룰 작성, 위협 헌팅, 인시던트 대응 자동화까지 실무 보안 운영의 모든 것을 다룹니다. 초급 개발자도 이해할 수 있도록 스토리텔링 형식으로 쉽게 설명합니다.
Tool Design - 에이전트 최적화 툴 설계
AI 에이전트가 사용하는 도구(Tool)를 효과적으로 설계하는 방법을 알아봅니다. 결정론적 시스템과 비결정론적 에이전트 사이의 계약 개념부터 MCP 툴 모범 사례까지, 실무에서 바로 적용할 수 있는 툴 설계 원칙을 다룹니다.
클라우드 보안 실전 완벽 가이드
AWS, Docker, Kubernetes 환경에서 보안을 자동화하고 취약점을 사전에 탐지하는 방법을 알아봅니다. 초급 개발자도 바로 적용할 수 있는 실전 보안 점검 스크립트와 CI/CD 파이프라인 보안 구축 방법을 다룹니다.
Phase 5 취약점 발굴과 분석 완벽 가이드
보안 전문가가 되기 위한 취약점 발굴의 핵심 기법을 다룹니다. 코드 리뷰부터 퍼징, 바이너리 분석까지 실무에서 바로 활용할 수 있는 기술을 초급자 눈높이에 맞춰 설명합니다.