프로덕션 RAG 시스템에서 청킹 전략 개선 실험

문제 상황

사내 문서 검색 RAG 시스템을 운영하던 중 사용자 피드백으로 검색 정확도 이슈가 계속 제기됐다. 특히 긴 문서에서 특정 섹션을 찾지 못하거나, 문맥이 잘린 답변이 자주 발생했다.

기존 시스템은 500토큰 고정 크기로 청킹하고 100토큰 오버랩을 적용하고 있었다. 단순하지만 문서 구조를 무시해 문맥이 끊기는 문제가 많았다.

청킹 전략 실험

세 가지 접근법을 테스트했다.

1. 문단 기반 청킹

def paragraph_chunking(text, max_tokens=500):
    paragraphs = text.split('\n\n')
    chunks = []
    current_chunk = []
    current_size = 0
    
    for para in paragraphs:
        para_tokens = len(tokenizer.encode(para))
        if current_size + para_tokens > max_tokens and current_chunk:
            chunks.append('\n\n'.join(current_chunk))
            current_chunk = [para]
            current_size = para_tokens
        else:
            current_chunk.append(para)
            current_size += para_tokens
    
    return chunks

문단 단위로 자르되 토큰 제한을 넘지 않도록 했다. 문맥 보존은 개선됐지만 문단이 너무 긴 경우 여전히 문제가 있었다.

2. 헤더 인식 청킹

마크다운 헤더를 기준으로 섹션을 구분하고, 각 청크에 상위 헤더 정보를 메타데이터로 추가했다.

def header_aware_chunking(markdown_text):
    sections = split_by_headers(markdown_text)
    chunks = []
    
    for section in sections:
        header_path = section['header_path']  # ['## 개요', '### 기술 스택']
        content = section['content']
        
        chunk = {
            'text': f"{' > '.join(header_path)}\n\n{content}",
            'metadata': {'headers': header_path}
        }
        chunks.append(chunk)
    
    return chunks

검색 시 헤더 정보가 컨텍스트로 포함돼 정확도가 크게 개선됐다.

3. 오버랩 최적화

기존 100토큰 고정 오버랩을 문장 단위 오버랩으로 변경했다. 청크 끝의 마지막 2문장을 다음 청크 시작 부분에 포함시켰다.

결과

테스트 데이터셋 500개 질문으로 평가했다.

  • 기존 시스템: 검색 성공률 62%
  • 문단 기반: 71%
  • 헤더 인식: 84%
  • 헤더 인식 + 문장 오버랩: 84%

헤더 인식 청킹을 프로덕션에 적용했고, 사용자 만족도도 눈에 띄게 개선됐다. 특히 기술 문서처럼 구조화된 문서에서 효과가 컸다.

추가 개선 사항

임베딩 모델은 여전히 OpenAI text-embedding-3-large를 사용 중이다. 추후 도메인 특화 파인튜닝도 고려해볼 만하다.