🔥알림🔥
① 테디노트 유튜브 - 구경하러 가기!
② LangChain 한국어 튜토리얼 바로가기 👀

2 분 소요

이번 포스팅에서는 랭체인(LangChain) 을 활용하여 PDF 문서를 로드하고, 문서의 내용에 기반하여 질의응답(Question-Answering) 하는 방법에 대해 알아보겠습니다.

이번 튜토리얼에서는 langchain 의 문서 로드 - 분할 - 벡터스토어(vectorstore)에 임베딩된 문서를 저장 하는 방법을 다룹니다. 여러 벡터스토어 중 오픈소스인 Chroma DB 를 활용합니다.

후반부에는 langchain hub 에서 프롬프트를 다운로드 받고, 이를 ChatGPT 모델과 결합하여 문서에 기반한 질의응답 Chain 을 생성합니다.


✔️ (이전글) LangChain 튜토리얼


🌱 환경설정

# 필요한 라이브러리 설치
# !pip install -q openai langchain langchainhub pypdf
# OPENAI_API
# import os

# os.environ['OPENAI_API_KEY'] = 'OPENAI API KEY 입력'
# 토큰 정보로드를 위한 라이브러리
# 설치: pip install python-dotenv
from dotenv import load_dotenv

# 토큰 정보로드
load_dotenv()
True

🔥 PDF 기반 질의 응답(Question-Answering)

다음은 비구조화된 데이터를 QA 체인(Question-Answering chain) 으로 변환하는 파이프라인에 대한 기술적 번역입니다:

  • 데이터 로드: 우선, 데이터를 로드해야 합니다. LangChain 통합 허브를 사용하여 전체 로더 세트를 둘러보세요.

  • 데이터 분할: 텍스트 분할기는 문서를 지정된 크기의 분할로 나눕니다.

  • 저장: 저장소(예: 종종 vectorstore)는 분할을 보관하고 종종 임베드합니다.

  • 검색: 앱은 저장소에서 분할을 검색합니다(예: 종종 입력 질문과 유사한 임베딩으로).

  • 생성: LLM은 질문과 검색된 데이터를 포함하는 프롬프트를 사용하여 답변을 생성합니다.

① 데이터 로드

PyPDFLoader 를 활용하여 PDF 파일을 로드 합니다.

from langchain.document_loaders import PyPDFLoader

# PDF 파일 로드
loader = PyPDFLoader("data/황순원-소나기.pdf")
document = loader.load()
document[0].page_content[:200] # 내용 추출
'- 1 -소나기\n황순원\n소년은 개울가에서 소녀를 보자 곧 윤 초시네 증손녀 (曾孫女 )딸이라는 걸 알 수 있었다 . \n소녀는 개울에다 손을 잠그고 물장난을 하고 있는 것이다 . 서울서는 이런 개울물을 보지 \n못하기나 한 듯이.\n벌써 며칠째 소녀는 , 학교에서 돌아오는 길에 물장난이었다 . 그런데 , 어제까지 개울 기슭에\n서 하더니 , 오늘은 징검다리 한가운'

② 데이터 분할

CharacterTextSplitter 로 chunk_size 기준으로 문서를 쪼갭니다. chunk_overlap 에 50개의 토큰을 지정하여 문서-문서 간 겹쳐지는 부분(overlap) 이 있도록 하여 비교적 유연한 요약 결과를 도출할 수 있도록 합니다.

from langchain.text_splitter import CharacterTextSplitter

text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=50)
texts = text_splitter.split_documents(document)

③ 저장 및 검색

OpenAIEmbeddings 를 활용하여 문서의 내용을 임베딩한 뒤, Chroma 벡터스토어(vectorstore) 에 저장합니다.

마지막 줄에는 as_retriever() 로 retriever 형태로 가져오는데, 이는 추후 사용자의 query 입력시, 입력된 query로 vectorestore에서 유사성이 높은 데이터를 추출해 낼 때 쓰입니다.

from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma

# 임베딩
embeddings = OpenAIEmbeddings()
# Chroma DB 에 저장
docsearch = Chroma.from_documents(texts, embeddings)
# retriever 가져옴
retriever = docsearch.as_retriever()

④ 프롬프트 템플릿

아래의 예제는 langchain hub 에서 RAG Prompt 를 가져오는 예제입니다.

이처럼 langchain hub 에서 공개된 프롬프트를 다운로드 받거나, ChatPromptTemplate 를 직접 생성하는 것도 가능합니다. 자세한 사항은 ConversationChain, 템플릿 사용법 에서 확인할 수 있습니다.

# langchain hub 에서 Prompt 다운로드 예시
# https://smith.langchain.com/hub/rlm/rag-prompt

from langchain import hub

rag_prompt = hub.pull("rlm/rag-prompt")
rag_prompt
ChatPromptTemplate(input_variables=['question', 'context'], output_parser=None, partial_variables={}, messages=[HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['question', 'context'], output_parser=None, partial_variables={}, template="You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.\nQuestion: {question} \nContext: {context} \nAnswer:", template_format='f-string', validate_template=True), additional_kwargs={})])

⑤ 생성

마지막 단계는 LLM 모델을 정의하고 Chain 을 생성하는 단계 입니다.

# LLM
from langchain.chat_models import ChatOpenAI

# ChatGPT 모델 지정
llm = ChatOpenAI(model_name="gpt-4-0613", temperature=0)
# RAG chain 생성
from langchain.schema.runnable import RunnablePassthrough

# pipe operator를 활용한 체인 생성
rag_chain = (
    {"context": retriever, "question": RunnablePassthrough()} 
    | rag_prompt 
    | llm 
)

⑥ 테스트

rag_chain.invoke("이 소설의 제목은 뭐야?")
AIMessage(content='이 소설의 제목은 "소나기"입니다.', additional_kwargs={}, example=False)
rag_chain.invoke("이 소설의 저자는 누구야?")
AIMessage(content='이 소설의 저자는 황순원입니다.', additional_kwargs={}, example=False)

댓글남기기