MultiRetriever
MultiRetriever
여러 텍스트 Retriever를 병렬로 실행하고, reciprocal rank fusion이나 중복 제거로 결과를 합치는 컴포넌트예요.
출처: 문서
본문
Experimental:
MultiRetriever는 실험 단계 컴포넌트예요. 향후 릴리스에서 사전 공지 없이 바뀌거나 제거될 수 있어요. 이 컴포넌트를 초기화할 때ExperimentalWarning이 출력돼요.
MultiRetriever는 임의 개수의 텍스트 Retriever를 하나의 컴포넌트로 묶어요. 모든 Retriever가 스레드 풀로 병렬 조회되고, 결과는 반환되기 전에 병합돼요.
이 컴포넌트는:
- 모든 Retriever를 동시에 조회해 성능을 높여요.
- 설정된
join_mode로 Retriever 간 결과를 병합해요. active_retrievers로 실행 시점에 일부 Retriever만 선택적으로 켤 수 있어요.
MultiRetriever에 넘기는 모든 Retriever는 TextRetriever 프로토콜을 구현해야 해요. 즉 run 메서드가 텍스트 query, filters, top_k를 받아야 하죠. 임베딩 기반 Retriever를 이 컴포넌트에서 쓰려면 TextEmbeddingRetriever로 감싸세요.
조인 모드
join_mode 파라미터는 여러 Retriever의 결과를 어떻게 병합할지 정해요.
reciprocal_rank_fusion(기본값): Reciprocal Rank Fusion 알고리즘으로 각 문서가 여러 검색 목록에서 차지한 순위를 바탕으로 점수를 매겨요. 여러 목록에서 높은 순위에 오른 문서일수록 점수가 높아지죠. 결과는 중복 제거 후 점수 내림차순으로 반환돼요. BM25와 임베딩 Retriever처럼 점수를 비교하기 어려운 Retriever를 합칠 때 권장하는 모드예요.concatenate: 모든 결과를 단일 목록으로 합치고 중복을 제거해요.
더 알아보기 (Learn more)
단독으로 쓰기
이 예제는 BM25 Retriever와 임베딩 기반 Retriever(TextEmbeddingRetriever로 감쌈)를 합친 MultiRetriever를 구성해요. 둘 다 병렬로 조회하고 결과는 reciprocal rank fusion으로 병합돼요.
이 페이지의 예제는 sentence-transformers-haystack 패키지의 Sentence Transformers 임베더를 사용해요. 예제를 실행하려면 설치하세요.
pip install sentence-transformers-haystack
from haystack import Document
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.document_stores.types import DuplicatePolicy
from haystack_integrations.components.embedders.sentence_transformers import (
SentenceTransformersDocumentEmbedder,
SentenceTransformersTextEmbedder,
)
from haystack.components.retrievers import (
InMemoryBM25Retriever,
InMemoryEmbeddingRetriever,
)
from haystack.components.retrievers import MultiRetriever, TextEmbeddingRetriever
from haystack.components.writers import DocumentWriter
documents = [
Document(
content="Renewable energy is energy that is collected from renewable resources.",
),
Document(
content="Solar energy is a type of green energy that is harnessed from the sun.",
),
Document(
content="Wind energy is another type of green energy that is generated by wind turbines.",
),
]
doc_store = InMemoryDocumentStore()
doc_embedder = SentenceTransformersDocumentEmbedder(
model="sentence-transformers/all-MiniLM-L6-v2",
)
doc_writer = DocumentWriter(document_store=doc_store, policy=DuplicatePolicy.SKIP)
doc_writer.run(documents=doc_embedder.run(documents)["documents"])
retriever = MultiRetriever(
retrievers={
"bm25": InMemoryBM25Retriever(document_store=doc_store),
"embedding": TextEmbeddingRetriever(
retriever=InMemoryEmbeddingRetriever(document_store=doc_store),
text_embedder=SentenceTransformersTextEmbedder(
model="sentence-transformers/all-MiniLM-L6-v2",
),
),
},
top_k=3,
)
result = retriever.run(query="green energy sources")
for doc in result["documents"]:
print(doc.content)
실행 시점에 Retriever 선택하기
active_retrievers 파라미터로 일부 Retriever만 실행할 수 있어요. 이름은 retrievers 딕셔너리의 키와 일치해야 해요. 위 예제를 이어서:
# Run only the BM25 retriever
result = retriever.run(query="green energy sources", active_retrievers=["bm25"])
for doc in result["documents"]:
print(doc.content)
RAG 파이프라인에서 쓰기
이 RAG 파이프라인은 LLM으로 답변을 생성하기 전에 MultiRetriever로 BM25 검색과 임베딩 검색을 합쳐요.
from haystack import Document, Pipeline
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.document_stores.types import DuplicatePolicy
from haystack.components.builders import ChatPromptBuilder
from haystack_integrations.components.embedders.sentence_transformers import (
SentenceTransformersDocumentEmbedder,
SentenceTransformersTextEmbedder,
)
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.retrievers import (
InMemoryBM25Retriever,
InMemoryEmbeddingRetriever,
)
from haystack.components.retrievers import MultiRetriever, TextEmbeddingRetriever
from haystack.components.writers import DocumentWriter
from haystack.dataclasses import ChatMessage
documents = [
Document(
content="Renewable energy is energy that is collected from renewable resources.",
),
Document(
content="Solar energy is a type of green energy that is harnessed from the sun.",
),
Document(
content="Wind energy is another type of green energy that is generated by wind turbines.",
),
]
doc_store = InMemoryDocumentStore()
doc_embedder = SentenceTransformersDocumentEmbedder(
model="sentence-transformers/all-MiniLM-L6-v2",
)
doc_writer = DocumentWriter(document_store=doc_store, policy=DuplicatePolicy.SKIP)
doc_writer.run(documents=doc_embedder.run(documents)["documents"])
prompt_template = [
ChatMessage.from_system(
"You are a helpful assistant that answers questions based on the provided documents.",
),
ChatMessage.from_user(
"Given these documents, answer the question.\nDocuments:\n"
"{% for doc in documents %}{{ doc.content }}\n{% endfor %}\n"
"Question: {{ question }}",
),
]
pipeline = Pipeline()
pipeline.add_component(
"retriever",
MultiRetriever(
retrievers={
"bm25": InMemoryBM25Retriever(document_store=doc_store),
"embedding": TextEmbeddingRetriever(
retriever=InMemoryEmbeddingRetriever(document_store=doc_store),
text_embedder=SentenceTransformersTextEmbedder(
model="sentence-transformers/all-MiniLM-L6-v2",
),
),
},
top_k=3,
),
)
pipeline.add_component(
"prompt_builder",
ChatPromptBuilder(
template=prompt_template,
required_variables=["documents", "question"],
),
)
pipeline.add_component("llm", OpenAIChatGenerator())
pipeline.connect("retriever.documents", "prompt_builder.documents")
pipeline.connect("prompt_builder.prompt", "llm.messages")
result = pipeline.run(
{
"retriever": {"query": "green energy sources"},
"prompt_builder": {"question": "What types of green energy exist?"},
},
)
print(result["llm"]["replies"][0].text)