Engineering

LongMemEval 벤치마크: 자체 스택으로 96% R@5

대화 기억 검색 벤치마크 LongMemEval에서 Schift Engine + schift-embed-1으로 96% Recall@5를 달성했습니다. 7가지 retrieval 전략 실험 기록.

최근 MemPalace라는 오픈소스 프로젝트가 LongMemEval에서 96.6% Recall@5를 기록했습니다. ChromaDB와 all-MiniLM-L6-v2 조합입니다.

같은 벤치마크를 우리 스택으로 돌려봤습니다. 외부 벡터 DB 없이, 외부 임베딩 모델 없이. Schift Engine과 schift-embed-1만으로.

결과는 96.0% R@5. 0.6pp 차이입니다. 하지만 이 글의 요점은 점수 비교가 아닙니다. 7가지 retrieval 전략을 같은 데이터셋에서 돌리면서 뭐가 되고 뭐가 안 되는지 확인한 기록입니다.

벤치마크 구조

LongMemEval은 질문 하나와 수십 개의 대화 세션(haystack)을 줍니다. 정답은 어떤 세션에 답이 있는지. retrieval 문제입니다. 생성이 아니라 검색.

100개 질문을 6개 카테고리에서 균등 샘플링했습니다: knowledge-update, temporal-reasoning, multi-session, single-session-user, single-session-assistant, single-session-preference.

질문마다 Schift Engine에 새 collection을 만들고, embed.schift.io로 haystack을 임베딩하고, 쿼리를 날리고, Recall@K와 NDCG@10을 측정한 뒤 collection을 삭제합니다.

실험한 것

7가지 모드. 같은 데이터셋, 같은 엔진, 같은 임베딩 모델. retrieval 전략만 다릅니다.

모드설명
vectoruser turn만 임베딩, 순수 vector search
L# Cache3계층 (L0 전체 세션, L1 user turn, L2 첫 3개 turn). weighted merge
vector+NLIvector + NLI 기반 graph edge + GraphSearch 확장
L#+NLIL# Cache + NLI edge + GraphSearch
temporalvector + Engine temporal filter (AS_OF)
128d / 384d같은 모델, 차원만 변경

결과

특별히 표기하지 않은 것은 전부 schift-embed-1, 1024d입니다.

모드R@1R@5R@10NDCG@10
L# Cache88%96%98%0.923
vector85%96%98%0.904
vector+NLI85%96%98%0.904
vector (384d)79%94%99%0.880
vector (128d)71%93%96%0.827
L#+NLI (graph)80%93%96%0.866
temporal82%93%95%0.874

주목할 숫자는 L# Cache의 **R@1 88%**입니다. 순수 vector 대비 +3pp. 첫 번째 결과가 정답이어야 하는 상황에서 계층 구조가 의미 있는 차이를 만들었습니다.

MemPalace는 ChromaDB + all-MiniLM-L6-v2(384d)로 96.6% R@5를 보고합니다. 우리 순수 vector search는 96.0%입니다.

L# Cache가 뭔가

같은 대화를 세 가지 해상도로 저장합니다. metadata의 level 태그로 구분합니다.

  • L0: 전체 세션 (user + assistant). 맥락은 최대, 노이즈도 최대.
  • L1: user turn만. assistant의 장황한 응답을 제거. 일반 vector 모드가 쓰는 것.
  • L2: 처음 3개 user turn. LLM 호출 없는 요약 프록시.

검색 시 각 레벨을 독립적으로 검색한 뒤 가중 합산합니다: 0.5 * L1 + 0.3 * L2 + 0.2 * L0. user의 의도가 담긴 L1에 가장 높은 가중치를 줍니다. L2는 recall 범위를 넓히고, L0는 동점을 해소합니다.

저장 비용은 vector 3배입니다. 검색 비용은 필터된 쿼리 3회. 이 벤치마크의 45개 세션 규모에서 오버헤드는 미미합니다. 수천 세션 규모에서는 L2가 조대 필터 역할을 하면서 L0/L1 전체 스캔을 회피하게 됩니다.

안 된 것들

NLI 기반 graph edge. 자체 fine-tuning한 NLI classifier(nli-deberta-v3-xsmall 기반, 22M params, CPU 동작)로 세션 요약 간 contradiction과 entailment를 감지하고, Engine의 knowledge graph에 CONTRADICTS / SUPERSEDES edge를 생성했습니다. GraphSearch가 이 edge를 따라 결과를 확장합니다.

결과는 더 나빴습니다. R@5가 96%에서 93%로 하락. graph boost가 관련 있지만 정답이 아닌 세션을 끌어올렸습니다. 45개 세션 haystack에서 graph가 너무 밀집하고 edge의 노이즈가 검색에 도움이 되지 않았습니다.

graph search 자체가 쓸모없다는 뜻은 아닙니다. signal-to-noise ratio의 문제입니다. 짧은 lead turn 요약에서 추론한 NLI edge는 의미론적으로는 맞지만 검색에는 관련 없었습니다.

Temporal filtering. Schift Engine은 temporal query를 네이티브로 지원합니다: TEMPORAL_AS_OF, TEMPORAL_BEFORE, TEMPORAL_BETWEEN. temporal-reasoning 질문에 AS_OF(question_date)를 적용해서 미래 세션을 제외하고 최신 우선 정렬을 시도했습니다.

temporal-reasoning R@5가 81%에서 63%로 하락. 필터가 너무 공격적이었습니다. 일부 정답 세션의 날짜가 질문 날짜와 깔끔하게 정렬되지 않아서 정답 자체가 필터링됐습니다. hard cutoff 대신 soft recency boosting이 필요합니다.

차원별 비교

같은 모델(schift-embed-1)에서 Matryoshka 차원을 바꿔가며 테스트했습니다.

차원R@1R@5R@10NDCG@10
204882%95%98%0.888
102485%96%98%0.904
38479%94%99%0.880
12871%93%96%0.827

1024d가 최적입니다. 더 높은 차원은 이 태스크에서 노이즈를 추가로 캡처할 뿐 도움이 되지 않았습니다. 384d는 R@10에서 99%로 오히려 최고 — 순위가 약간 다르지만 정답 세션이 여전히 가까이 있다는 의미입니다. 128d는 메모리 제약이 있는 edge 시나리오에서 93% R@5를 확보할 수 있는 선택지입니다.

카테고리별 분석

유형R@5 (vector 1024d)R@5 (L# Cache)
knowledge-update100%100%
multi-session100%100%
single-session-assistant100%100%
single-session-preference100%100%
single-session-user94%94%
temporal-reasoning81%81%

4개 카테고리는 풀렸습니다. 남은 갭은 temporal-reasoning(81%)과 single-session-user(94%). 시간 순서를 이해해야 하는 질문에서 순수 vector similarity는 “이전”과 “이후”를 인코딩하지 못합니다.

다음에 시도할 것

  1. Soft temporal reranking. 세션을 제외하는 대신, vector score에 recency decay factor를 곱하는 방식. hard filter가 아닌 soft signal.
  2. 선택적 graph expansion. 모든 이웃을 boost하는 대신, knowledge-update 질문에서만 SUPERSEDES edge를 따라 확장. edge 유형과 질문 유형의 매칭.
  3. 대규모 테스트. 45개 세션 haystack은 작습니다. L# Cache와 graph search는 수천 세션에서 brute-force vector search가 한계를 보이기 시작할 때를 위한 설계입니다. 이 벤치마크는 그 시나리오를 테스트하지 않습니다.

Ready to try Schift?

Switch embedding models without re-embedding. Start free.

Get started free