63일차 검색기 Retriever

차지예·2025년 8월 18일

생성AI

목록 보기
56/56
post-thumbnail

RAG 리트리버 11종 정리: 개념 → 동작 흐름 → 사용 시점


TL-DR

  • 기본은 VectorStoreRetriever로 시작하고,
  • 한국어 키워드 매칭 보완은 Kiwi-BM25Ensemble,
  • 질문이 애매하면 MultiQuery,
  • 긴 문서 맥락은 ParentDocument,
  • 토큰 절약은 ContextualCompression(+ 필요시 LongContextReorder),
  • 메타데이터 조건 검색은 SelfQuery,
  • 최신성 가중은 TimeWeighted,
  • 문서 하나에 다양한 표현 인덱싱은 MultiVector.

공통 전제

  • 예시 코드는 LangChain (Python) 기준이며, 실제 프로젝트에서는 사용하는 벡터DB/임베딩/LLM에 맞게 바꿔야 한다.
  • k(상위 반환 개수), 임베딩 모델, 토크나이저(한국어)는 성능에 큰 영향을 준다.

1) VectorStoreRetriever (기본형)

아이디어

  • 쿼리 임베딩 ↔ 문서 임베딩의 유사도(코사인 등)로 Top-k 최근접 이웃을 반환하는 표준 방식.

동작 흐름
1. embed(query) → 쿼리 벡터 q
2. 벡터DB에서 q와 가까운 k개 검색
3. 그대로 반환

언제 쓰나 (장단/주의)

  • ✅ 빠르고 단순. 대부분의 RAG의 시작점.
  • ⚠️ 어휘/표현 차이가 큰 질의에선 리콜이 낮을 수 있음.

코드 스니펫

from langchain_community.vectorstores import FAISS
from langchain.embeddings import HuggingFaceEmbeddings

vs = FAISS.from_texts(texts, HuggingFaceEmbeddings())
retriever = vs.as_retriever(search_kwargs={"k": 5})
docs = retriever.get_relevant_documents("질문")

2) ContextualCompressionRetriever

아이디어

  • “먼저 넉넉히 가져오고 → 쿼리와 무관/군더더기 부분을 압축”해서 컨텍스트 길이를 절약.

동작 흐름
1. 기본 리트리버로 Top-k(다소 크게) 수집
2. 압축기(LLM 기반/키워드 필터 등)로 문서 단위 축약/필터링
3. 압축된 컨텍스트만 반환

언제 쓰나

  • ✅ 토큰 제한이 타이트할 때, 긴 문서에서 핵심만 남길 때.
  • ⚠️ LLM 압축은 비용/지연 증가.

코드

from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor

compressor = LLMChainExtractor.from_llm(llm)
base = vs.as_retriever(k=20)
retriever = ContextualCompressionRetriever(base_compressor=compressor, base_retriever=base)
docs = retriever.get_relevant_documents("질문")

3) EnsembleRetriever

아이디어

  • 서로 다른 리트리버(BM25 + 벡터 등)의 결과를 가중 투표/스코어 합산으로 랭킹해 리콜/정확도 보완.

동작 흐름
1. 여러 리트리버로 각각 Top-k
2. 점수 정규화 & 가중합
3. 통합 랭크 상위 반환

언제 쓰나

  • ✅ 하이브리드(lexical + semantic)로 안정적인 리콜 확보.
  • ⚠️ 구성/가중치 튜닝 필요.

코드

from langchain.retrievers import EnsembleRetriever

retriever = EnsembleRetriever(
    retrievers=[bm25_retriever, vector_retriever],
    weights=[0.5, 0.5]
)
docs = retriever.get_relevant_documents("질문")

4) LongContextReorder

아이디어

  • 긴 컨텍스트에 넣을 때 문서 순서를 재배치(샌드위치 패턴 등)해 LLM의 회상률을 높임.

동작 흐름
1. Top-k 후보 수집
2. 인접 스팬이 묻히지 않도록 재배치
3. 재배열된 순서로 프롬프트에 삽입

언제 쓰나

  • ✅ 긴 컨텍스트에서 답변 품질/회상률 향상.
  • ⚠️ 재배열만 하므로 ‘무관 문서 제거’는 못함 → 압축과 병행 권장.

코드

from langchain.retrievers.document_compressors import LongContextReorder

reorder = LongContextReorder()
docs = base_retriever.get_relevant_documents("질문")
docs = reorder.compress_documents(docs, query="질문")

5) ParentDocumentRetriever

아이디어

  • 부모-자식 분할: 검색은 잘게 쪼갠 “자식 청크”로, 최종 제공은 부모(큰 단위)로 반환 → 맥락 유지 + 리콜↑.

동작 흐름
1. 인덱싱: 문서를 작은 청크로 분할(자식) + 부모 매핑 유지
2. 검색: 자식 청크 매치
3. 반환: 해당 부모 문서를 통째로/큰 단위로 재조립

언제 쓰나

  • ✅ 코드/규격서/긴 PDF처럼 넓은 문맥이 필요한 자료에 탁월.
  • ⚠️ 저장/조립 관리가 필요.

코드

from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
from langchain.text_splitter import RecursiveCharacterTextSplitter

docstore = InMemoryStore()
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400, chunk_overlap=50)
retriever = ParentDocumentRetriever(
    vectorstore=vs, docstore=docstore, child_splitter=child_splitter
)
docs = retriever.get_relevant_documents("질문")

6) MultiQueryRetriever

아이디어

  • LLM이 동의어/패러프레이즈/다른 관점다중 쿼리를 자동 생성 → 리콜 증대.

동작 흐름
1. LLM이 원쿼리 → q1, q2, q3 … 확장
2. 각 쿼리로 검색 후 병합/중복 제거
3. 통합 랭크 반환

언제 쓰나

  • ✅ 사용자 질문이 애매하거나 표현이 다양할 때 강력.
  • ⚠️ LLM 호출 수 증가, 중복 관리 필요.

코드

from langchain.retrievers.multi_query import MultiQueryRetriever

retriever = MultiQueryRetriever.from_llm(retriever=vs.as_retriever(k=5), llm=llm)
docs = retriever.get_relevant_documents("질문")

7) MultiVectorRetriever

아이디어

  • 문서 하나에 여러 벡터(원문 청크, 요약문, 키프레이즈 등)를 함께 인덱싱 → 다양한 질의 각도에 강함.

동작 흐름
1. 각 문서에 대해 (원문 임베딩 + 요약 임베딩 + 표제어 임베딩 …) 저장
2. 검색 시 다양한 표현에도 매칭될 확률↑
3. 매칭된 대표 문서(원문) 반환

언제 쓰나

  • ✅ 짧은 제목/요약만으로 잘 못 찾는 문서를 보완.
  • ⚠️ 인덱스 크기/구축 비용 증가.

코드

from langchain.retrievers.multi_vector import MultiVectorRetriever

retriever = MultiVectorRetriever(vectorstore=vs, id_key="doc_id")
# 인덱싱 시: retriever.add_texts([원문, 요약, 키워드], metadata={"doc_id": same_id})
docs = retriever.get_relevant_documents("질문")

8) SelfQueryRetriever

아이디어

  • LLM이 자연어 질문을 (a) 벡터 검색 키워드(b) 메타데이터 필터자기-질의(self-query) 파싱.

동작 흐름
1. 스키마(메타데이터 필드) 정의
2. LLM이 “2023 이후, 저자=Kim” 같은 필터와 검색어를 추출
3. 벡터 검색 + 메타 필터로 후보 반환

언제 쓰나

  • ✅ “기간/저자/카테고리 …” 구조화 조건이 많은 지식베이스에 최적.
  • ⚠️ 스키마 정의/예시 프롬프트 품질이 중요.

코드

from langchain.retrievers import SelfQueryRetriever
from langchain.chains.query_constructor.schema import AttributeInfo

metadata_field_info = [
    AttributeInfo(name="author", description="문서 저자", type="string"),
    AttributeInfo(name="year", description="발행 연도", type="integer"),
]
retriever = SelfQueryRetriever.from_llm(
    llm, vs, document_contents="논문 요약",
    metadata_field_info=metadata_field_info, verbose=True
)
docs = retriever.get_relevant_documents("김 저자의 2023년 이후 논문 알려줘")

9) TimeWeightedVectorStoreRetriever

아이디어

  • 유사도 + 최신성 가중치(시간 감쇠)로 점수 계산 → 최근 정보 선호.

동작 흐름
1. 벡터 유사도 점수 계산
2. score = sim - λ * decay(age) 형태로 가중
3. 높은 최신성/유사도 균형 상위 반환

언제 쓰나

  • ✅ 대화 메모리, 이슈 트래킹처럼 “최근 맥락”이 중요한 경우.
  • ⚠️ λ/감쇠 함수 튜닝 필요(너무 크면 오래된 지식 소실).

코드

from langchain.retrievers import TimeWeightedVectorStoreRetriever

retriever = TimeWeightedVectorStoreRetriever(
    vectorstore=vs, decay_rate=0.01, k=6
)
docs = retriever.get_relevant_documents("지금 이슈 상황 요약")

10) Kiwi-BM25Retriever (한국어 특화 BM25)

아이디어

  • 형태소 분석기 Kiwi로 한국어를 정확히 토크나이즈하고, BM25(역색인)로 정밀 키워드 검색.

동작 흐름
1. Kiwi로 문서/질의 형태소 분석 → 토큰화
2. 역색인 생성
3. BM25 점수로 상위 문서 반환

언제 쓰나

  • ✅ 한국어 조사/어미 처리에 강해 정확한 키워드 매칭에 유리.
  • ⚠️ 의미적 유사성은 약함 → 보통 벡터 검색과 앙상블 권장.

코드 (아이디어 예시)

from langchain_community.retrievers import BM25Retriever  # 구현체별 Kiwi 토크나이저 연동 필요

def kiwi_tokenize(text: str) -> list[str]:
    # Kiwi 형태소 분석 결과를 토큰 리스트로 변환하는 사용자 정의 함수
    ...

retriever = BM25Retriever.from_texts(texts, preprocess=kiwi_tokenize)
docs = retriever.get_relevant_documents("전력 소비량 이상치 처리 방법")

11) CC-EnsembleRetriever

아이디어

  • EnsembleRetrieverBM25(키워드) + Vector(의미)가중 결합잘 찾고,
  • ContextualCompressionRetriever불필요 문장을 제거/요약짧게 추려 넣는 파이프라인.

동작 흐름
1. BM25 리트리버와 벡터 리트리버 각각 준비
2. EnsembleRetriever로 Top-k 후보 통합 랭킹
3. LLM 기반 압축기(예: LLMChainExtractor) 설정
4. ContextualCompressionRetriever로 후보 문서 요약/필터링
5. 최종 컨텍스트만 LLM 프롬프트에 투입

언제 쓰나

  • ✅ 한국어 키워드 정확도의미 매칭을 동시에 챙기고 싶을 때
  • ✅ 문서가 길어 컨텍스트 토큰을 절약해야 할 때
  • ⚠️ 압축 단계로 지연·비용 증가 가능 → 캐시/임계값 튜닝 권장

0개의 댓글