import os
from dotenv import load_dotenv
import uuid
import tempfile
from langchain_upstage import UpstageEmbeddings
from langchain_chroma import Chroma
from langchain_community.document_loaders import PyPDFLoader
# 환경 변수 로드
load_dotenv()
# 세션 ID 생성
session_id = uuid.uuid4()
# PDF 파일 처리 및 인덱싱 함수
def process_pdf(file_path):
loader = PyPDFLoader(file_path)
pages = loader.load_and_split()
vectorstore = Chroma.from_documents(pages, UpstageEmbeddings(model="solar-embedding-1-large"))
return vectorstore
# RAG 체인 생성 함수
def create_rag_chain(vectorstore, api_key):
retriever = vectorstore.as_retriever(k=2)
from langchain_upstage import ChatUpstage
chat = ChatUpstage(upstage_api_key=api_key)
from langchain.chains import create_history_aware_retriever
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
contextualize_q_system_prompt = """이전 대화 내용과 최신 사용자 질문이 있을 때, 이 질문이 이전 대화 내용과 관련이 있을 수 있습니다.
이런 경우, 대화 내용을 알 필요 없이 독립적으로 이해할 수 있는 질문으로 바꾸세요.
질문에 답할 필요는 없고, 필요하다면 그저 다시 구성하거나 그대로 두세요."""
contextualize_q_prompt = ChatPromptTemplate.from_messages([
("system", contextualize_q_system_prompt),
MessagesPlaceholder("chat_history"),
("human", "{input}"),
])
history_aware_retriever = create_history_aware_retriever(chat, retriever, contextualize_q_prompt)
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
qa_system_prompt = """질문-답변 업무를 돕는 보조원입니다.
질문에 답하기 위해 검색된 내용을 사용하세요.
답을 모르면 모른다고 말하세요.
답변은 세 문장 이내로 간결하게 유지하세요.
## 답변 예시
📍답변 내용:
📍증거:
{context}"""
qa_prompt = ChatPromptTemplate.from_messages([
("system", qa_system_prompt),
MessagesPlaceholder("chat_history"),
("human", "{input}"),
])
question_answer_chain = create_stuff_documents_chain(chat, qa_prompt)
rag_chain = create_retrieval_chain(history_aware_retriever, question_answer_chain)
return rag_chain
# PDF 파일 처리 예제
uploaded_file_path = "path/to/your/pdf/file.pdf"
# 임시 파일 디렉토리에서 파일 처리
with tempfile.TemporaryDirectory() as temp_dir:
file_path = os.path.join(temp_dir, os.path.basename(uploaded_file_path))
with open(file_path, "wb") as f:
f.write(open(uploaded_file_path, "rb").read())
# PDF 파일 처리 및 인덱싱
vectorstore = process_pdf(file_path)
# RAG 체인 생성
rag_chain = create_rag_chain(vectorstore, os.getenv("UPSTAGE_API_KEY"))
# 질문에 대한 답변 생성 예제
question = "What is the history of AI?"
chat_history = []
result = rag_chain.invoke({"input": question, "chat_history": chat_history})
answer = result["answer"]
context = result["context"]
print("Answer:", answer)
print("Context:", context)
과정 예시
질문 "What are the key components of AI?"에 대해 답변 "The key components of AI include machine learning, natural language processing, and computer vision."가 생성되는 과정을 설명하겠습니다. 이 과정은 LangChain 라이브러리를 사용하여 PDF 문서에서 관련 정보를 검색하고, 그 정보를 바탕으로 답변을 생성하는 일련의 단계로 이루어집니다.
단계별 과정
1. PDF 파일 로드 및 전처리
- PDF 파일 업로드: 사용자가 "Artificial Intelligence Overview.pdf" 파일을 업로드합니다.
- 파일 저장 및 로딩: 업로드된 파일을 임시 디렉토리에 저장하고 PyPDFLoader를 사용하여 파일을 로딩합니다.
- 페이지 분할: PDF 파일을 페이지 단위로 분할하여 각 페이지를 개별 문서로 처리합니다.
loader = PyPDFLoader(file_path) pages = loader.load_and_split()
- 문서 임베딩: 각 페이지를 UpstageEmbeddings 모델을 사용하여 임베딩 벡터로 변환합니다.
- 벡터 스토어 저장: 임베딩된 문서를 Chroma 벡터 스토어에 저장합니다.
vectorstore = Chroma.from_documents(pages, UpstageEmbeddings(model="solar-embedding-1-large"))
retriever = vectorstore.as_retriever(k=2)
- 질문 입력: 사용자가 질문 "What are the key components of AI?"을 입력합니다.
- 질문 재구성: create_history_aware_retriever 함수를 사용하여 이전 대화 내용이 없으므로 질문을 그대로 유지합니다.
contextualize_q_system_prompt = """이전 대화 내용과 최신 사용자 질문이 있을 때, 이 질문이 이전 대화 내용과 관련이 있을 수 있습니다.
이런 경우, 대화 내용을 알 필요 없이 독립적으로 이해할 수 있는 질문으로 바꾸세요. 질문에 답할 필요는 없고, 필요하다면 그저 다시 구성하거나 그대로 두세요."""
contextualize_q_prompt = ChatPromptTemplate.from_messages([ ("system", contextualize_q_system_prompt), MessagesPlaceholder("chat_history"), ("human", "{input}"), ])
history_aware_retriever = create_history_aware_retriever(chat, retriever, contextualize_q_prompt)
- 문서 검색: history_aware_retriever를 사용하여 벡터 스토어에서 관련 문서를 검색합니다. 이 과정에서 retriever가 질문과 관련된 문서를 찾아 반환합니다.
retriever_results = history_aware_retriever.retrieve({"input": question, "chat_history": []})
4. 답변 생성
- 답변 생성 체인 구성: create_stuff_documents_chain 함수를 사용하여 검색된 문서를 바탕으로 답변을 생성하는 체인을 구성합니다.
qa_system_prompt = """질문-답변 업무를 돕는 보조원입니다.
질문에 답하기 위해 검색된 내용을 사용하세요. 답을 모르면 모른다고 말하세요. 답변은 세 문장 이내로 간결하게 유지하세요.
## 답변 예시 📍답변 내용: 📍증거: {context}"""
qa_prompt = ChatPromptTemplate.from_messages([ ("system", qa_system_prompt), MessagesPlaceholder("chat_history"), ("human", "{input}"), ])
question_answer_chain = create_stuff_documents_chain(chat, qa_prompt)
- 문서 검색과 답변 생성 결합: create_retrieval_chain 함수를 사용하여 문서 검색과 답변 생성 과정을 결합합니다.
rag_chain = create_retrieval_chain(history_aware_retriever, question_answer_chain)
- 답변 생성: rag_chain을 사용하여 검색된 문서를 바탕으로 질문에 대한 답변을 생성합니다.
result = rag_chain.invoke({"input": question, "chat_history": []}) answer = result["answer"]
질문 "What are the key components of AI?"에 대해 벡터 스토어에서 관련 문서를 검색한 후, 해당 문서를 바탕으로 생성된 답변이 "The key components of AI include machine learning, natural language processing, and computer vision."입니다.
요약
- PDF 파일을 로드하고 분할: PDF 파일을 페이지 단위로 로드하고 분할합니다.
- 문서 임베딩: 각 페이지를 임베딩하여 벡터 스토어에 저장합니다.
- 질문 입력 및 분석: 사용자가 질문을 입력하면, 이를 분석하여 관련 문서를 검색합니다.
- 답변 생성: 검색된 문서를 바탕으로 질문에 대한 답변을 생성합니다.
- 최종 답변 제공: 생성된 답변을 사용자에게 제공합니다.
이 과정은 LangChain 라이브러리의 다양한 기능을 활용하여 질문에 대한 정확하고 관련성 높은 답변을 생성하도록 설계되었습니다.
두 번째 질문 "Can you explain more about the second component?"가 들어왔을 때의 과정을 설명하겠습니다. 이 과정에서는 이전 대화 기록을 바탕으로 문맥을 고려하여 질문을 재구성하고, 관련 문서를 검색하고, 최종적으로 답변을 생성합니다.
### 단계별 과정
#### 1. 질문 입력 및 이전 대화 기록 유지
**이전 대화 기록**:
- 사용자: "What are the key components of AI?"
- 챗봇: "The key components of AI include machine learning, natural language processing, and computer vision."
**새로운 질문**:
- 사용자: "Can you explain more about the second component?"
```python
# 새로운 질문을 입력받습니다.
new_question = "Can you explain more about the second component?"
```
#### 2. 질문 재구성
**문맥을 고려한 질문 재구성**:
- "second component"가 이전 답변에서 "natural language processing"을 가리키는 것을 인식하고 질문을 재구성합니다.
- 재구성된 질문: "Can you explain more about natural language processing?"
```python
# create_history_aware_retriever 함수는 이전 대화 기록을 사용하여 질문을 재구성합니다.
reconstructed_question = "Can you explain more about natural language processing?"
```
`contextualize_q_prompt`와 `create_history_aware_retriever`를 사용하여 이 과정을 처리합니다:
```python
# 이전 대화 기록을 포함하여 질문을 재구성합니다.
history_aware_retriever = create_history_aware_retriever(chat, retriever, contextualize_q_prompt)
```
#### 3. 문서 검색
**검색 프로세스**:
- 재구성된 질문을 사용하여 벡터 스토어에서 관련 문서를 검색합니다.
- `history_aware_retriever`가 벡터 스토어에서 "natural language processing"에 대한 관련 문서를 검색합니다.
```python
retriever_results = history_aware_retriever.retrieve({"input": reconstructed_question, "chat_history": st.session_state.messages})
```
#### 4. 답변 생성
**답변 생성 체인**:
- 검색된 문서를 바탕으로 답변을 생성합니다.
- `question_answer_chain`이 검색된 문서를 바탕으로 "natural language processing"에 대해 자세히 설명합니다.
```python
qa_system_prompt = """질문-답변 업무를 돕는 보조원입니다.
질문에 답하기 위해 검색된 내용을 사용하세요.
답을 모르면 모른다고 말하세요.
답변은 세 문장 이내로 간결하게 유지하세요.
## 답변 예시
📍답변 내용:
📍증거:
{context}"""
qa_prompt = ChatPromptTemplate.from_messages([
("system", qa_system_prompt),
MessagesPlaceholder("chat_history"),
("human", "{input}"),
])
question_answer_chain = create_stuff_documents_chain(chat, qa_prompt)
```
**문서 검색과 답변 생성 결합**:
- `rag_chain`을 사용하여 문서 검색과 답변 생성 과정을 결합합니다.
```python
rag_chain = create_retrieval_chain(history_aware_retriever, question_answer_chain)
```
**답변 생성**:
- `rag_chain`을 사용하여 재구성된 질문에 대한 답변을 생성합니다.
```python
result = rag_chain.invoke({"input": reconstructed_question, "chat_history": st.session_state.messages})
answer = result["answer"]
```
#### 5. 최종 답변 제공
**답변 표시**:
- "Can you explain more about natural language processing?"에 대한 답변이 생성되어 사용자에게 제공됩니다.
- 예를 들어, 답변은 다음과 같이 될 수 있습니다: "Natural language processing (NLP) is a field of AI that focuses on the interaction between computers and humans through natural language. It involves various techniques such as tokenization, sentiment analysis, and machine translation."
```python
print("Answer:", answer)
```
### 요약
1. **질문 입력 및 이전 대화 기록 유지**: 새로운 질문을 입력받고 이전 대화 기록을 유지합니다.
2. **질문 재구성**: 이전 대화 내용을 바탕으로 질문을 문맥에 맞게 재구성합니다.
3. **문서 검색**: 재구성된 질문을 사용하여 벡터 스토어에서 관련 문서를 검색합니다.
4. **답변 생성**: 검색된 문서를 바탕으로 질문에 대한 답변을 생성합니다.
5. **최종 답변 제공**: 생성된 답변을 사용자에게 제공합니다.
이 과정을 통해, 챗봇은 "Can you explain more about the second component?"와 같은 문맥 의존적인 질문에 대해 정확하고 관련성 높은 답변을 제공할 수 있습니다.
'기술공부 > LLM' 카테고리의 다른 글
논문 리뷰: REALM(Retrieval-Augmented Language Model) (1) | 2025.02.03 |
---|---|
논문 리뷰: RAPTOR: Recursive Abstractive Processing for Tree-Organized Retrieval (0) | 2025.02.03 |
LLamaIndex vs. LangChain (1) | 2024.07.03 |
RAG 모델 만들기: Upstage, Document AI (0) | 2024.06.30 |
RAG ChatBot 만들기: Upstage, Solar LLM (0) | 2024.06.28 |