Skip to main content

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-basedLLM 요약추상적 질문, 개념 검색
Hypothetical Questions가상 질문Q&A 형태 검색
원본 + 요약 + 질문모두 벡터화최대 검색 범위 (비용 높음)

구현 시 고려사항

InMemoryStoreInMemoryByteStore는 프로세스 종료 시 데이터가 사라집니다. 프로덕션에서는 Redis, PostgreSQL 등 영속적 저장소를 사용하세요.
고려사항설명권장
저장소 영속성InMemory는 휘발성Redis/PostgreSQL 사용
인덱싱 비용Summary는 LLM 호출 필요대량 문서 시 비용 계산
검색 속도벡터 수 증가 → 속도 감소인덱스 최적화
일관성 유지원본 변경 시 요약도 업데이트업데이트 파이프라인 구축
시작 추천: 먼저 Parent Document Retriever로 검색 정밀도 향상을 확인하세요. 추가 개선이 필요하면 Summary-based Multi-vector를 적용합니다. 두 패턴 모두 기존 검색 파이프라인에 쉽게 통합할 수 있습니다.