RAG를 위한 파일 기반 방식 vs 벡터 방식 비교
요약
RAG 시스템 구축 시 파일 기반 키워드 검색과 벡터 검색의 성능을 5개 도메인, 5만 개의 문서를 통해 비교 실험했습니다. 키워드 검색은 정밀한 일치에 강점이 있고, 벡터 검색은 의미론적 유사성 탐색에 강점이 있음을 확인했습니다.
핵심 포인트
- 키워드 검색은 역색인을 통한 정확한 용어 일치(Precision)에 특화됨
- 벡터 검색은 임베딩을 통한 의미론적 근사(Approximate meaning)에 특화됨
- 두 방식은 상호 보완적인 기능이며 용도에 맞는 선택이 필요함
- 실험 결과 도메인에 따라 두 방식의 검색 성능(MRR@10) 차이가 발생함
파일 기반 컨텍스트 관리 (context management)가 최근 며칠 동안, 특히 Claude CoWork가 출시된 이후 큰 화제가 되고 있습니다. 이는 저의 호기심을 자극했고, 저는 몇 가지 실험을 시도해 보았습니다.
저는 Python 코드부터 과학 논문에 이르기까지 5개의 서로 다른 도메인에 걸쳐 실험을 진행했습니다. 인기 있는 데이터셋에서 50,000개의 문서를 수집하여 **5,000개의 쿼리 (query)**를 던졌습니다.
목표는 무엇이었을까요? 파일 기반 검색에 대한 모든 소음이 무엇인지, 그리고 그것이 실제로 유효한지 알아내는 것이었습니다!
저는 처음에 벡터 검색 (Vector search)이 모든 벤치마크를 압도할 것이라고 예상하며 시작했습니다. 모든 것이 임베딩 (embedding)이 되어야 하는 이유에 대해 또 다른 글을 쓸 준비가 되어 있었습니다. 하지만 새벽 2시, 제 Macbook Pro가 원자력 발전소처럼 뜨거워지는 것을 지켜보며, 우리는 공학적 현실과 항상 일치하지는 않는 '의미론적 꿈 (Semantic Dream)'을 팔고 있었다는 것을 깨달았습니다.
이 두 시스템이 실제로 하는 일
수치를 보기 전에, 이 모든 것을 설명해 주는 차이점입니다.
이 경우 키워드 검색(Keyword search)인 Tantivy는 역색인 (inverted index)을 구축하고 용어를 정확하게 일치시킵니다. 여기에는 의미에 대한 모델이 없습니다. 만약 쿼리가 "sort a list"라고 말하고 문서에 bubble_sort라고 되어 있다면, 키워드 검색은 아무것도 찾지 못합니다. 왜냐하면 이들은 서로 다른 문자열이기 때문입니다. 키워드 검색이 가진 것은 정밀도(precision)입니다. 쿼리의 용어가 문서의 용어와 동일할 때, 일치는 정확하며 그 어떤 것도 이를 앞설 수 없기 때문입니다.
이 경우 벡터 검색(Vector search)인 ChromaDB는 텍스트를 수치 표현 (numeric representation)으로 변환하고, 그 표현이 근처에 위치하는 문서를 찾습니다. 여기에는 의미에 대한 근사 모델 (approximate model)이 있으며 정확성의 개념은 없습니다. 이 방식은 "sort a list"를 bubble_sort와 연결할 것이며, 당신이 미토콘드리아에 대해 물었을 때 세포 구조에 관한 문서를 똑같이 기쁘게 반환할 것입니다. 왜냐하면 이 두 가지는 동일한 공간 내에서 가깝게 위치하기 때문입니다.
정확한 일치 (Exact matching)와 근사적 의미 (Approximate meaning)는 동일한 아이디어의 두 가지 구현 방식이 아닙니다. 이들은 서로 다른 두 가지 기능이며, 아래의 벤치마크는 여러분이 어디에 어떤 기능이 필요한지를 보여주는 지도입니다.
결과
우리는 정답 문서가 상위 10개 결과 중 얼마나 높은 순위에 위치하는지를 측정하는 MRR@10 (Mean Reciprocal Rank @10)으로 측정했습니다. 점수가 1.0이라는 것은 매번 올바른 문서가 1위로 순위가 매겨졌음을 의미합니다.
<table><colgroup><col> <col> <col> <col> <col> <col></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>데이터셋 (Dataset)</p></th><th colspan="1" rowspan="1"><p>도메인 (Domain)</p></th><th colspan="1" rowspan="1"><p>키워드, 정확한 일치 (Keyword, exact match)</p></th><th colspan="1" rowspan="1"><p>벡터, 의미론적 (Vector, semantic)</p></th><th colspan="1" rowspan="1"><p>승자 (Winner)</p></th><th colspan="1" rowspan="1"><p>차이 (Margin)</p></th></tr><tr><td colspan="1" rowspan="1"><p>CodeXGLUE</p></td><td colspan="1" rowspan="1"><p>자연어에서 코드로 (Natural language to code)</p></td><td colspan="1" rowspan="1"><p>0.2901</p></td><td colspan="1" rowspan="1"><p><strong>0.9143</strong></p></td><td colspan="1" rowspan="1"><p>벡터 (Vector)</p></td><td colspan="1" rowspan="1"><p>+0.6242</p></td></tr><tr><td colspan="1" rowspan="1"><p>MS MARCO</p></td><td colspan="1" rowspan="1"><p>실제 Bing 쿼리 (Real Bing queries)</p></td><td colspan="1" rowspan="1"><p>0.4035</p></td><td colspan="1" rowspan="1"><p><strong>0.5225</strong></p></td><td colspan="1" rowspan="1"><p>벡터 (Vector)</p></td><td colspan="1" rowspan="1"><p>+0.1190</p></td></tr><tr><td colspan="1" rowspan="1"><p>SQuAD</p></td><td colspan="1" rowspan="1"><p>Wikipedia 질의응답 (Wikipedia question answering)</p></td><td colspan="1" rowspan="1"><p>0.6048</p></td><td colspan="1" rowspan="1"><p>0.6136</p></td><td colspan="1" rowspan="1"><p>무승부 (Tie)</p></td><td colspan="1" rowspan="1"><p>+0.0088</p></td></tr><tr><td colspan="1" rowspan="1"><p>HotpotQA</p></td><td colspan="1" rowspan="1"><p>멀티홉 추론 (Multi-hop reasoning)</p></td><td colspan="1" rowspan="1"><p><strong>0.5494</strong></p></td><td colspan="1" rowspan="1"><p>0.4953</p></td><td colspan="1" rowspan="1"><p>키워드 (Keyword)</p></td><td colspan="1"rowspan="1"><p>+0.0541</p></td></tr><tr><td colspan="1" rowspan="1"><p>SciQ</p></td><td colspan="1" rowspan="1"><p>과학 시험 질문 (Science exam questions)</p></td><td colspan="1" rowspan="1"><p><strong>0.8145</strong></p></td><td colspan="1" rowspan="1"><p>0.6142</p></td><td colspan="1" rowspan="1"><p>키워드 (Keyword)</p></td><td colspan="1" rowspan="1"><p>+0.2003</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>전체 평균 (Mean, all five)</strong></p></td><td colspan="1" rowspan="1"></td><td colspan="1" rowspan="1"><p>0.5325</p></td><td colspan="1" rowspan="1"><p><strong>0.6320</strong></p></td><td colspan="1" rowspan="1"><p>벡터 (Vector)</p></td><td colspan="1" rowspan="1"><p>+0.0995</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>CodeXGLUE 제외 평균 (Mean, excluding CodeXGLUE)</strong></p></td><td colspan="1" rowspan="1"></td><td colspan="1" rowspan="1"><p><strong>0.5931</strong></p></td><td colspan="1" rowspan="1"><p>0.5614</p></td><td colspan="1" rowspan="1"><p>키워드 (Keyword)</p></td><td colspan="1" rowspan="1"><p>+0.0317</p></td></tr></tbody></table>
마지막 두 행을 함께 읽어보세요. 왜냐하면 그것이 전체 결과이기 때문입니다. 다섯 가지 데이터셋에 걸쳐 벡터 검색은 MRR(Mean Reciprocal Rank) 수치로 대략 10점 정도 앞서며, 이 숫자는 슬라이드에 올라갈 만한 수치입니다. CodeXGLUE를 제외하고 순위가 뒤바뀌면, 정확히 일치하는 키워드 검색이 세 점 차이로 앞섭니다.
벡터 검색은 일반적으로 검색(retrieval)에서 더 우수하지 않습니다. 단지 한 가지 작업에서는 3.15배 더 뛰어나며, 그 작업이 다른 모든 것의 평균을 좌우합니다.
CodeXGLUE는 이 세트에서 가장 순수한 의미적 간극(semantic-gap) 과제입니다. 왜냐하면 코드가 무엇을 해야 하는지에 대한 자연어 설명은 실제로 그것을 수행하는 코드와 거의 어휘를 공유하지 않기 때문입니다. 정확히 일치하는 검색은 비교할 것이 아무것도 없어서 0.2901점을 기록합니다. 이것이 바로 벡터 검색이 존재하는 경우이며, 이 상황에서 벡터 검색은 결정적으로 승리합니다.
SciQ는 그 반대입니다. 과학 시험 문제들은 특정 개체(entities)에 의존하며, 그러한 환경에서 단어는 개념의 근사치가 아니라 핵심(key)입니다. 'Mitochondria(미토콘드리아)'는 세포와 유사한 어떤 것이 아닙니다. 정확한 일치(Exact matching) 방식은 해당 용어를 포착하여 0.8145점을 기록하는 반면, 벡터 검색(vector search)은 임베딩 공간(embedding space)에서 근처에 위치한 문서들로 표류하며 0.6142점을 기록합니다.
SQuAD는 무승부입니다. 시드 평균(seed averaging)을 내지 않은 단일 실행에서의 차이는 0.0088로, 이는 노이즈(noise) 범위 내에 있으며 이를 벡터의 승리로 보고하는 것은 부정직한 일일 것입니다.
수치로 보는 벡터 세금 (The vector tax)
이 포스트의 이전 버전에서는 임베딩 생성(embedding generation)이 세금(tax)이라고만 언급했을 뿐, 그 규모가 어느 정도인지는 말하지 않았습니다. 그 수치는 76.4배입니다.
<table><colgroup><col> <col> <col> <col></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>데이터셋 (Dataset)</p></th><th colspan="1" rowspan="1"><p>키워드 인덱싱 (Keyword indexing)</p></th><th colspan="1" rowspan="1"><p>벡터 임베딩 및 인덱싱 (Vector embedding and indexing)</p></th><th colspan="1" rowspan="1"><p>비율 (Ratio)</p></th></tr><tr><td colspan="1" rowspan="1"><p>CodeXGLUE</p></td><td colspan="1" rowspan="1"><p>445.75 ms</p></td><td colspan="1" rowspan="1"><p>43,098.93 ms</p></td><td colspan="1" rowspan="1"><p>96.7x</p></td></tr><tr><td colspan="1" rowspan="1"><p>MS MARCO</p></td><td colspan="1" rowspan="1"><p>418.56 ms</p></td><td colspan="1" rowspan="1"><p>26,335.61 ms</p></td><td colspan="1" rowspan="1"><p>62.9x</p></td></tr><tr><td colspan="1" rowspan="1"><p>SQuAD</p></td><td colspan="1" rowspan="1"><p>410.60 ms</p></td><td colspan="1" rowspan="1"><p>35,920.01 ms</p></td><td colspan="1" rowspan="1"><p>87.5x</p></td></tr><tr><td colspan="1" rowspan="1"><p>HotpotQA</p></td><td colspan="1" rowspan="1"><p>394.98 ms</p></td><td colspan="1" rowspan="1"><p>29,538.14 ms</p></td><td colspan="1" rowspan="1"><p>74.8x</p></td></tr><tr><td colspan="1" rowspan="1"><p>SciQ</p></td><td colspan="1" rowspan="1"><p>444.30 ms</p></td><td colspan="1" rowspan="1"><p>26,670.01 ms</p></td><td colspan="1" rowspan="1"><p>60.0x</p></td></tr><tr><td colspan="1"rowspan="1"><p><strong>합계, 50,000개 문서</strong></p></td><td colspan="1" rowspan="1"><p><strong>2.11초</strong></p></td><td colspan="1" rowspan="1"><p><strong>161.6초</strong></p></td><td colspan="1" rowspan="1"><p><strong>76.4x</strong></p></td></tr></tbody></table >
이는 동일한 머신에서 동일한 코퍼스 (Corpus)를 대상으로 했을 때, 초당 309개 문서 대비 23,650개 문서에 달하는 속도입니다.
이 결과가 시사하는 바는 에이전트 (Agent)가 방금 학습한 내용을 언제 사용할 수 있느냐 하는 점입니다. 만약 에이전트가 저장소 (Repository)나 백 페이지 분량의 문서를 읽고 동일한 턴 (Turn) 내에 행동해야 한다면, 완전 일치 인덱싱 (Exact-match indexing)은 사실상 즉각적이며 임베딩 (Embedding)은 커피 한 잔 마실 시간 정도의 지연을 발생시킵니다. 만약 지금 읽고 나중에 행동한다면, 이러한 비용은 분할 상환되어 더 이상 중요하지 않게 됩니다.
두 시스템 모두를 넘어서는 결과
HotpotQA는 표에서 가장 작은 차이를 보이며, 그 아래에 가장 교훈적인 실패 사례를 보여줍니다.
해당 질문들은 예를 들어 두 잡지 중 어느 것이 먼저 설립되었는지 확인하는 것과 같이 두 개의 문서를 연결하는 것을 요구합니다. 벡터 검색 (Vector search)은 첫 번째 엔티티 (Entity)에 대한 문서는 안정적으로 검색해내겠지만, 두 번째 엔티티를 위한 가교 문서 (Bridge document)는 놓칠 것입니다. 왜냐하면 두 번째 문서가 첫 번째 엔티티에 관한 내용이 주를 이루는 쿼리 (Query)와 충분히 유사하지 않기 때문입니다. 답은 검색 가능했지만, 그 근거는 검색되지 않은 것입니다.
이것은 유사성 (Similarity)의 문제가 아니며, 어떤 리랭커 (Reranker)로도 해결할 수 없습니다. 완전 일치 (Exact matching) 방식도 의미론적 유사성 (Semantic similarity) 방식도 두 문서가 연결되어 있다는 사실에 대한 표현을 가지고 있지 않습니다. 두 시스템 모두 쿼리에 대해 독립적으로 문서를 순위 매기며, 가교 문서는 정의상 쿼리와 닮지 않은 문서입니다. 이러한 유형의 질문에 답하기 위해서는 관계 그 자체를 저장해야 하며, 이것이 바로 제3의 메커니즘이자 메모리 시스템에 그래프 구조 (Graph structure)가 존재하는 이유입니다.
답을 찾는 것은 그것을 증명할 충분한 컨텍스트 (Context)를 보유하는 것과 같지 않습니다. 두 번째 문서 없이 첫 번째 문서만 검색하는 에이전트는 오류를 내는 대신, 근거가 없는 확신에 찬 출력을 생성하게 됩니다.
방법론, 그리고 그 모든 문제점
누구나 이를 다시 실행할 수 있으며, 이를 평가하는 사람이라면 어디가 취약한지 알아야 합니다.
데이터셋당 10,000개의 문서와 1,000개의 쿼리, 총 50,000개와 5,000개를 대상으로 하며, 2026년 1월 14일 16GB RAM을 탑재한 Apple M4에서 실행되었습니다. 키워드 검색 (Keyword search)은 기본 분석기 (default analyzer)를 사용하는 Tantivy 0.22.0을 사용했습니다. 벡터 검색 (Vector search)은 384 차원의 all-MiniLM-L6-v2를 사용하는 ChromaDB 0.4.0 이상을 사용했습니다. 양쪽 방식 모두 청킹 (Chunking)은 수행하지 않았습니다. 점수 산정 (Scoring) 방식은 MRR@10을 사용했습니다.
이 카테고리의 대부분의 벤치마크가 잘못하고 있는 부분인 점수 산정에 대하여. 관련성 (Relevance)은 src/evaluation/metrics.py에서 각 데이터셋의 정답 (ground truth)과 문서 ID를 정확히 매칭함으로써 프로그래밍 방식으로 평가되었습니다. 이 파이프라인 어디에도 LLM 판사 (LLM judge)는 없습니다. 프롬프트 파일, 판정 편향 (judging bias), 또는 등가 규칙 (equivalence rule)에 의해 결과가 바뀔 수 있는 요소는 없습니다. 이는 대화형 벤치마크에 대한 저희의 일부 연구를 포함하여, 이 카테고리에서 발표된 대부분의 수치에 대해서는 단정 지을 수 없는 부분입니다. 동일한 코퍼스 (corpus)에서 다시 실행하면 동일한 표가 생성됩니다.
저희에게 제기될 수 있는 순서대로 정리한 여섯 가지 한계점입니다.
-
시드 (seed) 값에 따른 평균값이 아닌 단일 실행 결과이므로, 미세한 차이는 의미가 없으며 SQuAD 결과는 무승부로 간주해야 합니다.
-
all-MiniLM-L6-v2는 작고 비교적 오래된 임베딩 (embedding) 모델입니다. 더 강력한 임베더 (embedder)를 사용한다면 벡터 방식의 수치가 높아질 가능성이 매우 크며, HotpotQA의 결과 방향이 바뀔 수도 있습니다. 솔직하게 말씀드리면, 이는 최상의 벡터 설정이 아닌 일반적인 기본 설정을 측정하는 것입니다.
-
Tantivy는 튜닝 없이 기본 분석기로 실행되었으므로, 키워드 방식 또한 동일하게 튜닝되지 않은 상태입니다.
-
청킹 (Chunking)을 수행하지 않았으며, 이는 두 방식 모두에 영향을 미치고 긴 문서에서는 벡터 검색에 더 큰 영향을 미칩니다.
-
데이터 수집 (Ingestion) 시간은 M4에서의 로컬 임베딩 결과입니다. 호스팅되는 임베딩 API를 사용하면 네트워크 왕복 시간(round trips) 때문에 수치가 나빠질 수도 있고, 배치 추론 (batched inference) 덕분에 좋아질 수도 있으므로, 76.4배라는 수치는 상수가 아닌 비용의 형태(shape)로 취급해야 합니다.
MRR@10은 정답 문서가 어디에 순위가 매겨지는지를 측정합니다. 이는 에이전트가 그 이후에 올바른 답변을 생성했는지 여부는 측정하지 않습니다. 검색 순위 (Retrieval rank)와 작업 성공 (task success)은 서로 다른 것이며, 우리는 누군가에게 지적받기보다 차라리 우리가 직접 이를 말하는 편을 택하겠습니다.
이로써 해결되지 않는 것들
이 벤치마크는 다른 사람이 작성하였고 변하지 않는 고정된 문서 코퍼스 (corpus) 위에서 실행됩니다. 에이전트 메모리 (Agent memory)는 이 중 어느 것에도 해당하지 않습니다. 에이전트 메모리는 매 턴마다 성장하며, 에이전트 스스로가 작성하고, 어제의 사실이 오늘의 사실과 모순될 수도 있습니다.
다음 포스트에서는 코퍼스가 문서가 아닌 대화 기록 (conversation history)이 될 때 두 시스템에 어떤 일이 발생하는지를 다룹니다. 바로 이 지점에서 흥미로운 실패 사례들이 나타납니다. 그래프 구조 (graph structure)와 추출 (extraction)이 어디에 위치하는지를 포함한 아키텍처 논쟁은 저희의 build vs buy agent memory 페이지에 있으며, 토큰 경제학 (token economics)에 대한 공식적인 처리는 Agentic Context Management에 관한 저희 논문에 기술되어 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기