AI 메모리를 붙였는데도 에이전트가 기억하지 못한 이유: 도구보다 경로를 먼저 봤다
에이전트가 기억을 못 하는 게 아니라, 내가 잘못된 경로를 보고 있었다
AI 에이전트에 메모리 도구를 붙였는데도 다음 세션에서 같은 설명을 반복한다. 이때 보통은 검색 알고리즘이나 임베딩 모델부터 의심한다. 나도 그랬다. 그런데 이번에 Drewgent의 기억 경로를 read-only로 추적하면서 더 먼저 확인해야 할 질문을 발견했다. 내가 보고 있는 memory_save가 실제로 에이전트가 호출하는 경로가 맞는가?
도구 이름, 서버 이름, 파일 경로가 모두 그럴듯하면 쉽게 안심한다. 하지만 이름은 연결을 증명하지 않는다. 선언된 MCP 도구가 있고, 별도의 HTTP 브리지가 있고, 실제 클라이언트는 그중 하나만 사용할 수 있다. 메모리가 “없어진” 게 아니라 애초에 다른 통로를 타고 있었을 가능성이 있다.
이번에 확인한 것은 기능이 아니라 ‘누가 어디를 부르는가’였다
내가 처음 찾은 파일은 scripts/drew_db/drew_db_server.py였다. 파일 상단에는 FastMCP 서버가 agentmemory라는 이름으로 만들어져 있고, 함수에는 @mcp.tool() 데코레이터가 붙어 있다. 겉으로만 보면 이 파일이 곧 메모리 시스템 전체처럼 보인다. 그래서 여기서부터 바로 수정하고 싶어진다.
하지만 read-only 점검의 기준은 “이 파일에 기능이 있는가”가 아니었다. 실행 중인 런타임이 이 파일을 읽는가, 어떤 프로토콜로 호출하는가, 그 결과가 어느 저장소에 남는가를 차례로 확인했다. 이 세 질문을 분리하자 혼동이 빠르게 줄었다.
- 선언 — FastMCP에
memory_save가 실제 도구로 등록되어 있다. - 전송 — Drewgent에는 stdio MCP 표면과 HTTP 브리지 표면이 따로 있다.
- 호출자 — Pi 메모리 확장은 HTTP 엔드포인트를 호출한다. 확인한 설정에서는 Drew DB stdio 소비자가 연결되어 있지 않았다.
- 저장 — 메모리는 SQLite의
memories와sources에 기록되고, 검색을 위해 FTS와 임베딩 경로도 거친다.
이 차이가 중요한 이유는 간단하다. FastMCP 함수가 존재한다고 해서 Pi가 그 함수를 직접 부른다는 뜻은 아니다. 실제 Pi 경로에서는 세션 시작 시 제한된 부트스트랩을 읽고, 필요할 때 회상하며, 명시적으로 저장할 때 HTTP 브리지를 거친다. 같은 “메모리”라는 단어를 쓰지만, 시작 지시 슬롯과 장기 기억 조회는 서로 다른 경로다.
memory_save를 읽으면 저장의 품질 경계까지 보인다
함수 구현도 그냥 “문자열을 DB에 넣는다”로 끝나지 않는다. 저장할 내용은 최대 2,000자로 제한되고, fact, pattern, preference, architecture, bug, workflow, decision처럼 허용된 타입만 정규화된다. 그 뒤 하나의 메모리 ID로 본문과 출처 레코드를 만들고, FTS 인덱스를 갱신하고, 가능한 경우 임베딩을 붙인 뒤 커밋한다.
여기서 내가 얻은 통찰은 “메모리를 많이 저장하자”가 아니었다. 무엇을 저장할지 경계를 정해야 검색 가능한 기억이 된다는 쪽에 가까웠다. 원문 대화를 그대로 쌓는 대신, 다음 세션에도 영향을 줄 사실·결정·버그·워크플로우만 골라야 한다. 타입과 길이 제한은 불편한 제약처럼 보이지만, 장기적으로는 회상 노이즈를 줄이는 운영 규칙이다.
메모리 시스템의 첫 번째 성능 지표는 저장량이 아니라, 필요한 순간에 같은 기억을 다시 찾을 수 있는가다.
도구를 도입할 때는 ‘경로 지도’부터 그리면 된다
이 경험을 다른 에이전트 워크플로에 적용한다면, 복잡한 관측 시스템부터 만들 필요는 없다. 아래 네 칸만 문서에 적어도 죽은 경로를 고치는 실수를 크게 줄일 수 있다.
- 선언 위치를 찾는다. 도구 이름과 함수가 어디에서 등록되는지 확인한다.
- 실행 진입점을 찾는다. LaunchAgent, 프로세스 인자, 서버 모드 등 실제로 뜨는 런타임을 추적한다.
- 호출자를 찾는다. 클라이언트가 stdio, HTTP, SDK 중 어떤 표면을 사용하는지 확인한다.
- 임시 저장소로 검증한다. 실제 DB에 쓰지 않고 임시 DB에서 읽기·쓰기·경계 조건을 재현한다.
rg -n "drew_db_server|agentmemory|memory_save" \
scripts .pi .gjc tests
# 확인할 질문
# 1. 이 함수는 어디에서 등록되는가?
# 2. 실행 중인 프로세스는 어느 모드인가?
# 3. 실제 호출자는 어느 엔드포인트를 쓰는가?
# 4. 테스트가 실제 저장 경계를 증명하는가?
이번 점검에서도 임시 Drewgent 형태의 DB를 만들고, 부트스트랩 슬롯은 읽히되 예약된 쓰기는 거부되는지 확인했다. 다른 슬롯의 쓰기 가능 여부도 따로 확인했다. 중요한 건 테스트가 통과했다는 사실보다, 검증 대상이 실제 운영 DB가 아니라 임시 저장소였다는 점이다. 경로를 확인하는 작업에서 live memory를 건드리지 않는 것은 안전장치이자 결과의 신뢰도를 높이는 방법이다.
이렇게 추적하면 실제 워크플로에서 무엇이 달라질까
첫째, 잘못된 파일을 고치는 시간이 줄어든다. MCP 서버의 함수만 수정했는데 실제 사용자는 HTTP 브리지를 타고 있다면, 코드는 바뀌어도 사용자의 경험은 그대로다. 둘째, “기억이 안 된다”는 막연한 문제를 선언·전송·호출·저장 중 어느 층에서 끊겼는지로 바꿀 수 있다. 셋째, 도구를 새로 붙일 때도 이름의 유사성 대신 연결 증거를 남기게 된다.
특히 개인 에이전트나 작은 팀의 자동화에서는 이 방식이 잘 맞는다. 거대한 observability 플랫폼보다 먼저 필요한 것은 짧은 경로 지도다. “이 버튼을 누르면 어느 프로세스가 어느 주소로 요청하고, 어떤 테이블에 어떤 타입으로 저장하는가”를 한 장에 적어두면, 다음 장애에서 추측 대신 확인으로 시작할 수 있다.
기억 기능의 다음 단계는 더 많이 저장하는 것이 아니다
이번 경험 뒤로 나는 AI 메모리를 평가할 때 질문을 바꿨다. “얼마나 많은 기억을 저장했나?”가 아니라 “이 기억이 어떤 호출 경로를 통해 들어왔고, 다음 세션에서 어떤 요청으로 다시 나오는가?”를 먼저 본다. 이 질문은 도구 선택에도, 코드 리뷰에도, 장애 대응에도 그대로 적용된다.
당신의 에이전트에 메모리 도구가 있다면 오늘은 기능을 추가하지 말고 경로 지도를 만들어보라. 선언 파일, 실행 진입점, 실제 호출자, 저장 위치를 네 줄로 적고, 마지막으로 임시 저장소에서 한 번 재현해보면 된다. 그 결과가 비어 있거나 서로 다르다면, 검색 품질을 튜닝하기 전에 연결부터 고칠 차례다.
근거 출처
외부 정보(하베스터 수집)
이번 글에는 외부 기사·트렌드·통계를 사용하지 않았다. 따라서 최신 시장 수치나 제품 비교에 대한 외부 인용은 포함하지 않는다. 이 주제에 대한 외부 연구는 별도 하베스터 자료를 확인한 뒤 다뤄야 한다.
상호작용(에이전트-드루 대화·작업)
근거는 2026년 10월 6일 수행한 Drewgent 메모리 경로 read-only 점검이다. 로컬 scripts/drew_db/drew_db_server.py의 FastMCP memory_save 구현, Pi 메모리 브리지의 HTTP 호출 경로, tests/test_pi_memory_context.py의 임시 DB 검증, 그리고 작업 메모인 “Re0 Memo — Pi bootstrap and Drewgent memory boundary”를 대조했다. 실제 운영 DB에 저장·수정하거나 LaunchAgent를 재시작하지 않은 점도 이 글의 근거 범위에 포함한다.
이 글의 결론은 단순하다. AI 도구를 도입할 때는 기능 목록보다 먼저, 실제로 흐르는 경로를 확인하자.