
새벽 3시의 나에게 기억을 선물하다: Cognee의 메모리 라이프사이클(Memory Lifecycle)을 활용한 구축기
요약
Cognee의 메모리 라이프사이클(remember, recall, improve, forget)을 활용하여 장애 대응 시 맥락을 보존하는 'BlackoutOps' 구축기를 소개합니다. 별도의 저장소 없이 Cognee의 실시간 호출만으로 사고 당시의 상황을 재구성하고 근본 원인을 학습하는 구조를 다룹니다.
핵심 포인트
- Cognee의 4가지 메모리 동사를 활용한 지식 관리 설계
- 자체 저장소 없이 Cognee API 호출만으로 동작하는 아키텍처
- 장애 대응(Incident Response) 시 컨텍스트 상실 문제 해결
- 데이터 보존 정책을 준수하는 메모리 망각(forget) 기능 구현
새벽 3시에 호출을 받습니다. 반쯤 잠든 상태로 운영 환경(production) 문제를 해결합니다. 그래프가 초록색으로 변할 때쯤이면, 상황에 대한 맥락(context)은 이미 사라져 버립니다.
저는 WeMakeDevs × Cognee의 "The Hangover Part AI: Where's My Context?" 해커톤을 위해 BlackoutOps — 사고 다음 날을 위한 브레인(incident brain) — 를 구축했습니다. 이 도구는 당신이 기억하지 못하는 밤의 상황을 재구성하고, 팀이 이전에 유사한 장애를 겪은 적이 있는지 알려주며, 확인된 근본 원인(root cause)을 학습합니다. 그리고 — 데이터 보존 정책(retention policies)은 실재하므로 — 명령에 따라 확실하게 망각(forget)합니다.
이것은 솔직한 빌드 스토리입니다. 어떻게 Cognee의 네 가지 메모리 동사 — remember, recall, improve, forget — 를 중심으로 모든 기능을 설계했는지, 각 동사가 빌드 과정에서 실제로 어떤 역할을 했는지, 그리고 라이브 플랫폼을 통해 무료로 얻게 된 두 가지 실전 사례(war stories)를 소개합니다.
🔗 Repo: https://github.com/devanshug2307/blackoutops (MIT)
내가 구축한 것
흥미로운 점은 앱 자체가 아니라, 이 앱이 자체적인 저장 공간을 전혀 가지고 있지 않다는 점입니다. 데이터베이스도, 캐시(cache)도, 로컬 벡터 인덱스(local vector index)도 없습니다. 워룸(war-room) UI의 모든 버튼은 단일 search() 엔드포인트가 아니라, Cognee의 메모리 라이프사이클(memory lifecycle)을 중심으로 구성된 Cognee에 대한 실시간 호출입니다.
Project: BlackoutOps — 사고 다음 날을 위한 브레인(incident brain)
Built for: WeMakeDevs x Cognee, "The Hangover Part AI" (2026년 7월)
Stack: FastAPI + 정적 워룸 UI, 약 150줄의 메모리 레이어(memory layer)
...
(아래는 워룸 UI입니다 — 왼쪽: 밤사이의 가공되지 않은 파편들; 중간: 디브리핑(debrief) 채팅; 오른쪽: 메모리 라이프사이클(memory lifecycle), 각 카드는 실행되는 정확한 Cognee Cloud 호출로 라벨링되어 있습니다.)
새벽 3시의 숙취
모든 온콜(on-call) 엔지니어는 이 특유의 숙취를 알고 있습니다. 02:47에 Redis 메모리 경고가 발생합니다. 03:15에 PagerDuty가 당신을 찾아냅니다. 이후 40분 동안 당신은 서버실에 있는 너구리가 됩니다: kubectl logs, redis-cli INFO, 롤백(rollback), 그리고 런북(runbook)에서 어렴풋이 기억나는 설정 플래그(config flag). 03:56이 되면 대시보드가 초록색으로 변하고 당신은 다시 잠에 듭니다.
다음 날 아침, 그 치열한 전투의 유일한 흔적은 초록색 대시보드와 막연한 공포감뿐입니다. 무엇이 고장 났을까요? 실제로 무엇을 실행했나요? 롤백 (rollback)은 왜 작동했을까요? 그리고 6개월 뒤에 가장 큰 대가를 치르게 되는 질문: 우리는 정확히 이와 똑같은 장애를 전에 본 적이 없었나요?
보통 그러한 지식은 스크롤 기록 (scrollback) 속에서 사라지며, 조직은 똑같은 축출 폭풍 (eviction storm)을 처음부터 계속해서 다시 진단합니다. 해커톤의 주제는 "내 컨텍스트는 어디에 있는가?"였고, 이것이 제가 아는 가장 문자 그대로의 컨텍스트 상실 (lost-context) 문제입니다.
RAG가 아닌 그래프 (Graph)를 사용하는 이유
장애의 흔적들은 다섯 가지 서로 다른 방언의 파편들로 존재합니다: Prometheus 알람, PagerDuty 호출, 졸린 상태로 보낸 Slack 메시지, 셸 히스토리 (shell history), 배포 로그 (deploy logs). 그 밤의 _의미_는 단일 파편이 아니라 그들 사이의 연결 고리에 존재합니다:
- 02:30의 배포가 02:45의 축출 폭풍 (eviction storm)을 유발했다 (caused)
- 03:33의
rollout undo가 바로 그 배포를 대상으로 했다 (targeted) - 어젯밤은 3주 전의 장애와 동일한 실패 패턴을 공유한다 (shares a failure pattern) — 서비스는 다르지만, 공유 Redis 내 TTL이 없는 키 (TTL-less-keys-in-shared-Redis)라는 동일한 실수
벡터 저장소 (vector store)는 당신에게 "유사한 텍스트"를 제공할 수는 있습니다. 하지만 3주 간격으로 발생한 두 장애가 옷만 다르게 입었을 뿐 같은 질병이라는 사실은 알려줄 수 없습니다. 그것은 그래프 순회 (graph traversal)의 영역입니다. Cognee는 가공되지 않은 텍스트를 흡수하여 서비스, 사람, 명령, 원인이 서로 연결된 엔티티 (entities)가 되는 지식 그래프 (knowledge graph)를 구축합니다. 덕분에 저는 연결된 답변들을 거저 얻을 수 있습니다. 두 번의 장애 밤을 시드로 사용했을 때, 제 데모 그래프는 **98개의 노드 (nodes)와 186개의 엣지 (edges)**로 구성되었습니다.
4가지 동사 라이프사이클 (Four-Verb Lifecycle)
Cognee의 API는 데이터베이스가 아니라 메모리 라이프사이클 (memory lifecycle)입니다. 기능을 네 가지 동사에 매핑하는 순간, 아키텍처가 문제로부터 자연스럽게 도출되었습니다. 저의 전체 메모리 레이어는 동사당 하나의 정직한 함수로 구성되어 있으며, 총 약 150줄 정도입니다.
| Cognee call | 코드 내 구현 | BlackoutOps에서 수행하는 역할 |
|---|---|---|
remember() | ingest_artifacts() | 각 원시 운영 조각(alert, Slack 메시지, 셸 기록 등)을 개별 인시던트 데이터셋의 영구 그래프 메모리로 변환합니다. |
| ... | ||
하나의 결정이 이 모든 것을 지탱합니다: 데이터셋은 의미의 단위입니다. 각 인시던트는 자체 데이터셋(incident_2026_07_05, incident_2026_06_14_redis_storm)을 갖습니다. 이 단 하나의 선택이 격리성, 인시던트 간 질문에 대한 명시적 라우팅, 그리고 정밀한 삭제 기능을 제공합니다. |
remember(): 밤에 흡수되다 (Ingested)
모든 것은 동일한 cognee 패키지를 serve()를 사용하여 Cognee Cloud에 연결하고, 각 원시 아티팩트를 영구 그래프 메모리로 푸시하는 것에서 시작됩니다. 파싱도, 스키마도 필요 없습니다. Cognee가 개체 추출(entity extraction)과 그래프 구축을 수행합니다.
import cognee
# 동일한 패키지이지만, 이제 메모리는 Cognee Cloud에 존재함
...
앱 내에는 두 번째로, 더 교묘한 remember가 있으며, 이것이 전체 구축에서 가장 저렴한
이 지점에서 데모는 단순한 챗봇(chatbot)을 넘어 하나의 메모리(memory)가 됩니다. 어젯밤의 장애는 checkout-service v2.14.1이 캐시 워밍(cache warming)을 수행하면서, 공유 redis-cache에 TTL(Time To Live, 유효 기간)이 없는 세션별 키를 작성하여 발생했습니다. 이로 인해 maxmemory가 가득 찼고, 이는 에비션 스톰(eviction storm, 데이터 삭제 폭풍)을 유발하여 체크아웃 세션 조회를 방해했으며, 결과적으로 api-gateway의 502 에러로 이어지는 연쇄 장애를 일으켰습니다. 3주 전에도 promo-service가 세일 기간 동안 정확히 똑같은 TTL 없는 작업을 수행한 적이 있었습니다. "이런 일이 전에도 있었나요?"라고 물으면, 그래프(graph)는 공유 엔티티(entities)를 통해 이 두 사건을 연결하고 "네, 여기 쌍둥이 같은 사례가 있습니다"라고 답합니다. 이것이 바로 일반적인 벡터 스토어(vector store)는 헤매지만, 지식 그래프(knowledge graph)는 완벽하게 해내는 지점입니다.
improve(): 학습하는 메모리
사후 분석(Postmortem) 보고서는 보통 아무도 읽지 않는 위키(wiki)로 흘러가기 마련입니다. 하지만 여기서는 사후 분석 내용을 기록하면, 인간이 확인한 근본 원인(root cause)이 그래프 메모리에 다시 기록된 후 Cognee의 전용 인리치먼트(enrichment, 정보 보강) 단계를 거치게 됩니다. 이 부분은 가장 미묘한 뉘앙스를 가진 동사인 만큼, 매우 정확하고 솔직하게 설명하고 싶습니다.
# 1) 확인된 패턴이 전체 remember 파이프라인을 통해 다시 입력됩니다
await cognee.remember(POSTMORTEM, dataset_name="incident_2026_07_05")
...
사용자가 체감하는 효과는 실재합니다. 사후 분석을 기록한 후 "다음에는 무엇을 주의해야 할까요?"라고 물으면, 공유 Redis 내의 TTL 없는 세션별 키라는 확인된 패턴이 첫 번째로 순위가 매겨져 답변되며, 이제 6월에 발생한 장애와도 상호 참조(cross-referenced)됩니다. 메모리가 인간에 의해 검증된 진실에 맞춰 적응하는 것입니다. 이것은 라이프사이클(lifecycle) 단계 중 거의 아무도 사용하지 않는 부분이지만, 로그 더미를 조직의 지식 자산으로 바꾸는 핵심적인 부분입니다. 저는 UI에 있는 그대로의 결과(receipt)를 보여줍니다. 거친 부분을 숨기는 구축 이야기는 오히려 더 나쁜 이야기이기 때문입니다.
forget(): 기능으로서의 삭제
장애 데이터에는 내부 호스트 이름(hostnames)과 인간의 실수가 가득하므로, 데이터 보존 정책(retention policies)이 필요합니다. BlackoutOps는 삭제를 설정 페이지에 숨겨진 사과문이 아니라, 데모의 핵심적인 흐름(first-class demo beat)으로 다룹니다.
receipt = await cognee.forget(
dataset="incident_2026_06_14_redis_storm",
memory_only=True, # 그래프 + 벡터 메모리 삭제, 데이터셋 이름은 유지
...
6월의 사고 데이터를 삭제하고, Cognee의 삭제 영수증(deletion receipt)을 받은 뒤, 다시 그에 대해 질문해 보세요. 그러면 뇌는 그날 밤에 대한 기억이 전혀 없다고 솔직하게 답합니다. recall() 함수가 상세한 사고 교차 분석 답변에서 클릭 한 번 만에 _"그 일에 대한 기억이 없습니다"_로 변하는 것을 지켜보는 것은, 그 어떤 개인정보 보호 정책 문구보다도 설득력이 있습니다. 증명 가능한 망각(Provable forgetting)은 기억하는 것보다 더 드물고, 더 가치 있습니다.
존재하지 않았던 테넌트 (The Tenant That Didn't Exist)
빌드 도중, 새로 프로비저닝(provisioned)한 Cognee Cloud 테넌트로 보내는 모든 요청이 Could not resolve host 오류와 함께 실패했습니다. 테넌트는 라이브(live) 상태였습니다. 올바른 IP를 대상으로 curl --resolve를 실행하면 정상적인 하트비트(heartbeat)가 반환되었습니다. 원인은 제 Mac의 리졸버(resolver)가 Cloudflare의 1.1.1.1을 사용하고 있었기 때문인데, 이 리졸버가 테넌트의 DNS 레코드가 존재하기 전의 NXDOMAIN을 캐싱하고 있었습니다. Google의 8.8.8.8은 이미 정답을 알고 있었지만, Cloudflare는 부재(absence)를 자신 있게 기억하고 있었던 것입니다.
이 해결책은 프로젝트의 일부가 되었습니다. 시스템 리졸버가 테넌트 호스트에 대해 실패할 경우, DNS-over-HTTPS를 통해 이를 해석하고 socket.getaddrinfo를 패치하여 정답을 고정(pin)하는 작은 dns_fallback.py를 만들었습니다:
def ensure_resolvable(hostname: str) -> str:
try:
socket.getaddrinfo(hostname, 443)
...
그다음, 양파 껍질의 두 번째 층을 만났습니다. uvicorn 환경에서는 여전히 실패했는데, uvloop가 C 언어로 DNS를 처리하며 socket.getaddrinfo를 전혀 호출하지 않기 때문이었습니다. --loop asyncio 플래그 하나를 추가하고 나서야 모든 것이 정상 작동했습니다. 아이러니하게도 느껴집니다. 오래된 메모리(stale memory)에 관한 앱을 만들면서, 정작 저는 무언가가 존재하지 않았음을 기억하는 오래된 캐시(stale cache)에 패배했으니까요. 부정적 기억(Negative memory) 또한 여전히 기억입니다. DNS조차 forget()이 필요했습니다.
라이브 플랫폼에서의 버그 (A Bug on a Live Platform)
해커톤 기간 중 라이브 클라우드에서 빌드하는 것은 최고의 퍼저 (fuzzer)이며, 저는 제 데모를 직접 망가뜨림으로써 정직한 방식으로 다음 버그를 발견했습니다. 이 클라우드 빌드에서는, 전체 (full) forget()으로 이전에 삭제된 데이터셋 이름에 remember()를 시도하면 409 RetryError[ProgrammingError]와 함께 영구적으로 실패합니다. 해당 이름이 툼스톤 (tombstoned, 삭제 표식 처리) 되었기 때문입니다. memory_only=True를 사용한 forget()은 이 문제가 발생하지 않으므로, BlackoutOps는 이 방식으로 배포됩니다. 제가 업스트림 (upstream)에 제출한 최소 재현 코드 (minimal repro)는 다음과 같습니다:
async def cycle(name, memory_only):
await cognee.remember("v1", dataset_name=name)
await cognee.forget(dataset=name, memory_only=memory_only)
...
이 이슈는 topoteretes/cognee#3895로 업스트림에 전달되었습니다. 이것이 제 히스토리 데이터셋 이름이 깔끔한 incident_2026_06_14가 아니라 incident_2026_06_14_redis_storm인 이유이기도 합니다. 테스트 도중 이미 깔끔한 이름을 툼스톤 (tombstoned) 처리해 버렸기 때문입니다. 이 흉터는 코드베이스에 남아 있으며, 저는 의도적으로 그것을 남겨두었습니다.
Cognee 빌더를 위한 네 가지 교훈
Cognee를 기반으로 구축을 시작하려 한다면, 제가 시작 단계에서 알았더라면 좋았을 것들은 다음과 같습니다:
- 검색 (search) 중심이 아닌 라이프사이클 (lifecycle) 중심으로 설계하세요. 기능을 네 가지 동사에 매핑하는 순간, 각 동사당 하나의 정직한 함수를 갖는 아키텍처가 자연스럽게 완성되었습니다.
- 데이터셋 (Datasets)은 의미의 단위입니다. 인시던트별 (또는 고객별, 프로젝트별) 데이터셋을 사용하면 라우팅 (routing), 격리 (isolation), 그리고 정밀한
forget()기능을 별도 구현 없이 얻을 수 있습니다. - 세션 메모리 (Session memory)는 가장 저렴하게 감탄을 자아내는 요소입니다. 파라미터 하나만으로 앱은 컨텍스트 (context)를 유지한 채 새로고침을 견뎌낼 수 있습니다. 커스텀 상태 (custom state)를 구축하기 전에 이를 먼저 고려하세요.
- 그렙 (grep)이 답할 수 없는 질문을 데모하세요. 모든 도메인에는 벡터 스토어 (vector store)가 헤매는 교차적인 질문이 하나씩 있습니다. 인시던트의 경우 그것은 "우리가 이전에 이것을 본 적이 있는가?"입니다. 여러분만의 질문을 찾아내고 그래프 (graph)가 그에 답하게 만드세요.
아키텍처 개요
war-room UI (static) Cognee Cloud (tenant)
chat . lifecycle . graph remember -> knowledge graph
| HTTP recall -> graph + session
...
자체 데이터베이스를 보유하지 않는 이 앱의 특성은 제가 사과해야 할 한계가 아니라, 이 프로젝트의 핵심 논지입니다. 뇌가 곧 제품이며, 그 뇌가 바로 Cognee입니다. 재구성하고, 질문하고, 학습하고, 망각하십시오.
FAQ
Cognee의 메모리 라이프사이클(Memory Lifecycle)이란 무엇인가요?
단일 데이터베이스 호출 대신 네 가지 동사를 사용합니다: remember()는 텍스트를 지식 그래프 (Knowledge Graph)로 인입시키고, recall()은 해당 그래프와 세션 메모리 (Session Memory)를 가로질러 검색하며, improve()는 확인된 패턴으로 메모리를 풍부하게 만들고, forget()은 이를 삭제합니다. 이 네 가지를 중심으로 설계하는 것이 메모리를 마치 실제 메모리처럼 느껴지게 만드는 핵심입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기