RAG 시스템의 Context Window 최적화 - 청크 크기와 검색 정확도 트레이드오프

문제 상황

사내 문서 검색 RAG 시스템에서 사용자들이 "관련 없는 내용이 포함된다"는 피드백이 계속 들어왔다. 특히 긴 문서에서 특정 정보를 물어볼 때 주변 context까지 함께 가져와 LLM이 혼란스러워하는 경우가 많았다.

기존 설정은 청크 크기 512 토큰, 오버랩 50 토큰이었다.

실험 과정

청크 크기를 256, 384, 512, 768 토큰으로 나눠 각각 테스트했다. 평가 지표는 검색 정확도(top-3 recall), 응답 관련성, 응답 생성 시간이었다.

def create_chunks(text, chunk_size=256, overlap=50):
    tokens = tokenizer.encode(text)
    chunks = []
    
    for i in range(0, len(tokens), chunk_size - overlap):
        chunk = tokens[i:i + chunk_size]
        chunks.append(tokenizer.decode(chunk))
    
    return chunks

100개의 테스트 쿼리로 측정한 결과:

  • 256 토큰: recall 0.82, 관련성 4.2/5, 평균 1.8초
  • 384 토큰: recall 0.79, 관련성 4.0/5, 평균 1.5초
  • 512 토큰: recall 0.71, 관련성 3.6/5, 평균 1.3초
  • 768 토큰: recall 0.68, 관련성 3.4/5, 평균 1.2초

결론

청크를 작게 나눌수록 검색 정확도는 올라가지만, 검색해야 할 벡터 수가 증가해 레이턴시가 늘어난다. 우리 시스템은 정확도를 우선시해 256 토큰을 선택했고, 대신 인덱스를 HNSW에서 IVF-PQ로 변경해 속도를 보완했다.

오버랩은 50에서 30으로 줄였다. 테스트 결과 큰 차이가 없었고, 저장 공간을 20% 절약할 수 있었다.

추가 개선

문서 타입별로 청크 크기를 다르게 가져가는 방법도 고려 중이다. API 문서는 128 토큰으로, 설계 문서는 384 토큰으로 분리하면 더 나은 결과를 얻을 수 있을 것 같다.