Multi-vector Retrieval
하나의 문서에 대해 여러 개의 벡터를 생성하여 검색 성능을 높이는 전략입니다. 작은 청크로 정밀하게 검색하되, 더 넓은 문맥을 반환할 수 있습니다.
Parent Document Retriever
작은 청크로 검색하되, 더 큰 부모 문서를 반환하는 패턴입니다.
핵심 아이디어: 검색은 작은 청크로 정밀하게, 반환은 큰 청크로 문맥을 풍부하게.
코드 예제
from langchain.retrievers import ParentDocumentRetriever
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain.storage import InMemoryStore
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma(collection_name="child_chunks", embedding_function=embeddings)
# 작은 청크 (검색용)
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
# 큰 청크 (반환용)
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
store = InMemoryStore()
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=child_splitter,
parent_splitter=parent_splitter,
)
# 문서 추가
retriever.add_documents(docs)
# 검색: 작은 청크로 매칭 → 부모 문서 반환
results = retriever.invoke("RAG 검색 전략")
# → 2000자 단위의 부모 청크가 반환됨
파라미터 가이드
| 파라미터 | 역할 | 권장 값 |
|---|
| child chunk_size | 검색 정밀도 결정 | 200~500자 |
| parent chunk_size | 반환 문맥 크기 결정 | 1000~3000자 |
| 비율 (parent/child) | 검색-문맥 균형 | 3~5배 |
장단점
| 장점 | 단점 |
|---|
| 검색 정밀도와 문맥 풍부함 동시 달성 | InMemoryStore 사용 시 영속성 없음 |
| 검색용/반환용 청크 크기 독립 조정 | 인덱싱 시간 증가 |
| 작은 청크로 정확한 매칭 가능 | 저장 공간 증가 |
Multi-vector with Summary
원본 문서와 LLM 요약을 모두 벡터화하여, 다양한 관점에서 검색합니다.
코드 예제
from langchain.chat_models import init_chat_model
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain.retrievers.multi_vector import MultiVectorRetriever
from langchain.storage import InMemoryByteStore
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
import uuid
llm = init_chat_model("gpt-4o-mini", temperature=0)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma(collection_name="summaries", embedding_function=embeddings)
store = InMemoryByteStore()
id_key = "doc_id"
retriever = MultiVectorRetriever(
vectorstore=vectorstore,
byte_store=store,
id_key=id_key,
)
# 요약 생성 프롬프트
summary_prompt = ChatPromptTemplate.from_messages([
("system", "다음 문서의 핵심 내용을 2-3문장으로 요약하세요."),
("human", "{text}"),
])
summary_chain = summary_prompt | llm | StrOutputParser()
# 각 문서에 대해 요약 생성 + 저장
doc_ids = [str(uuid.uuid4()) for _ in chunks]
summaries = []
for chunk in chunks:
summary = summary_chain.invoke({"text": chunk.page_content})
summaries.append(summary)
# 요약 문서 생성 (doc_id로 원본과 연결)
from langchain_core.documents import Document
summary_docs = [
Document(page_content=summary, metadata={id_key: doc_ids[i]})
for i, summary in enumerate(summaries)
]
# 벡터 DB에 요약 저장
retriever.vectorstore.add_documents(summary_docs)
# 원본 문서를 docstore에 저장
retriever.docstore.mset(list(zip(doc_ids, chunks)))
# 검색: 요약으로 매칭 → 원본 문서 반환
results = retriever.invoke("RAG 검색 전략")
Multi-vector 변형 패턴
| 패턴 | 벡터화 대상 | 적합한 경우 |
|---|
| Parent Document | 작은 자식 청크 | 긴 문서, 정밀 검색 |
| Summary-based | LLM 요약 | 추상적 질문, 개념 검색 |
| Hypothetical Questions | 가상 질문 | Q&A 형태 검색 |
| 원본 + 요약 + 질문 | 모두 벡터화 | 최대 검색 범위 (비용 높음) |
구현 시 고려사항
InMemoryStore와 InMemoryByteStore는 프로세스 종료 시 데이터가 사라집니다. 프로덕션에서는 Redis, PostgreSQL 등 영속적 저장소를 사용하세요.
| 고려사항 | 설명 | 권장 |
|---|
| 저장소 영속성 | InMemory는 휘발성 | Redis/PostgreSQL 사용 |
| 인덱싱 비용 | Summary는 LLM 호출 필요 | 대량 문서 시 비용 계산 |
| 검색 속도 | 벡터 수 증가 → 속도 감소 | 인덱스 최적화 |
| 일관성 유지 | 원본 변경 시 요약도 업데이트 | 업데이트 파이프라인 구축 |
시작 추천: 먼저 Parent Document Retriever로 검색 정밀도 향상을 확인하세요. 추가 개선이 필요하면 Summary-based Multi-vector를 적용합니다. 두 패턴 모두 기존 검색 파이프라인에 쉽게 통합할 수 있습니다.