에이전트 메모리에서 벡터 전용 검색을 중단한 이유 (그리고 그 대안)
Why we stopped using vector-only retrieval for agent memory (and what we use instead)
핵심 요약
벡터 검색의 한계를 극복하기 위해 BM25와 RRF를 결합한 하이브리드 검색 방식을 도입한 경험 공유.
- 벡터 검색 한계 — 키워드 검색과 고유 명사 식별이 어렵고 데이터 규모가 커질수록 성능이 저하됨.
- 하이브리드 검색 도입 — 벡터 검색과 BM25를 병렬 실행하고 RRF로 결과를 병합하여 정확도를 높임.
- 성능 최적화 — RRF와 재순위화(reranker)를 통해 100ms 이내의 빠른 응답 속도와 높은 정밀도를 확보함.
- 실무적 접근 — 복잡한 파이프라인 없이 PostgreSQL 트리거를 활용해 효율적으로 인덱스를 관리함.
에이전트 파이프라인에 지속적인 메모리를 처음 구축했을 때, 우리는 벡터 검색(pgvector, 코사인 유사도, 매 턴마다 top-k 검색)을 선택했습니다. 표준적인 설정이고, 잘 작동하며, 이해하기 쉬웠죠.
개발 중에는 괜찮았습니다. 하지만 프로덕션 환경에서는 예측 가능한 방식으로 실패하기 시작했습니다.
우리가 겪은 실패 모드는 다음과 같습니다:
정확한 키워드 회상. 사용자가 "스테이징용으로 설정한 API 키 접두사가 뭐야?"라고 물어봅니다. 저장된 메모리에는 sk-stg-0041이 포함되어 있습니다. "API 키 접두사 스테이징"으로 벡터 검색을 하면 가끔 찾아내긴 하지만, 메모리 저장소가 커지고 API 관련 항목이 수십 개가 되면 유사도 점수가 너무 촘촘하게 뭉쳐서 신뢰할 수 있는 순위 매기기가 어려워집니다. 특정 식별자는 임베딩에 의미론적으로 인코딩되지 않기 때문입니다. BM25는 이를 쉽게 찾아냅니다.
희귀한 고유 명사. 임베딩 모델이 충분히 학습하지 못한 특정 프레임워크 이름, 회사 이름, 또는 커스텀 식별자는 깔끔하게 클러스터링되지 않습니다. "Graphiti"라는 단어가 의미론적으로 유사한 토큰 근처에 있지 않는 한, 벡터 검색으로 "Graphiti"가 포함된 메모리를 안정적으로 검색할 수 없습니다. BM25는 이에 대해 O(1)입니다. 문자열 일치니까요.
규모에 따른 밀도. 벡터 검색은 저장소가 커질수록 성능이 저하됩니다. 메모리가 많아질수록 이웃이 많아지고 검색 결과에 노이즈가 섞입니다. 메타데이터 필터링(사용자, 최신성, 주제별)을 추가할 수 있지만, 이는 완화책일 뿐 해결책은 아닙니다. 정밀도 꼬리(precision tail)는 계속 나빠집니다.
해결책: RRF를 사용한 하이브리드 검색
우리는 이제 벡터 검색과 BM25(PostgreSQL tsvector 사용)를 병렬로 실행하고 Reciprocal Rank Fusion(RRF)을 사용하여 병합합니다.
const [vectorResults, bm25Results] = await Promise.all([
vectorSearch(query, userId),
keywordSearch(query, userId)
]);
return reciprocalRankFusion(vectorResults, bm25Results);
RRF 공식: score = Σ 1 / (k + rank_i) (k=60). 두 목록 모두에 나타나는 결과는 점수가 올라갑니다. 한쪽에서는 순위가 높지만 다른 쪽에는 없는 결과도 여전히 노출됩니다.
tsvector 컬럼은 PostgreSQL 트리거를 통해 업데이트되므로 별도의 인덱싱 파이프라인이 필요 없습니다.
두 쿼리를 동시에 실행하면 지연 시간은 합산이 아니라 ~max(vector_latency, bm25_latency)가 됩니다. 실제로 두 쿼리 모두 충분히 빠르게 실행되어 검색 단계는 p95 기준 100ms 미만으로 유지됩니다.
더 중요한 검색(예: 잘못된 회상이 실제 문제를 일으키는 고객 지원)의 경우, 상위 20개 후보에 대해 크로스 인코더 리랭커(cross-encoder reranker)를 추가합니다. 30~80ms가 추가되지만 단일 홉 사실 관계 쿼리의 정밀도를 의미 있게 향상시킵니다.
이런 방식을 시도해 본 분 계신가요? 다들 규모에 따라 어떤 검색 설정을 사용하고 있는지 궁금합니다.

