langchain / / 2024. 6. 27. 06:52

[langchain] 다중 쿼리 검색기(MultiQueryRetriever)

거리 기반 벡터 데이터베이스 검색은 쿼리를 고차원 공간에 임베드(표현)하고, "거리"를 기준으로 유사한 임베드된 문서를 찾는다. 하지만, 검색은 쿼리의 미묘한 표현 변화나 임베딩이 데이터의 의미를 잘 포착하지 못하는 경우 다른 결과를 낼 수 있다. 이러한 문제를 수동으로 해결하기 위해 프롬프트 엔지니어링/튜닝이 때때로 수행되지만, 이는 번거로울 수 있다.

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)

참고

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유