거리 기반 벡터 데이터베이스 검색은 쿼리를 고차원 공간에 임베드(표현)하고, "거리"를 기준으로 유사한 임베드된 문서를 찾는다. 하지만, 검색은 쿼리의 미묘한 표현 변화나 임베딩이 데이터의 의미를 잘 포착하지 못하는 경우 다른 결과를 낼 수 있다. 이러한 문제를 수동으로 해결하기 위해 프롬프트 엔지니어링/튜닝이 때때로 수행되지만, 이는 번거로울 수 있다.
MultiQueryRetriever
는 사용자 입력 쿼리에 대해 다양한 관점에서 여러 쿼리를 생성하기 위해 LLM을 사용하여 프롬프트 튜닝 과정을 자동화한다. 각 쿼리마다 관련 문서 세트를 검색하고, 모든 쿼리에서 고유한 합집합을 취하여 더 큰 잠재적으로 관련 있는 문서 세트를 얻는다. 동일한 질문에 대해 여러 관점을 생성함으로써, MultiQueryRetriever
는 거리 기반 검색의 한계를 극복하고 더 풍부한 결과 세트를 얻을 수 있다.
MultiQueryRetriever를 한번 사용해보자. 우선 웹문서를 로딩하기 위해 beautifulsoup을 설치하자
pipenv install beautifulsoup4
import logging
import langchain
from dotenv import load_dotenv
from langchain.retrievers import MultiQueryRetriever
from langchain.vectorstores.chroma import Chroma
from langchain_community.document_loaders import WebBaseLoader
from langchain_openai import OpenAIEmbeddings
from langchain_openai.chat_models import ChatOpenAI
from langchain_text_splitters import RecursiveCharacterTextSplitter
load_dotenv()
# Load blog post
loader = WebBaseLoader("https://ko.wikipedia.org/wiki/%EB%B9%84%ED%83%80%EB%AF%BC")
data = loader.load()
# Split
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=0)
splits = text_splitter.split_documents(data)
# VectorDB
embedding = OpenAIEmbeddings()
vectordb = Chroma.from_documents(documents=splits, embedding=embedding)
question = "비타민은 무엇인가?"
llm = ChatOpenAI(temperature=0)
retriever = MultiQueryRetriever.from_llm(retriever=vectordb.as_retriever(), llm=llm)
langchain.debug = True
logging.getLogger("langchain.retrievers.multi_query").setLevel(logging.INFO)
docs = retriever.invoke(question)
print(len(docs))
for doc in docs:
print(doc.page_content)
프롬프트의 내용
{
"prompts": [
"Human: You are an AI language model assistant. Your task is \n to generate 3 different versions of the given user \n question to retrieve relevant documents from a vector database. \n By generating multiple perspectives on the user question, \n your goal is to help the user overcome some of the limitations \n of distance-based similarity search. Provide these alternative \n questions separated by newlines. Original question: 비타민은 무엇인가?"
]
}
질문은 비타민은 무엇인가? 라고 물었지만 실제 질문은 아래와 같이 3가지 질문을 만들었다.
{
"text": [
"1. 어떤 것이 비타민인가요?",
"2. 비타민이란 무엇을 의미하나요?",
"3. 비타민에 대해 알려주세요."
]
}
MultiQueryRetriever는 질문을 분석하여 관련 문서를 벡터저장소에서 검색하여 최종 답변을 생성한다. 즉, 질문을 좀 더 포괄적으로 바꾸어 답변을 얻고자 하는 것이다.
프롬프트를 직접 작성
아래와 같이 프롬프트를 직접 작성할 수도 있다. 실행되는 결과는 동일하다.
load_dotenv()
output_parser = LineListOutputParser()
prompt = PromptTemplate(
input_variables=["question"],
template="""You are an AI language model assistant. Your task is to generate five
different versions of the given user question to retrieve relevant documents from a vector
database. By generating multiple perspectives on the user question, your goal is to help
the user overcome some of the limitations of the distance-based similarity search.
Provide these alternative questions separated by newlines.
Original question: {question}""",
)
llm = ChatOpenAI(temperature=0)
chain = {"question": RunnablePassthrough()} | prompt | llm | StrOutputParser()
vectordb = Chroma.from_documents(documents=splits, embedding=embedding)
question = "비타민은 무엇인가?"
multiquery_retriever = MultiQueryRetriever.from_llm(
llm=chain, retriever=vectordb.as_retriever()
)
unique_docs = retriever.invoke(question)
len(unique_docs)
참고
반응형