RAG 시스템 구축하며 마주친 청크 사이즈 문제
배경
사내 기술 문서와 API 스펙을 검색하는 시스템을 개선하는 작업을 맡았다. 기존 Elasticsearch 기반 키워드 검색으로는 자연어 질의 처리가 어려워, OpenAI Embeddings와 Pinecone을 활용한 RAG 시스템으로 전환을 진행했다.
문제 상황
초기에는 LangChain의 기본 설정(chunk_size=1000, overlap=200)을 그대로 사용했는데, 검색 결과가 생각보다 좋지 않았다. 특히 긴 API 가이드 문서에서 필요한 부분을 찾지 못하는 경우가 많았다.
실험 과정
청크 사이즈를 500, 1000, 2000으로 나눠서 50개 질의로 테스트했다.
from langchain.text_splitter import RecursiveCharacterTextSplitter
def create_splitter(chunk_size, overlap):
return RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=overlap,
separators=["\n\n", "\n", ". ", " ", ""]
)
# 각 사이즈별 임베딩 생성
for size in [500, 1000, 2000]:
splitter = create_splitter(size, int(size * 0.2))
chunks = splitter.split_documents(docs)
결과적으로 500자는 정확한 문장을 찾지만 앞뒤 맥락이 없어 LLM이 답변을 잘못 생성했고, 2000자는 관련 없는 내용까지 포함돼 혼란을 줬다.
해결
최종적으로 800자로 설정하고, overlap을 150자로 조정했다. 추가로 문서 타입별로 다른 전략을 적용했다.
- API 레퍼런스: 함수 단위로 분할 (평균 600자)
- 가이드 문서: 섹션 단위 분할 (평균 1000자)
- 릴리즈 노트: 버전별 분할 (평균 400자)
def split_by_doc_type(doc, doc_type):
if doc_type == 'api':
return split_by_function(doc)
elif doc_type == 'guide':
return split_by_section(doc)
else:
return default_splitter.split_text(doc)
검색 품질이 체감상 30% 정도 개선됐고, 특히 복잡한 질의에 대한 답변 정확도가 높아졌다.
교훈
청크 사이즈는 정답이 없다. 문서 특성과 질의 유형에 따라 실험이 필요하다. 메타데이터를 잘 활용하면 하이브리드 전략도 가능하다.