RAG & LANCHAIN (5)- Langchain 기반 RAG 기본 구조

이영락·2024년 8월 27일
0

인공지능 공부

목록 보기
9/33

1. 환경 설정

  • API 키 로드, 필요한 라이브러리 불러오기, LangSmith 추적 설정 등은 기본적으로 동일합니다.
from dotenv import load_dotenv
from langchain_teddynote import logging
import os

# 환경 변수 로드 (API 키 등)
load_dotenv()

# 모델 및 캐시 경로 설정
os.environ["TRANSFORMERS_CACHE"] = "./cache/"
os.environ["HF_HOME"] = "./cache/"

# LangSmith 추적 설정 (프로젝트 이름 입력)
logging.langsmith("RAG-Project")
LangSmith 추적을 시작합니다.

2. 문서 로드

  • 문서 데이터를 로드하여 검색 가능한 형태로 준비합니다. 이 과정에서는 문서 로더와 텍스트 분할기를 사용합니다.
from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 문서 로드
loader = TextLoader(file_path="your_document.txt")

# 문서 분할
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
documents = text_splitter.split_documents(loader.load())

어떤 문서 로더와 코드 텍스트 분활기(CodeTextSplitter)를 쓸지 고려해야함!

3. 임베딩 생성 및 벡터 저장소 구축

  • 문서의 텍스트를 임베딩(벡터화)하고, 이를 벡터 저장소에 저장합니다. 이 단계에서 사용되는 임베딩 모델과 벡터 저장소를 설정합니다.
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS

# 임베딩 모델 설정
embedding_model = OpenAIEmbeddings()

# 벡터 저장소 구축
vector_store = FAISS.from_documents(documents, embedding_model)

4. 검색기(Retriever) 설정

  • 검색기를 설정하여 사용자가 입력한 질문과 관련된 문서를 벡터 저장소에서 검색할 수 있게 합니다.
from langchain.retrievers import VectorStoreRetriever

# 검색기 설정
retriever = VectorStoreRetriever(vectorstore=vector_store)

5. 모델 로드

  • 답변 생성을 위한 언어 모델을 로드합니다.
from langchain_openai import ChatOpenAI

# 언어 모델 로드
from langchain_openai import ChatOpenAI

# ChatOpenAI 객체 생성
gpt = ChatOpenAI(
    temperature=0.7,  # 창의성과 일관성을 조절
    model_name="gpt-4o",  # 모델명
    streaming = True
)

6. 프롬프트 템플릿 및 결과 파서 설정

  • 모델의 출력을 적절한 형식으로 변환하기 위해 결과 파서를 설정하고, 프롬프트 템플릿을 구성합니다.
from langchain.prompts import PromptTemplate


# 프롬프트 템플릿 구성
prompt_template = PromptTemplate.from_template(
    "Given the following context, answer the question: {context}\n\nQuestion: {question}\n\nAnswer:"
)

7. 체인 구성

  • 프롬프트 템플릿, 모델, 결과 파서를 체인으로 연결합니다.
from langchain_core.output_parsers import StrOutputParser

# 결과 파서 설정
output_parser = StrOutputParser()

from langchain.chains import RetrievalQA

# RAG 체인 구성
rag_chain = RetrievalQA.from_chain_type(
    llm=gpt,
    chain_type="stuff",
    retriever=retriever,
    return_source_documents=True,
    output_parser=output_parser  # 체인에 결과 파서를 포함
)

Q1. LangChain에서는 '|'으로 연결했는데 그 방식이 바뀐 이유는?

1. 일반적인 체인 구성 방식 (| 연산자를 사용하여 연결):

이 방식은 LangChain에서 체인을 구성할 때, 각 구성 요소를 단순하게 파이프(|) 연산자를 사용해 연결하는 방식입니다. 이 경우, 각 구성 요소는 Runnable 객체로 간주되며, 순차적으로 실행됩니다. 예를 들어:

from langchain_core.output_parsers import StrOutputParser

# 프롬프트와 모델을 연결하는 체인 생성
chain = prompt | gpt | StrOutputParser()

여기서 prompt, gpt, 그리고 StrOutputParser는 각각의 작업을 수행하고 다음 단계로 결과를 전달하는 구조입니다.

2. RetrievalQA 체인 구성 방식:

RetrievalQA는 RAG 구현을 위한 LangChain에서 제공하는 특정 체인입니다. 이 체인은 검색과 생성 단계를 함께 관리하며, 사용자가 복잡한 구조를 쉽게 사용할 수 있도록 from_chain_type이라는 메서드를 제공합니다. 이 메서드는 검색기(retriever), LLM(llm), 결과 파서(output_parser)와 같은 여러 요소를 하나의 체인으로 결합합니다.

예를 들어:

from langchain.chains import RetrievalQA
from langchain_core.output_parsers import StrOutputParser

# 결과 파서 설정
output_parser = StrOutputParser()

# RAG 체인 구성
rag_chain = RetrievalQA.from_chain_type(
    llm=gpt,
    chain_type="stuff",
    retriever=retriever,
    return_source_documents=True,
    output_parser=output_parser  # 체인에 결과 파서를 포함
)

이 방법은 단순한 파이프 연산자 대신, 복잡한 체인을 보다 쉽게 관리하고 구성할 수 있도록 도와줍니다. RetrievalQA 체인은 내부적으로 다양한 단계를 포함하고 있으며, 이 단계를 사용자가 쉽게 설정할 수 있도록 메서드를 제공합니다.

요약:

  • 일반적인 체인 구성: | 연산자를 사용하여 프롬프트, 모델, 파서를 직접 연결.
  • RetrievalQA 체인 구성: 특정 체인 유형(RetrievalQA)은 자체적인 체인 구성 메서드를 제공하며, 이를 통해 보다 복잡한 구조를 쉽게 관리 가능.

두 방법은 사용 목적에 따라 선택될 수 있으며, RetrievalQA는 특히 RAG 같은 복잡한 체인을 구성할 때 편리하게 사용할 수 있습니다.

8. 체인 실행 및 스트리밍 출력

  • 사용자의 질문을 입력으로 받아 검색과 답변 생성이 이루어집니다. 스트리밍을 통해 실시간으로 출력을 받습니다.
from langchain_teddynote.messages import stream_response

# 질문에 대한 답변 생성 (스트리밍 출력 포함)
answer = gpt.stream("What is the capital of South Korea?")
stream_response(answer)  # 스트리밍 출력

전체 코드 예시

from dotenv import load_dotenv
from langchain_teddynote import logging
from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.retrievers import VectorStoreRetriever
from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQA
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_teddynote.messages import stream_response

# 1. 환경 설정
load_dotenv()
logging.langsmith("RAG-Project")
LangSmith 추적을 시작합니다.

# 2. 문서 로드 및 분할
loader = TextLoader(file_path="your_document.txt")
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
documents = text_splitter.split_documents(loader.load())

# 3. 임베딩 생성 및 벡터 저장소 구축
embedding_model = OpenAIEmbeddings()
vector_store = FAISS.from_documents(documents, embedding_model)

# 4. 검색기 설정
retriever = VectorStoreRetriever(vectorstore=vector_store)

# 5. 모델 로드
gpt = ChatOpenAI(temperature=0, model_name="gpt-4o", streaming=True)

# 6. 프롬프트 템플릿 및 결과 파서 설정
prompt_template = PromptTemplate.from_template(
    "Given the following context, answer the question: {context}\n\nQuestion: {question}\n\nAnswer:"
)
output_parser = StrOutputParser()

# 7. 체인 구성
rag_chain = RetrievalQA.from_chain_type(
    llm=gpt,
    chain_type="stuff",
    retriever=retriever,
    return_source_documents=True,
    output_parser=output_parser
)

# 8. 체인 실행 및 스트리밍 출력
answer = gpt.stream("What is the capital of South Korea?")
stream_response(answer)

함수로 정리

전체 코드를 함수로 구조화하여 각 단계가 명확하게 분리되도록 만들어 보겠습니다. 각 함수는 특정 작업을 수행하도록 설계되어 있으며, 이를 통해 코드의 재사용성과 가독성을 높일 수 있습니다.

1. 환경 설정

  • 환경 설정과 로그 설정을 함수로 정의합니다.
from dotenv import load_dotenv
from langchain_teddynote import logging
import os

def initialize_environment():
    # 환경 변수 로드(API 키등)
    load_dotenv()
    
    # 모델 및 캐시 경로 설정
    os.environ["TRANSFORMERS_CACHE"] = "./cache/"
    os.environ["HF_HOME"] = "./cache/"
    
    # LangSmith 추적 설정(프로젝트 이름 입력)
    logging.langsmith("RAG-Project")
    print("LangSmith 추적을 시작합니다.")

2. 문서 로드 및 분할

  • 문서 로드 및 텍스트 분할 작업을 함수로 정의합니다.
from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

def load_and_split_document(file_path):
    
    # 문서 로드
    loader = TextLoader(file_path=file_path)
    
    # 문서 분할
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
    documents = text_splitter.split_documents(loader.load())
    return documents

3. 임베딩 생성 및 벡터 저장소 구축

  • 임베딩 생성 및 벡터 저장소 구축을 함수로 정의합니다.
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS

def create_vector_store(documents):
    
    # 임베딩 모델 설정
    embedding_model = OpenAIEmbeddings()
    
    # 벡터 저장소 구축
    vector_store = FAISS.from_documents(documents, embedding_model)
    return vector_store

4. 검색기 설정

  • 벡터 저장소를 기반으로 검색기를 설정하는 함수를 정의합니다.
from langchain.retrievers import VectorStoreRetriever

def setup_retriever(vector_store):
    
    # 검색기 설정
    retriever = VectorStoreRetriever(vectorstore=vector_store)
    return retriever

5. 모델 로드

  • 언어 모델을 로드하는 함수를 정의합니다.
# 언어 모델 로드
from langchain_openai import ChatOpenAI

def load_language_model():
   
   #ChatOpenAI 객체 생성
   gpt = ChatOpenAI(temperature=0.7, model_name="gpt-4o", streaming=True)
    return gpt

6. 프롬프트 템플릿 및 결과 파서 설정

  • 프롬프트 템플릿과 결과 파서를 설정하는 함수를 정의합니다.
from langchain.prompts import PromptTemplate


def setup_prompt():
	# 프롬프트 템플릿 구성
    prompt_template = PromptTemplate.from_template(
        "Given the following context, answer the question: {context}\n\nQuestion: {question}\n\nAnswer:"
    )
    output_parser = StrOutputParser()
    return prompt_template, output_parser

7. 체인 구성

  • 각 컴포넌트를 연결하여 체인을 구성하는 함수를 정의합니다.
from langchain_core.output_parsers import StrOutputParser

def setup_parser():
	# 프롬프트 템플릿 구성
    output_parser = StrOutputParser()
    return prompt_template, output_parser


from langchain.chains import RetrievalQA

def create_rag_chain(gpt, retriever, output_parser):
    rag_chain = RetrievalQA.from_chain_type(
        llm=gpt,
        chain_type="stuff",
        retriever=retriever,
        return_source_documents=True,
        output_parser=output_parser
    )
    return rag_chain

8. 체인 실행 및 스트리밍 출력

  • 질문에 대한 답변을 생성하고 스트리밍 출력을 수행하는 함수를 정의합니다.
from langchain_teddynote.messages import stream_response

def generate_answer(rag_chain, question):
    answer = rag_chain({"question": question})
    stream_response(answer["result"])  # 스트리밍 출력
    return answer

전체 코드 구조화

모든 함수를 통합하여 전체 워크플로를 구현하는 최종 코드는 다음과 같습니다.

#1. 환경설정
from dotenv import load_dotenv
from langchain_teddynote import logging
import os

def initialize_environment():
    # 환경 변수 로드(API 키등)
    load_dotenv()
    
    # 모델 및 캐시 경로 설정
    os.environ["TRANSFORMERS_CACHE"] = "./cache/"
    os.environ["HF_HOME"] = "./cache/"
    
    # LangSmith 추적 설정(프로젝트 이름 입력)
    logging.langsmith("RAG-Project")
    print("LangSmith 추적을 시작합니다.")


#2. 문서 로드 및 분할
from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

def load_and_split_document(file_path):
    
    # 문서 로드
    loader = TextLoader(file_path=file_path)
    
    # 문서 분할
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
    documents = text_splitter.split_documents(loader.load())
    return documents


#3. 임베딩 생성 및 벡터 저장소 구축
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS

def create_vector_store(documents):
    
    # 임베딩 모델 설정
    embedding_model = OpenAIEmbeddings()
    
    # 벡터 저장소 구축
    vector_store = FAISS.from_documents(documents, embedding_model)
    return vector_store


#4. 검색기 설정
from langchain.retrievers import VectorStoreRetriever

def setup_retriever(vector_store):
    
    # 검색기 설정
    retriever = VectorStoreRetriever(vectorstore=vector_store)
    return retriever


#5. 모델 로드 
# 언어 모델 로드
from langchain_openai import ChatOpenAI

def load_language_model():
   
   #ChatOpenAI 객체 생성
   gpt = ChatOpenAI(temperature=0.7, model_name="gpt-4o", streaming=True)
    return gpt

#6. 프롬프트 템플릿 및 결과 파서 설정
from langchain.prompts import PromptTemplate


def setup_prompt():
	# 프롬프트 템플릿 구성
    prompt_template = PromptTemplate.from_template(
        "Given the following context, answer the question: {context}\n\nQuestion: {question}\n\nAnswer:"
    )
    output_parser = StrOutputParser()
    return prompt_template, output_parser
    
# 7. 체인 구성
from langchain_core.output_parsers import StrOutputParser

def setup_parser():
	# 프롬프트 템플릿 구성
    output_parser = StrOutputParser()
    return prompt_template, output_parser


from langchain.chains import RetrievalQA

def create_rag_chain(gpt, retriever, output_parser):
    rag_chain = RetrievalQA.from_chain_type(
        llm=gpt,
        chain_type="stuff",
        retriever=retriever,
        return_source_documents=True,
        output_parser=output_parser
    )
    return rag_chain
    
 #8.체인 실행 및 스트리밍 출력
 from langchain_teddynote.messages import stream_response

def generate_answer(rag_chain, question):
    answer = rag_chain({"question": question})
    stream_response(answer["result"])  # 스트리밍 출력
    return answer

def main():
    # 1. 환경 설정
    initialize_environment()

    # 2. 문서 로드 및 분할
    documents = load_and_split_document(file_path="your_document.txt")

    # 3. 임베딩 생성 및 벡터 저장소 구축
    vector_store = create_vector_store(documents)

    # 4. 검색기 설정
    retriever = setup_retriever(vector_store)

    # 5. 모델 로드
    gpt = load_language_model()

    # 6. 프롬프트 템플릿 및 결과 파서 설정
    prompt_template, output_parser = setup_prompt_and_parser()

    # 7. 체인 구성
    rag_chain = create_rag_chain(gpt, retriever, output_parser)

    # 8. 체인 실행 및 스트리밍 출력
    question = "What is the capital of South Korea?"
    generate_answer(rag_chain, question)

if __name__ == "__main__":
    main()
profile
AI Engineer / 의료인공지능

0개의 댓글

관련 채용 정보