본문 바로가기

AI

LangChain | WebResearchRetriever을 활용하여 RAG (Retrieval Augmented Generation) 구현하기

지난 포스트에서는 LangChain을 활용하여 5세 아이의 단어공부를 도와주는 간단한 어플리케이션 코드를 작성해 보았다.

2023.08.16 - [AI] - LangChain이란? | 파이썬으로 LangChain 시작하기

 

LangChain이란? | 파이썬으로 LangChain 시작하기

참고문서: https://python.langchain.com/docs/get_started/quickstart.html 실습파일: LangChain이란? LangChain은 언어모델, 특히 대규모 언어모델(LLM)을 활용하여 구동하는 애플리케이션을 개발하기 위한 프레임워크

littlefoxdiary.tistory.com

 

이처럼 자유롭거나 창의적인 Open-World 질문에 대해서는 대규모 언어모델이 다소 창의적이거나 좋은 답변은 줄 수 있지만, 도메인 지식이나 전문성이 필요한 태스크에 대해서는 정확한 답변을 하지 않을 수 있다.

 

이런 경우 활용할 수 있는 방법이 바로, 검색 결과를 LLM 인풋에 활용하는 RAG(Retrieval Augmented Generation)이다.

RAG는 생성형 언어모델의 생성 품질과 정확성을 향상하기 위해 검색 기반 모델을 결합하는 방법이다.

RAG에서는 외부 소스에서 검색한 정보를 활용하여 더 정확하고 맥락에 맞는 응답을 생성할 수 있도록 LLM의 능력을 보완하고 강화한다.

 

BingChat에서 제공하는 답변과 그에 대한 reference가 RAG가 결합된 LLM의 대표적인 모습이다.

 

여행과 음악을 다루지는 않습니다만.....

 

LangChain에서 제공하는 Data connection 모듈을 사용하면 BingChat과 같이 외부 정보를 활용하여 LLM 어플리케이션의 결과를 받아보는 것이 가능하다.

Data connection에서는 가지고 있는 소스로부터 정보를 자체적으로 인덱싱해두고, query를 통해 찾아오도록 구현하는 것이 가능하지만 간단하게 구글과 같은 인터넷 검색 기능을 활용하는 것도 가능하다. 

 

본 포스팅에서는 LangChain에서 제공하는 Retriever에 대해 알아보고,

이중 WebResearchRetriever을 활용하여 RAG를 구현하는 실습을 진행해보고자 한다.

 


Retriever

Retriever(검색기)는 비정형 쿼리가 주어질 때 관련된 문서를 반환하는 인터페이스이다. 이는 일반적인 벡터 스토리지보다는 일반적인 기능으로, Retriever에서는 문서를 저장하는 기능까지 담당하기보다는 문서를 검색하여 반환하기만 하는 것에 집중한다.

물론 벡터 스토리지는 Retriever의 중요한 백본 중 하나이지만, 벡터 스토리지를 사용하지 않는 다른 검색기를 사용하는 것도 가능하다.

 

LangChain의 기본 검색기의 BaseRetriever의 퍼블릭 API는 아래와 같다.

  • get_relevant_documents : 쿼리에 대해 관련된 문서를 받아옴
  • aget_relevent_documents : 쿼리에 대해 비동기적으로 관련된 문서를 받아옴

 

from abc import ABC, abstractmethod
from typing import Any, List
from langchain.schema import Document
from langchain.callbacks.manager import Callbacks

class BaseRetriever(ABC):
    ...
    def get_relevant_documents(
        self, query: str, *, callbacks: Callbacks = None, **kwargs: Any
    ) -> List[Document]:
        """주어진 쿼리에 대해 관련된 문서를 받아옴.
        Args:
            query: 검색을 위한 쿼리 (문자열, string)
            callbacks: Callback manager 혹은 callbacks의 리스트
        Returns:
            관련 문서들을 리턴
        """


    async def aget_relevant_documents(
        self, query: str, *, callbacks: Callbacks = None, **kwargs: Any
    ) -> List[Document]:
        """비동기적으로 쿼리에 대해 관련된 문서를 받아옴
        Args:
            query: 검색을 위한 쿼리 (문자열, string)
            callbacks: Callback manager 혹은 callbacks의 리스트
        Returns:
            관련 문서들을 리턴
        ...

 

검색기들 중 유용한 하나는 Vectorstore Retriever이다. 예를 들면 ChromaDB와 같은 벡터저장소를 활용하여 임베딩된 벡터를 저장하고 검색할 수 있다. 벡터 스토리지를 활용한 검색기 구현은 다음 포스팅에서 자세히 다루도록 하겠다.


WebResearchRetriever

쿼리가 주어질 때, WebResearchRetriever은 아래의 프로세스를 수행한다.

  • 관련된 Google 검색을 위한 셋트를 구성한다
  • 각각에 대해 검색을 수행한다
  • 모든 관련된 URL을 로드한다
  • 페이지 내용을 임베딩한 후, 쿼리와의 유사도 검색을 수행한다.

 

환경 세팅

WebResearchRetriever을 실행하기 위해서는 Croma DB 설치 및 Google Search API를 활성화해야 한다.

 

Croma DB 설치하기

 

pip install chromadb

 

⚠️ 이때 아래와 같이 sqlite 버전 에러가 난다면, pysqlite3-binary를 다시 설치한 후 아래와 같이 버전을 업데이트하면 된다.

RuntimeError: Your system has an unsupported version of sqlite3. Chroma requires sqlite3 >= 3.35.0.

 

해결 방법:

 

pip install pysqlite3-binary

python3 #python 실행

__import__('pysqlite3')
import sys
sys.modules['sqlite3'] = sys.modules.pop('pysqlite3')

 

 

 

Google Search API 키 발급받기

1. Google Cloud credential 콘솔(링크)에서 GOOGLE_API_KEY를 발급받기

2. Google의 Programmable Search Engine에서 GOOGLE_CSE_ID 발급받기 (링크)

사용 방법

1. Google Search 쿼리를 생성할 LLM을 지정한다. (아래 예시에서는 ChatOpenAI 사용)

 

from langchain.retrievers.web_research import WebResearchRetriever
import os
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.chat_models.openai import ChatOpenAI
from langchain.utilities import GoogleSearchAPIWrapper

os.environ["GOOGLE_CSE_ID"] = "XXX"
os.environ["GOOGLE_API_KEY"] = "XXX"
os.environ["OPENAI_API_KEY"] = "XXX"

# Vectorstore 셋팅하기
vectorstore = Chroma(embedding_function=OpenAIEmbeddings(),
                     persist_directory="./chroma_db_oai")

# Search Query를 위한 LLM
search_llm = ChatOpenAI(temperature=0)

# SearchAPI Wrapper 객체 생성하기
search = GoogleSearchAPIWrapper()

# Web Research Retriever 셋팅하기
web_research_retriever = WebResearchRetriever.from_llm(
    vectorstore=vectorstore,
    llm=search_llm, 
    search=search, 
)

 

2. WebResearchRetriever 결과를 기반으로 답변하도록 RetrievalQAWithSourcesChain 활용하기

 

from langchain.chains import RetrievalQAWithSourcesChain
response_llm = ChatOpenAI(temperature=0.90)
qa_chain = RetrievalQAWithSourcesChain.from_chain_type(response_llm,
                                                       retriever=web_research_retriever)

 

3. 추론해 보기

 

user_input = "뉴진스 민지 생일 언제야?"
result = qa_chain({"question": user_input})
result

Source 페이지를 방문해 보면 실제 정보를 바탕으로 답변을 생성한 것을 확인할 수 있다.