OpenSearchBM25Retriever
OpenSearchBM25Retriever
OpenSearch Document Store에서 검색어와 일치하는 문서를 가져오는 키워드 기반 Retriever예요.
출처: 문서
본문
OpenSearchBM25Retriever는 OpenSearchDocumentStore에서 검색어와 일치하는 문서를 가져오는 키워드 기반 Retriever예요. 문서와 검색어의 유사도를 BM25 알고리즘으로 결정하는데, 두 문자열 사이의 가중 단어 겹침(weighted word overlap)을 계산하죠.
OpenSearchBM25Retriever는 단어 겹침으로 문자열을 매칭하기 때문에, 사람 이름·제품 이름·ID·명확한 오류 메시지의 정확한 일치를 찾는 데 자주 쓰여요. BM25 알고리즘은 매우 가볍고 단순해요. 그럼에도 도메인 밖(out-of-domain) 데이터에서는 더 복잡한 임베딩 기반 접근을 이기기 어려운 경우가 많죠.
query 외에도 OpenSearchBM25Retriever는 top_k(가져올 최대 문서 수)와 검색 공간을 좁히는 filters 같은 선택 파라미터를 받아요. fuzziness 파라미터로 비정확한 퍼지 매칭이 수행되는 방식을 조정할 수 있어요. 또한 all_terms_must_match 파라미터로 검색어의 모든 용어가 일치해야 하는지 지정할 수 있는데, 기본값은 False예요.
검색어를 문서에 더 유연하게 매칭하고 싶다면, LLM이 만든 벡터로 관련 정보를 검색하는 OpenSearchEmbeddingRetriever를 쓸 수 있어요.
설정과 설치
OpenSearch 인스턴스를 설치하고 실행해요.
Docker가 설정되어 있다면 Docker 이미지를 받아 실행하는 것을 권장해요.
docker pull opensearchproject/opensearch:3.5.0
docker run -p 9200:9200 -p 9600:9600 -e "discovery.type=single-node" -e "ES_JAVA_OPTS=-Xms1024m -Xmx1024m" -e "OPENSEARCH_INITIAL_ADMIN_PASSWORD=<custom-admin-password>" opensearchproject/opensearch:3.5.0
대안으로 OpenSearch integration GitHub에 가서 제공된 docker-compose.yml로 OpenSearch를 실행하는 Docker 컨테이너를 시작할 수 있어요.
docker compose up
실행 중인 OpenSearch 인스턴스가 준비되면 opensearch-haystack 통합을 설치해요.
pip install opensearch-haystack
더 알아보기 (Learn more)
단독으로 쓰기
이 Retriever는 OpensearchDocumentStore와 인덱싱된 문서가 필요해요. 단독으로는 쓸 수 없어요.
RAG 파이프라인에서 쓰기
OPENAI_API_KEY를 환경 변수로 설정한 뒤 다음 코드를 실행해요.
from haystack_integrations.components.retrievers.opensearch import (
OpenSearchBM25Retriever,
)
from haystack_integrations.document_stores.opensearch import OpenSearchDocumentStore
from haystack import Document
from haystack import Pipeline
from haystack.components.builders.answer_builder import AnswerBuilder
from haystack.components.builders import ChatPromptBuilder
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.document_stores.types import DuplicatePolicy
# OpenAIChatGenerator reads the OPENAI_API_KEY environment variable by default.
# Create a RAG query pipeline
prompt_template = [
ChatMessage.from_user(
"""
Given these documents, answer the question.\nDocuments:
{% for doc in documents %}
{{ doc.content }}
{% endfor %}
\nQuestion: {{question}}
\nAnswer:
""",
),
]
document_store = OpenSearchDocumentStore(
hosts="http://localhost:9200",
use_ssl=True,
verify_certs=False,
http_auth=("admin", "<custom-admin-password>"),
)
# Add Documents
documents = [
Document(content="There are over 7,000 languages spoken around the world today."),
Document(
content="Elephants have been observed to behave in a way that indicates a high level of self-awareness, such as recognizing themselves in mirrors.",
),
Document(
content="In certain parts of the world, like the Maldives, Puerto Rico, and San Diego, you can witness the phenomenon of bioluminescent waves.",
),
]
# DuplicatePolicy.SKIP param is optional, but useful to run the script multiple times without throwing errors
document_store.write_documents(documents=documents, policy=DuplicatePolicy.SKIP)
retriever = OpenSearchBM25Retriever(document_store=document_store)
rag_pipeline = Pipeline()
rag_pipeline.add_component(name="retriever", instance=retriever)
rag_pipeline.add_component(
instance=ChatPromptBuilder(template=prompt_template, required_variables="*"),
name="prompt_builder",
)
rag_pipeline.add_component(instance=OpenAIChatGenerator(), name="llm")
rag_pipeline.add_component(instance=AnswerBuilder(), name="answer_builder")
rag_pipeline.connect("retriever", "prompt_builder.documents")
rag_pipeline.connect("prompt_builder.prompt", "llm.messages")
rag_pipeline.connect("llm.replies", "answer_builder.replies")
rag_pipeline.connect("retriever", "answer_builder.documents")
question = "How many languages are spoken around the world today?"
result = rag_pipeline.run(
{
"retriever": {"query": question},
"prompt_builder": {"question": question},
"answer_builder": {"query": question},
},
)
print(result["answer_builder"]["answers"][0])
다음은 예시 출력이에요.
# GeneratedAnswer(
# data='Over 7,000 languages are spoken around the world today.',
# query='How many languages are spoken around the world today?',
# documents=[
# Document(id=cfe93bc1c274908801e6670440bf2bbba54fad792770d57421f85ffa2a4fcc94, content: 'There are over 7,000 languages spoken around the world today.', meta: {'source_index': 1}, score: 3.263233),
# Document(id=7f225626ad1019b273326fbaf11308edfca6d663308a4a3533ec7787367d59a2, content: 'In certain parts of the world, like the Maldives, Puerto Rico, and San Diego, you can witness the ph...', meta: {'source_index': 2}, score: 0.51940084)],
# meta={'model': 'gpt-5-mini-2025-08-07', 'index': 0, 'finish_reason': 'stop',
# 'usage': {'completion_tokens': 86, 'prompt_tokens': 85, 'total_tokens': 171,
# 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0,
# 'reasoning_tokens': 64, 'rejected_prediction_tokens': 0},
# 'prompt_tokens_details': {'audio_tokens': 0, 'cache_write_tokens': None, 'cached_tokens': 0}},
# 'all_messages': [ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>, ...)]})
추가 참고 자료
🧑🍳 Cookbook: PDF-Based Question Answering with Amazon Bedrock and Haystack