Elasticsearch로 만든 에이전트 장기 메모리 레이어: R@10 0.89
We built a persistent agent memory layer on Elasticsearch with 0.89 recall
TL;DR Highlight
메모리를 3개 인덱스로 나누고 하이브리드 검색을 붙였더니 R@10 0.89
Who Should Read
세션이 끝나면 사용자 맥락을 잊어버리는 에이전트의 장기 메모리를 설계하려는 백엔드 개발자
Core Mechanics
- 긴 컨텍스트는 메모리가 아니다
- 컨텍스트 윈도우는 한 번의 추론용 단기 기억이다.
- 세션이 끝나도 남고 사용자별로 꺼내 쓰는 영구 저장소가 따로 필요하다고 한다.
- 메모리를 3개 인덱스로 나눴다
- 에피소딕(시간순 사건), 시맨틱(안정적인 사실), 프로시저럴(단계별 플레이북)로 나눠 각각 인덱스를 둔다.
- 쓰기 빈도와 노화 규칙이 달라서 한 통에 담을 수 없다는 이유다.
- 검색은 BM25+벡터를 RRF로 합치고 리랭커로 마무리
- BM25는 버전 번호·에러 코드 같은 글자 그대로의 매칭을 잡고, Jina v5 밀집 벡터는 표현이 다른 질문을 잡는다.
- 각 갈래에서 80개씩 가져와 rank_constant=30으로 합친 뒤 Jina v2 크로스인코더로 재정렬한다.
- 에이전트가 질문을 바꿔 쓰는 문제는 사전 검색으로 막았다
- 에이전트는 도구 호출 전에 사용자 메시지를 말바꿈해서 버전 번호 같은 글자가 사라진다.
- 그래서 매 턴 원문 메시지로 자동 pre-recall을 먼저 돌려 대화에 주입한다.
- 모순되는 사실은 삭제하지 않고 대체한다
- 시스템 프롬프트 규칙 하나로 write_memory에 supersedes_id를 넘기게 한다.
- 사용자가 명시적으로 부정하면 contradiction=harsh로 새 사실에 작은 신뢰도 감점을 준다.
- 쓰기는 매 턴 바로 하고 정리는 나중에 한다
- 사용자 메시지마다 에피소딕 이벤트를 즉시 쓰며, 비용은 메시지당 ES 쓰기 1회로 100ms 미만이라고 한다.
- 정리(consolidation)는 LLM이 에피소드를 시맨틱 사실·프로시저럴 플레이북으로 승격하며, 프로덕션에서는 24시간마다 같은 백그라운드 작업을 권장한다.
Community Reactions
- ES는 이 용도에 과하다는 의견
- SQLite, LanceDB 같은 가벼운 벡터 DB가 더 적절하다는 댓글이 있었다.
- ES를 AI 호환으로 만들려는 흐름이라는 시각이다.
- 다른 도구로 이미 만들어 썼다는 경험
- SQLite로 에이전트에 메모리를 직접 넣었다는 경험이 있었다.
- Typesense로 벡터+BM25 하이브리드 검색을 쓴다거나 Manticore Search를 추천하는 의견도 있었다.
- 성능 향상의 증거가 있냐는 의문
- 이런 복잡한 구성이 에이전트 성능을 실제로 높인다는 증거가 있냐는 질문이 있었다.
- 놓친 11%를 사용자가 체감하는지 묻는 댓글도 있었다.
- 글 문체와 용어에 대한 불만
- 글이 LLM이 쓴 것 같아 읽기 힘들다는 의견이 여럿 있었다.
- 에피소딕 메모리라고 부르는 것이 신뢰를 깎는다는 반응도 있었다.
How to Apply
- 오늘 에이전트 시스템 프롬프트에 supersede 규칙 넣기
- 메모리 쓰기 도구가 있다면 모순이 생길 때 삭제 대신 supersedes_id로 새 사실을 쓰게 하는 규칙을 추가해라.
- 아래 코드 예시 프롬프트를 붙여 모순 처리 동작부터 확인해라.
- 도구 호출 전에 원문 메시지로 사전 검색 돌리기
- 에이전트가 질문을 말바꿈하며 버전·에러 코드를 잃는 RAG 파이프라인에 적용해라.
- 매 턴 사용자 원문으로 먼저 검색해 결과를 대화에 주입해라.
- 메모리를 사건·사실·절차로 나눠 저장하기
- 현재 한 통에 담긴 메모리를 세 종류 인덱스로 분리해라.
- 플레이북에는 success_count와 failure_count를 두고 사용자 확인·거절 발화로 올려라.
- 검색을 BM25+벡터 RRF 후 리랭크로 바꾸기
- 후보를 각 갈래에서 넉넉히 가져온 뒤 크로스인코더로 재정렬해라.
- 사용자별 격리는 API 키에 DLS 쿼리를 붙여라.
Try It
# Claude나 ChatGPT에 붙여넣기 — 원문 supersession 규칙의 간이 버전
너는 사용자의 장기 메모리를 관리하는 에이전트다. 아래 '기존 메모리'와 '사용자 메시지'를 보고 JSON으로만 답해라.
규칙:
- 사용자가 기존 사실과 모순되면 삭제하지 말고 새 사실을 쓰고 supersedes_id에 옛 사실 id를 넣어라.
- 사용자가 명시적으로 부정하거나 바로잡으면("아니야 그거 틀려") contradiction="harsh".
- 이사, 업그레이드, 취향 변경 같은 일상적 변화면 contradiction="natural".
- 사용자에게 확인을 묻지 마라. 모순 자체가 신호다.
- "잊어줘"라고 명시한 경우에만 forget을 써라.
출력 형식: {"action":"write|forget|none","text":"...","supersedes_id":"...","contradiction":"harsh|natural"}
기존 메모리:
{{기존 메모리 목록(id: 내용)}}
사용자 메시지:
{{사용자 메시지}}Terminology
관련 논문
GPT-5.6 Sol, 평화 모드 Factorio를 13.5시간에 클리어
GPT-5.6 Sol이 Codex 낮은 사고 수준으로 평화 모드 Factorio 로켓 발사까지 갔다
AI가 SRE를 대체하는 게 아니라 실력을 깎아먹는다
인시던트 95%를 AI에 맡기자 나머지 5%를 다룰 SRE의 실력이 녹슬 수 있다
Cursor Marketplace의 Claude Code 확장에 심어진 SSH 백도어
Cursor Marketplace의 변조된 Claude Code 확장이 SSH 백도어를 심었다
AI-native 엔지니어링 조직 만들기: FurtherAI 9개월 회고
배포 전 과정을 단계별로 자동화하자 엔지니어 1인당 일일 머지가 약 5배 늘었다
Minecraft·Mindustry에서 AI 에이전트 군집 50마리를 돌려 봤다
에이전트 50개를 Minecraft에 풀었더니 배급 장부와 관료제가 생겼다
Cursor Agent SDK 출시: 에이전트를 코드로 정의하고 한 줄로 배포
Cursor가 에이전트를 코드로 정의·배포하는 Agent SDK를 정식 공개했다
에이전트가 코드를 짜는 시대, 엔지니어가 여전히 알아야 할 것: 기초·폭넓은 지식·필요할 때 깊이 파기
에이전트가 짠 코드의 오너십은 기초, 폭넓은 지식, 즉시 깊이 파는 능력에 달렸다
AI가 일을 줄여준다더니, 이렇게 많이 일한 적이 없다
에이전트를 병렬로 돌렸더니 GitHub 활동이 엔지니어 시절 수준으로 돌아왔다