나의 AI 메모리 벤치마크 결과는 98.3%였다. 그 수치는 사실이었지만, 가치가 없었다.
요약
Claude를 위한 MCP 메모리 서버인 Bastra Recall의 벤치마크 설계 오류를 분석하고, 실제 사용 환경을 반영한 새로운 평가 방식을 제안합니다. 단순 키워드 매칭이 아닌 페르소나 기반의 패러프레이징 쿼리를 통해 임베딩 모델의 실질적인 성능 향상 효과를 검증했습니다.
핵심 포인트
- 단순 트리거 문구 매칭은 벤치마크로서 가치가 낮음
- 다양한 페르소나를 활용한 패러프레이징 쿼리 설계의 중요성
- BM25와 임베딩 하이브리드 방식이 원격 쿼리 재현율을 크게 개선
- 실제 사용자의 언어 변화를 시뮬레이션하는 것이 핵심
지난 포스트에서 저는 Bastra Recall을 소개했습니다. 이는 Claude에게 로컬 Obsidian 보관함(vault) 내의 일반 Markdown 형식으로 영구적인 메모리를 제공하는 MIT 라이선스 MCP 메모리 서버입니다. 저는 검색(retrieval) 및 벤치마킹(benchmarking)에 대한 후속 글을 약속했습니다.
이제 그 내용을 공개합니다. 시작은 제가 틀렸다는 고백부터입니다.
의미 없었던 98.3%
초기에 저는 실제 제 보관함을 대상으로 평가(eval)를 실행했습니다: 59개의 메모리가 있었고, 각 메모리에 대해 해당 메모리의 고유 트리거 문구(trigger phrase)를 쿼리(query)로 사용했습니다. 결과는 다음과 같습니다:
- Recall@1: 98.3% (58/59)
- Recall@3: 100%
- MRR: 0.992
저는 약 일주일 동안 기분이 좋았습니다. 그러다 깨달았습니다. 이 벤치마크는 동의어 반복(tautology)이라는 것을 말이죠. Recall의 모든 메모리는 해당 메모리가 언제 다시 나타나야 하는지를 설명하는 트리거 문구인 recall_when 필드를 가지고 있습니다. 각 메모리를 자신의 트리거로 쿼리하는 것은, 검색 엔진을 테스트할 때 당신이 원하는 페이지의 정확한 제목을 검색하는 것과 같습니다. 당연히 결과는 좋을 수밖에 없습니다.
그 수치는 실제였습니다. 다만 중요한 것을 측정하지 못했을 뿐입니다. 즉, 미래의 세션에서 상황을 완전히 다른 단어로 설명했을 때 올바른 메모리가 돌아오는가 하는 점 말입니다. 아무도 몇 주 뒤에 자신의 트리거 문구를 그대로 다시 타이핑하지 않습니다. 사람들은 말을 바꾸고(paraphrase), 언어를 전환하며, 어렴풋이 기억합니다.
그래서 저는 고통을 줄 수 있도록 설계된 벤치마크를 구축했습니다.
실제로 실패할 수 있는 벤치마크
제 실제 381개 메모리 보관함을 대상으로 한 설정은 다음과 같습니다:
- 6개의 페르소나 에이전트(persona agents) — 각각 고정된 말투를 가진 별도의 LLM 에이전트들입니다: 간결한 독일어, 장황한 독일어, 주니어 개발자 영어, 시니어 개발자 영어, 독일어/영어 코드 스위칭("Denglisch"), 그리고 에러 메시지를 그대로 인용하는 에이전트입니다. 모든 패러프레이즈(paraphrase)가 똑같이 들리는 모드 붕괴(mode collapse)를 방지하기 위해 각 말투당 하나의 에이전트를 배치했습니다.
- 각 페르소나는 30개의 계층화된 메모리에 대해 쿼리를 재작성합니다 → 총 180개의 쿼리가 생성되며, k=3에서 평가됩니다.
- 쿼리는 근접(near, 원문 표현과 유사) 및 원격(far, 심하게 패러프레이즈됨)으로 나뉩니다. 근접 쿼리는 이미 한계치(ceiling)에 도달해 있어 아무런 정보도 주지 않기 때문입니다.
이것은 실제 실패 모드, 즉 몇 주 후의 AI 세션이 저장된 상황을 자신의 언어로 설명하는 상황을 시뮬레이션하기 위함입니다.
정직한 수치의 실체
어휘 검색 (Lexical search, BM25) 단독 사용 시: 원격(far) 쿼리에 대해 Recall@3 63.1%를 기록했습니다. 이것이 98.3%라는 수치 뒤에 숨겨진 진실입니다. 심하게 패러프레이즈(paraphrased)된 쿼리에 대해, 순수 키워드 검색은 3분의 1 이상의 경우를 놓칩니다.
다음 네 가지 결과가 저를 더욱 놀라게 했습니다:
-
임베딩 (Embeddings)은 정확히 어려운 케이스들을 구제합니다. 로컬 임베딩 레이어 (Ollama + embeddinggemma, BM25와의 하이브리드 방식)를 추가하자 원격 재현율(far-recall)이 63.1%에서 79.6%로 상승(+16.5pp)했으며, '전혀 검색되지 않음(not retrieved at all)' 사례가 103건 중 20건에서 7건으로 줄었습니다. 가장 어려웠던 목소리들이 가장 큰 이득을 얻었습니다. 예를 들어, 주니어 개발자 영어 페르소나(junior-dev-English persona)는 40.0%에서 73.3%로 급증했습니다. 만약 사용자가 당신과 다르게 표현한다면 (다른 언어, 다른 경험 수준), 바로 그 지점이 벡터(vectors)가 제값을 하는 곳입니다.
-
제가 가장 좋아하던 기능은 여기서 아무런 역할도 하지 못했습니다. recall_when 트리거 문구(trigger phrases)는 Recall에서 가장 가중치가 높은 검색 필드이며, 근접(near) 쿼리에서는 매우 훌륭합니다. 하지만 k=3인 패러프레이즈된 원격 쿼리에서는 테스트의 모든 분기에서 측정된 성능 향상이 거의 제로에 가까웠습니다. 이러한 동의어 반복(tautology)은 양날의 검과 같습니다. 이 기능이 이전 벤치마크에서 영웅적으로 보였던 이유는, 이전 벤치마크가 정확히 이 기능에 유리하게 조작되어 있었기 때문입니다.
-
쓰기 시점의 패러프레이즈(Write-time paraphrases) 역시 도움이 되지 않았습니다. Recall은 저장 시점에 메모리의 트리거에 대한 패러프레이즈를 선택적으로 생성(doc2query)하고 이를 함께 인덱싱할 수 있습니다. 미래의 세션이 입력할 문구가 이미 인덱스에 자리 잡고 있을 수 있다는 아이디어입니다. 패러프레이즈된 쿼리에 대응하는 정확한 지렛대처럼 들립니다. 하지만 이 원격 쿼리 프로필에서, 해당 방식은 일반 BM25 대비 성능 향상을 전혀 만들어내지 못했습니다 (~63% Recall@3, 어휘적 베이스라인과 동일한 수준). 오직 밀집 벡터(dense vectors)만이 그 격차를 메웠습니다. 교훈: 그럴듯한 검색 아이디어가 곧 성능 향상을 의미하지는 않습니다. 믿기 전에 먼저 측정하십시오.
-
남은 격차는 재현율(recall)의 문제가 아니라, 랭킹(ranking)의 문제입니다.
하이브리드 방식(hybrid arm)에서는 103개의 원거리 쿼리 대상(far-query targets) 중 9697개가 후보군(candidate pool)에 포함되었으며, 평균 순위(mean rank)는 약 2.32.6을 기록했습니다. 인덱스(index)가 대상들을 찾아내기는 하지만, 정렬(ordering)이 항상 그것들을 최상단에 노출시키지는 못합니다. 이는 정밀도/재순위화(precision/re-ranking)의 문제이며, 이는 별개의(그리고 나중에 다룰) 싸움입니다.
한 가지 주의사항을 말씀드리자면, 정직한 벤치마킹(benchmarking)이란 사실을 명시하는 것을 의미합니다. 페르소나 쿼리(persona queries)는 메모리 요약본(memory digests)으로부터 생성되었기 때문에, 절대적인 수치는 실행 시마다 비교할 수 없습니다. 견고한 신호(robust signal)는 동일한 쿼리에 대한 방식 간의 교차 비교(cross-arm comparison)입니다.
제품에 미친 변화
BM25가 기본값으로 유지됩니다. npx bastra-recall install을 새로 설치하면 설정이 필요 없는 어휘 검색(lexical search)을 사용할 수 있습니다. 모델 다운로드나 데몬(daemon) 의존성도 없습니다. 원래 문구와 거의 유사한 쿼리의 경우, 이미 성능이 한계치(ceiling)에 도달해 있습니다.
임베딩(Embeddings)은 설정 한 줄만으로 완전히 로컬(local)에서 실행할 수 있습니다. Ollama를 실행하면 하이브리드 검색(hybrid search)이 활성화되며, 원거리 재현율(far-recall)이 약 16포인트 상승합니다. 클라우드도, API 키도 필요 없습니다. 벡터(vectors)는 사용자의 기기에서 계산됩니다.
재순위화(Re-ranking)는 로드맵에 포함되어 있으며, 볼트 규모(vault scale)에 따라 단계적으로 적용될 예정입니다. 데이터에 따르면 남은 점수들이 바로 그곳에 있기 때문입니다.
무엇이든 검색(retrieval) 기능을 구축하고 있다면 가져가야 할 교훈
-
평가 쿼리(eval queries)가 인덱스 필드(index fields)에서 파생되었다면, 당신의 벤치마크는 동의어 반복(tautology)에 불과합니다. 당신은 검색(retrieval)이 아니라 문자열 중첩(string overlap)을 측정하고 있는 것입니다.
-
의역 생존력(paraphrase survival)을 테스트하십시오. 현실적인 쿼리는 몇 주 후에, 당신의 정확한 단어를 기억하지 못하는 누군가(또는 무언가)에 의해 작성됩니다. 다양한 목소리, 그리고 당신의 현실이 그러하다면 다양한 언어를 고려하십시오.
-
"검색되지 않음(not retrieved)"과 "순위가 잘못 매겨짐(mis-ranked)"을 분리하십시오. 이 둘은 Recall@k 수치상으로는 동일해 보이지만, 완전히 다른 해결책이 필요합니다.
-
아픈 수치를 공개하십시오. 63.1%는 98.3%가 가졌던 그 어떤 가치보다 더 유용한 사실입니다.
시도해 보기
npx bastra-recall install
이 명령어를 입력하면 가이드가 포함된 설정이 시작됩니다. 선택 메뉴를 통해 볼트(vault), AI 클라이언트, 그리고 (선택적으로) 시맨틱 재현율(semantic recall)을 플래그 없이 선택할 수 있습니다. 질문 과정을 건너뛰고 싶다면 다음을 입력하십시오.
npx bastra-recall install all
여전히 초기 단계(0.7.6)이며, 여전히 macOS/Apple Silicon/Node 22+ 환경을 지원하고, 여전히 MIT 라이선스입니다: github.com/n0mad-ai/bastra-recall
만약 여러분이 AI 메모리 시스템을 위한 검색 (retrieval) 성능을 벤치마크했거나, 제 방법론에 허점이 있다고 생각하신다면 댓글로 알려주세요. 제가 제 수치에 의문을 제기했던 지난번에는 제품이 눈에 띄게 개선되었습니다. 그리고 정직한 벤치마크를 중요하게 생각하신다면, 별점 (star) 하나가 다른 사람들이 이 저장소 (repo)를 찾는 데 큰 도움이 됩니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기