Ssul's Blog
[ChatGPT] OpenAI임베딩 이용해서 RAG구현(langchain,vectordb) 본문
RAG의 구동 개념이 궁금하면 우선 아래글 확인하시고,
이번 글에서는 Langchain과 벡터DB를 활용해서, RAG를 구현하는 실전 진행
#1. 기획(계획)
- RAG에 사용할 주요 저서를 pdf파일이 있다
- 해당 pdf 본문을 500개 단위로 끝어서 > openai임베딩을 하여 > 벡터DB에 저장한다.
- 사용자가 질문을 하면, 질문을 openai임베딩한다
- 임베딩된 사용자의 질문과 임베딩된 pdf본문을 비교한다 > 가장 유사한 문단 3개를 가져온다
- 임베딩된 3개 문단을 다시 문장으로 바꾼다 > 3개의 문단과 사용자의 질문을 AI한테 한다
- 응답을 받아 사용자에게 출력해준다
#2. 필요한 라이브러리를 설치한다
pip install langchain openai chromadb tiktoken langchain-openai
[./database/embedding_chroma.py]
#3. pdf파일 본문을 쪼개서, 임베딩 하고, vectordb에 저장
- 주요 저서파일을 ./bookdata폴더 밑에 저장
(*맥 미리보기로 pdf파일의 표지나 목차를 삭제했는데, 이럴경우 글자가 깨져서 읽히니 참고)
load_dotenv()
openai.api_key = os.getenv('OPENAI_API_KEY')
# 디렉토리 설정
CUR_DIR = os.path.dirname(os.path.abspath(__file__))
CHROMA_PERSIST_DIR = os.path.join(CUR_DIR, 'chroma_persist')
CHROMA_COLLECTION_NAME= "ai-kwon-book"
- openai 임베딩을 사용하기 위해, openai셋팅
- vectordb가 저장될 폴더를 설정한다.
- vectordb의 collectionname을 설정한다. 일반적인 DB의 테이블 명이라 생각하면 됨
LOADER_DICT = {
"pdf": PyPDFLoader,
"txt": TextLoader,
"md": UnstructuredMarkdownLoader,
"ipynb": NotebookLoader,
}
loader = LOADER_DICT.get('pdf')
if loader is None:
raise ValueError("Not supported file type")
# pdf 문서 불러오기
loader = DirectoryLoader('./bookdata', glob="*.pdf", loader_cls=loader)
docs = loader.load()
print('문서의 개수 :', len(docs))
- 문서의 종류에 따라, 가져올 Loader을 연결해주는 LOADER_DICT선언
- ./bookdata폴더에 있는 pdf파일을 모두 불러온다.
- docs에 pdf파일들을 읽은 data가 저장
# textsplitter로 문서를 쪼개기
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100)
texts = text_splitter.split_documents(docs)
print('문서의 페이지 수 :', len(texts))
- chunk사이즈 500으로, 100은 겹치게 해서 문서의 내용을 쪼갠다
- 쪼갠 문장모음을 texts에 저장
# 임베딩해서, 벡터db에 저장
vectordb = Chroma.from_documents(
texts,
OpenAIEmbeddings(),
persist_directory=CHROMA_PERSIST_DIR,
collection_name=CHROMA_COLLECTION_NAME,
)
- 임베딩을 진행하고,
- vectordb로 저장(vectordb 폴더, collection네임)
[./api.py]
#4. 고객이 질문을 받는다
@app.post("/chat")
def generate_chat(req: UserRequest):
context = req.dict()
user_input = context["user_message"]
messages = create_prompt(user_input)
#### 나중에 vectordb에서 가져온 검색값3개랑 질문 다시 보낼 예정
- 고객이 채팅창에 입력한다.
- 입력받은 메세지로, create_prompt함수를 호출한다.
[./chain.py]
#5. 고객의 질문을 기반으로 시스템 메세지를 만든다
def create_prompt(query):
# 질문과 가장 관련있는 본문 3개를 가져옴
result = query_vectordb(query, use_retriever=True)
print(result[0].page_content)
print(result[1].page_content)
print(result[2].page_content)
system_message = f"""
You're a great counselor. The articles below are taken from my books that are relevant to your questions.
Documents:
doc1: """ + result[0].page_content + """
doc2: """ + result[1].page_content + """
doc3: """ + result[2].page_content + """
Instead of just answering the question, please refer to this and answer it.
Be sure to answer in Korean
"""
user_content = f"""User question: "{str(query)}". """
messages = [
{"role": "system", "content": system_message},
{"role": "user", "content": user_content},
]
return messages
- 고객이 입력한 질문을 query_vectordb함수에 보내서, 총 3개의 관련도 높은 데이터를 가져온다.
- 가져온데이터 3개를 시스템 메세지에 참고하라고 넣어준다.
- 고객의 원래질문을 붙여넣어준다
- 완성된 최종 messages를 반환한다.
[./rag.py]
#6. 고객의 질문을 기반으로 vectordb에서 관련 본문 3개를 리턴한다
def query_vectordb(query: str, use_retriever: bool = False):
from pprint import pprint
vectordb = Chroma(
persist_directory=CHROMA_PERSIST_DIR,
embedding_function=OpenAIEmbeddings(),
collection_name=CHROMA_COLLECTION_NAME,
)
if use_retriever:
retriever = vectordb.as_retriever(search_kwargs={"k": 3})
top_docs = retriever.get_relevant_documents(query)
else:
top_docs = vectordb.similarity_search(query, k=3)
pprint(top_docs)
return top_docs
- 앞에서 저장했던 chromadb를 불러온다.
- chromadb에 고객의 입력문장을 넣어서, 가장 관련성 높은 top3를 가져온다.
- use_retriever가 True면 retriever사용. False면 db직접사용(후자를 선호)
#7. 정리
- 참고할만한 문서/도서를 openai 임베딩으로 임베딩하여, vectordb에 저장(embedding_chroma.py)
- 입력받은 사용자의 문장을 저장된 vectordb데이터와 비교해서, 가장 관련도가 높은 top3본문 가져오기(rag.py)
- 가져온 본문을 가지고 > 시스템메세지 작성 > 사용자의 입력더해서 최종 messages 완성(chain.py)
- 완성된 messages를 가지고, AI에게 질문해서 좋은 응답 받아오기(api.py)
[최종 api.py코드]
load_dotenv()
openai.api_key = os.getenv('OPENAI_API_KEY')
client = OpenAI()
app = FastAPI()
class UserRequest(BaseModel):
user_message: str
CUR_DIR = os.path.dirname(os.path.abspath(__file__))
@app.post("/chat")
def generate_chat(req: UserRequest):
context = req.dict()
user_input = context["user_message"]
messages = create_prompt(user_input)
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=messages,
temperature=0.4,
max_tokens=500
)
print(response.choices[0].message.content)
return response.choices[0].message.content
'AI & ML > 사용하기' 카테고리의 다른 글
RAG가 적용된 나만의 챗봇 만들기(langchain, FastAPI, streamlit) (0) | 2024.05.09 |
---|---|
[ChatGPT] openai 임베딩 사용해서 RAG구현(생코딩,csv파일) (0) | 2024.02.14 |
[Langchain #1] 기본채팅부터 커스텀parser사용 (0) | 2024.02.13 |
Fine-tuning with LoRA(PEFT)로 스팸문자 분류기 만들기 (1) | 2024.02.04 |
[ChatGPT] OpenAI function_call 제대로 이해하기 (0) | 2024.02.02 |