메뉴
HN
Hacker News 41일 전

일래스틱서치로 구축한 AI 에이전트 영구 기억층

IMP
8/10
핵심 요약

AI 에이전트가 이전 대화를 기억하지 못하는 한계를 극복하기 위해 일래스틱서치(Elasticsearch) 기반의 영구 기억 아키텍처를 제안합니다. 인지과학에 기반한 3가지 기억 유형(에피소드, 의미, 절차)을 분리하여 저장하고, 하이브리드 검색과 RRF, 재정렬(reranker) 기법을 적용해 0.89라는 높은 회상율(Recall)을 달성했습니다. 컨텍스트 창의 비용과 망각 현식을 해결할 수 있는 실질적인 장기 기억 시스템 구현 가이드입니다.

번역된 본문

에이전트 빌더(Agent Builder)가 이제 정식 출시(GA)되었습니다. Elastic Cloud 체험판으로 시작해 보시고, 에이전트 빌더 문서를 확인해 보세요.

일래스틱서치에 에이전트 기억 구축하기 3개의 인덱스, 재정렬(reranker)을 적용한 하이브리드 회상(Hybrid recall), 대체(Supersession), 감쇠(Decay), 그리고 DLS. 에이전트를 위한 영구 기억 계층의 아키텍처와 그 성과 수치에 대해 알아봅니다.

사라(Sarah)의 스마트 전구가 흰색만 표시됩니다. 스마트홈 비서는 허브를 재부팅해 보라고 제안합니다. 그녀는 3월에 이미 그렇게 했었고, 지난주에도 다시 했지만 아무것도 고쳐지지 않았습니다. 에이전트는 이 사실을 모르며, 개가 센서 케이블을 씹어 놓았다는 것도 모릅니다. 중요했던 과거 기록, 효과가 있었던 것, 없었던 것, 그리고 사라가 누구인지에 대한 정보는 각 세션이 끝나면 사라집니다.

이에 대한 일반적인 해결책은 이전 컨텍스트를 모두 컨텍스트 창(Context window)에 밀어 넣는 것입니다. 하지만 이는 비용과 지연 시간(Latency) 측면에서 문제가 발생하며, 프롬프트의 양 끝부분에서 먼 곳에 있는 정보를 모델이 무시하는 것으로 잘 알려진 "중간 누락(Lost in the middle)" 효과로 인해 실패하게 됩니다. 100만 토큰의 컨텍스트 창은 그저 스크래치패드(임시 메모장)일 뿐입니다. 이는 기억 시스템이 아닙니다.

컨텍스트 창은 단기 기억, 즉 단일 추론을 위한 활성 작업 공간입니다. 여기에 결여된 것은 장기 기억입니다. 세션이 종료된 후에도 유지되고, 수년간의 상호작용으로 확장 가능하며, 내용, 시간, 사용자별로 팩트(사실 정보)를 검색할 수 있는 영구 저장소가 필요합니다.

이 글은 일래스틱서치(Elasticsearch)에 구축되고 인지 과학의 세 가지 범주를 중심으로 구조화된, 실제 에이전트 기억 시스템의 아키텍처에 대해 설명합니다. 이 시스템은 RRF와 교차 인코더 재정렬(cross-encoder reranker)을 사용하는 하나의 하이브리드 회상 쿼리, 모순되는 정보를 처리하기 위한 대체(Supersession), 그리고 사용자별 DLS(Document-Level Security) 격리를 특징으로 합니다. 168개의 질문으로 구성된 QA 평가에서 R@10(상위 10개 검색 결과의 회상율)은 평균 0.89를 기록했으며, 다른 사용자(테넌트) 간의 데이터 유출은 전혀 없었습니다. 전체 구현은 GitHub에 공개되어 있으며, 이 글은 이 시스템이 왜 이러한 방식으로 설계되었는지에 초점을 맞춥니다.

에이전트 기억 저장소가 수행해야 할 일들 사용자가 "저번에 어떤 해결책을 시도해 보았나요?"라고 물으면, 이는 정확한 일치 제약 조건이 있는 시간적 쿼리입니다. 또는 "내 스마트 전구는 왜 흰색만 표시되나요?"라고 물을 수 있는데, 이는 개인 기억과 공유 카탈로그가 결합된 것을 필요로 합니다.

기억 자체는 균일하게 동작하지 않습니다. 사용자가 겪은 이벤트, 사용자에 대한 안정적인 팩트, 단계별 플레이북(절차)은 각각 쓰기 빈도와 노화 규칙이 다르므로, 저장소는 이 유형을 인식하고 그에 따라 처리해야 합니다. 그리고 여러 사용자가 사용하는 환경에서는 각 사용자의 기억이 다른 모든 사용자에게 보이지 않아야 합니다.

새로운 이벤트는 빠르게 누적되므로 이를 내구성 있는 유형으로 통합해야 합니다. 그렇지 않으면 인덱스가 건초더미처럼 변해 버립니다. 사용자가 기억된 팩트와 모순될 때, 감사 추적을 유지하기 위해 이전 버전은 삭제되는 대신 새 버전으로 대체(Supersede)되어야 합니다. 오래된 팩트가 최신 팩트보다 더 높은 순위를 차지해서는 안 되며, 사용자가 자주 참조하는 팩트가 우선순위에서 밀려나서도 안 됩니다. 그리고 전체 기억 계층은 하나의 에이전트 런타임에 얽매이지 않고 MCP(Model Context Protocol)를 지원하는 모든 클라이언트에서 접근할 수 있어야 합니다.

이러한 요구 사항을 벡터 저장소, 키워드 엔진, 감사 계층, 별도의 인증 서비스로 나누어 구현하면, 고장 날 수 있는 4가지 요소가 생기고 회상할 때마다 추가적인 네트워크 왕복이 발생하게 됩니다. 이 요구 사항들은 결국 검색 엔진의 기능을 설명하는 것이므로, 이 구현체는 검색 엔진을 사용합니다. 이 글의 나머지 부분에서는 이 각각의 요소들을 살펴봅니다.

세 가지 유형의 에이전트 기억: 에피소드, 의미, 절차 첫 번째 설계 결정은 저장할 기억의 범주입니다. 모든 것을 그냥 저장하면 신호(의미 있는 정보) 없는 건초더미만 만들게 됩니다. COALA 프레임워크를 통해 LLM 에이전트에 도입된 에피소드 기억, 의미 기억, 절차 기억 간의 인지 심리학적 분할은 이미 적절한 범주를 가지고 있으며, 이는 3개의 Elasticsearch 인덱스로 깔끔하게 매핑됩니다.

에피소드 기억(Episodic memory). 타임스탬프가 찍힌 이벤트입니다. 어떠한 추출이나 해석 이전에, 사용자의 각 턴이 들어오는 대로 기록됩니다. 이것의 대부분은 수명이 짧으며 항상 보관할 가치가 있는 것은 아닙니다. 몇몇 항목은 나중에 내구성 있는 팩트에 대한 증거가 됩니다.

의미 기억(Semantic memory). 사용자에 대해 추출되고 정제된, 안정적인 선언(사실 정보)입니다. 사라는 Lumio Hub v2를 소유하고 있습니다. 사라는 iOS 17.4를 사용 중입니다. 사라의 허브는 3월에 재부팅되었습니다. 이러한 정보는 세션 전반에 걸쳐 유지되며 에이전트가 기반을 두고 판단하는 근거가 됩니다.

절차 기억(Procedural memory). 여러 단계로 이루어진 플레이북입니다. 지그비(Zigbee) 연결 끊김 문제를 해결하는 방법...

원문 보기
원문 보기 (영어)
Agent Builder is available now GA. Get started with an Elastic Cloud Trial , and check out the documentation for Agent Builder here . Building agent memory on Elasticsearch Three indices, hybrid recall with a reranker , supersession, decay, and DLS. The architecture and the numbers behind a persistent memory layer for agents . Sarah's smart bulbs are only showing white. Her smart-home assistant suggests resetting the hub. She did that in March, and again last week; neither reset fixed anything. The agent doesn't know that, and it doesn't know about the dog chewing through her sensor cables either. The history that mattered, what worked, what didn't, and who Sarah is ended with each session. The standard workaround is to stuff prior context into the context window. That breaks down on cost, on latency , and on the well-documented " lost in the middle " effect, where models ignore facts placed far from the prompt's edges. A 1M- token context window is a scratchpad. It is not a memory system. The context window is short-term memory: the active reasoning space for a single inference . What is missing is long-term memory: a persistent store that survives session end, scales to years of interaction, and lets you retrieve facts by content, by time, and by user. This post is about the architecture of a real agent memory system, built on Elasticsearch and structured around three categories from cognitive science , one hybrid recall query with RRF and a cross-encoder reranker, supersession for contradictions, and per-user DLS isolation. On a QA-style eval over 168 questions, R@10 averages 0.89 with zero cross-tenant leaks. The full implementation is on GitHub ; this post is about why it is shaped the way it is. What an agent memory store has to do A user asks "what fix did we try last time?" , a temporal query with an exact-match constraint. Or "Why are my smart bulbs only showing white?" , which needs personal memory blended with a shared catalog. Memory itself doesn't behave uniformly: events the user lived, stable facts about them, and step-by-step playbooks all have different write rates and aging rules, so the store has to recognize the type and treat each accordingly. And in any multi-user deployment, each user's memory has to stay invisible to every other user. Fresh events accumulate fast enough that they have to be consolidated into the durable kinds, or the index turns into a haystack. When a user contradicts a recalled fact, the old version has to be superseded rather than deleted, so the audit trail stays. Older facts shouldn't outrank fresh ones, and facts the user touches often shouldn't sink. And the whole memory layer should be reachable by any MCP -speaking client, not tied to one agent runtime. Splitting these across a vector store, a keyword engine, an audit layer, and a separate auth service means four things that can break and extra round-trips on every recall. The requirements describe a search engine, so this implementation uses one. The rest of this post walks through each. Three types of agent memory: episodic, semantic, procedural The first design decision is what categories of memory to store at all. Just saving everything builds a haystack with no signal. The cognitive-psychology split between episodic, semantic, and procedural memory , surfaced for LLM agents in the COALA framing , already has the right categories, and they map cleanly onto three Elasticsearch indices. Episodic memory. Time-stamped events: each user turn as it lands, before any extraction or interpretation. Most of it is short-lived: not always worth keeping. A few entries become evidence for durable facts later. Semantic memory. Distilled, stable assertions about the user. Sarah owns a Lumio Hub v2. Sarah is on iOS 17.4. Sarah's hub was reset in March. These survive across sessions and are what the agent grounds in. Procedural memory. Multi-step playbooks. How to troubleshoot Zigbee disconnects. Processes, not facts. Each carries success_count and failure_count , incremented by consolidation when the user confirms a fix worked or didn't. The counters are surfaced to the consolidation LLM as context when it considers whether to refine or replace a playbook. Each category has a different lifecycle . Episodic is written constantly and decays. Semantic is curated, deduped, and superseded as the user changes. Procedural accumulates outcome feedback ( success_count , failure_count ) that feeds consolidation. One bucket cannot model that. Three indices, one per memory type, let each follow its own write rate, its own aging rules, and its own update rules without coupling them. Alongside these three sits a fourth retrieval surface: world data already in Elasticsearch (catalog, knowledge base ). It is not "memory" in the cognitive sense, but the agent reads it through the same hybrid-retrieval pipeline (covered in the next section), so it belongs in the same picture. The recall pipeline: hybrid retrieval with RRF and a reranker Memory is recalled with a two-stage hybrid search : RRF over BM25 + Jina v5 dense, then a cross-encoder reranker on the merged candidates. Each document is indexed two ways from one write: the raw text lands in the BM25 inverted index , and copy_to routes the same value into a semantic_text field that auto-generates Jina v5 vectors. Indexing the same content twice keeps the storage footprint flat: one source-of-truth write produces both retrieval legs ( index mapping ). Each leg solves a different problem. BM25 anchors literal-token matches that an agent paraphrase would dissolve: version numbers, error codes, proper nouns like "Lumio Hub v2." Dense vectors catch the semantic shape of a question whose answer uses different words. Either leg alone misses cases that the other handles, and RRF fuses their rankings without having to calibrate BM25 scores against cosine similarities. Over-fetch. A reranker can only re-order what it sees, so the candidate pool needs to be wide. The hybrid retriever fetches 80 candidates per leg and RRF-fuses with rank_constant=30 (tighter than the ES default of 60, so top-ranked items dominate more). ( _rrf_fetch ) Reranker. A Jina v2 cross- encoder scores the merged candidates against the user query. Where BM25 and the bi-encoder dense both score query and document independently, a cross-encoder scores them jointly, with full attention across the pair, which is a stronger relevance signal at higher per-pair cost. That's what motivates the two-stage pipeline: over-fetch cheaply with the hybrid retriever, then rerank a small candidate pool with the more expensive scorer ( _rerank ). One subtlety, shown in the diagram above. The agent's tool kit includes recall_memory (defined in tools.py ), which the model calls during a turn. A single call fans across all three memory indices and the catalog at once: the agent doesn't pick a memory type, because the retriever's ranking and per-index decay handle routing on its behalf. The second subtlety is paraphrasing. Agents almost always rewrite the user's message before reaching for that tool, which strips literal version numbers, error codes, and proper nouns from the query before BM25 ever sees them. So every turn opens with an automatic pre-recall on the verbatim user message, injected into the conversation as if the agent had made the call itself ( agent.py ). Writing and consolidating agent memory. Two operations move memory from "what just happened" into "what is durable about this user." Write. Every user turn writes one episodic event (ID, exact message, timestamp and more) before the LLM responds. The ID is assigned by Elasticsearch on write, the DLS query on Sarah's API key keeps the doc scoped to her on every subsequent recall, and the timestamp is what the time-decay function (below) reads to rank the event against newer ones. Agent replies aren't stored. The conversation history a