1. Retrievers

LangChain에서 캡슐화된 인터페이스인 Retriever는 구조화되지 않은 쿼리를 기반으로 관련 문서를 반환할 수 있습니다. Retriever 디자인의 목적은 로컬 데이터를 쿼리하기 쉽게 하는 것입니다. 벡터 저장소는 Retriever의 기본 구현으로 사용될 수 있으며, LangChain은 Retriever 인터페이스의 여러 기본 구현을 지원합니다.

2. Retriever 시작하기

2.1. 설치

Retriever를 얻는 방법을 보여주기 위해 Qdrant 벡터 데이터베이스를 사용하겠습니다.

%pip install --upgrade --quiet qdrant-client

2.2. OpenAI API 키 얻기

OpenAIEmbeddings를 사용하기 전에, OpenAI API 키를 얻어야 합니다.

import getpass
import os

os.environ["OPENAI_API_KEY"] = getpass.getpass("OpenAI API 키:")

2.3. 문서 데이터 가져오고 Qdrant 클라이언트 얻기

다음 코드는 문서 데이터를 가져오고 Qdrant 클라이언트를 얻기 위한 방법을 보여줍니다.

from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import Qdrant
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter

loader = TextLoader("../../modules/state_of_the_union.txt")
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
docs = text_splitter.split_documents(documents)

embeddings = OpenAIEmbeddings()

qdrant = Qdrant.from_documents(
    docs,
    embeddings,
    path="/tmp/local_qdrant",
    collection_name="my_documents",
)

2.4. Retriever 얻기

다음 코드는 Qdrant에서 Retriever를 얻는 방법을 보여줍니다.

retriever = qdrant.as_retriever()
retriever

다음과 같이 질문과 관련된 문서를 쿼리할 수 있습니다:

docs = retriever.get_relevant_documents("케터지 브라운 잭슨에 대해 어떻게 말했는가")

또한, 다음과 같이 Retriever의 유사도 임계값을 설정할 수도 있습니다:

retriever = db.as_retriever(
    search_type="similarity_score_threshold", search_kwargs={"score_threshold": 0.5}
)

추가로, Retriever를 최상위 'K'개의 가장 유사한 레코드를 반환하도록 설정할 수도 있습니다. 예를 들어, 최상위 2개의 가장 유사한 레코드를 반환하도록 설정합니다:

retriever = db.as_retriever(search_kwargs={"k": 2})

2.5. LCEL에서 Retriever 사용하기

Retriever는 Runnable 객체이기 때문에, 다른 Runnable 객체와 쉽게 결합하여 워크플로우를 조율할 수 있습니다:

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

template = """다음 문맥을 기반으로 질문에 답변하세요:

{context}

질문: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
model = ChatOpenAI()

def format_docs(docs):
    return "\n\n".join([d.page_content for d in docs])

chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | model
    | StrOutputParser()
)

chain.invoke("대통령이 기술에 대해 어떻게 얘기했나요?")

과정 설명:

  • 단계 1: 첫 번째 단계는 프롬프트 템플릿을 위한 매개변수를 준비하기 위해 contextquestion 두 속성을 포함한 사전을 생성하는 것입니다. context 매개변수는 invoke 메서드에 전달된 매개변수인 "대통령이 기술에 대해 어떻게 얘기했나요?"에 기반하여 retriever에서 생성되며, 이는 질문과 유사한 문서를 검색한 후 format_docs 함수를 사용하여 문서 배열을 형식화하고 context 속성에 할당합니다. RunnablePassthrough 함수는 체인의 매개변수(사용자의 입력 질문)를 question 속성에 복사합니다.
  • 단계 2: 첫 번째 단계에서 생성된 사전을 프롬프트 템플릿에 전달하여 서식을 지정합니다.
  • 단계 3: 프롬프트 템플릿에서 생성된 서식이 모델에 전달됩니다.
  • 단계 4: 모델 호출 결과를 출력 파서 StrOutputParser에 전달합니다.

3. 사용자 정의 Retriever

4.1. Retriever 인터페이스 소개

Retriever 인터페이스는 매우 간단하며, 사용자 정의 Retriever를 쉽게 작성할 수 있습니다.

4.2. 사용자 정의 검색기 예제

다음은 사용자 정의 검색기의 예제입니다. 이 예제는 어떻게 사용자 정의 검색기를 작성하고 관련 문서를 검색하는지를 보여줍니다.

from langchain_core.retrievers import BaseRetriever
from langchain_core.callbacks import CallbackManagerForRetrieverRun
from langchain_core.documents import Document
from typing import List

class CustomRetriever(BaseRetriever):
  
    def _get_relevant_documents(
        self, query: str, *, run_manager: CallbackManagerForRetrieverRun
    ) -> List[Document]:
        return [Document(page_content=query]

retriever = CustomRetriever()

retriever.get_relevant_documents("bar")

위의 섹션을 공부하여, 개념, 검색 방법 및 검색기의 사용자 정의에 대한 깊은 이해를 얻을 수 있습니다.