
나의 코딩 세션 6,679개를 기억하도록 AI 학습시키기
요약
1인 개발자가 과거의 수천 개 코딩 세션 데이터를 활용해 AI 에이전트의 메모리 시스템을 구축한 사례를 소개합니다. 단순한 모델 업그레이드 대신, 과거의 프롬프트와 도구 호출 기록을 검색 가능한 데이터베이스로 만들어 에이전트의 컨텍스트를 강화했습니다.
핵심 포인트
- 과거 코딩 세션의 트랜스크립트를 활용한 개인용 메모리 구축
- 에이전트가 추측 대신 검색을 통해 과거 해결책을 참조하도록 설계
- 인덱스 파일(MEMORY.md)을 통한 효율적인 컨텍스트 관리
- 1인 기업의 조직적 기억(Institutional Memory) 부재 문제 해결
더 나은 모델은 내가 6주 전에 동일한 버그를 어떻게 해결했는지 알지 못합니다. 하지만 내 과거 세션의 전체 텍스트 인덱스(full-text index)는 알고 있습니다. 그리고 나의 에이전트(agents)들은 추측을 허용받기 전에 반드시 이를 검색해야 합니다.
올해 내가 AI에게 준 가장 유용한 업그레이드는 더 똑똑한 모델이 아니었습니다. 그것은 바로 나의 기록이 담긴 데이터베이스였습니다.
나는 1인 기업으로서 소수의 작은 앱들과 한 무리의 AI 에이전트들을 운영합니다. 내가 업무를 위해 앉을 때마다 코딩 세션(coding session)을 시작하며, 그 세션은 모든 프롬프트(prompt), 모든 명령(command), 모든 도구 호출(tool call)과 함께 트랜스크립트(transcript) 형태로 디스크에 저장됩니다. 몇 년에 걸쳐 이는 아무도 다시 열어보지 않는 수천 개의 파일로 쌓였습니다. "그 서명 키(signing key)를 어떻게 설정했었지" 또는 "지난번에 빌드(build)를 망가뜨린 게 뭐였지"와 같은 모든 컨텍스트(context)가 그 안에 있었습니다. 다만 검색이 불가능했기에, 실제로는 사라진 것이나 다름없었습니다.
팀 동료가 있다면 기억해 주었을 것입니다. 하지만 1인 기업에는 동료가 없습니다. 그래서 나는 대신 메모리(memory)를 구축했습니다.
문제: 1인 기업은 잊어버린다
일반적인 팀에서는 조직의 기억(institutional memory)이 사람들에게 존재합니다. 누군가는 당신이 3월에 수행한 마이그레이션(migration), 불안정한 테스트(flaky test)에 대한 임시 해결책(workaround), 라이브러리 B 대신 A를 선택한 이유를 기억합니다. 당신이 물어보면 그들이 답해줄 것이고, 당신은 처음부터 다시 조사할 필요가 없습니다.
혼자 일할 때, 그 기억은 정확히 단 한 곳에만 존재합니다. 바로 내 머릿속입니다. 그리고 내 머릿속은 형편없는 데이터베이스입니다. 전체 텍스트 검색(full-text search)도 안 되고, 조용히 데이터 행(rows)을 누락시키기도 합니다. 디스크에 있는 트랜스크립트에는 모든 것이 있었지만, 수천 개의 JSONL 파일이 담긴 폴더는 기억이 아니라 묘지였습니다.
따라서 목표는 명확했습니다. 모든 과거 세션을 검색 가능하게 만들고, 나의 에이전트들이 실제로 이를 사용하게 만드는 것이었습니다.
기계가 구축하는 하나의 큰 레이어(layer) 위에, 내가 직접 작성한 두 개의 작은 레이어가 놓여 있습니다.
내가 직접 작성한 두 개의 레이어
메모리는 세 개의 레이어로 구성되며, 상위 두 개는 작고 인간적인 것입니다.
레이어 1은 인덱스 파일 (index file) — 단 하나의 MEMORY.md 파일로, 매 세션이 시작될 때 컨텍스트 (context)에 로드됩니다. 이 파일은 의도적으로 매우 작게 유지하며, 메모리당 대략 한 줄 정도로 구성되어 항상 지니고 다녀도 비용이 거의 들지 않습니다. 여기에는 지식이 담겨 있는 것이 아니라, 지식에 대한 _포인터 (pointers)_가 담겨 있습니다: "이전 스크린샷으로부터 UI 상태를 단정 짓지 마세요 — 먼저 다시 확인하세요."
레이어 2는 지식 그 자체 — memory/ 폴더 내에 사실(fact) 하나당 하나의 마크다운 (Markdown) 파일로 존재하며, 인덱스의 한 줄이 현재 내가 하고 있는 일과 관련이 있어 보일 때만 읽어 들입니다. 누군가가 나에게 준 수정 사항, 코드에는 보이지 않는 제약 조건, 참조 링크 등이 여기에 해당합니다.
이 두 레이어는 함께 큐레이션된 계층 (curated tier)을 형성합니다: 작고, 신호 밀도가 높으며 (high-signal), 수동으로 편집됩니다. 매우 훌륭하지만 한계가 있습니다. 6,679개의 모든 세션에 대해 일일이 노트를 손으로 작성할 수는 없습니다. 그것이 바로 세 번째 레이어가 존재하는 이유입니다.
기계가 구축하는 레이어
레이어 3은 약 230줄의 파이썬 (Python) 스크립트와 SQLite의 FTS5 엔진을 통해 구축된 모든 세션 트랜스크립트 (transcript)의 전체 텍스트 인덱스 (full-text index)입니다.
구조는 간단합니다. 세 개의 테이블로 구성됩니다: sessions (세션당 하나의 메타데이터 행), turns (턴당 하나의 행 — 역할, 타임스탬프, 텍스트), 그리고 검색을 위해 텍스트를 인덱싱하는 FTS5 가상 테이블인 turns_fts입니다. 트리거 (Triggers)가 턴이 삽입되거나 삭제될 때마다 검색 인덱스를 동기화하므로, 제가 수동으로 관리할 필요가 없습니다.
인덱싱은 증분 방식 (incremental)으로 이루어집니다. 스크립트는 프로젝트 로그 디렉토리를 탐색하며, 각 트랜스크립트에 대해 파일의 수정 시간을 해당 세션을 마지막으로 인덱싱했을 때와 비교합니다. 변경되지 않은 파일은 건너뛰고, 변경된 세션은 기존의 턴을 삭제한 후 다시 읽어 들입니다. 스크립트를 다시 실행하는 것은 비용이 저렴하며 멱등성 (idempotent)을 보장합니다. 여러 코딩 세션이 동시에 실행되는 상황에서도 견딜 수 있도록, 데이터베이스는 30초의 busy timeout과 함께 WAL 모드로 열립니다.
검색은 단 한 줄의 명령어로 가능합니다:
python3 session_indexer.py --search "signing key"
이 명령은 FTS5 MATCH를 실행하여 검색 결과의 관련성에 따라 순위를 매기고, 각 결과에 대한 snippet() — 즉, 일치하는 텍스트와 그 양옆의 몇 단어 문맥을 포함한 조각 — 을 가장 관련성이 높은 것부터 출력합니다. 저는 이를 단일 프로젝트로 범위를 제한할 수도 있습니다. 이것은 시맨틱 검색 (Semantic Search) 이 아니며, 시맨틱 검색이 되려고 노력하지도 않습니다. 이것은 내 키보드가 만들어낸 말뭉치(Corpus) 위에서 순위가 매겨지는 grep입니다.
마지막 인덱스 실행 기준으로, 해당 말뭉치는 6,679개의 세션과 286,382개의 턴 — 약 460 MB의 데이터베이스이며, 대략 30개의 프로젝트 폴더에 걸쳐 있습니다. 가장 큰 단일 프로젝트 하나만 해도 2,415개의 세션을 보유하고 있습니다. 이 수치는 매일 증가하는데, 제가 완료하는 모든 세션이 다음 인덱서 실행 시 검색 가능해지기 때문입니다. (제 개인 설정 문서에는 여전히 "1,800+"라고 적혀 있습니다. 시스템이 구축된 지 몇 달 만에 자체 문서를 초과해 버렸습니다.)
"지어내는 것"보다 "찾아보는 것"이 기본값이 될 때까지 인덱스는 아무런 역할도 하지 못합니다.
디스크 공간을 차지할 가치를 만드는 규칙
실제로 중요한 부분은 바로 여기이며, 그것은 데이터베이스가 아닙니다. 바로 규칙입니다.
아무도 쿼리(Query)하지 않는 인덱스는 그저 값비싼 디스크 공간일 뿐입니다. 그래서 제 에이전트들이 실행하는 지침은 매우 단호합니다: "지난번에 이걸 어떻게 했지?"라는 질문 — 즉, 과거의 작업으로 거슬러 올라가는 어떤 질문이든 — 을 던지면, 먼저 인덱스를 검색하고 찾은 내용을 인용할 것. 만약 일치하는 것이 없다면 "관련 기록 없음"이라고 말하고, 절대로 지어내지 말 것.
이것이 비결의 전부입니다. 제 과거에 대해 확신에 차 있지만 틀린 답변을 내놓는 비용은 매우 높지만, 검색 비용은 밀리초(ms) 단위입니다. 따라서 검색은 필수이며 추측은 금지됩니다. 제 설정의 일부는 이미 이 방식에 의존하고 있습니다. 바로 이 글들을 초안 작성할 때 사용하는 기술은, 모델이 일어나지도 않은 그럴듯해 보이는 일화를 만들어내도록 두는 대신, 제가 실제로 수행했던 작업들에 근거를 두기 위해 세션 검색을 실행합니다.
만약 이 내용이 익숙하게 들린다면, 그것은 제가 몇 주 전에 폐기한 자동화 작업에서 얻은 교훈과 같습니다. 시스템은 지목할 수 있는 결과를 변화시킬 때만 운영할 가치가 있습니다. 이번 시스템은 제 몫을 다하고 있습니다. 근거가 있는 답변 하나하나가 제가 기억을 되살려 재구성하거나, 잘못 답변할 필요가 없었던 답변들이기 때문입니다.
생각하지 않아도 신선함을 유지하기
업데이트해야 한다는 사실을 기억해야 하는 기억은 기억이 아닙니다. 따라서 인덱서 (indexer)는 고정된 시계가 아니라 저의 작업 방식에 맞춘 주기에 따라 스스로 실행됩니다.
세션이 시작될 때, 저렴한 훅 (hook)이 이미 존재하는 내용을 읽기만 할 뿐 스캐닝 (scanning)은 하지 않습니다. 진짜 작업은 제가 새로운 작업을 시작하기 위해 컨텍스트 (context)를 비울 때 발생합니다. 그것이 자연스러운 체크포인트 (checkpoint)이므로, 이때 인덱서가 증분 패스 (incremental pass)와 패턴 스캔 (pattern scan)을 수행합니다. 그리고 제가 컨텍스트를 비우지 않고 긴 시간을 보낼 경우를 대비하여, 백스톱 (backstop)이 최대 2시간에 한 번씩 무거운 작업을 실행합니다. 실제로 인덱스는 하루에 5~8번 새로고침되며, 저는 명령어를 입력할 필요가 전혀 없습니다.
다른 무언가도 동일한 경로를 따릅니다. 작은 탐지기 (detector)가 저의 최근 프롬프트 (prompts)와 명령어를 읽고, 비밀 정보와 경로, 숫자를 제거한 뒤 남은 것들의 지문 (fingerprint)을 추출합니다. 동일한 지문이 세 개 이상의 별도 세션에서 나타나면 후보로 떠오릅니다. "이 작업을 수동으로 여러 번 수행하셨습니다. 스크립트 (script)로 만드는 것이 좋겠습니다."라고 말이죠. 이것이 스스로 무언가를 승격시키지는 않습니다. 반복을 표시하고 제가 결정하기를 기다릴 뿐입니다. 메모리는 제가 같은 말을 반복하고 있다는 것을 알아차립니다.
제가 실제로 여기서 가져올 점
만약 당신이 혼자 일하거나, 당신 자신이 조직의 기억 역할을 할 만큼 작은 팀에서 일한다면, 훔칠 가치가 있는 부분은 스키마 (schema)가 아닙니다:
- 세상의 정보뿐만 아니라 당신 자신의 기록을 인덱싱(Index)하세요. 모델은 이미 세상에 대해 알고 있습니다. 모델이 알 수 없는 것은 당신이 지난 3월에 무엇을 했는지입니다. 그 간극을 메우는 것이 가장 저렴하면서도 가장 레버리지(leverage)가 높은 작업입니다.
- 추측하기 전에 검색을 의무화하세요. "찾아보기"가 "지어내기"보다 기본값(default)이 되기 전까지 인덱스는 아무런 역할도 하지 못합니다. 에이전트(agent)가 읽을 수 있는 곳에 그 규칙을 명시하세요.
- 수동으로 작성하는 레이어(layer)를 최소화하세요. 백과사전을 만드는 것이 아니라 포인터(pointer)를 큐레이션(curate)하고, 롱테일(long tail) 데이터는 기계가 처리하도록 두세요.
- 유지보수를 당신의 의지력이 아닌 일정에 맡기세요. 기억을 갱신하는 것이 당신의 기억력에 의존한다면, 그 데이터는 부패할 것입니다.
더 나은 모델을 갖는 것은 다른 모든 사람이 얻는 것과 동일한 업그레이드입니다. 당신 자신의 작업에 대한 검색 가능한 메모리는 실제로 당신만이 가질 수 있는 단 하나의 우위(edge)입니다. 그리고 1인 기업에게 있어, 그것은 처음부터 끝까지 모든 과정을 함께해 온 동료와 가장 유사한 존재입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기