당신의 AI 에이전트에게는 아마도 벡터 데이터베이스(Vector Database)가 필요하지 않을 것입니다
요약
AI 에이전트의 기억 구현 시 무조건적인 벡터 데이터베이스 사용 대신, 회상(Recall)과 사용자 모델링(User modeling)을 분리하여 설계할 것을 제안합니다. Hermes Agent는 SQLite와 전문 검색을 활용해 복잡한 임베딩 과정 없이 효율적인 장기 기억을 구현합니다.
핵심 포인트
- 기억을 회상(검색)과 사용자 모델링(추론)으로 분리해야 함
- 회상은 전문 검색(Full-text search)이 효율적일 수 있음
- 사용자 모델링은 요약 및 추론 메커니즘이 필요함
- Hermes Agent는 SQLite를 통해 복잡성 없이 기억을 관리함
이 글은 Hermes Agent Challenge를 위한 제출물입니다: Hermes Agent에 대해 작성하기
당신의 AI 에이전트에게는 아마도 벡터 데이터베이스(Vector Database)가 필요하지 않을 것입니다
현재 에이전트를 구축하는 과정에는 하나의 반사적인 행동이 있습니다. 에이전트가 "무언가를 기억해야 한다"고 결정하면, 10분 안에 관리형 벡터 데이터베이스(Vector-DB) 티어를 비교하고, 임베딩 모델(Embedding model)을 선택하며, 청크 크기(Chunk size)와 중첩(Overlap)에 대해 스스로 속삭이며 고민하기 시작합니다.
직접 실행할 수 있는 가장 유능한 오픈 에이전트 중 하나인 Hermes Agent는 장기 기억(Long-term memory)을 위해 이 모든 과정을 건너뜁니다. 이 에이전트는 SQLite와 전문 검색(Full-text search)을 사용하여 과거 세션을 기억합니다. 임베딩(Embeddings)도, 벡터 인덱스(Vector index)도, 관리해야 할 코사인 유사도 임계값(Cosine-similarity threshold)도 없습니다.
제가 처음 이것을 발견했을 때, 저는 이것이 임시 방편(Placeholder)이라고 생각했습니다. 즉, "진지하게" 개발하기 전에 뜯어내 버릴 부분이라고 말이죠. 하지만 이것을 더 오래 살펴볼수록, 오히려 이것이 진지한 선택처럼 보였고, 기존의 반사적인 행동이 임시 방편처럼 보였습니다.
이 포스트는 왜 그런지, 어디에서 문제가 발생하는지, 그리고 여러분이 자신의 에이전트에 기억 기능을 구축할 때 무엇을 바꿔야 하는지에 대해 다룹니다. 임베딩(Embeddings)을 다뤄본 적이 없다면 걱정하지 마세요. 다음 두 섹션에서 쉬운 언어로 빠르게 설명해 드립니다. 만약 RAG(Retrieval-Augmented Generation)를 전문적으로 구축하신다면, "키워드 검색이 실제로 패배하는 지점" 섹션으로 건너뛰셔도 좋습니다.
하나의 단어 아래 숨겨진 두 가지 서로 다른 문제
"기억(Memory)"이라는 단어는 서로 거의 공통점이 없는 두 가지 작업에 사용되는데, 이 둘을 혼동하는 것이 많은 에이전트 설계가 잘못되는 지점입니다.
첫 번째 작업은 **회상(Recall)**입니다: 과거 세션에서 무슨 일이 있었는가? 지난 화요일에 저에게 배포 스크립트를 설정해 달라고 요청하셨죠. 제가 그 대화와 우리가 최종적으로 결정한 스크립트를 찾을 수 있을까요? 이것은 점점 쌓여가는 대화 기록(Transcripts) 더미에서 정보를 찾아내는 검색(Retrieval)입니다.
두 번째 작업은 **사용자 모델링(User modeling)**입니다: 이 사람은 누구이며 어떤 방식으로 일하는 것을 선호하는가? 특정 사건이 아니라, 서서히 구축되는 프로필입니다. 예를 들어, 이 사용자는 간결한 답변을 선호하고, Windows를 사용하며, 묻지도 않고 설정을 건드리는 것을 싫어한다는 식의 정보입니다.
이러한 정보들은 서로 다른 도구를 필요로 합니다. 회상 (Recall)은 검색 (Search) 문제인 반면, 사용자 모델링 (User modeling)은 요약 및 추론 (Summarization-and-inference) 문제입니다. Hermes는 이 둘을 분리하여 처리하며, 이러한 분리가 설계가 유지되는 핵심 이유 중 하나입니다. 회상은 전문 검색 (Full-text search)을 통해 실행됩니다. 사용자 모델링은 Honcho로 넘겨지며, Honcho는 대화 전반에 걸쳐 진화하는 사용자의 "변증법적 (dialectic)" 모델을 유지합니다. 두 개의 문제에 두 개의 메커니즘을 사용하여, 하나의 도구가 두 가지 역할을 모두 수행하도록 강요하지 않습니다.
이 포스트의 나머지 내용은 회상 (Recall) 부분에 관한 것입니다. 왜냐하면 그 부분이 바로 모든 사람이 본능적으로 해결을 위해 벡터 데이터베이스 (Vector database)를 찾는 부분이기 때문입니다.
90초 입문: 벡터 (Vectors) vs. 전문 검색 (Full-text search)
이미 이 내용을 완벽히 알고 있다면 건너뛰셔도 좋습니다. 모른다면, 전체적인 개념은 다음과 같습니다.
**벡터 데이터베이스 (Vector database)**는 텍스트를 임베딩 (Embeddings) — 모델이 생성하는 긴 숫자 리스트 — 형태로 저장하며, _유사한 의미_를 가진 것들이 서로 가깝게 위치하도록 배치합니다. "환불은 어떻게 받나요"라고 물으면, 두 문장이 공유하는 단어가 없더라도 "구매 제품 반품"에 관한 구절을 찾아낼 수 있습니다. 이러한 의미론적 도달 범위 (Semantic reach)가 바로 초능력입니다. 비용은 다음과 같습니다. 입력되는 모든 데이터와 출력되는 모든 쿼리에 대해 임베딩 모델 (Embedding model)을 실행해야 하며, 벡터를 저장하고 인덱싱해야 하고, 검색 결과가 모호하고 다소 불투명하다는 점을 받아들여야 합니다. 왜 _그것_이 반환되었을까요? 수학적으로 가깝다고 판단했기 때문입니다. 그 수학을 읽으려 노력해 보시기 바랍니다.
전문 검색 (Full-text search) — SQLite의 FTS5가 제공하는 방식 — 는 키워드 매칭 (Keyword matching)의 성숙한 버전입니다. 이는 실제 단어들을 인덱싱한 다음, 관련성 공식 (BM25: 대략적으로, 문서에 더 자주 등장하는 희귀한 단어일수록 더 높은 점수를 부여함)에 따라 결과의 순위를 매깁니다. "shopify 환불 정책"을 검색하면 해당 용어들이 실제로 나타나는 세션들을 찾아 가장 관련성이 높은 순서대로 보여줍니다. 이는 빠르고 저렴하며, 결정적으로 결과가 왜 매칭되었는지 그 이유를 정확히 읽을 수 있습니다.
하나는 의미(meaning)를 찾으려 하고, 다른 하나는 단어(words)를 찾으려 합니다. 통념에 따르면 에이전트는 의미가 필요하므로 벡터(vectors)가 필요하다고 합니다. Hermes는 _세션 회상 (session recall)_을 위해서라면 단어만으로도 충분하며, 단어가 부족한 부분은 벡터 스택을 구축하는 비용보다 더 저렴하게 보완할 수 있다고 조용히 베팅하고 있습니다.
Hermes가 실제로 기억하는 방법
세 개의 계층이 있으며, 그 중 어느 것도 생소한 것이 아닙니다. 그것이 핵심입니다.
1. 모든 세션은 FTS5로 인덱싱되어 SQLite에 저장됩니다. 여러분의 모든 CLI 및 메시징 대화는 전체 텍스트 인덱싱(full-text indexed)이 완료된 하나의 로컬 데이터베이스 파일(~/.hermes/state.db)에 저장됩니다. 나중에 관련이 있을 법한 내용이 생기면, 에이전트는 여러분이 코드베이스를 검색하는 방식과 동일하게 용어(terms)를 통해 자신의 기록을 검색하며, 관련성 순으로 순위가 매겨진 _실제 메시지(actual messages)_를 가져옵니다. 이는 메시지를 손실 압축하여 의역한 것이 아닙니다. 개념적으로 회상은 유사도 쿼리(similarity query)보다는 다음과 같은 형태에 더 가깝습니다:
-- 실제 Hermes 쿼리는 아니지만, 그 형태를 나타냅니다
SELECT session_id, snippet(messages, 0, '[', ']', '…', 12)
FROM messages
...
실제로 로컬 파일을 대상으로 할 때 이 작업은 약 20ms 내에 완료됩니다. 이동할 곳이 없기 때문에 네트워크 홉(network hop)도 발생하지 않습니다.
2. 별도의 제한된 계층이 선별된 노트를 보유합니다. 가공되지 않은 로그 옆에, Hermes는 ~/.hermes/memories/ 경로에 에이전트가 관리하는 두 개의 작은 파일을 유지합니다. 하나는 보관할 가치가 있는 사실들을 기록한 MEMORY.md 파일(약 800 토큰 제한)이고, 다른 하나는 USER.md 프로필(약 500 토큰)입니다. 이 파일들은 세션이 시작될 때 고정된 스냅샷(frozen snapshot)으로서 시스템 프롬프트(system prompt)에 주입되며, 에이전트는 추가(add), 교체(replace), 삭제(remove) 작업을 통해 의도적으로 이를 편집합니다. 이것이 바로 정제된 계층(distilled layer)입니다. 지금까지 말한 모든 것을 담는 것이 아니라, 에이전트가 평문(plain language)으로 적어둘 가치가 있다고 판단한 소수의 사항들만을 담습니다. 이는 여러분의 셸 히스토리(shell history)와 직접 작성하여 관리하는 README의 차이와 같습니다. 무엇을 남길지에 대한 응축 작업은 여기에서 일어나며, 회상 경로(recall path)를 방해하지 않으므로 검색 자체가 추측값을 반환하는 일은 결코 발생하지 않습니다.
3. 심층적인 사용자 모델링 (Deep user modeling)은 위임됩니다. 당신이 누구인지에 대한 더 풍부하고 진화하는 모습을 그리기 위해, Hermes는 Honcho에게 작업을 넘길 수 있습니다. Honcho는 각 대화가 끝난 후 이를 추론하여 당신의 선호도, 습관, 목표에 대한 통찰을 축적합니다. 이는 선택 사항이며, 의도적으로 원시 회상 (raw recall)과는 다른 시스템으로 설계되었습니다.
전체적인 구조는 다음과 같습니다:
┌────────────────────────────────────────────────┐
│ 이 대화 (작업 컨텍스트, working context) │
└───────────────┬────────────────────────────────┘
...
회상 경로 (recall path) 어디에도 임베딩 서비스 (embedding service)는 없습니다. 계속 활성화해 두어야 할 벡터 인덱스 (vector index)도 없습니다. 비행기 안에서도 열어서 읽을 수 있는 데이터베이스 파일 하나면 충분합니다.
이것이 들리는 것보다 더 나은 기본값인 이유
나중에 벡터 (vectors)에 대해 공정하게 평가하고 싶으니, 우선 이 지루한 접근 방식이 당신에게 무엇을 가져다주는지 구체적으로 말씀드리겠습니다.
실행 비용이 거의 들지 않습니다. 모든 임베딩 기반 메모리는 양쪽 끝에서 세금을 지불합니다. 저장할 때 임베딩을 해야 하고, 모든 쿼리 (query)를 할 때도 임베딩을 해야 합니다. 낮은 볼륨에서는 무시할 만한 수준이지만, 하루 종일 메모리를 읽고 쓰는 상시 가동 에이전트 (always-on agent)에게는 비용이 누적되며, 로컬 파일과 API 호출 제로로 작동하던 경로에 네트워크 의존성을 추가하게 됩니다.
디버깅이 가능합니다. 제가 매번 과소평가하는 부분입니다. FTS (Full-Text Search, 전문 검색) 기반 에이전트가 잘못된 것을 회상할 때는, 직접 쿼리를 실행하여 정확히 왜 그런 일이 발생했는지 확인할 수 있습니다. 반면 벡터 기반 에이전트가 잘못된 것을 회상할 때는, 임베딩 모델의 취향을 역공학 (reverse-engineer)하려고 애쓰며 코사인 유사도 (cosine distances)를 뚫어지게 쳐다보고 있어야 합니다. 전자는 화요일 오후에 바로 고칠 수 있는 문제지만, 후자는 "가끔 메모리가 이상한 것 같아요"라고 적힌 티켓이 됩니다.
휴대성이 좋고 검사가 가능합니다. 메모리는 파일입니다. 백업할 수 있고, 다른 기기로 복사할 수 있으며, grep으로 검색할 수 있고, 에이전트가 무엇을 알고 있는지 감사(audit)하거나 알지 말아야 할 것을 삭제할 수 있습니다. 보안 검토자에게 당신의 벡터 인덱스 안에 무엇이 들어있는지 설명하려고 시도해 보세요. 대신 그들이 직접 쿼리할 수 있는 SQLite 파일을 건네주십시오.
그것은 당신의 통제에서 벗어나 표류하지 않습니다. 임베딩 모델 (Embedding Model)을 교체하면 — 새로운 버전이나 다른 제공업체로 — 엄밀히 말해 기존의 벡터들은 새로운 벡터들과는 다른 공간에 존재하게 됩니다. 많은 팀이 이 문제를 대충 덮어두고 넘어가지만, 이는 "왜 검색 (Retrieval) 성능이 조용히 저하되었는가"라는 문제의 실제적이고 반복적인 원인이 됩니다. 키워드 인덱스 (Keyword Index)에는 모델 버전의 부담이 없습니다. 2024년의 "refund"라는 단어는 2026년의 "refund"와 같습니다.
오프라인 및 저사양 하드웨어에서도 작동합니다. Hermes의 전체적인 기획 의도는 당신이 소유한 인프라, 아마도 저렴한 장치 위에서 동작하는 에이전트입니다. 전문 검색 (Full-text search)은 그런 환경에 매우 적합합니다. 반면 벡터 스택 (Vector stack)은 더 많은 것을 요구합니다.
이 중 어느 것도 특별히 영리한 기술은 아닙니다. 하지만 이들을 모두 합쳐 "관련 있는 과거 대화를 찾아라"라는 특정 작업을 수행하게 한다면, 그 정당성을 부정하기 어렵습니다.
키워드 검색이 실제로 패배하는 지점
이제 솔직한 이야기를 해보겠습니다. "벡터가 전혀 필요 없다"라고 말하는 것은 잘못된 교훈이 될 것이기 때문입니다.
전문 검색 (Full-text search)은 당신이 예상하는 바로 그 지점, 즉 단어는 일치하지 않지만 의미는 일치할 때 실패합니다. 만약 3개월 전에 "릴리스 롤백 (rolling back a release)"에 대해 논의했고 오늘 "배포 되돌리기 (reverting a deploy)"에 대해 묻는다면, BM25는 어깨를 으쓱하며 모른 척할 것입니다. 그것은 이 두 가지가 같은 개념이라는 사실을 알지 못합니다. 임베딩 (Embeddings)이라면 이를 잡아낼 수 있을 것입니다. 다른 실제적인 약점들은 다음과 같습니다:
- 개념적 또는 모호한 쿼리 (Conceptual or fuzzy queries) ("지연 시간 (latency)이 급증했을 때 우리가 시도했던 그것")와 같이 키워드가 기억나지 않아 제공할 수 없는 경우.
- 동의어가 많거나 전문 용어가 풍부한 도메인, 즉 동일한 개념이 열 가지의 표면적 형태를 가진 경우.
- 교차 언어 검색 (Cross-lingual recall), 즉 저장된 텍스트와 쿼리가 심지어 같은 언어로 되어 있지 않은 경우.
이것은 실제적인 문제이며, 그렇지 않은 척하는 것은 이 도전 과제에 이미 가득한 종류의 옹호론일 것입니다. 하지만 Hermes의 설계에는 이를 완화해 주는 두 가지 요소가 있으며, 두 번째 요소가 흥미로운 부분입니다.
첫째, 큐레이션된 MEMORY.md 레이어는 중요한 사실들을 깨끗하고 표준적인 언어로 기록합니다. 따라서 당신의 가장 중요한 기억들은 6주 전의 대화 기록에서 정확한 단어를 재현할 수 있는지 여부에 달려 있지 않습니다.
둘째 — 그리고 이것이 벡터 데이터베이스(Vector-DB) 방식이 놓치는 부분입니다 — 검색을 수행하는 주체는 단순한 검색 파이프라인(retrieval pipeline)이 아니라 _에이전트(agent)_입니다. 만약 "reverting a deploy"라는 검색 결과가 비어 있다면, 유능한 에이전트는 스스로 "rollback"을 시도한 다음, "release", 그리고 "revert"를 차례로 시도할 수 있습니다. RAG 파이프라인은 단 한 번의 쿼리를 실행하고 그 결과가 무엇이든 그대로 받아들입니다. 반면 에이전트는 자신의 서류 보관함을 뒤지며, 원하는 서랍을 찾을 때까지 질문을 재구성(reformulating)할 수 있습니다. 이는 단순한 키워드 매칭(keyword matching)이 놓칠 법한 상당한 양의 정보를 복구해 내며, 비용은 몇 개의 추가 토큰(tokens) 외에는 들지 않습니다. 이것이 의미론적 격차(semantic gap)를 완전히 메워주지는 않겠지만, 임베딩(embedding) 없이도 그 격차를 좁혀줍니다.
그것만으로 충분하지 않다면 어떨까요? FTS5(Full-Text Search)와 벡터 인덱스(vector index)는 적이 아닙니다. 성숙한 접근 방식은 하이브리드(hybrid)입니다. 정확하고 저렴한 90%의 영역에는 키워드 검색(keyword search)을 사용하고, 개념적인 롱테일(long tail) 영역에는 임베딩(embeddings)을 계층적으로 쌓는 것입니다. Hermes는 말 그대로 이런 방식으로 구축되었습니다. 키워드 재현율(keyword recall)은 기본적으로 제공되며, Mem0, Supermemory, Honcho와 같은 의미론적 제공자(semantic provider)를 내장된 저장소를 대체하는 대신 함께 실행할 수 있습니다. 하이브리드 모델은 나중에 덧붙이는 편법(hack)이 아닙니다. 그것은 제품으로 출시된 설계(shipped design)입니다. 벡터로 시작하는 것은 순서가 뒤바뀐 것입니다. 비싸고 모호한 도구에 먼저 비용을 지불하고, 저렴하고 정확한 도구는 나중에 추가하게 되는데, 그마저도 하지 못할 수 있습니다.
실제로 사용할 수 있는 의사결정 가이드
프레임워크를 걷어내고, 당신의 에이전트 메모리를 위한 결정 사항은 다음과 같습니다:
| 만약 당신에게 필요한 것이… | 선택할 것 | 이유 |
|---|---|---|
| "우리가 X를 했던 세션을 찾아줘" | 전체 텍스트 검색 (FTS5) | 저렴하고, 빠르며, 디버깅이 가능하고, 정확함 |
| ... |
Hermes가 조용히 보여주고 있는 일반적인 원칙은 다음과 같습니다: 기본적으로 지루하고 읽기 쉬운 도구를 사용하고, 그 도구가 실패한 특정 쿼리를 지목할 수 있을 때만 비싸고 불투명한 도구를 추가하십시오. 이것은 메모리에 관한 규칙이 아닙니다. 그것은 단지 엔지니어링(engineering)입니다. 모든 블로그 포스트가 과거의 포스트들이 jQuery를 가정했던 것처럼 벡터 데이터베이스를 당연하게 가정하는 분야에서는, 이 사실을 잊기 쉽습니다.
핵심 요약 (The takeaway)
Hermes의 메모리에서 흥미로운 점은 그것이 영리하기 때문이 아닙니다. 오히려 의도적으로 영리하지 않게 설계되었음에도 불구하고 제대로 작동한다는 점입니다. SQLite, 전문 검색 (Full-text search), 요약 단계 (Summarization pass), 그리고 사용자를 모델링하기 위한 별도의 트랙. 네 가지 평범한 요소들이 각각 자신이 진정으로 잘하는 한 가지 일만을 수행합니다.
저는 무언가 생략된 부분이 있을 것이라 예상하며 이 글을 읽기 시작했습니다. 하지만 글을 마칠 때쯤에는 대부분의 에이전트 프로젝트가 반대 방향으로 편법을 쓰고 있다는 생각을 하게 되었습니다. 습관적으로 벡터 (Vectors)를 찾고, 거의 사용하지도 않을 의미론적 회상 (Semantic recall)을 위해 비용을 지불하며, 정작 일상적으로 중요한 것들, 즉 비용, 휴대성, 그리고 "왜 그것을 기억했는가?"라는 질문에 답할 수 있는 능력을 포기하고 있다는 점 말입니다.
만약 에이전트에 메모리를 추가하려 한다면, 먼저 지루한 방식부터 시도해 보세요. 텍스트를 인덱싱 (Index) 하세요. 오래된 내용은 요약하세요. 단어로 검색하세요. 임베딩 (Embeddings)에 손을 뻗기 전에, 그것이 진정으로 실패하는 지점이 어디인지 측정해 보세요. 저처럼, 실제로 임베딩이 필요한 경우가 얼마나 드문지 깨닫고 놀라게 될 가능성이 큽니다.
만약 여러분이 두 가지 방식 모두로 에이전트 메모리를 배포해 본 경험이 있다면, 전문 검색 (Full-text search)이 한계에 부딪혔던 지점이 어디였는지 듣고 싶습니다. 그 경계선이야말로 실제로 흥미로운 질문이며, 아직 아무도 이를 기록해 두지 않은 것 같습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기