RAG에서 텍스트 분할(Text Splitting)이 중요한 이유
RAG(Retrieval-Augmented Generation) 시스템은 외부 문서에서 정보를 검색해 생성 모델에게 전달하는 방식으로 작동합니다. 이때, 검색 정확도와 응답 품질을 결정짓는 핵심 요소 중 하나가 바로 "텍스트 분할"입니다.
텍스트 분할은 긴 문서를 적절한 길이의 청크로 나누는 과정인데, 이를 잘못하면 의미가 끊기거나 중요한 문맥이 누락되면서 검색 및 생성 성능이 급격히 떨어질 수 있습니다. 특히 LLM이 참조할 수 있는 토큰 수가 제한되어 있기 때문에, 적절한 길이, 문맥 보존, 중복 최소화가 매우 중요합니다.
Text Splitter의 다양한 전략
LangChain에서는 다양한 형태의 텍스트 분할기를 제공하며, 각각의 splitter는 특화된 목적을 갖고 있습니다. 아래는 자주 사용되는 splitter들과 함께 그 목적, 특징, 그리고 예시 코드입니다.
1. CharacterTextSplitter
가장 단순한 방식으로, 지정한 문자 수를 기준으로 텍스트를 나눕니다. 단어 경계를 고려하지 않기 때문에 문맥 단절이 발생할 수 있습니다.
from langchain.text_splitter import CharacterTextSplitter
splitter = CharacterTextSplitter(separator="\n", chunk_size=500, chunk_overlap=50)
docs = splitter.split_text(long_text)
2. RecursiveCharacterTextSplitter
여러 구분자(예: 문단 → 문장 → 단어 등)를 재귀적으로 적용하여 가장 자연스러운 위치에서 분할합니다. 문맥을 최대한 보존하면서도 원하는 chunk 크기를 맞추기 위해 널리 사용됩니다.
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
separators=["\n\n", "\n", ".", " ", ""],
chunk_size=500,
chunk_overlap=50
)
docs = splitter.split_text(long_text)
3. TokenTextSplitter
OpenAI의 tokenizer(BPE 기반)를 활용하여 실제 토큰 단위로 분할합니다. 모델에 들어가는 입력 크기를 정밀하게 제어하고 싶을 때 유용합니다.
from langchain.text_splitter import TokenTextSplitter
splitter = TokenTextSplitter(chunk_size=200, chunk_overlap=20)
docs = splitter.split_text(long_text)
4. SemanticChunker
텍스트의 의미적 구조를 기반으로 청크를 나눕니다. LLM을 활용해 문맥 흐름을 고려한 청크를 만들기 때문에 가장 자연스러운 결과를 낼 수 있지만 속도가 느릴 수 있습니다.
from langchain_experimental.text_splitter import SemanticChunker
from langchain.embeddings import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()
chunker = SemanticChunker(embeddings)
docs = chunker.split_text(long_text)
5. CodeSplitter
Python, JavaScript 등 코드 파일을 문법 단위(함수, 클래스 등)로 분할합니다. 코드 기반 문서를 처리하는 데 특화되어 있어 RAG 기반 코드 문서 검색 시스템에 적합합니다.
from langchain.text_splitter import PythonCodeTextSplitter
splitter = PythonCodeTextSplitter(chunk_size=50, chunk_overlap=10)
docs = splitter.split_text(code_string)
6. MarkdownHeaderTextSplitter
Markdown 문서의 헤더(#, ##, ### 등)를 기준으로 청크를 나눕니다. 구조화된 문서나 위계 구조가 있는 글을 분석할 때 매우 효과적입니다.
from langchain.text_splitter import MarkdownHeaderTextSplitter
headers = [("#", "H1"), ("##", "H2"), ("###", "H3")]
splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers)
docs = splitter.split_text(markdown_string)
7. HTMLHeaderTextSplitter
HTML 문서에서 <h1>, <h2> 등의 헤더 태그를 기준으로 분할합니다. 웹 문서나 크롤링한 데이터를 구조적으로 나눌 때 유용합니다.
from langchain.text_splitter import HTMLHeaderTextSplitter
headers = [("h1", "H1"), ("h2", "H2"), ("h3", "H3")]
splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers)
docs = splitter.split_text(html_string)
8. RecursiveJSONSplitter
중첩된 JSON 데이터를 field 단위로 재귀적으로 분석하고 분할합니다. 대규모 JSON 기반 로그나 API 응답 데이터를 다룰 때 매우 효과적입니다.
from langchain.text_splitter import RecursiveJsonSplitter
splitter = RecursiveJsonSplitter(max_chunk_size=500, max_overlap=50)
docs = splitter.split_json(json_data)
어떤 splitter를 써야 할까?
어떤 텍스트 분할기를 선택할지는 목적과 데이터 종류에 따라 달라집니다:
- 일반 문서:
RecursiveCharacterTextSplitter
추천 - Markdown/HTML:
MarkdownHeaderTextSplitter
,HTMLHeaderTextSplitter
- 코드 기반:
PythonCodeTextSplitter
또는CodeSplitter
- 정확한 토큰 제어:
TokenTextSplitter
- 의미 기반 분할:
SemanticChunker
- 복잡한 JSON 구조:
RecursiveJsonSplitter
적절한 splitter를 고르는 것만으로도 검색 효율성과 생성 품질을 큰 폭으로 개선할 수 있습니다.
'AI > RAG' 카테고리의 다른 글
임베딩(Embeddings)이란 무엇인가요? (1) | 2025.05.02 |
---|---|
RAG 시스템의 핵심 단계: 텍스트 분할(Text Splitter)이란? (0) | 2025.04.01 |
RAG 시스템의 핵심, 검색기(Retriever)란? (1) | 2025.04.01 |
RAG(Retrieval Augmented Generation)를 사용해야 하는 이유와 장점 (0) | 2025.03.28 |