프로덕션 RAG 시스템 구축하며 마주친 청크 사이즈 문제
배경
사내 기술 문서와 위키를 검색하는 RAG 시스템을 구축했다. OpenAI Embeddings API와 Pinecone을 사용했고, GPT-4를 통해 답변을 생성하는 구조였다.
초기에는 단순하게 1000자 단위로 문서를 split했는데, 검색 결과가 생각보다 좋지 않았다.
문제 상황
"배포 프로세스는 어떻게 되나요?" 같은 질문에 배포 섹션 중간부터 잘린 청크가 반환되거나, 앞뒤 맥락 없이 코드 블록만 덩그러니 나오는 경우가 많았다.
특히 마크다운 테이블이나 코드 블록이 청크 경계에서 잘리면서 구조가 깨지는 문제도 있었다.
해결 과정
1. 의미 단위 분할
고정 길이 대신 markdown 구조를 파싱해서 섹션 단위로 분할했다. ## 헤더를 기준으로 1차 split하고, 너무 길면 paragraph 단위로 추가 분할했다.
def chunk_by_section(content, max_size=800):
sections = re.split(r'\n#{1,3}\s', content)
chunks = []
for section in sections:
if len(section) <= max_size:
chunks.append(section)
else:
# paragraph 단위 추가 분할
paragraphs = section.split('\n\n')
current = ''
for p in paragraphs:
if len(current) + len(p) > max_size:
chunks.append(current)
current = p
else:
current += '\n\n' + p
if current:
chunks.append(current)
return chunks
2. 오버랩 추가
청크 간 100자 오버랩을 두어 경계에서 맥락이 끊기는 문제를 완화했다.
3. 메타데이터 보강
각 청크에 원본 문서 제목, 섹션 헤더, URL을 메타데이터로 추가했다. 검색 시 필터링에도 활용하고, LLM에게 출처를 명확히 전달할 수 있었다.
결과
검색 정확도가 체감상 크게 개선됐다. 특히 코드 예제나 절차적 설명이 포함된 질문에서 완전한 답변을 생성하는 비율이 높아졌다.
청크 사이즈는 결국 도메인과 문서 구조에 따라 다를 수밖에 없다는 걸 확인했다. 우리 케이스에서는 800자 + 섹션 단위 분할이 적절했다.