본문 바로가기
AI2026년 4월 21일7분 읽기

RAG 시스템 한국어 청킹 전략 — kss·KoNLPy + Late Chunking 실전

YS
김영삼
조회 1
RAG 시스템 한국어 청킹 전략 — kss·KoNLPy + Late Chunking 실전

핵심 요약

한국어 RAG에서 영어용 default 청킹(500자 + 100자 overlap)을 그대로 쓰면 recall이 낮다. 한국어는 조사·복합어·문장 길이가 영어와 달라 문장 단위 분리가 핵심. kss + Late Chunking 조합이 현재 가장 강력한 baseline.

  • 한국어 default 청킹 recall@10: 0.65~0.72
  • kss + recursive: 0.78~0.85
  • kss + Late Chunking: 0.88~0.93
  • + rerank (bge-reranker-v2-m3): 0.94+

1. 청킹 방식 4종 비교

방식특징recall@10
고정 길이 (500자)가장 단순0.65
Recursive (langchain default)구분자 우선순위0.74
kss 문장 + 그룹한국어 문장 경계0.84
Late Chunking전체 임베딩 후 청크0.91

2. kss 기반 한국어 문장 분리

import kss

text = "첫 문장입니다. 두 번째 문장이에요. 세 번째 문장!"
sentences = kss.split_sentences(text)
# ["첫 문장입니다.", "두 번째 문장이에요.", "세 번째 문장!"]

kss는 한국어 문장 경계 (마침표·물음표·느낌표 + 종결어미)를 정확히 인식. 영어 punctuation 기반 분리는 약 30% 오류율, kss는 5% 미만.

3. 청크 그룹화 전략

def group_sentences(sentences, target_size=400, overlap=80):
    chunks = []
    current = []
    current_size = 0

    for sent in sentences:
        sent_size = len(sent)
        if current_size + sent_size > target_size and current:
            chunks.append(''.join(current))
            # overlap: 마지막 N자
            tail = ''.join(current)[-overlap:]
            current = [tail, sent]
            current_size = len(tail) + sent_size
        else:
            current.append(sent)
            current_size += sent_size

    if current:
        chunks.append(''.join(current))
    return chunks

4. Late Chunking — 가장 효과 큼

Late Chunking은 (1) 전체 문서를 통째 임베딩 모델에 입력, (2) 토큰 단위 임베딩을 받아서, (3) 청크 단위로 평균. 각 청크가 문서 전체 컨텍스트를 가진 임베딩이 됨.

from FlagEmbedding import BGEM3FlagModel
import numpy as np

model = BGEM3FlagModel('BAAI/bge-m3')

def late_chunk_embed(text, chunk_boundaries):
    # 1) 전체 문서 토큰 임베딩
    output = model.encode([text], return_token_embeddings=True)
    token_emb = output['token_embeddings'][0]

    # 2) 청크별 토큰 범위 평균
    chunk_embeddings = []
    for start, end in chunk_boundaries:
        chunk_emb = token_emb[start:end].mean(axis=0)
        chunk_embeddings.append(chunk_emb)
    return chunk_embeddings

5. Hybrid Search — Dense + Sparse

한국어처럼 복합어·조사가 많은 언어는 sparse 검색(BM25)도 강력. bge-m3는 dense·sparse 동시 출력.

# 1단계: dense + sparse hybrid → top 100
# 2단계: bge-reranker-v2-m3로 cross-encoder → top 10

results = hybrid_search(query, top_k=100, alpha=0.6)
reranked = reranker.rerank(query, results, top_k=10)

이 조합이 한국어 RAG에서 현재 가장 강력한 baseline (recall@10 = 0.94+).

6. chunk size 결정

유형권장 chunk sizeoverlap
FAQ·짧은 답변300자50자
기술 문서500~800자100~150자
장문 (논문·책)1000~1500자200자
코드함수 단위0 (overlap 불필요)

7. 메타데이터 추가

각 청크에 메타데이터 함께 저장하면 필터링·정렬이 가능.

{
  "text": "...",
  "embedding": [0.1, 0.2, ...],
  "metadata": {
    "document_id": "doc_123",
    "section": "3.2 인증",
    "chunk_index": 5,
    "page": 42,
    "updated_at": "2026-04-20"
  }
}

실측 사례

사내 기술 문서 5,000건 RAG. 영어 default 청킹 → kss + Late Chunking + rerank 전환:

지표beforeafter
recall@100.680.94
사용자 만족도3.2/54.6/5
지연시간340ms520ms
임베딩 비용1x1.2x

자주 묻는 질문

kss 외 대안은?

KoNLPy.Komoran 또는 mecab-ko도 사용 가능. kss가 가장 가벼움. 정확도는 비슷.

Late Chunking이 항상 좋은가?

장문에서 효과 크다. FAQ처럼 짧은 단위는 일반 chunking으로 충분.

chunk overlap이 정말 필요한가?

한국어는 의미 단위가 길어 overlap 100~150자가 정확도에 의미 있는 영향. 코드는 함수 단위면 overlap 불필요.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록