프로덕션 RAG 시스템의 청크 크기 최적화 과정
문제 상황
사내 문서 검색 시스템에서 사용자들이 "특정 정책의 예외 조항"처럼 맥락이 중요한 질문에 대해 관련 없는 답변을 받는다는 피드백이 반복됐다. 로그를 확인하니 retrieval 단계에서 이미 잘못된 문서 청크를 가져오고 있었다.
기존에는 512 토큰 고정 크기로 문서를 분할했는데, 문단 중간에서 잘리는 경우가 많았고, 이로 인해 embedding 품질이 떨어졌다.
청크 전략 실험
세 가지 접근을 테스트했다:
1. 토큰 크기 조정 (256, 512, 1024)
- 256: recall은 높지만 노이즈 증가
- 1024: 맥락은 좋으나 irrelevant 정보 포함
- 결론: 크기만 바꿔서는 근본적 해결 안 됨
2. Semantic chunking
- LangChain의 SemanticChunker 활용
- 문장 간 embedding 유사도 기반 분할
- 문단의 의미 단위를 유지하면서 청크 생성
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai import OpenAIEmbeddings
chunker = SemanticChunker(
OpenAIEmbeddings(),
breakpoint_threshold_type="percentile",
breakpoint_threshold_amount=75
)
chunks = chunker.create_documents([document])
3. Hierarchical chunking
- 큰 청크(부모)와 작은 청크(자식) 동시 저장
- 검색은 작은 청크로, 컨텍스트는 부모 청크 전달
- 구현 복잡도 대비 효과가 크지 않아 보류
적용 결과
Semantic chunking + 오버랩 50 토큰으로 전환 후:
- Recall@5: 0.67 → 0.82
- 평균 청크 크기: 512 → 387 (가변)
- 사용자 만족도 설문 긍정 응답: 61% → 84%
특히 정책 문서처럼 "단, 다음의 경우는 예외로 한다" 같은 조건절이 많은 문서에서 개선이 두드러졌다.
추가 고려사항
- 비용: OpenAI embedding API 호출이 증가했지만, 청크 수 자체는 비슷해서 월 $40 정도 증가에 그쳤다
- 재색인: 전체 문서 3만 건 재처리에 약 6시간 소요, 주말에 배포
- 모니터링: Langfuse로 retrieval 품질 메트릭 추적 중
결국 "의미 있는 단위"로 자르는 게 고정 크기보다 훨씬 효과적이었다. RAG 시스템 구축 시 청킹 전략은 초기부터 신중하게 설계할 필요가 있다.