본문 바로가기

IT, Computer

Retrieval Augmented Generation(RAG) 복습 (3) LangChain 활용

반응형

 

 

목차


    썸네일


    서론

    지난 포스팅에서는 API를 통한 llm 셋팅에 대해서 알아보았다. 이번엔 LangChain을 이용한 RAG 구성에 대해 알아보고자 한다. 이를 위해 langchain은 어떤것인지 알아보고 코드와 함께 결과에 대해 알아보고자 한다.


    LangChain이란?


    지난 포스팅에서는 API를 통한 llm 셋팅에 대해서 알아보았다. 이번엔 LangChain을 이용한 RAG 구성에 대해 코드와 함께 알아보고자 한다. Langchain은 자체 DB가 있어서 별도 코드 작성 필요 없이 깔끔하게 할 수 있다는 장점이 있다. 프롬프트도 디폴트로하기에 따로 작성하지 않아도 된다. LangChain이 없다면 모든걸 다 직접 작성해야된다고 한다.  문서파싱, 청킹, 임베딩, 질문 다 직접 넘겨야되며, 패키지도 많다. 이 포스팅을 보다보면 embedding = OpenAIEmbeddings(model = 'text-embedding-3-large') <<< 이런 걸 선언하는 과정에서 "..? 내가 다 하는거 같은데?" 라고 생각하겠지만, 그게 langchain이 해주는거라고 보면된다. 다 걔가 지원해 주는 도구라니까....


     

    본 포스팅은 크게 네가지 과정이 있다. 사실 2+3이 한 단락이니 정말 크게보면 3가지 스텝임.

     

    1. 문서의 내용을 읽고, 문서를 쪼갠다.
        쪼개는 이유는 1) 토큰수 초과로 답변 생성 못할 수 있고, 2) 문서가 길면 답변 생성이 오래 걸리기 때문.
    2. 쪼갠 문서를 임베딩해서, 벡터 데이터 베이스에 저장한다.
    3. 질문이 있으면, 벡터 데이터 베이스에서 유사도 검색.
    4. 유사도 검색으로 가져온 문서롤 LLM에 질문과 같이 전달.


    1. 문서를 읽고 쪼개는 과정.

    문서 내용 읽는 건 document loader를 사용할 것임. 그런데,

    loader = Docx2txtLoader('./읽을워드파일.docx')
    document = loader.load()

     

     

    단순 이렇게 하면 document의 길이가 1이게 된다. 이걸 text_splitters를 사용해서 쪼개야 됨을 의미한다. langchain-text-splitters를 설치한 뒤, 아래와 같이 스플리터를 만든다. 참고로 splitter는 reculsive랑 character splitter가 자주 쓰이는데, 후자는 구분자를 하나밖에 못넣음. 근데 reculsive는 list로 구분자를 넣기에, 이게 더 성능이 좋다고 한다.

    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size = 1500, # 청크 하나가 갖는 토큰 수 
        chunk_overlap= 200, # 청크끼리 겹치는 정도 - overlap으로 유사도 검색시 원하는 문서를 가질 수 있는 확률을 높이는 역할을 한다.
        
    ) 
    loader = Docx2txtLoader('./워드파일.docx')
    document = loader.load_and_split(text_splitter=text_splitter)

     

    이후 len(document)를 하면 1이 아닌 숫자가 나오는걸 볼 수 있다. 하나의 리스트가 된 셈이다.


    2. 쪼갠 문서를 임베딩해서, 벡터 데이터 베이스에 저장한다. 3. 질문이 있으면, 벡터 데이터 베이스에서 유사도 검색.

    이제 위에서 쪼갠 문서를 임베딩 할 차례다. 이 과정을 모르면 첫 포스팅에 설명이 되어있다. 거기에 대해 알고 싶다면 여기로 가면 된다. 

     

    Retrieval Augmented Generation(RAG) 복습 (1)

    목차RAG란?Retrival : (언어 모델이 갖고 있지 않은 정보) 데이터를 가져오는 것.Augmented : AR/VR에서의 A도 Augmented임. 마치 있는 것처럼.Generation : 컴퓨터가 생성하는거 llm이 알아서 하는부분이다.우리

    quiseol.com

     

    from langchain_openai import OpenAIEmbeddings
    embedding = OpenAIEmbeddings(model = 'text-embedding-3-large') 
    #디폴트 값(얘도 한국어 지원함)보단 'text-embedding-3-large'가 성능이 더 좋다고 한다.

    아무튼 임베딩을 하기 위해 openai의 임베딩을 사용할 것이다. 임베딩한 걸 벡터db에 넣을 차례인 것이다. %pip install langchain-chroma를 통해 크로마를 설치하고,

    from langchain_chroma import Chroma
    database = Chroma.from_documents(documents = document, embedding = embedding)

    다음과 같이 선언해서 데이터 베이스를 선언한다. 이 과정이 우리가 쪼개놓은 리스트를 임베딩을 활용해서 저장하는 중인것이다. 이후 retrived_docs = database.similarity_search('질문') 을 선언하면 유사도 검색을 한다. 데이터 베이스에서 질문과 가장 유사한 것들을 찾아내는 과정인 것이다.


    4. 유사도 검색으로 가져온 문서롤 LLM에 질문과 같이 전달.

    마지막 스텝이다. 크게 세가지 스텝인데 1)openai 모델을 설정한다. 2)  유사도 검색으로 가져온 retrived_docs를 프롬프트에 넣는다. 3) invoke를 통해서 답변을 가져온다.  해당 과정은 아래와 같다. 

    from langchain_openai import ChatOpenAI
    llm = ChatOpenAI(model='gpt-4o')
    prompt = f"""[Identity]
    - 당신은 최고의 ㅇㅇㅇ 입니다.
    - [Context]를 참고해서 사용자의 질문에 답변해주세요.
    [Context]
    {retrived_docs}
    Question:{query}
    """
    print(llm.invoke(prompt))

    만약에 위 코드가 되지 않는다면,

     

    API_KEY를 넣지 않아서라고 판단된다. 알아서 해결하고 오시길.. 이건 내가 해줄수가 없다. https://wikidocs.net/231180 이렇게 하기도 하고, 난 dotenv를 사용해서한다. '.env'파일에 api key를 넣어둔 뒤, load_dotenv()를 선언하는 것이다. 보통 파라미터에 api_key = 'your-api-key' 이렇게 하면 되던데 얘네는 또 모르겠다. 아마 될 듯. 

    api-key 오류


    끝으로

    다음 포스팅에서는 retrieval을 효과적으로 활용하기 위해 langchain hub를 사용할 예정이다.지난 포스팅을 보려면 여기로 https://quiseol.com/entry/RAG-2

     

    Gemini API 사용 방법, Retrieval Augmented Generation(RAG) 복습 (2)

    목차서론지난 포스팅에서는 RAG에 대한 전반적인 내용에 대해 알아보았다. 이번에는 실습을 복습할 예정이다. 우선 셋팅부터 진행하겠다. 보통 open-ai를 사용하지만, 난 구글 클라우드 크래딧이

    quiseol.com