- Published on
RAG 정확도 급락? 벡터DB 재색인·HNSW 튜닝
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
RAG 시스템이 잘 돌아가다가 어느 날부터 답변이 엉뚱해지면, 대부분 원인은 LLM이 아니라 검색 단계의 리콜(recall) 저하입니다. 특히 벡터DB를 HNSW로 운영 중이라면, 데이터가 누적되는 과정에서 인덱스 품질이 서서히 나빠지거나(혹은 설정 변경으로 급격히), 재색인이 필요한 상태가 되기도 합니다.
이 글은 “정확도 급락”을 측정 가능한 증상으로 쪼개고, (1) 무엇이 깨졌는지 빨리 판별한 뒤 (2) 벡터DB 재색인과 (3) HNSW 튜닝으로 리콜을 회복하는 실전 절차를 다룹니다.
1) 정확도 급락을 먼저 숫자로 정의하기
RAG 품질 이슈를 “정확도”로만 표현하면 원인 추적이 어렵습니다. 아래 3가지를 분리해 측정하면 진단 속도가 빨라집니다.
- Retrieval Recall@k: 정답 문서(또는 정답 청크)가 Top-k에 포함되는 비율
- Context Precision: Top-k 컨텍스트 중 실제로 답에 기여하는 비율(노이즈 비율)
- Generation Faithfulness: 주어진 컨텍스트에 근거해 답했는지(환각 여부)
현장에서 “갑자기 이상해졌다”는 케이스는 대개 Recall@k가 떨어진 경우가 많습니다. 즉, LLM이 나빠진 게 아니라 가져온 컨텍스트가 틀린 것입니다.
최소 평가 세트 만들기
운영 중인 질의 로그에서 50~200개 정도를 샘플링하고, 각 질의에 대해 “정답 청크 id” 또는 “정답 문서 id”를 라벨링합니다. 이 라벨이 있어야 재색인 전후, HNSW 파라미터 변경 전후의 리콜 변화를 객관적으로 비교할 수 있습니다.
2) 급락 원인 Top 6: 재색인 전에 확인할 것
재색인과 튜닝은 비용이 큽니다. 먼저 아래를 점검해 설정/데이터 불일치를 제거하세요.
2.1 임베딩 모델/차원 변경으로 인한 드리프트
- 임베딩 모델 버전이 바뀌었는데 기존 벡터를 그대로 둔 경우
- 차원 수가 바뀌었는데(예: 768에서 1024) DB 스키마는 그대로인 경우
- 정규화 방식(코사인 유사도 전제의 L2 정규화) 변경
이 경우는 “정확도 서서히 하락”이 아니라 특정 시점 이후 급락 패턴이 흔합니다.
2.2 거리 함수 불일치: cosine vs dot vs L2
임베딩 생성은 코사인 기반을 전제로 했는데, 벡터DB 인덱스는 dot product로 검색하거나 반대인 경우가 있습니다. 특히 코사인 검색을 하려면 벡터를 L2 정규화하거나 DB가 cosine을 지원해야 합니다.
2.3 청크 전략 변경으로 인한 id/메타데이터 꼬임
- 청크 크기, overlap, 문서 파서가 바뀌었는데 기존 id를 재사용
- 동일 문서가 중복 ingest되어 최신/구버전이 섞임
- 필터링 메타데이터(tenant, language, access scope)가 누락
검색은 맞게 되는데 “권한 밖 문서가 섞인다”거나 “예전 정책 문서를 가져온다” 같은 형태로 나타납니다.
2.4 필터 조건의 미세한 변화
벡터 검색은 대개 topK 이후에 메타데이터 필터를 적용하거나, 반대로 필터 후 검색을 수행합니다. 구현이 바뀌면 리콜이 급락할 수 있습니다.
- 예:
topK=20으로 뽑은 뒤 필터링했더니 남는 게 2개뿐 - 해결: 필터 선적용 또는
topK를 크게 올리고 후필터
2.5 업데이트/삭제가 많은 워크로드에서 인덱스 품질 저하
HNSW는 동적 삽입을 지원하지만, 삭제가 많거나 업데이트가 잦으면 “논리 삭제 tombstone”이 쌓이면서 검색 품질이나 지연이 악화될 수 있습니다. 벡터DB에 따라 컴팩션/리빌드가 필요한 시점이 있습니다.
2.6 리소스 압박으로 인한 검색 품질 저하(간접 원인)
CPU 스로틀링이나 메모리 압박이 심하면 검색 요청이 타임아웃/부분 실패로 처리되어 결과가 빈약해질 수 있습니다. 특히 쿠버네티스 환경에서 노드 디스크나 이미지 GC 이슈가 겹치면 예기치 않은 성능 저하가 나기도 합니다. 인프라 쪽 증상이 의심되면 EKS에서 nodefs ImageGC로 Pod가 Evicted될 때 같은 체크리스트로 먼저 안정화하세요.
3) “재색인”이 필요한 신호
다음 중 2개 이상이면 재색인을 진지하게 고려할 타이밍입니다.
- 임베딩 모델 버전이 바뀌었거나, 임베딩 파이프라인(정규화/전처리)이 바뀜
- 삭제/업데이트가 누적되어 tombstone이 많아짐
- HNSW 파라미터를 보수적으로 잡아두었고 데이터가 크게 증가함
- 오프라인 평가에서 Recall@k가 이전 대비 유의미하게 하락
- 동일 질의에 대해 결과가 불안정(일관성 저하)
4) 재색인 전략: “전량 리빌드” vs “점진적 재색인”
4.1 전량 리빌드(권장: 품질 우선)
- 새 컬렉션(또는 새 인덱스)을 만들고
- 전체 문서를 재청킹, 재임베딩 후
- HNSW를 새로 빌드
- 스위치오버(트래픽 전환)
장점: 품질이 가장 예측 가능하고, tombstone/파편화가 깔끔히 제거됩니다.
4.2 점진적 재색인(권장: 비용/시간 제약)
- 최신 문서부터 재임베딩하여 새 컬렉션에 적재
- 일정 기간 듀얼 리드(두 컬렉션에서 검색 후 머지)
- 커버리지 확보 후 구 컬렉션 폐기
장점: 다운타임이 없고 비용을 분산할 수 있습니다.
4.3 스위치오버 안전장치
- 질의 샘플에 대한 Recall@k 비교 리포트
- p95 지연, 타임아웃률, 빈 결과 비율 모니터링
- 롤백 가능한 라우팅(피처 플래그)
5) HNSW 핵심 파라미터 이해: 무엇이 리콜을 깎는가
HNSW는 그래프 기반 근사 최근접 탐색(ANN)이며, 보통 아래 파라미터가 품질과 비용을 좌우합니다.
M: 각 노드가 유지하는 최대 이웃 수(그래프 밀도)efConstruction: 인덱스 빌드 품질(클수록 빌드 느리지만 리콜 상승)efSearch: 검색 시 탐색 폭(클수록 느리지만 리콜 상승)
일반적인 현상은 다음과 같습니다.
- 데이터가 커졌는데
efSearch를 그대로 두면 리콜이 떨어질 수 있음 M이 너무 작으면 그래프가 성기고, 군집 간 점프가 어려워 리콜이 낮아짐efConstruction이 너무 작으면 애초에 그래프 품질이 낮아 튜닝으로도 한계가 있음
6) HNSW 튜닝 절차(실전): “검색 튜닝”부터 시작
인덱스를 다시 빌드하지 않고도, 우선 efSearch만 조정해 리콜이 회복되는지 확인하세요. 가장 싸고 빠른 실험입니다.
6.1 1단계: efSearch 스윕
- 고정: 인덱스(현재 상태),
topK - 변경:
efSearch를 32, 64, 128, 256… 단계적으로 증가 - 측정: Recall@k, p95 latency, QPS
보통 efSearch를 올리면 리콜이 올라가지만 지연이 증가합니다. 목표는 “리콜 목표치를 만족하는 최소 efSearch”를 찾는 것입니다.
6.2 2단계: topK와의 상호작용
RAG에서 topK를 늘리면 리콜이 오르지만 컨텍스트 노이즈도 늘어납니다. 따라서 topK만 키우는 것은 부작용이 큽니다.
권장 순서:
efSearch로 리콜을 먼저 확보- 그 다음
topK는 5~20 범위에서 최소화 - 필요하면 reranker를 추가
6.3 3단계: 그래도 리콜이 안 나오면 재빌드(M, efConstruction)
efSearch를 크게 올려도 리콜이 충분히 안 나오면, 인덱스 품질 자체가 낮을 가능성이 큽니다. 이때는 재색인 시점에 다음을 조정합니다.
M증가: 메모리 사용량 증가, 빌드 시간 증가, 리콜 개선efConstruction증가: 빌드 시간 증가, 리콜 개선
경험적으로는 아래 같은 출발점을 많이 씁니다(데이터/차원/분포에 따라 달라짐).
M: 16 또는 32efConstruction: 128 또는 256efSearch: 64~256 사이에서 목표 리콜에 맞춰 선택
7) 코드 예제: 오프라인 리콜 평가 + HNSW 파라미터 스윕
아래는 “질의-정답 문서 id” 평가셋을 기준으로 Recall@k를 계산하는 예시입니다. 벡터DB 호출부만 여러분 환경에 맞게 바꾸면 됩니다.
from dataclasses import dataclass
from typing import Callable, Iterable
@dataclass
class EvalSample:
query: str
gold_doc_id: str
@dataclass
class SearchHit:
doc_id: str
score: float
# search_fn은 (query, top_k, ef_search) 입력을 받아 SearchHit 리스트를 반환한다고 가정
SearchFn = Callable[[str, int, int], list[SearchHit]]
def recall_at_k(samples: Iterable[EvalSample], search_fn: SearchFn, top_k: int, ef_search: int) -> float:
total = 0
hit = 0
for s in samples:
total += 1
hits = search_fn(s.query, top_k, ef_search)
if any(h.doc_id == s.gold_doc_id for h in hits):
hit += 1
return hit / max(total, 1)
def sweep_ef_search(samples: list[EvalSample], search_fn: SearchFn, top_k: int, ef_values: list[int]):
rows = []
for ef in ef_values:
r = recall_at_k(samples, search_fn, top_k=top_k, ef_search=ef)
rows.append((ef, r))
return rows
# 사용 예
# ef_values = [16, 32, 64, 128, 256]
# print(sweep_ef_search(eval_samples, search_fn, top_k=10, ef_values=ef_values))
이 결과를 기준으로, 리콜이 급락한 시점의 인덱스와 “재색인 후 인덱스”를 동일한 평가셋으로 비교하면 원인(인덱스 품질 vs 데이터/임베딩 문제)을 빠르게 가를 수 있습니다.
8) 재색인 파이프라인 체크리스트(실수 방지)
재색인은 단순히 벡터를 다시 넣는 작업이 아니라, 재현 가능한 데이터 파이프라인을 만드는 작업입니다.
- 문서 원본 버전 고정(스냅샷)
- 청킹 로직 버전 고정(파서, 토크나이저, chunk size, overlap)
- 임베딩 모델 버전 고정(모델명, 리비전, 차원)
- 정규화/전처리 고정(소문자화, 공백 정리, 특수문자 처리)
- 메타데이터 스키마 고정(tenant, ACL, 언어, 문서 타입)
- ingest idempotency 보장(중복 삽입 방지 키)
특히 데이터 파이프라인에서 텍스트 인코딩이 흔들리면, 같은 문서라도 임베딩이 달라져 검색이 불안정해질 수 있습니다. ETL 과정에서 디코딩 오류가 잦다면 PyArrow Invalid - UTF-8 디코딩 오류 해결 가이드 같은 패턴을 참고해 입력을 먼저 안정화하세요.
9) 운영 팁: “정확도 급락”을 알람으로 만들기
RAG 품질은 사용자 불만으로 먼저 발견되면 이미 늦습니다. 아래 지표를 주기적으로 수집하면 급락을 조기 감지할 수 있습니다.
- 빈 결과 비율: Top-k 결과가 0개이거나 필터 후 남는 게 0개인 비율
- 평균 유사도/상위 점수 분포의 이동: 상위 score가 갑자기 낮아지면 임베딩 드리프트 의심
- 동일 질의 반복 시 결과 안정성: Top-k의 Jaccard 유사도
- 오프라인 평가셋 Recall@k 주간 리포트
또한 검색 지연이 늘면 efSearch를 올리기 어렵고, 결국 리콜을 희생하는 운영으로 흐르기 쉽습니다. 인프라 레벨에서 타임아웃과 병목을 함께 점검하는 습관이 중요합니다. 네트워크/게이트웨이 계층에서 오류가 증가한다면 AWS ALB 502/504 급증 - 타임아웃 7곳 점검처럼 상위 레이어부터 병목을 제거하세요.
10) 결론: 가장 빠른 복구 루트
RAG 정확도 급락을 복구하는 실전 우선순위는 보통 아래가 가장 효율적입니다.
- 오프라인 평가셋으로 Recall@k 하락을 확인(감이 아닌 숫자)
- 임베딩/거리함수/메타데이터/필터 불일치부터 제거
efSearch스윕으로 빠른 리콜 회복 시도- 그래도 부족하면 재색인 계획 수립(새 컬렉션 + 스위치오버)
- 재색인 시
M,efConstruction을 함께 재설계
핵심은 “LLM이 답을 못하는 문제”로 보기 전에, 검색이 정답 컨텍스트를 가져오고 있는지를 먼저 확인하는 것입니다. 벡터DB 재색인과 HNSW 튜닝은 비용이 큰 작업이지만, 올바른 평가와 절차를 갖추면 가장 확실하게 RAG 품질을 되살릴 수 있습니다.