본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 06. 08:04

에이전트에게 플랫 파일(Flat-file) 메모리가 벡터 DB보다 유리한 시점은 언제인가?

요약

에이전트의 장기 기억 구현 시 모든 데이터를 벡터 DB에 의존하는 대신, 항상 필요한 핵심 사실은 플랫 파일(Flat-file)로 관리할 것을 제안합니다. 유사도 검색의 확률적 한계를 극복하고 운영 복잡성을 줄이는 효율적인 메모리 구조를 다룹니다.

핵심 포인트

  • 항상 필요한 컨텍스트는 검색 레이어가 아닌 프롬프트에 직접 포함해야 함
  • 유사도 검색은 확률적이라 필수 정보 누락(버그)을 유발할 수 있음
  • 벡터 DB 대신 수정이 용이하고 가벼운 마크다운 파일 활용 권장
  • 인덱스 파일의 행 수를 제한하여 컨텍스트 잘림 현상을 방지

대부분의 "내 에이전트에게 장기 기억을 부여하는 방법"에 관한 튜토리얼들은 모두 똑같은 레시피로 바로 뛰어듭니다. 모든 것을 임베딩(Embedding)하고, 벡터 DB(Vector DB)에 쏟아부은 다음, 런타임(Runtime) 시점에 유사도(Similarity)에 따라 상위 k개(top-k)를 검색하는 방식입니다. 사용자의 문서, 코드베이스, 과거 지원 티켓과 같은 대규모 코퍼스(Corpus)에서 정보를 검색할 때는 이 방식이 정확히 맞습니다.

하지만 에이전트가 기억해야 하는 것 중 상당수는 코퍼스가 아닙니다. 그것은 **항상 참인 사실(Always-true facts)**의 작은 집합입니다. 사용자가 누구인지, 그들의 기술 스택(Stack)은 무엇인지, 그들의 확고한 선호도는 무엇인지, 그리고 당신이 작업 중인 프로젝트 뒤에 숨겨진 결정 사항들이 그것입니다. 이러한 부분에 대해서는 RAG(Retrieval-Augmented Generation)가 잘못된 도구이며, 사람들은 습관적으로 RAG를 선택한다고 생각합니다.

제가 내린 결정 규칙은 다음과 같습니다:

만약 컨텍스트(Context)가 100%의 확률로 로드되어야 한다면, 그것은 검색 레이어(Retrieval layer)에 속해서는 안 됩니다. 검색(Retrieval)은 가끔 로드하는 것을 위한 것입니다. 모든 프롬프트(Prompt)에 포함되기를 원하는 것이라면 그냥 프롬프트에 넣어야 합니다.

항상 참인 데이터 집합에서 벡터 스토어(Vector store)가 패배하는 이유

1. 유사도 검색(Similarity retrieval)은 반드시 필요한 컨텍스트에 대해 손실이 발생합니다. "사용자의 스택과 제약 사항을 항상 로드하라"는 것은 보장(Guarantee)입니다. 상위 k개 코사인 유사도(Cosine similarity)는 확률(Probability)입니다. 만약 사용자의 질문이 저장된 선호도와 어휘적으로 유사하지 않다면, 그것은 검색되지 않으며, 에이전트는 항상 알고 있어야 했던 무언가를 "잊어버리게" 됩니다. 확률로 구현된 보장은 버그입니다.

2. 임베딩(Embedding)은 읽거나 직접 수정할 수 없습니다. 에이전트가 무언가를 잘못 기억할 때, 당신은 파일을 열어 해당 라인을 수정하고 싶을 것입니다. 벡터 스토어를 사용하면 다시 임베딩(Re-embedding)하고 기도하는 수밖에 없습니다.

3. 운영상의 부담(Operational weight). 형태가 절대 변하지 않는 몇 KB의 사실들을 위해 DB를 호스팅하고, 루프(Loop) 안에 임베딩 모델을 두고, 실패할 수 있는 검색 단계를 두어야 합니다. "Go를 선호하고, 장황한 출력을 싫어함"과 같은 내용을 기억하기 위해 너무 많은 가동 부품(Moving parts)이 필요합니다.

확장 가능한 지루한 선택지

항상 참인 데이터 집합을 위해 저는 에이전트가 시작 시에 로드하는 구조화된 마크다운(Markdown) 파일을 선택했으며, 확장이 가능하도록 만드는 한 가지 엄격한 규칙을 적용했습니다:

인덱스 파일(Index file)은 항상 컨텍스트(Context)에 포함되는 유일한 요소입니다. 이 파일은 엄격한 상한선(~200행)을 가지며, 주제 파일(Topic files)을 가리키는 한 줄짜리 포인터와 모든 세션에 적용되는 소수의 사실(Facts)들을 담고 있습니다. 세부 사항은 에이전트가 관련이 있을 때만 여는 주제 파일에 저장됩니다.

이 상한선이 핵심 비결입니다. 이는 단순히 "시스템 프롬프트(System prompt)에 다 집어넣기" 식의 단순한 메모리 방식이 초래하는 두 가지 실패 모드를 방지합니다:

  • 인덱스 비대화(Index bloat) → 조용한 잘림(Silent truncation). 항상 로드되는 파일이 무제한으로 커지게 두면, 결국 컨텍스트 예산(Context budget)을 초과하여 잘려 나가게 됩니다. 이 경우 에이전트는 에러 없이 메모리를 잃게 됩니다. 인덱스에 상한을 두고 세부 사항을 연결된 파일로 밀어냄으로써, 항상 켜져 있는(Always-on) 데이터의 점유율을 아주 작게 유지할 수 있습니다.
  • 유도 가능한 사실(Derivable facts)의 저장. 에이전트가 스스로 재구성할 수 있는 것은 암기하지 마세요: 코드 컨벤션(코드를 읽으면 됨), 파일 레이아웃(grep으로 찾으면 됨), git 히스토리(git log가 권위 있는 정보임). 메모리는 에이전트가 찾아볼 수 있는 곳 어디에도 적혀 있지 않은 것들을 위한 것입니다 — "이 재작업은 기술 부채가 아니라 컴플라이언스 마감 기한 때문에 진행되는 것이다", "테스트 시 DB를 모킹(Mock)하지 마라", "Go 언어를 선호한다"와 같은 것들 말입니다.

두 계층을 모두 사용하기

이것은 RAG 대 파일(Files)의 대결이 아닙니다. 서로 다른 두 가지 질문에 답하는 두 개의 계층입니다:

  • RAG → " 쿼리(Query)와 관련된 것이 무엇인가?" (계속해서 커지는 코퍼스(Corpus))
  • 플랫 파일(Flat files) → "매번 사실인 것은 무엇인가?" (제한된 범위 내의 항상 켜져 있는 컨텍스트)

이 분리는 무제한적인 부분이 항상 켜져 있는 예산에 절대 영향을 주지 않도록 의도된 것입니다. 사람의 기술 스택과 선호도는 크게 늘어나지 않지만, 문서 더미는 계속 늘어납니다. 이들을 서로 다른 계층에 두십시오.

구현

저는 이 컨벤션을 의존성이 없는 마크다운(Markdown) 템플릿과 폴더 구조를 구성해 주는 설정 스크립트로 패키징하여, 직접 일일이 만들 필요가 없도록 했습니다:

github.com/LuciferForge/claude-code-memory (MIT, 무료)

제가 Claude Code를 위해 만들었기 때문에 기본값은 Claude Code의 경로로 설정되어 있지만, 이 컨벤션은 에이전트 불가지론적(Agent-agnostic)입니다. 시작 컨텍스트(Startup-context) 파일을 가진 것이라면 무엇이든 사용할 수 있습니다.

다른 분들은 이 경계선을 어떻게 나누는지 진심으로 듣고 싶습니다. "검색(Retrieval)에 넣을 것"과 "항상 로드된 프롬프트(Always-loaded prompt)에 넣을 것" 사이의 당신만의 기준점은 어디인가요? 그리고 항상 켜져 있는 컨텍스트(Always-on context)에서 발생하는 조용한 절단(Silent-truncation) 실패 모드를 경험해 보신 분이 계신가요? 있다면 어떻게 발견하셨나요?

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0