상위-하위 및 작은 단위에서 큰 단위로 검색
정밀한 검색을 위해 작은 하위 청크를 저장하되, 더 풍부한 문맥을 제공하도록 더 큰 상위 청크를 LLM에 반환하여 검색 정밀도와 생성 품질의 균형을 맞춥니다.
상위-하위 및 작은 단위에서 큰 단위로 검색은(는) CoddyKit의 무료 AI Engineering Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Engineering Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
정밀도와 문맥 사이의 딜레마
RAG 시스템에는 다음과 같은 상충 관계가 있습니다. 작은 조각은 각 조각이 하나의 아이디어에 집중하므로 높은 정밀도로 검색되지만, LLM이 완전한 답변을 생성하는 데 필요한 주변 문맥이 부족합니다. 큰 조각은 풍부한 문맥을 제공하지만 하나의 질의와 강하게 일치하기보다는 여러 질의와 약하게 일치하므로 검색 정밀도가 낮아집니다. 상위-하위 분할은 이 딜레마를 해결합니다.
상위-하위 구조
상위-하위 분할에서는 동일한 문서에서 두 계층의 조각을 만듭니다. 하위 조각은 작고(예: 1~3문장) 검색을 위해 임베딩하고 색인합니다. 상위 조각은 더 큰 섹션(예: 전체 단락 또는 페이지)이며 별도로 저장합니다. 하위 조각이 검색되면 대신 해당 상위 조각을 LLM에 반환합니다.
조각 계층 구축
첫 단계는 문서를 큰 상위 조각으로 나눈 다음, 각 상위 조각을 더 작은 하위 조각으로 나누는 것입니다. 각 하위 조각은 일반적으로 parent_id 메타데이터 필드와 같은 참조를 유지해 자신의 상위 조각을 가리킵니다. 이 매핑을 사용하면 검색된 하위 조각만으로도 전체 상위 구절을 조회할 수 있습니다.
from langchain.text_splitter import RecursiveCharacterTextSplitter
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=1500, chunk_overlap=0)
child_splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=30)
parent_chunks = parent_splitter.split_documents(docs)
child_chunks = []
for i, parent in enumerate(parent_chunks):
children = child_splitter.split_documents([parent])
for child in children:
child.metadata['parent_id'] = i
child_chunks.extend(children)하위 조각만 색인하기
하위 조각만 임베딩해 벡터 데이터베이스에 저장합니다. 상위 조각은 별도의 키-값 저장소(메모리 내 사전, Redis 또는 문서 데이터베이스)에 저장합니다. 이렇게 하면 벡터 색인은 조밀하고 정밀하게 유지하면서 풍부한 문맥은 외부에 보관할 수 있습니다.
# Store parents in a docstore
parent_store = {i: chunk.page_content for i, chunk in enumerate(parent_chunks)}
# Embed and index only children
vectorstore = Chroma.from_documents(
child_chunks,
embedding=OpenAIEmbeddings()
)검색: 하위 조각 입력, 상위 조각 출력
검색 중에는 사용자 질의를 임베딩하고 하위 조각과 대조합니다. 상위 k개의 하위 조각을 찾은 다음, 해당 parent_id 참조를 상위 조각 저장소에서 조회해 확인합니다. 그런 다음 하위 조각이 아니라 상위 구절을 LLM 프롬프트에 삽입합니다. LLM은 넓은 문맥을 받고, 검색은 정밀하게 수행됩니다.
def retrieve_with_parents(query, vectorstore, parent_store, k=5):
child_results = vectorstore.similarity_search(query, k=k)
seen_parent_ids = set()
parent_contexts = []
for child in child_results:
pid = child.metadata['parent_id']
if pid not in seen_parent_ids:
parent_contexts.append(parent_store[pid])
seen_parent_ids.add(pid)
return parent_contextsLangChain ParentDocumentRetriever
LangChain은 이 패턴을 기본으로 구현한 ParentDocumentRetriever 클래스를 제공합니다. 상위 분할기, 하위 분할기, 하위 임베딩을 위한 벡터 저장소, 상위 문서를 위한 문서 저장소를 지정하면 됩니다. 이 클래스가 계층 구조를 구성하고 검색을 투명하게 처리합니다.
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
store = InMemoryStore()
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=child_splitter,
parent_splitter=parent_splitter
)
retriever.add_documents(docs)
results = retriever.invoke('What is the refund policy?')작은 단위에서 큰 단위로 검색하기
작은 단위에서 큰 단위로 검색하기는 동일한 개념을 가리키는 또 다른 이름입니다. 작고 정확한 조각을 검색한 다음, 이를 주변 맥락까지 확장하여 LLM에 전달합니다. 일부 구현에서는 고정된 상위 문서로 확장하는 대신 인접한 문장의 윈도우로 확장합니다. 이렇게 하면 일치한 조각의 앞뒤 문장을 모델에 제공하여 맥락의 연속성을 유지할 수 있습니다.
def retrieve_with_window(query, vectorstore, sentences, window=2, k=5):
results = vectorstore.similarity_search(query, k=k)
expanded = []
for r in results:
idx = r.metadata['sentence_index']
start = max(0, idx - window)
end = min(len(sentences), idx + window + 1)
expanded.append(' '.join(sentences[start:end]))
return expanded상위 조각 중복 제거
하나의 질의에 대해 동일한 상위 문서에서 나온 여러 하위 조각이 모두 검색될 수 있습니다. 중복을 제거하지 않으면 동일한 상위 문장이 프롬프트에 여러 번 나타나 토큰을 낭비하게 됩니다. 맥락을 구성하기 전에 항상 상위 ID를 기준으로 중복을 제거해야 합니다. 위의 retrieve 함수에 있는 코드 예제에서는 seen_parent_ids 집합을 사용하여 이를 처리합니다.
상위-하위 조각화를 사용해야 하는 경우
상위-하위 검색은 문서가 명확한 계층 구조를 가질 때 가장 효과적입니다. 예를 들어 장과 절로 구성된 문서, 문단으로 구성된 글, 하위 절이 있는 위키 등이 이에 해당합니다. 특히 정확한 질문에는 국소적인 답변이 필요하지만, 그 답변이 더 넓은 맥락의 절 안에서만 의미를 갖는 긴 기술 문서에서 매우 효과적입니다.
하위 및 상위 단위 크기 선택
일반적인 구성은 다음과 같습니다. 하위 조각은 200~400토큰(하나의 핵심 아이디어에 집중), 상위 조각은 1,000~2,000토큰(완전한 절)으로 설정합니다. 하위 조각이 너무 작으면 그 자체로는 의미가 부족한 개별 문장이 됩니다. 상위 조각이 너무 크면 처음에 피하려고 했던 맥락 희석 문제가 다시 나타나기 시작합니다.
접근 방식 비교: 요약
지금까지 살펴본 조각화 전략을 요약하면 다음과 같습니다. 고정 크기 조각화는 빠르지만 맥락을 끊고, 의미 기반 조각화는 주제의 일관성을 유지하며, 상위-하위 조각화는 검색 정확도와 LLM 맥락의 풍부함을 모두 최적화합니다. 긴 문서를 처리하는 대부분의 운영 RAG 시스템에서는 상위-하위 조각화가 관리 가능한 복잡도로 가장 우수한 검색 품질을 제공합니다.
빠른 확인
이 단원에서 배운 상위-하위 조각화에 대한 이해도를 확인해 보세요.
단원 복습
이 단원에서는 하위 조각이 정확한 검색을 제공하고 상위 조각이 풍부한 맥락을 제공한다는 점, LangChain의 ParentDocumentRetriever가 이를 자동으로 구현한다는 점, 그리고 상위 ID를 기준으로 중복을 제거하면 맥락이 반복되는 것을 방지할 수 있다는 점을 배웠습니다. 다음으로는 코드 파일과 HTML 문서에 맞는 문서별 조각화 전략을 살펴봅니다.
AI 튜터와 함께 Python을(를) 배우세요 — 무료
브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.
- 코스
- 30
- 레슨
- 120
자주 묻는 질문
“상위-하위 및 작은 단위에서 큰 단위로 검색” 강의는 무료인가요?
네 — “상위-하위 및 작은 단위에서 큰 단위로 검색” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Engineering Academy 강의 전체를 잠금 해제할 수 있습니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“상위-하위 및 작은 단위에서 큰 단위로 검색”에서 뭘 배우나요?
정밀한 검색을 위해 작은 하위 청크를 저장하되, 더 풍부한 문맥을 제공하도록 더 큰 상위 청크를 LLM에 반환하여 검색 정밀도와 생성 품질의 균형을 맞춥니다. 브라우저에서 직접 실행하는 실습 코드로 AI Engineering Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
AI Engineering Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 AI Engineering Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“상위-하위 및 작은 단위에서 큰 단위로 검색” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 AI Engineering Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 AI Engineering Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 순진한 청킹이 검색에 해로운 이유
- 임베딩 유사도를 활용한 의미 기반 청킹
- 상위-하위 및 작은 단위에서 큰 단위로 검색
- 코드와 HTML을 위한 문서별 전략