본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 24. 21:42

최고의 에이전트 메모리 레이어를 설계하셨군요. 이제 그것을 제대로 사용하기만 하면 됩니다!!!

요약

AI 에이전트의 메모리 저장 과정에서 발생하는 4가지 주요 실패 모드(누락, 불완전, 오류, 과잉 기록)를 분석합니다. 모델이 미래 세션에 대한 이해관계가 없어 발생하는 문제와 호스트 시스템의 네이티브 기능과의 충돌 문제를 다룹니다.

핵심 포인트

  • 에이전트 메모리 설계 시 발생하는 4가지 실패 모드 분석
  • 모델이 단일 턴 최적화로 인해 기록 작업을 생략하는 이유 설명
  • 호스트 시스템의 네이티브 메모리 기능과 외부 메모리 간의 충돌 문제
  • 구조화된 저장소(Structured Store) 유지의 어려움

드디어 당신의 시스템이 자체 벤치마크에서 Mem0를 이겼습니다. 새로운 DB를 가동합니다. 모든 것이 좋습니다. 혼란은 줄어들고 생산성은 올라갑니다. 하지만 일주일이나 이주일이 지나면, 시스템은 금붕어처럼 변해버립니다. 저장소를 열어보면 그곳은 '피의 결혼식(Red Wedding)'처럼 아수라장이 되어 있습니다. 당신의 에이전트는 당신이 원하는 것을 전혀 저장하지 않았거나, 필요한 것의 절반만 저장했거나, 아무 의미 없는 것을 저장했거나, 혹은 모든 것을 저장해 버렸습니다! 이것이 현실입니다 (C'Est La Vie).

저는 제가 이 문제를 해결했다는 것을 당신에게 설득해 보려 합니다. 만약 그렇지 못하더라도, 당신의 모델을 통제하는 데 도움이 될지도 모릅니다. 왜냐하면 저는 에이전트의 통제 밖에 있는 로컬 활성 메모리(local active memory)인 Recall을 구축하면서 모든 실패 모드(failure mode)를 직접 겪었기 때문입니다.

실패 모드들

  1. 조용히 기록하지 않음. 모델에게 지속적인 무언가를 기억하라고 요청합니다. 모델은 "알겠습니다"라고 말하고 다음으로 넘어갑니다. 하지만 저장소에는 아무것도 남지 않습니다. 오류도, 경고도 없이, 단지 기록(write) 없이 턴(turn)이 종료된 것입니다. 이것은 가장 흔하면서도 포착하기 가장 어려운 문제인데, 대화 내부에서는 모든 것이 정상적으로 보이는 것처럼 느껴지기 때문입니다.

  2. 절반만 기록함. 모델이 하나의 사실은 기록하지만, 그만큼 중요했던 나머지 세 가지는 누락합니다. 또는 헤드라인만 기록하고 그 뒤에 숨겨진 추론(reasoning)은 기록하지 않아, 나중에 세션에서 근거 없는 주장만을 마주하게 됩니다. 저장소는 채워지지만, 실행할 수 없는 파편들로 가득 차게 됩니다.

  3. 잘못된 것을 기록함. 만약 당신의 메모리가 구조화되어 있다면 (필수 필드, 타입이 지정된 레코드, 신뢰도(confidence), 증거 링크 등), 모델이 구조를 잘못 채울 수 있습니다. 결정(decision)이 들어가야 할 자리에 스쳐 지나가는 관찰(observation)을 넣거나, 신뢰도를 빈칸으로 남겨두거나, "이것이 저것을 수정함"이라는 링크를 실제 레코드가 아닌 자유 형식의 라벨(free-text label)에 연결해 버립니다. 서류상으로는 스키마(schema)를 충족하지만, 실제로는 쓸모가 없게 됩니다.

  4. 모든 것을 기록함. 과잉 교정입니다. 모델이 턴 전체를 저장소에 쏟아붓습니다. 모든 여담, 모든 막다른 길, 그리고 때로는 영구 저장해서는 안 될 비밀까지 말이죠. 이제 첫 번째 문제에 더해 두 번째 문제가 발생합니다. 묻혀버린 데이터는 손상된 데이터와 다를 바 없기 때문입니다.

이런 일이 발생하는 이유

모델은 미래의 세션에 대해 아무런 이해관계가 없습니다. 단일 턴 (single turn) 내에서, 컨텍스트 윈도우 (context window)는 이미 모델이 필요로 하는 모든 것을 보유하고 있습니다. 외부 저장소에 기록하는 것은 모델의 관점에서 볼 때, 자신이 직접 경험하지 못할 미래의 세션, 즉 다른 누군가에게 이득이 되는 작업일 뿐입니다. 모델은 눈앞의 턴을 마무리하는 것에 최적화되어 있으며, 기록 작업은 가장 먼저 생략되는 대상이 됩니다.

보통 경쟁자가 존재합니다. 만약 당신의 에이전트가 Claude Code와 같은 호스트 내부에서 실행된다면, 해당 호스트는 아마도 기본 시스템 프롬프트 (system prompt)에 연결된 자체 메모리 기능을 제공할 것입니다. 두 개의 "이것을 저장해"라는 경로가 존재할 때, 당신의 기술보다 모델의 근본 지침에 더 가까운 네이티브 (native) 경로가 승리합니다. 당신의 메모리 시스템이 완벽하게 갖춰져 있더라도, 내장된 기능과의 쓰기 경쟁에서 매번 패배할 수 있습니다. 저는 단일 변수 테스트를 통해 이를 확인했습니다. 네이티브 기능이 켜져 있을 때, 제 시스템이 구조화된 저장소 (structured store)를 아무리 강력하게 요청하더라도 모델은 매번 사용자의 사실 정보를 플랫 파일 (flat files)에 기록했습니다.

쓰기는 읽기보다 어렵습니다. 읽기는 자유 형식입니다. 질문을 던지면 텍스트를 얻으면 됩니다. 하지만 구조화된 쓰기는 스키마 (schema)를 충족해야 함을 의미하며, 모델이 마찰 (friction)을 느끼는 순간, 모델은 저항이 가장 적은 경로, 즉 쓰기를 건너뛰거나 구조화되지 않은 산문을 쏟아내는 쪽을 택합니다. 여기서 마찰은 결코 작은 요인이 아닙니다.

루프 내에 피드백이 없습니다. 모델이 잘못된 구조로 기록했는데 그 쓰기 작업이 아무런 알림 없이 조용히 실패한다면, 모델은 무엇이 잘못되었는지 배울 수 없습니다. 모델은 그냥 어깨를 으쓱하며 계속 진행할 뿐입니다. 신호 없는 준수 (adherence)는 동전 던지기와 같으며, 모델은 매 턴마다 조금씩 더 자주 실패하게 됩니다.

효과가 없는 세 가지 해결책

프롬프트에서 더 강하게 말하기. 본능적으로 대문자를 사용하여 "항상 내구성이 있는 사실을 메모리에 기록하십시오"라고 추가하고 끝내려 할 것입니다. 이것은 프롬프트 잔소리 (prompt-nagging)입니다. 이는 모델의 고유한 경로 (native pathway)와 경쟁하며 결국 패배합니다. 매 턴마다 토큰 비용이 발생하며, 효과는 점차 감쇠합니다. 모델은 몇 턴 동안은 복종하지만, 곧 스스로 합리화하기 시작합니다 ("이것은 그냥 간단한 메모일 뿐이야", "나중에 적을게"). 또한 모델 간의 호환성이 떨어지므로, 모델을 교체하는 날 바로 처음부터 다시 시작해야 합니다.

모든 것을 기록하고 나중에 정리하기. 모델이 무엇이 내구성이 있는지 결정하지 못한다면, 일단 모두 기록하게 한 뒤 나중에 큐레이션 (curation) 하라는 방식입니다. 이는 '빈 저장소 문제 (empty-store problem)'를 '큐레이션 부채 문제 (curation-debt problem)'로 맞바꾸는 것이며, 스키마 (schema)를 사용하는 목적 자체를 무력화하고, 저장소에 비밀 정보가 유출되는 정확한 경로를 제공합니다. 당신은 준수 (adherence) 문제를 해결한 것이 아닙니다. 실패 지점을 하류 (downstream)로 옮겼을 뿐이며, 결코 끝내지 못할 정리 작업만을 추가했을 뿐입니다.

스키마를 준수하도록 모델을 미세 조정 (Fine-tune) 하기. 훈련 (training)에 의존하면, 스키마 변경에 취약하고 특정 모델에 종속되며, 여전히 경쟁하는 고유 기능을 해결하지 못하는 무겁고 비용이 많이 드는 해결책을 얻게 됩니다. 이는 결국 배선 (wiring) 문제로 밝혀질 사안에 대해 너무 큰 망치를 휘두르는 격이며, 그 배선 문제는 해결되지 않은 채 바로 그 아래에 그대로 남아 있게 됩니다.

실제로 도움이 되는 두 가지 쉬운 해결책

경쟁자를 끄기. 이것은 가장 큰 도움이 되는 단 한 번의 변화이며, 단 한 줄이면 충분합니다. 만약 호스트 시스템이 자체적인 자동 메모리 (auto-memory) 기능을 제공한다면, 시스템 내에 "이것을 저장하라"는 경로가 오직 하나만 존재하도록 이를 비활성화하십시오. Claude Code의 경우 CLAUDE_CODE_DISABLE_AUTO_MEMORY=1입니다. 경쟁자가 사라지면, 제대로 무장된 에이전트는 더 이상 방해 요소가 없기 때문에 스스로 구조화된 저장소 (structured store)를 찾게 됩니다. "조용히 기록하지 않는" 문제의 대부분은 모델이 거부했기 때문이 아니었습니다. 모델이 다른 곳에 기록하고 있었기 때문입니다.

쓰기 마찰 (write friction)을 낮추세요. 모델이 판단할 수 있는 몇 가지 입력값(기록 유형, 제목, 본문, 신뢰도, 몇 가지 주제)만 받아서 스키마에 유효한 (schema-valid) 객체를 생성해 주는 작은 헬퍼 (helper)를 제공하세요. 그러면 모델은 구조화된 페이로드 (structured payload)를 직접 조립하는 것을 멈추고, 대신 핵심적인 두세 개의 필드에 집중하게 됩니다. Recall에서는 이를 통해 매 세션의 첫 번째 쓰기 작업에서 발생하는 스키마 마찰 세금 (schema-friction tax)을 제거했으며, 이것이 바로 "잘못된 것을 기록하는" 문제의 대부분이 발생하는 지점이었습니다. 모델이 부주의했던 것이 아닙니다. 모델은 과부하 상태에서 사무적인 업무 (clerical work)를 수행하도록 요청받았고, 예상 가능한 방식으로 지름길을 택했을 뿐입니다.

이 두 가지 방법은 큰 도움이 됩니다. 하지만 이 방법들만으로는 쓰기가 적절한 순간에 발생하는지, 혹은 수정 사항이 기존 값 옆에 나란히 놓이는 대신 기존 값을 대체하는지를 보장하지는 못합니다. 이를 위해서는 모델이 아닌 시스템이 규율 (discipline)을 유지해야 합니다.

진정한 해결책: Ta dun Ta da 훅 (hooks)

지속 가능한 정답은 모델에 의존하는 것을 멈추고, 순전파 (forward pass)의 시작과 끝 사이에서 동작을 수행하는 이벤트로부터 트리거되는 훅 (hooks)으로 준수 (adherence)의 부담을 옮기는 것입니다.

턴 (turn)이 시작될 때 메모리를 주입하세요. 세션 시작 시 또는 프롬프트 제출 시점에 작동하는 훅을 통해, 인밴드 (in-band) 방식으로 "메모리 저장소가 존재하니, 기억에 의존하기 전에 그것을 읽으라"고 전달한 뒤, 이 프롬프트와 관련하여 이미 저장된 내용의 미니 인덱스(ids 및 제목 등 무겁지 않은 정보)를 모델에게 전달하세요. 이는 두 가지 일을 동시에 수행합니다. 읽기를 선택적인 예의가 아닌 기본값 (default)으로 만들며, 모델에게 무엇이 보관되어 있는지 보여줌으로써 "기억으로부터 단언하기" 및 "사용자가 이미 말한 것을 다시 묻기"와 같은 실패를 차단합니다. 먼저 읽는 것은 쓰기를 의미 있게 만드는 방법이기도 합니다. 현재 상태를 확인한 모델은 중복된 내용이 아닌 해결책 (resolution)을 작성하게 됩니다.

쓰기 시점(write time)에는 구조를 인밴드(in-band)로 강제하십시오. 저장소(store) 앞에 검증 게이트(validation gate)를 배치하여, 형식이 잘못되었거나 비밀 정보(secret) 형태의 쓰기가 발생할 경우 조용히 실패하거나 저장소를 손상시키는 대신, 모델이 즉시 수정할 수 있는 읽기 가능한 오류와 함께 튕겨 나가게 하십시오. 이것이 바로 "잘못된 것을 쓰는 것"과 "모든 것을 쓰는 것"을 잡아내는 지점입니다. 스키마(schema)는 모델이 준수하기 위해 기억해야 하는 대상이 아니라, 시스템이 보장하는 대상이 됩니다. 동일한 게이트에서 비밀 정보를 거부하므로, 유출된 토큰(token)이 애초에 그래프(graph)에 도달하지 않도록 할 수 있습니다.

실질적인 턴(turn)이 끝날 때, 쓰기를 유도(nudge)하십시오. 해당 턴이 지속 가능한(durable) 무언가를 생성했는지, 그리고 아무것도 쓰이지 않았는지를 확인하고 이를 요청하는 스톱 훅(stop hook)을 사용하십시오. 이는 반대편에서 "조용히 쓰지 않는" 간극을 메워줍니다. 즉, 모델이 잊어버렸더라도 턴이 끝나기 전에 시스템이 한 번 더 요청하게 됩니다.

수정의 형태는 세 곳 모두 동일합니다. 모델의 역할은 오직 모델만이 할 수 있는 부분, 즉 무엇이 지속 가능한지 그리고 그것에 대해 얼마나 확신하는지를 판단하는 부분으로 축소됩니다. 모든 기계적인 것(언제 읽을지, 언제 쓸지, 쓰기가 어떤 형태를 취할지)은...

여기에 제가 고생하며 찾아낸 작은 방정식이 숨어 있습니다. 복종(Obedience)은 세 가지 요소의 곱입니다: 해당 턴에서의 모델의 의도(intent), 그리고 당신이 마련해 둔 장치들(기술, 헬퍼, 훅)입니다. 이것이 바로 "더 강하게 말하기(tell it harder)"가 단독으로는 실패하는 이유입니다. 나머지 두 요소를 디버깅하는 동안 이 요소는 소리 없이 0이 될 가능성이 가장 높은 요인이기 때문입니다.

미래의 모습

평상시처럼 운영한다면, 당신의 메모리 시스템은 가능한 가장 값비싼 방식으로 실패하게 됩니다. 즉, 작동하고 있는 것처럼 보인다는 점입니다. 저장소는 존재하고, 쓰기 (writes) 작업도 가끔 발생하며, 세션이 세 버전이나 지난 정보를 자신 있게 말하거나, 10분 전에 답변했던 질문을 다시 하거나, 이전 실행에서 이미 알고 있었던 내용을 처음부터 다시 유도하기 시작할 때까지는 당신이 알아차리지 못합니다. 저장소는 신뢰할 수 없는 무덤이 되고, 당신은 조용히 수동으로 컨텍스트 (context)를 붙여넣는 방식으로 돌아가게 됩니다. 이제 당신은 아무런 쓸모도 없는 데이터베이스를 유지 관리하고 있는 것이며, 이는 데이터베이스가 아예 없는 것보다 엄연히 더 나쁜 상황입니다.

이를 해결하면, 그 효과는 복리로 쌓입니다. 세션들이 상속됩니다. 모델은 행동하기 전에 읽고, 스스로를 수정할 때 해결책을 기록하며, 새로운 값을 기존 값 옆에 쌓는 대신 기존 값을 대체 (supersedes)합니다. 따라서 현재의 답변은 항상 최상단에 위치하면서도 그 아래에는 여전히 히스토리 (history)가 살아남습니다. 메모리는 사용하면 할수록 더 유용해집니다. 모든 수정 작업이 저장소를 더 시끄럽게 만드는 대신 더 정교하게 만들기 때문입니다. 당신은 자신의 도구에게 자신의 프로젝트를 다시 설명하는 일을 멈추게 됩니다. 그것이 바로 에이전트 메모리 (agentic memory)의 온전한 약속이었습니다.

제가 RAG (Retrieval-Augmented Generation), 검색을 위해 설계된 별도의 임베딩 모델 (embedding models)에 대해 이야기하지 않고 자동 메모리 (automemory)에만 살짝 언급한 이유는, 비장의 무기를 아껴두고 싶었기 때문입니다.

저는 지난 5~6개월의 대부분을 에이전트를 위한 푸시 방식의 메모리 기질 (push-style memory substrate)인 Recall을 만드는 데 쏟았습니다. Recall은 구조화된 레코드 (structured records), 계산 및 보정된 신뢰도 (confidence), 출처 (provenance)를 포함한 방향성 가치 업데이트 (directional value updates), 그리고 위에서 설명한 훅 (hooks)을 갖추고 있습니다. 이는 오픈 소스이며, 다른 시스템에서의 동작에 대한 모든 피드백을 환영합니다. 시간을 내어 읽어주셔서 감사합니다. github.com/hendrixx-cnc/recall.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0