검색(Retrieval)은 기억이 아니다: AI를 위한 추론 계층 구축하기
요약
단순한 검색(Retrieval) 기반 메모리의 한계를 극복하기 위해 추론 계층을 포함한 새로운 AI 에이전트 메모리 구조를 제안합니다. 데이터 저장, 패턴 추출을 위한 추론(Angel), 그리고 추론 시점에 모델을 조종하는 행동(Whisper)의 세 가지 계층으로 구성됩니다.
핵심 포인트
- 단순 검색은 수동적이며 에이전트의 실수를 방지하기에 부족함
- SQLite 기반의 통합 벡터/텍스트 저장소로 데이터 정합성 확보
- Angel 계층을 통해 반복 검증된 패턴만을 장기 기억으로 승격
- Whisper를 통해 정적 프롬프트 대신 상황에 맞는 가이드라인 주입
AI 에이전트와 함께 코딩해 본 사람이라면 누구나 그 기분을 압니다. 세션 중에는 매우 영리합니다. 당신의 패턴을 따르고, 당신의 결정을 존중하며, 버그를 수정합니다. 하지만 터미널을 닫고 다음 날 다시 열면, 그것은 낯선 존재가 되어 있습니다. 당신이 거부했던 패턴을 다시 제안하고, 이미 수정했던 버그를 다시 불러옵니다.
일반적인 해결책은 메모리 도구(memory tool)를 사용하는 것입니다. mem0, Zep, Letta, 혹은 수동으로 관리하는 CLAUDE.md 같은 것들 말이죠. 저희도 그것들을 시도해 보았지만 똑같은 벽에 부딪혔습니다.
검색(Retrieval)은 기억하는 것과 같지 않다
모든 메모리 도구는 동일한 세 가지 작업을 수행합니다: 텍스트 저장, 임베딩(embedding), 그리고 다시 찾아내기. 저장(Store) → 검색(search) → 청크(chunks) 반환. 이것이 검색(retrieval)이며 유용하긴 하지만, 수동적(passive)입니다. 질문을 받을 때까지 기다렸다가, 가장 유사하게 일치하는 텍스트를 건네줄 뿐입니다.
코딩 에이전트는 좀처럼 스스로 '질문'하지 않습니다. 대신 행동합니다. 이 저장소(repo)가 경계(boundary)에서 유효성을 검사한다는 사실을 에이전트가 회상하기를 바랄 때쯤이면, 에이전트는 이미 코드를 잘못된 방식으로 작성한 상태일 것입니다. 요청이 있을 때만 작동하는 검색은 아무도 찾아가지 않는 도서관과 같습니다.
따라서 질문은 "어떻게 더 많이 저장할 것인가?"가 아니었습니다. 질문은 "실수가 발생하기 실수를 저지르기 모델 앞에 어떻게 올바른 가이드를 제시할 것인가?"였습니다. 이러한 관점의 전환이 제품의 핵심입니다.
하나의 데이터베이스 위에 구축된 세 가지 계층
메모리(Memory). 하나의 SQLite 데이터베이스가 sqlite-vec를 통해 진실의 원천(source of truth)이자 벡터 저장소(vector store) 역할을 합니다. 별도의 벡터 DB도, 두 시스템의 정합성을 맞추기 위한 동기화 작업도 필요 없습니다. 작업 내용은 에이전트의 훅(hooks)에 의해 비침습적으로 기록됩니다. 검색은 하이브리드(hybrid) 방식입니다: 벡터 유사도(vector similarity)와 BM25 전문 검색(full-text search)을 결합한 후, 크로스 인코더(cross-encoder)로 재순위화(rerank)합니다. 이 계층은 기본 요건(table stakes)이며, 대략 다른 도구들이 수행하는 작업과 유사합니다.
Angel (추론/reasoning). 쿼리(query)를 기다리는 대신 저장된 내용에 대해 생각하는 지속적인 프로세스입니다. 패턴을 추출하고, 어떤 패턴을 표면화할 가치가 있는지 결정합니다. 패턴이 승격되기 전에, 여러 번의 독립적인 패스(passes)가 해당 패턴으로 수렴해야 합니다. 단 한 번의 실행에서만 발견되었다면 그것은 노이즈(noise)이지만, 여러 번의 실행이 동일한 결과에 도달한다면 그것은 실제(real)입니다. 이러한 검증 과정을 통해 단일 세션의 편향(bias)이 장기 기억에 남는 것을 방지합니다.
Whisper (행동/behavior). 별도의 섹션을 할애할 가치가 있는 부분입니다.
Whispers: 추론 시점에 모델을 조종하기 (steering the model at inference time)
정적 프롬프트 (Static prompts)는 확장성이 없습니다. 만약 CLAUDE.md 파일을 관리해 본 적이 있다면 그 실패 사례를 잘 알 것입니다. 파일은 점점 커지고, 모든 규칙은 항상 "켜져" 있으며, 세 번째 세션에 이르면 모델은 그 거대한 벽을 절반쯤 무시하게 됩니다.
그래서 우리는 이를 뒤집었습니다. 정적 프롬프트는 정체성을 나타내는 필수 요소로 축소됩니다. 규칙, 교훈, 관례와 같은 모든 상황적인 요소는 **위스퍼 (whisper)**가 됩니다. 즉, 트리거 조건(trigger condition)이 태그된 가이드라인이며, 관련성이 생길 때까지 휴면 상태로 유지됩니다. 세션 시작 시, 그리고 그 형태에 따라 필요하다고 판단되는 모든 턴(turn)에서, 경량 디텍터 (lightweight detector)가 "기억된 가이드라인이 도움이 될 만한 순간인가?"라고 묻습니다. 만약 그렇다면, 모델이 응답하기 전에 해당 위스퍼가 컨텍스트 (context)에 주입됩니다.
그러면 모델은 가중치 업데이트 (weight update) 없이도 마치 그 규칙을 내재화한 것처럼 행동합니다. 이는 가중치에 접근할 수 없는 상태에서 파인튜닝 (fine-tuning)을 하는 것과 가장 유사한 방식입니다. 경사 하강법 (gradient descent) 대신 적시 컨텍스트 (just-in-time context)를 통해 조종하는 것입니다. 검색 (Retrieval)이 모델이 더 많이 알게 만든다면, 위스퍼 (whisper)는 모델이 다르게 행동하게 만듭니다. 데이터베이스와 튜터 (tutor)의 차이와 같습니다.
솔직한 부분: 오작동 (misfires)
적시 주입 (Just-in-time injection)에는 명백한 실패 사례가 있습니다. 바로 작동하지 말아야 할 때 작동하는 것입니다. 그리고 그 비용은 대칭적이지 않습니다. 올바른 위스퍼는 약간의 신뢰를 얻지만, 흐름을 방해하는 잘못된 위스퍼는 큰 비용을 치르게 됩니다. 사람들은 짜증 나는 경험을 기억하기 때문입니다.
따라서 기준은 "절대 오작동하지 않는 것"이 아닙니다. 그것을 쫓다 보면 시스템이 너무 소심해져서 유용성을 잃게 됩니다. 기준은 다음과 같습니다: 잘못된 위스퍼는 무시하기 쉬워야 한다. 방해가 되지 않고, 쉽게 묵살할 수 있으며, 절대 흐름을 막아서는 안 됩니다. 이것을 제대로 구현한다면, 수천 번의 조용한 올바른 넛지 (nudge)가 가끔 발생하는 무시되는 상황보다 훨씬 더 큰 가치를 발휘할 것입니다.
현재 진행 상황
코드는 귀하의 신뢰 경계 (trust boundary) 내에 머뭅니다. 생성 과정은 제3자의 중간 박스를 거치지 않고, 귀하의 자격 증명을 사용하는 귀하의 로컬 에이전트 (local agent)를 통해 실행됩니다. 추론 (reasoning)과 위스퍼 오케스트레이션 (whisper orchestration)이 바로 저희의 영역입니다.
엔진은 실제 프로젝트 전반에서 매일 실행되며, 장기 기억 (long-memory) 측면에서 경쟁력 있게 벤치마크를 수행하고 있습니다. 저희가 지금 구축하고 있는 것은 지루하지만 반드시 필요한 부분입니다. 바로 처음 만나는 사용자(stranger)의 첫 세션을 만족스럽게 만드는 것입니다.
만약 여러분이 에이전트 메모리 (agent memory)와 싸워왔다면 — 직접 구현해 보았거나, .cursorrules 파일을 완벽하게 만들기 위해 고군분투했다면 — 여러분이 어떤 방식으로 접근했는지, 특히 오작동 (misfire) 문제에 대해서는 어떻게 대응했는지 듣고 싶습니다. 저는 아직 그 문제를 완전히 해결한 사람은 없다고 생각합니다.
Sonn은 sonn.dev에서 활동하고 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기