"""
LangChain RecursiveCharacterTextSplitter 완전 가이드
각 옵션의 역할과 우선순위를 예시로 설명
핵심 개념:
1. Separator (구분자): 텍스트를 나누는 기준점들의 우선순위 리스트
2. Chunk Size: 각 청크의 최대 크기
3. Overlap: 청크 간 중복되는 부분의 크기
4. is_separator_regex: 구분자를 정규식으로 처리할지 여부
"""
from langchain.text_splitter import RecursiveCharacterTextSplitter
import re
print("=" * 60)
print("1. 기본 RecursiveCharacterTextSplitter 동작 원리")
print("=" * 60)
sample_text = """
안녕하세요! 이것은 첫 번째 문단입니다.
여러 줄로 구성되어 있습니다.
이것은 두 번째 문단입니다.
AI와 머신러닝에 대해 설명하겠습니다.
세 번째 문단에서는 딥러닝을 다룹니다.
신경망과 역전파에 대해 알아봅시다.
마지막 문단입니다.
"""
basic_splitter = RecursiveCharacterTextSplitter(
chunk_size=50,
chunk_overlap=10,
)
chunks = basic_splitter.split_text(sample_text)
print(f"총 청크 수: {len(chunks)}")
for i, chunk in enumerate(chunks):
print(f"청크 {i+1} (길이: {len(chunk)}): {repr(chunk)}")
print("\n" + "="*60)
print("2. Separator 우선순위 시스템")
print("="*60)
default_separators = [
"\n\n",
"\n",
" ",
""
]
print("기본 구분자 우선순위:")
for i, sep in enumerate(default_separators, 1):
print(f"{i}순위: {repr(sep)}")
print("\n" + "="*60)
print("3. Chunk Size와 Overlap 상세 동작")
print("="*60)
text_for_chunking = """첫 번째 문장입니다. 두 번째 문장입니다. 세 번째 문장입니다. 네 번째 문장입니다. 다섯 번째 문장입니다."""
configs = [
{"chunk_size": 30, "chunk_overlap": 0, "desc": "overlap 없음"},
{"chunk_size": 30, "chunk_overlap": 10, "desc": "overlap 있음"},
{"chunk_size": 50, "chunk_overlap": 15, "desc": "큰 청크, 큰 overlap"}
]
for config in configs:
print(f"\n--- {config['desc']} (size={config['chunk_size']}, overlap={config['chunk_overlap']}) ---")
splitter = RecursiveCharacterTextSplitter(
chunk_size=config['chunk_size'],
chunk_overlap=config['chunk_overlap']
)
chunks = splitter.split_text(text_for_chunking)
for i, chunk in enumerate(chunks):
print(f"청크 {i+1}: {repr(chunk)} (길이: {len(chunk)})")
if len(chunks) > 1:
for i in range(len(chunks)-1):
current = chunks[i]
next_chunk = chunks[i+1]
overlap_found = ""
for j in range(1, min(len(current), len(next_chunk)) + 1):
if current[-j:] == next_chunk[:j]:
overlap_found = current[-j:]
if overlap_found:
print(f" → 청크 {i+1}과 {i+2} 간 실제 겹침: {repr(overlap_found)}")
print("\n" + "="*60)
print("4. Custom Separator 사용법")
print("="*60)
special_text = """제목1###내용1입니다. 여러 줄에 걸쳐 작성됩니다.
추가 내용도 있습니다.###제목2###내용2입니다. 이것도 긴 내용입니다.
더 많은 정보를 포함합니다.###제목3###마지막 내용입니다."""
custom_splitter = RecursiveCharacterTextSplitter(
separators=["###", "\n", " ", ""],
chunk_size=50,
chunk_overlap=5
)
chunks = custom_splitter.split_text(special_text)
print("커스텀 구분자 결과:")
for i, chunk in enumerate(chunks):
print(f"청크 {i+1}: {repr(chunk)}")
print("\n" + "="*60)
print("5. is_separator_regex 옵션")
print("="*60)
regex_text = """섹션1: 내용입니다.
섹션2: 다른 내용입니다.
섹션3: 마지막 내용입니다.
Chapter1: 첫 번째 챕터
Chapter2: 두 번째 챕터"""
regex_splitter = RecursiveCharacterTextSplitter(
separators=[r"섹션\d+:", r"Chapter\d+:", "\n", " "],
chunk_size=40,
chunk_overlap=5,
is_separator_regex=True
)
chunks = regex_splitter.split_text(regex_text)
print("정규식 구분자 결과:")
for i, chunk in enumerate(chunks):
print(f"청크 {i+1}: {repr(chunk)}")
normal_splitter = RecursiveCharacterTextSplitter(
separators=[r"섹션\d+:", r"Chapter\d+:", "\n", " "],
chunk_size=40,
chunk_overlap=5,
is_separator_regex=False
)
chunks_normal = normal_splitter.split_text(regex_text)
print("\n정규식 미사용 결과:")
for i, chunk in enumerate(chunks_normal):
print(f"청크 {i+1}: {repr(chunk)}")
print("\n" + "="*60)
print("6. 실무 예제: Python 코드 분할")
print("="*60)
python_code = '''def function1():
"""첫 번째 함수"""
print("Hello")
return True
def function2():
"""두 번째 함수"""
for i in range(5):
print(i)
return False
class MyClass:
"""클래스 예제"""
def __init__(self):
self.value = 0
def method1(self):
return self.value
'''
code_splitter = RecursiveCharacterTextSplitter(
separators=[
"\n\nclass ",
"\n\ndef ",
"\n\n",
"\n",
" ",
""
],
chunk_size=100,
chunk_overlap=10,
is_separator_regex=False
)
chunks = code_splitter.split_text(python_code)
print("Python 코드 분할 결과:")
for i, chunk in enumerate(chunks):
print(f"청크 {i+1}:\n{chunk}")
print("-" * 30)
print("\n" + "="*60)
print("7. 길이 함수 커스터마이징 (토큰 기반)")
print("="*60)
def token_length_function(text: str) -> int:
"""간단한 토큰 길이 계산 (실제로는 tiktoken 등 사용)"""
return len(text.split()) + text.count('.') + text.count(',')
token_splitter = RecursiveCharacterTextSplitter(
chunk_size=10,
chunk_overlap=2,
length_function=token_length_function,
separators=["\n\n", "\n", ". ", " ", ""]
)
test_text = "이것은 테스트 문장입니다. 토큰 기반으로 분할됩니다. 각 단어와 구두점이 토큰으로 계산됩니다."
chunks = token_splitter.split_text(test_text)
print("토큰 기반 분할 결과:")
for i, chunk in enumerate(chunks):
token_count = token_length_function(chunk)
print(f"청크 {i+1} (토큰 {token_count}개): {repr(chunk)}")
print("\n" + "="*60)
print("8. RecursiveCharacterTextSplitter 동작 순서")
print("="*60)
print("""
동작 순서:
1. separators 리스트의 첫 번째 구분자로 텍스트 분할 시도
2. 분할된 각 부분이 chunk_size보다 작으면 완료
3. 크면 다음 우선순위 구분자로 재귀적 분할
4. 모든 구분자로 분할해도 크면 강제로 chunk_size만큼 자름
5. chunk_overlap만큼 겹치게 청크 구성
우선순위:
1. separators[0] > separators[1] > ... > separators[-1]
2. chunk_size는 절대적 제한
3. chunk_overlap은 chunk_size 내에서 동작
4. length_function으로 길이 계산 방식 변경 가능
""")
print("\n" + "="*60)
print("9. 실제 사용 시 권장 설정")
print("="*60)
recommended_configs = {
"일반 텍스트": {
"separators": ["\n\n", "\n", ". ", " ", ""],
"chunk_size": 1000,
"chunk_overlap": 100
},
"코드 분할": {
"separators": ["\n\nclass ", "\n\ndef ", "\n\n", "\n", " ", ""],
"chunk_size": 500,
"chunk_overlap": 50
},
"마크다운": {
"separators": ["\n## ", "\n### ", "\n\n", "\n", " ", ""],
"chunk_size": 800,
"chunk_overlap": 80
},
"대화형 텍스트": {
"separators": ["\n\n", "\n", ". ", "! ", "? ", " ", ""],
"chunk_size": 600,
"chunk_overlap": 60
}
}
for use_case, config in recommended_configs.items():
print(f"\n{use_case} 권장 설정:")
for key, value in config.items():
print(f" {key}: {value}")
print("\n" + "="*60)
print("마무리: 핵심 포인트")
print("="*60)
print("""
핵심 포인트:
1. separators는 우선순위 리스트 - 앞쪽이 우선순위 높음
2. chunk_size는 최대 크기 제한 - 이보다 크면 강제 분할
3. chunk_overlap은 문맥 유지를 위한 겹침 - 너무 크면 비효율
4. is_separator_regex=True면 정규식 패턴 사용 가능
5. length_function으로 토큰 기반 계산 등 커스터마이징 가능
실무 팁:
- chunk_overlap은 보통 chunk_size의 10-20% 추천
- 구분자는 의미적 경계를 우선으로 설정
- 정규식은 복잡한 패턴이 필요할 때만 사용
- 용도에 맞게 구분자와 크기 조정 필수
""")