프록시와 Cognee를 사용하여 CLI 코딩 에이전트에 지속성 메모리(Persistent Memory)를 부여하는 방법
요약
CLI 코딩 에이전트의 상태가 없는(stateless) 한계를 극복하기 위해 지속성 메모리를 부여하는 cliMEM 프록시 구축 방법을 소개합니다. Cognee의 그래프 및 벡터 메모리 엔진을 활용하여 대화 중 결정 사항과 규칙을 자동으로 추출하고 저장합니다.
핵심 포인트
- CLI 에이전트와 AI 제공자 사이에 로컬 프록시를 배치하여 컨텍스트 주입
- 규칙 기반 추출기를 통해 대화 내용을 결정, 관례, 미결 사항 등으로 정제
- Cognee를 활용한 그래프 및 벡터 메모리 기반의 프로젝트별 독립적 기억 저장
- LLM 요약 대신 휴리스틱 방식을 사용하여 비용과 지연 시간 최적화
매일 아침, 저는 터미널을 열고 어제 작업했던 AI에게 프로젝트 전체를 다시 설명합니다. 아키텍처, 명명 규칙(naming conventions), 절반쯤 수정 중이었던 버그 — 이 모든 것이 사라집니다. 터미널을 닫으면 컨텍스트(context)도 사라집니다. 저희는 이에 지쳐 cliMEM을 구축했습니다.
문제점
Claude Code, OpenCode, 또는 Codex CLI를 사용해 보셨다면, 이 에이전트들이 얼마나 뛰어나게 발전했는지 알고 계실 것입니다. 이들은 코드베이스를 읽고, 여러 파일에 걸쳐 리팩터링(refactor)을 수행하며, 사용자가 설명하는 것보다 더 빠르게 버그를 수정합니다. 하지만 이들은 모두 한 가지 당혹스러운 결함을 공유합니다. 바로 완전히 상태가 없는(stateless) 방식이라는 점입니다.
모든 세션은 제로(zero)에서 시작됩니다. 에이전트는 당신이 지난 화요일에 무엇을 결정했는지, 팀이 어떤 규칙을 따르는지, 또는 어떤 스레드(thread)를 미완성 상태로 남겨두었는지 알지 못합니다. 매번 모든 것을 다시 설명하거나, 에이전트가 어제 내린 결정을 자신 있게 부정하는 모습을 지켜봐야 합니다.
일반적인 해결책은 모든 것을 거대한 지침 파일(instructions file)에 집어넣거나 더 큰 컨텍스트 윈도우(context window)를 사용하는 것입니다. 하지만 이는 확장성(scale)이 없습니다. 컨텍스트 윈도우는 가득 차고, 지침 파일은 오래되어 쓸모없게 되며, 그 중 어느 것도 실제로 중요한 것, 즉 대화 도중에 내려진 결정, 자연스럽게 형성된 규칙, 나중에 다시 돌아오려 했던 미결 스레드 등을 포착하지 못합니다.
저희가 원했던 것은 말하기는 간단하지만 구축하기는 까다로운 것이었습니다. 바로 팀 동료처럼 각 프로젝트를 기억하는 에이전트 — 우리의 작업 방식을 바꾸지 않으면서, 프로젝트별로 자동으로 기억하는 에이전트입니다. 이것이 저희 팀 AI ALCHEMISTS가 WeMakeDevs hangover 해커톤에서 구축한 것입니다.
작동 원리
cliMEM은 CLI 에이전트와 AI 제공자(AI provider) 사이에 위치하는 로컬 프록시(proxy) 서버입니다. 에이전트는 자신의 제공자와 대화하고 있다고 생각하지만, 실제로는 저희와 대화하고 있는 것입니다.
CLI Agent → cliMEM proxy (localhost:8000) → AI Provider
│
├── 모든 요청에 기억된 컨텍스트를 주입(injects)합니다
...
캡처(Capturing). 요청이 흐르는 동안, 프록시는 대화의 모든 메시지를 기록(log)합니다.
기억하기(Remembering). 세션이 종료되면 — Ctrl+C를 누르거나 유휴 시간 초과(idle timeout)가 발생하면 — 채팅 로그는 filter.py로 전달됩니다. 이는 규칙 기반 추출기(rule-based extractor)로, 가공되지 않은 대화를 결정(decision), 관례(convention), 미결 사항(open_thread), 아키텍처(architecture)와 같은 카테고리로 분류하여 원자적이고 독립적인 사실(atomic, self-contained facts)로 정제합니다. 우리는 LLM 요약 단계 대신 휴리스틱(heuristics)을 의도적으로 선택했습니다. 추가적인 API 비용이 들지 않고, 지연 시간(latency)이 늘어나지 않으며, 또 다른 모델 호출에 의존할 필요가 없기 때문입니다. 이러한 사실들은 그래프 + 벡터 메모리 엔진(graph + vector memory engine)인 Cognee에 저장되며, 프로젝트의 작업 디렉토리(working directory)로 범위(scope)가 제한되어 컨텍스트가 프로젝트 간에 서로 섞이지 않습니다.
회상하기(Recalling). 새로운 세션에서 프록시는 사용자의 프롬프트를 가로채고, Cognee에서 관련 사실을 검색한 뒤, 요청을 전달하기 전에 이를(및 실시간 파일 트리와 함께) 시스템 프롬프트(system prompt)에 주입(injects)합니다. 에이전트의 원래 지침은 보존됩니다 — 우리는 교체하는 것이 아니라 추가(append)할 뿐입니다. AI는 프로젝트 전체의 메모리를 갖춘 상태로 답변하며, 사용자는 아무것도 변하지 않았음을 느끼지 못합니다.
설정은 두 개의 명령어로 이루어집니다: climem start는 프록시를 실행하고, climem configure claude (또는 opencode, 또는 codex)는 에이전트가 프록시를 가리키도록 설정합니다. 한 번의 명령어로 원래 설정을 복구할 수 있습니다.
실제로 무엇이 잘못되었나
해커톤 후기(writeups)에서는 보통 이 부분을 생략합니다. 우리는 생략하지 않겠습니다. 이러한 문제들이 우리의 밤 대부분을 앗아갔으며, 각각의 문제는 튜토리얼에서는 배울 수 없었던 교훈을 주었습니다.
- 아무 일도 일어나지 않을 때만 발생하는 충돌
우리 서버는 종료될 때DatabaseNotCreatedError와 함께 계속 죽었으며 — 오랫동안 우리는 이를 일관되게 재현할 수 없었습니다. 이는 가장 최악의 버그 유형입니다. 무작위로 발생하는 것처럼 보이는 버그 말입니다.
마침내 원인을 추적했을 때, 그 원인은 거의 우스꽝스러울 정도였습니다. Cognee의 데이터베이스 스키마(database schema)는 마이그레이션(migrations)이 실행되기 전까지는 존재하지 않습니다. 우리의 store_memory() 함수에는 조기 반환(early return) 로직이 있었습니다 — 만약 세션에서 기억할 만한 사실이 생성되지 않으면 cognee.add()를 호출하지 않았고, 결과적으로 스키마가 생성되지 않았습니다. search_memory()는 설계상 (try/except로 감싸져) 조용히 실패했습니다. 하지만 improve_memory()에는 그러한 보호 장치가 없었습니다 — 이 함수는 존재하지 않는 데이터베이스에 그대로 충돌했습니다.
트리거 조건은 무엇이었을까요? 아무런 흥미로운 내용이 오가지 않은 세션이었습니다. 우리의 모든 테스트 메시지는 일상적인 질문들이었습니다 — 결정 사항도, 사실 관계도 없었죠 — 그래서 우리가 입력한 내용과는 전혀 무관해 보이는 시점에 매번 충돌이 발생했습니다. 우리는 터미널에서 cognee.run_migrations()를 수동으로 실행하여 이를 "해결"했고, 약 한 시간 동안 안도감을 느꼈습니다. 그래서 우리는 이를 코드 내부로 옮겼습니다. 서버 시작 시 await cognee.run_migrations()를 한 번 호출하도록 하여, 멱등성(idempotent)을 갖추고, 보이지 않으며, 영구적으로 작동하게 만들었습니다.
- 세 가지 임베딩 제공자, 세 가지의 서로 다른 실패 방식
이 구간은 해커톤 기간 중 가장 사기를 꺾는 시기였습니다. 프로젝트의 핵심인 메모리 엔진(memory engine)이 그 어떤 것도 임베딩하지 못했습니다.
NVIDIA NIM은 우리의 요청을 잘못된 형식(malformed)이라며 거부했습니다. 깊이 파고든 결과, 두 가지 API 계약(API contract) 불일치를 발견했습니다. NVIDIA는 input_type 필드("query" 대 "passage")를 요구하지만 Cognee는 이를 전혀 보내지 않았고, 반대로 Cognee는 NIM 모델이 수용하지 않는 dimensions 파라미터를 전달하고 있었습니다. Cognee의 임베딩 엔진은 OpenAI 사양(spec)을 기반으로 구축되어 있어, 제공자별로 파라미터를 추가하거나 제거할 방법이 없었습니다. 즉, 우리는 계약의 양쪽 측면 모두에서 막혀버린 상태였습니다.
Jina는 우리의 플랜 B였습니다. 하지만 단 하나의 요청이 기기를 떠나기도 전에 실패했습니다. Cognee가 Jina의 모델을 올바른 토크나이저(tokenizer)에 매핑하지 못해, 청킹(chunking) 단계인 0단계부터 깨져버린 것입니다. (현재는 상위 PR인 cognee#3762가 이러한 유형의 버그를 해결하고 있습니다.)
이 모든 과정은 속도 제한(rate-limited)이 걸린 API 키들과 해커톤의 제한된 시간 속에서 이루어졌습니다. 설계한 아키텍처가 서류상으로는 완벽하게 작동하는데, 정작 제어할 수 없는 단 하나의 의존성(dependency)이 계속해서 거부하는 것을 지켜보는 데에는 특별한 종류의 좌절감이 따릅니다. 우리는 결국 외부 API와 싸우는 것을 완전히 중단하고 fastembed로 전환했습니다. 완전히 로컬에서 작동하는 임베딩이기에 위반해야 할 API 계약도 없고, 부딪혀야 할 속도 제한도 없었습니다. 때로는 게임을 거부하는 것이 승리하는 전략이 되기도 합니다.
- 최악의 버그를 업스트림 기여(Upstream Contribution)로 전환하기
우리가 가장 자랑스럽게 생각하는 부분입니다. 해커톤이 끝난 후에도 우리는 NVIDIA 문제를 그냥 지나치지 않았습니다. Cognee의 저장소(repo)를 조사한 결과, 차원(dimensions) 문제는 이미 닫힌 이슈(cognee#1961)와 절반 정도 관련이 있었지만, input_type의 차이는 전혀 보고되지 않은 상태였습니다. 아무도 이 문제에 부딪히지 않았거나, 혹은 아무도 기록하지 않았던 것입니다.
그래서 우리는 직접 해결책을 구축했습니다. 새로운 EMBEDDING_INPUT_TYPE 설정, NVIDIA NIM을 위한 프로바이더 감지(provider 필드 또는 모델 접두사(prefix)를 통해 감지 — 이 둘이 일치하지 않을 수 있다는 점을 우리는 고생 끝에 배웠습니다), 지원되지 않는 dimensions 파라미터의 조건부 생략, 그리고 셀프 호스팅(self-hosted) NIM 스타일 서버를 위해 extra_body를 통한 input_type 전달 기능을 포함했습니다. 4개의 유닛 테스트(unit tests)를 모두 통과했으며, 기존 테스트 스위트에서 회귀(regression)는 발생하지 않았습니다. 현재 풀 리퀘스트(pull request)가 topoteretes/cognee로 전달되는 중입니다.
해커톤 기간 동안 몇 시간의 잠을 포기하게 만들었던 버그가 우리의 첫 번째 오픈 소스 기여(open-source contribution)가 되고 있습니다. 이러한 거래는 가치 있는 일이라고 느낍니다.
솔직한 제약 사항
우리는 Amrita Vishwa Vidyapeetham의 학생들로서 9시부터 5시까지 수업을 듣습니다. 따라서 이 프로젝트는 수업 사이의 틈새 시간, 늦은 밤, 강의 사이의 디버깅, 그리고 채팅을 통한 많은 조율을 통해 만들어졌습니다. 시간이 더 있었다면 두 번째 문제 정의(problem statement)에도 도전할 수 있었겠지만, 대신 우리는 제대로 작동하는 한 가지를 완성하는 쪽을 택했습니다. 우리는 다시 그 선택을 하더라도 똑같이 행동할 것입니다.
실제 작동 모습
📺 60초 데모 시청하기
⭐ GitHub에서 저장소(repo)에 스타(Star)를 눌러주세요 — 만약 사용해 보다가 무언가 고장 난다면 이슈(issue)를 열어주세요. 다른 사람의 문서화되지 않은 함정(gotcha)을 디버깅하는 것이 어떤 기분인지 우리는 정확히 알고 있습니다.
더 많은 인사이트와 CLIMEM에 대한 소식은 저희 인스타그램 페이지를 팔로우하세요 https://www.instagram.com/alchemists.ai/
다음 단계
유휴 타임아웃(idle timeout)을 테스트 모드에서 해제하고, 사실 추출기(fact extractor)를 강화하며, NVIDIA NIM input_type 수정 사항을 Cognee에 반영하는 것입니다. 해커톤 버그의 가장 완벽한 결말은 머지된 PR(merged PR)이기 때문입니다.
WeMakeDevs 해커톤에서 Team AIALCHEMISTS가 제작했습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기