rag 기법으로 처리하기에는 너무 길어질 수 있기 때문에 text splitters모듈로 문맥을 유지하면서 문장을 적절히 분할해야한다.
spaCy는 파이썬으로 개발된 자연어 처리 라이브러리이다.
자연어 처리는 인간이 일상에서 사용하는 언어를 컴퓨터가 이해하고 분석할 수 있는 일련의 기술이다.
python3 -m pip install spacy
python3 -m spacy download ko_core_news_sm
from langchain.document_loaders import PyMuPDFLoader
from langchain.text_splitter import SpacyTextSplitter #← SpacyTextSplitter를 가져옴
file_path = "/Users/jeong-yuyeong/Documents/GitHub/LangChain/03_retrieval/sample.pdf"
loader = PyMuPDFLoader(file_path) # sample.pdf 로드
documents = loader.load()
text_splitter = SpacyTextSplitter( #← SpacyTextSplitter를 초기화
chunk_size=300, #← 분할할 크기를 설정
pipeline="ko_core_news_sm" #← 분할에 사용할 언어 모델을 설정
)
splitted_documents = text_splitter.split_documents(documents) #← 문서를 분할
print(f"분할 전 문서 개수: {len(documents)}")
print(f"분할 후 문서 개수: {len(splitted_documents)}")
의 결과는 다음과 같다.
분할 전 문서 개수: 12
분할 후 문서 개수: 70
위 코드에서는 SpacyTextSplitter를 가져오고 chunk_size 매개변수를 통해 문장을 분할할 크기를 설정한다.
open ai 임베딩을 이용하려면 파이썬 패키지인 tiktoken이 필요하다.
python3 -m pip install tiktoken
벡터 데이터 베이스로 크로마를 사용한다. 크로마는 오픈 소프 벡터 데이터베이스로 설치로 쉽게 작동 가능하다.
python3 -m pip install chromadb
from langchain.document_loaders import PyMuPDFLoader
from langchain.embeddings import OpenAIEmbeddings #← OpenAIEmbeddings 가져오기
from langchain.text_splitter import SpacyTextSplitter
from langchain.vectorstores import Chroma #← Chroma 가져오기
loader = PyMuPDFLoader("./sample.pdf")
documents = loader.load()
text_splitter = SpacyTextSplitter(
chunk_size=300,
pipeline="ko_core_news_sm"
)
splitted_documents = text_splitter.split_documents(documents)
embeddings = OpenAIEmbeddings( #← OpenAIEmbeddings를 초기화
model="text-embedding-ada-002" #← 모델명을 지정
)
database = Chroma( #← Chroma를 초기화
persist_directory="./.data", #← 영속화 데이터 저장 위치 지정
embedding_function=embeddings #← 벡터화할 모델을 지정
)
database.add_documents( #← 문서를 데이터베이스에 추가
splitted_documents, #← 추가할 문서 지정
)
print("데이터베이스 생성이 완료되었습니다.") #← 완료 알림
이를 통해 랭체인을 사용하여 pdf 문장을 읽고 문장을 적절한 크기로 분할하고, 그 문장을 벡터화해 데이터베이스에 저장하기까지의 작동을 확인하고 작업을 완료했다.
파이썬으로 채팅화면을 만들 수 있는 chainlit 이란 라이브러리를 사용해 브라우저에서 실제 사용할 수 있는 애플리케이션을 만들어보자.
python -m pip install chainit==0.5.1
import chainlit as cl
@cl.on_chat_start #← 채팅이 시작될 때 실행할 함수를 정의
async def on_chat_start():
await cl.Message(content="준비되었습니다! 메시지를 입력하세요!").send() #← 초기에 표시할 메시지를 보냄
@cl.on_message #← 메시지를 보낼 때 실행할 함수를 정의
async def on_message(input_message):
print("입력된 메시지: " + input_message)
await cl.Message(content="안녕하세요!").send() #← 챗봇의 답변을 보냄
import chainlit as cl
from langchain.chat_models import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings
from langchain.prompts import PromptTemplate
from langchain.schema import HumanMessage
from langchain.vectorstores import Chroma
embeddings = OpenAIEmbeddings(
model="text-embedding-ada-002"
)
chat = ChatOpenAI(model="gpt-3.5-turbo")
prompt = PromptTemplate(template="""문장을 바탕으로 질문에 답하세요.
문장:
{document}
질문: {query}
""", input_variables=["document", "query"])
database = Chroma(
persist_directory="./.data",
embedding_function=embeddings
)
@cl.on_chat_start
async def on_chat_start():
await cl.Message(content="준비되었습니다! 메시지를 입력하세요!").send()
@cl.on_message
async def on_message(input_message):
print("입력된 메시지: " + input_message)
documents = database.similarity_search(input_message) #← input_message로 변경
documents_string = ""
for document in documents:
documents_string += f"""
---------------------------
{document.page_content}
"""
result = chat([
HumanMessage(content=prompt.format(document=documents_string,
query=input_message)) #← input_message로 변경
])
await cl.Message(content=result.content).send() #← 챗봇의 답변을 보냄
import os
import chainlit as cl
from langchain.chat_models import ChatOpenAI
from langchain.document_loaders import PyMuPDFLoader
from langchain.embeddings import OpenAIEmbeddings
from langchain.prompts import PromptTemplate
from langchain.schema import HumanMessage
from langchain.text_splitter import SpacyTextSplitter
from langchain.vectorstores import Chroma
embeddings = OpenAIEmbeddings(
model="text-embedding-ada-002"
)
chat = ChatOpenAI(model="gpt-3.5-turbo")
prompt = PromptTemplate(template="""문장을 기반으로 질문에 답하세요.
문장:
{document}
질문: {query}
""", input_variables=["document", "query"])
text_splitter = SpacyTextSplitter(chunk_size=300, pipeline="ko_core_news_sm")
@cl.on_chat_start
async def on_chat_start():
files = None #← 파일이 선택되어 있는지 확인하는 변수
while files is None: #← 파일이 선택될 때까지 반복
files = await cl.AskFileMessage(
max_size_mb=20,
content="PDF를 선택해 주세요",
accept=["application/pdf"],
raise_on_timeout=False,
).send()
file = files[0]
if not os.path.exists("tmp"): #← tmp 디렉터리가 존재하는지 확인
os.mkdir("tmp") #← 존재하지 않으면 생성
with open(f"tmp/{file.name}", "wb") as f: #← PDF 파일을 저장
f.write(file.content) #← 파일 내용을 작성
documents = PyMuPDFLoader(f"tmp/{file.name}").load() #← 저장한 PDF 파일을 로드
splitted_documents = text_splitter.split_documents(documents) #← 문서를 분할
database = Chroma( #← 데이터베이스 초기화
embedding_function=embeddings,
# 이번에는 persist_directory를 지정하지 않음으로써 데이터베이스 영속화를 하지 않음
)
database.add_documents(splitted_documents) #← 문서를 데이터베이스에 추가
cl.user_session.set( #← 데이터베이스를 세션에 저장
"database", #← 세션에 저장할 이름
database #← 세션에 저장할 값
)
await cl.Message(content=f"`{file.name}` 로딩이 완료되었습니다. 질문을 입력하세요.").send() #← 불러오기 완료를 알림
@cl.on_message
async def on_message(input_message):
print("입력된 메시지: " + input_message)
database = cl.user_session.get("database") #← 세션에서 데이터베이스를 가져옴
documents = database.similarity_search(input_message)
documents_string = ""
for document in documents:
documents_string += f"""
---------------------------
{document.page_content}
"""
result = chat([
HumanMessage(content=prompt.format(document=documents_string,
query=input_message)) #← input_message로 변경
])
await cl.Message(content=result.content).send()
p138