LlamaIndex 기본 설정만으로 문서 QA 품질을 믿어도 될까?
아니다. VectorStoreIndex에 SimpleVectorStore, 기본 청크 크기, similarity_top_k=2를 그대로 쓰면 소규모 프로토타입은 돌아가지만, 그 상태로 "검색이 잘 된다"고 판단할 근거는 없다. 판단 기준은 내 문서에서 정답 근거가 실제 top-k에 들어오는지, 답변이 그 근거를 벗어나지 않는지, 질의당 비용과 지연시간이 허용 범위인지 세 가지다. 이 글은 그 세 가지를 직접 바꿔가며 확인한 과정을 정리한다.
핵심 정리
- 기본값(
SimpleVectorStore, 청크 1024,top_k=2)은 실험용 출발점이지 운영 기준이 아니다. - 청킹·임베딩·
top_k·검색 방식을 바꿀 때마다 Hit Rate@k, MRR@k를 함께 기록해야 비교가 가능하다. - 검색 평가와 응답 평가는 분리해야 하며, 최종 답변 점수만 보면 실패 원인을 찾기 어렵다.
- 합성 질문 평가셋만으로 출시 판단을 내리기보다 오타·긴 질문·답 없는 질문을 섞은 자체 골드셋이 필요하다.
top_k를 늘리면 검색 품질이 오를 수 있지만 토큰 비용과 지연시간도 같이 늘어난다.
이번 실험에서 검증하려 한 것은 단순하다. 사내 한국어 문서(약 300쪽 분량의 규정·매뉴얼 PDF를 노드로 분할) 기반 QA 챗봇 후보로 LlamaIndex를 검토하면서, "청킹 크기를 바꾸면 검색이 달라지는가", "임베딩 모델을 바꾸면 비용과 품질이 어떻게 움직이는가", "top_k를 늘리는 게 항상 이득인가"를 각각 수치로 확인하려 했다. 결과를 먼저 말하면, 세 변수 모두 정답률과 비용을 동시에 흔들었고 어느 하나도 "이것만 올리면 끝"이라는 답은 없었다.
청크 크기 512·1024·1536 중 뭘 써야 할까?
정답은 하나로 고정되지 않는다. LlamaIndex 코드 기준 문서는 기본 청크 크기를 1024 토큰으로 안내하지만, 버전과 설정에 따라 달라질 수 있다고 명시한다. 실험에서는 같은 문서 코퍼스를 512·1024·1536 토큰으로 각각 분할해 동일 질의 30개에 대해 Hit Rate@4를 비교했는데, 조항이 짧고 번호가 많은 규정 문서에서는 512 토큰 쪽이 조항 경계를 덜 깨는 대신 문맥이 끊겨 답변이 불완전해지는 경우가 있었고, 1536 토큰 쪽은 반대로 문맥은 풍부했지만 서로 다른 조항이 한 노드에 섞여 리랭킹 없이는 혼동이 생겼다. 중요한 건 숫자 자체가 아니라 "표·목록·조항 경계가 보존되는가"를 청크 크기와 별개로 눈으로 확인하는 과정이었다. 1024 토큰 기본값에서 시작해 위아래로 한 단계씩 움직여보는 방식이 비용 대비 합리적이었다.
임베딩 모델을 바꿀 때 뭘 같이 고정해야 할까?
모델명·차원·정규화 여부·배치 크기·언어 조건을 함께 고정해야 비교가 의미 있다. LlamaIndex 인덱싱 문서는 대규모 데이터에서 임베딩 생성이 시간과 비용을 유발할 수 있다고 설명하는데, 실제로 300쪽 분량 문서를 노드로 쪼갠 뒤 임베딩을 다시 돌릴 때마다 생성 시간과 호출 비용이 체감될 정도로 쌓였다. 실험에서는 두 종류의 임베딩(하나는 범용, 하나는 한국어 특화)을 같은 청크·같은 질의셋에 적용했는데, 검색 성능 차이보다 먼저 눈에 띈 건 임베딩 생성 비용과 배치 처리 시간 차이였다. 모델을 바꿀 때마다 인덱스를 통째로 다시 구축해야 한다는 점도 운영 관점에서는 무시하기 어려운 비용이었다. 이 부분은 "어느 임베딩이 더 좋다"보다 "내 데이터·언어 조건에서 재구축 비용을 감당할 수 있는가"를 먼저 따지는 게 실용적이었다.
top_k와 벡터 단독·하이브리드 검색은 어떻게 비교해야 할까?
top_k를 2·4·8로 늘리며 비교하고, 고유명사나 조항 번호가 들어간 질문은 벡터 검색만으로 판단하지 않아야 한다. 공식 예제는 리트리버를 similarity_top_k=2로 구성하지만, 이는 정답률을 보장하는 기준이 아니라 실험용 값이라고 명시한다. 실험에서 top_k=2일 때 놓쳤던 근거 일부가 top_k=4에서는 잡혔지만, top_k=8로 더 늘렸을 때 추가로 잡힌 근거는 거의 없었고 대신 프롬프트에 들어가는 문맥 토큰만 늘었다. 또한 제품 코드나 조항 번호처럼 정확한 문자열이 중요한 질문에서는 벡터 검색만으로 종종 근접하지만 틀린 조항을 가져왔는데, 벡터 저장소 통합 문서도 벡터 저장소를 검색 인덱스로 쓸 수 있다고만 설명할 뿐 특정 검색 품질을 보장하지는 않는다. 이런 질문 유형은 키워드 검색이나 하이브리드 검색을 별도로 비교하는 편이 안전했다.
검색 품질과 응답 품질은 왜 나눠서 봐야 할까?
둘을 합쳐서 하나의 점수로만 보면 실패 원인을 찾을 수 없기 때문이다. RetrieverEvaluator는 Hit Rate, MRR, Precision, Recall, AP, NDCG를 제공하며, 공식 예제는 문서 QA에서 최소한 Hit Rate@k와 MRR@k를 함께 기록할 것을 보여준다. 실험에서는 generate_question_context_pairs로 합성한 질문·정답 문맥 쌍으로 1차 검색 평가를 돌린 뒤, 별도로 만든 골드셋(오타가 섞인 질문, 비교 질문, 답이 아예 없는 질문 포함)으로 2차 평가를 돌렸다. 두 평가 결과가 꽤 달랐는데, 합성 질문은 문서 문장과 어휘가 겹치는 경우가 많아 Hit Rate가 높게 나오는 반면, 실제 사용자 질문 스타일에서는 같은 설정으로도 수치가 낮아졌다. OpenAI 쿡북의 RAG 평가 설명도 검색 단계와 응답 단계를 분리해 평가해야 원인 분석이 가능하다고 강조한다. 질의마다 question, retrieved_node_ids, similarity_score, top_k, chunk_size, embedding_model, latency, cost를 기록해두니 설정을 바꿀 때마다 "무엇이 좋아지고 무엇이 나빠졌는지"를 표로 바로 비교할 수 있었다.
이 실험 설계는 어디서 막힐까?
가장 먼저 막힌 지점은 SimpleVectorStore 기반 실험을 20개가 넘는 다른 벡터 저장소로 그대로 확장할 수는 없다는 것이었다. 공식 벡터 저장소 문서는 필터링, 하이브리드 검색, 동시성, 운영비가 저장소마다 다르다고 설명하는데, 로컬 메모리 기반 실험에서 얻은 Hit Rate가 운영용 벡터 DB에서 그대로 재현된다는 보장은 없었다. 또한 합성 질문 평가셋은 문서 문장과 어휘 중복이 많아 지표가 낙관적으로 나오는 경향이 있었고, 실제 사용자 질문의 다양성(오타, 복합 질문, 도메인 은어)을 충분히 대표하지 못했다. 비용 측면에서도 VectorStoreIndex는 기본적으로 질의당 LLM 호출을 쓰기 때문에 비용 분석 문서가 말하듯 top_k와 응답 모드를 바꿀 때마다 토큰 사용량이 같이 움직였고, 이 부분은 소규모 샘플로는 운영 규모의 p95 지연시간을 정확히 추정하기 어려웠다. 문서 1만 건 규모 인덱싱 시간과 동시 질의 처리량은 이번 실험 범위에서 다루지 못한 영역으로 남겨둔다.
이번 실험에서 남은 결론은 무엇인가?
2026년 기준으로 정리하면, LlamaIndex의 기본 구성은 빠르게 돌려보기엔 충분하지만 운영 판단에는 부족했다. 청킹·임베딩·검색 방식·top_k를 하나씩 바꿔보면서 얻은 가장 분명한 교훈은, 숫자 하나(예: Hit Rate 상승)만 보고 설정을 확정하면 비용이나 지연시간 같은 다른 축이 조용히 나빠질 수 있다는 점이다. 다음 실험에서는 하이브리드 검색과 리랭커를 조합한 조건을 추가하고, 운영 규모에 가까운 질의량으로 p95 지연시간과 질의당 비용을 다시 측정할 계획이다. llama-index 및 임베딩·벡터 DB의 정확한 버전과 실행 날짜를 기록해두지 않으면 다음 실험에서 왜 결과가 달라졌는지 구분할 수 없다는 점도 이번에 다시 확인했다.