fastAPI 심화
- Chart.js - pdf, csv 파일 업로드 후 데이터 정제하여 llm으로 처리 후 결과 도출 - sqlite로 데이터 저장 - ORM - SQLAlchemy
This commit is contained in:
@@ -1,25 +1,33 @@
|
||||
from fastapi import APIRouter, Request, UploadFile
|
||||
from backend.services.llm_service import question_and_answer
|
||||
from backend.schemas.basic_schema import QuestionRequest
|
||||
from backend.services.rag_service import upload_document
|
||||
from backend.services.rag_service import upload_document, rag_chat, rag_chat_stream
|
||||
from fastapi.responses import StreamingResponse
|
||||
router = APIRouter(prefix="/api")
|
||||
|
||||
# http://127.0.0.1:8000/api/question
|
||||
@router.post("/question")
|
||||
async def question(req:QuestionRequest):
|
||||
async def question(req: QuestionRequest):
|
||||
answer = question_and_answer(req.question)
|
||||
|
||||
return {"message" : answer}
|
||||
|
||||
# http://127.0.0.1:8000/api/rag/upload
|
||||
@router.post("/rag/upload")
|
||||
async def fileUpload(file:UploadFile):
|
||||
async def file_Upload(file: UploadFile):
|
||||
# 서비스 호출
|
||||
return upload_document(file)
|
||||
|
||||
|
||||
# http://127.0.0.1:8000/api/rag/question
|
||||
|
||||
@router.post("/rag/question")
|
||||
async def question():
|
||||
pass
|
||||
async def question(req: QuestionRequest):
|
||||
answer = rag_chat(req.question)
|
||||
|
||||
return {"message" : answer}
|
||||
|
||||
@router.post("/rag/question/stream")
|
||||
async def question_stream(req: QuestionRequest):
|
||||
answer = rag_chat_stream(req.question)
|
||||
|
||||
return StreamingResponse(rag_chat_stream(req.question), media_type="text/plain")
|
||||
@@ -1,6 +1,90 @@
|
||||
# pdf 업로드 => 분할 => 인덱스 생성
|
||||
def upload_document(file):
|
||||
pass
|
||||
import os
|
||||
|
||||
from langchain_community.document_loaders import PyPDFLoader
|
||||
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
||||
from langchain_community.vectorstores import FAISS
|
||||
from langchain_core.runnables import RunnablePassthrough
|
||||
from langchain_core.output_parsers import StrOutputParser
|
||||
from langchain_core.prompts import ChatPromptTemplate
|
||||
|
||||
from backend.ai.embedding import watson_embedding
|
||||
from backend.ai.llm import watson_llm
|
||||
|
||||
UPLOAD_PATH = "uploads"
|
||||
|
||||
def upload_document(file):
|
||||
# file 저장
|
||||
file_path = os.path.join(UPLOAD_PATH, file.filename)
|
||||
|
||||
with open(file_path, "wb") as f:
|
||||
f.write(file.file.read())
|
||||
|
||||
# pdf 업로드 => 분할 => 인덱스 생성
|
||||
# pdf 로드
|
||||
loader = PyPDFLoader(file_path)
|
||||
docs = loader.load()
|
||||
|
||||
splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=30)
|
||||
chunks = splitter.split_documents(docs)
|
||||
|
||||
faiss_store = FAISS.from_documents(documents=chunks, embedding=watson_embedding)
|
||||
faiss_store.save_local("./db/vectorstore")
|
||||
|
||||
return {"message": "업로드 성공"}
|
||||
|
||||
# 질문 => 유사도 검색 => 문서 => llm 답변 생성
|
||||
def rag_chat(question: str):
|
||||
|
||||
faiss_store = FAISS.load_local("./db/vectorstore", watson_embedding, allow_dangerous_deserialization=True)
|
||||
|
||||
retriever = faiss_store.as_retriever(search_kwargs={"k": 3})
|
||||
|
||||
### LLM
|
||||
# 1. prompt
|
||||
message = """\
|
||||
당신은 PDF 기반 RAG AI 입니다.
|
||||
다음 문서를 참고해서 질문에 답변하세요.
|
||||
|
||||
문서:
|
||||
{context}
|
||||
|
||||
질문:
|
||||
{question}
|
||||
"""
|
||||
|
||||
rag_prompt = ChatPromptTemplate.from_template(message)
|
||||
# 2. chain
|
||||
chain = {"context": retriever, "question": RunnablePassthrough()} | rag_prompt | watson_llm | StrOutputParser()
|
||||
|
||||
# 3. answer
|
||||
answer = chain.invoke(question)
|
||||
return answer
|
||||
|
||||
async def rag_chat_stream(question: str):
|
||||
|
||||
faiss_store = FAISS.load_local("./db/vectorstore", watson_embedding, allow_dangerous_deserialization=True)
|
||||
|
||||
retriever = faiss_store.as_retriever(search_kwargs={"k": 3})
|
||||
|
||||
### LLM
|
||||
# 1. prompt
|
||||
message = """\
|
||||
당신은 PDF 기반 RAG AI 입니다.
|
||||
다음 문서를 참고해서 질문에 답변하세요.
|
||||
|
||||
문서:
|
||||
{context}
|
||||
|
||||
질문:
|
||||
{question}
|
||||
"""
|
||||
|
||||
rag_prompt = ChatPromptTemplate.from_template(message)
|
||||
# 2. chain
|
||||
chain = {"context": retriever, "question": RunnablePassthrough()} | rag_prompt | watson_llm | StrOutputParser()
|
||||
|
||||
# 3. answer
|
||||
# chain.stream() : 동기방식(요청 => 응답을 할 떄까지 기다리는 방식)
|
||||
# chain.astream() : 비동기방식(다른 일 처리 가능)
|
||||
async for chunk in chain.astream(question):
|
||||
yield chunk
|
||||
@@ -17,28 +17,58 @@ async function ask() {
|
||||
}
|
||||
|
||||
// 파일 업로드
|
||||
document.querySelector("#uploadBtn").addEventListener("click", uploadFile)
|
||||
async function uploadFile()
|
||||
{
|
||||
const fileInput = document.querySelector("#file");
|
||||
document.querySelector("#uploadBtn").addEventListener("click",uploadFile)
|
||||
async function uploadFile() {
|
||||
const fileInput = document.querySelector("#file")
|
||||
// 첨부파일 정보 가져오기
|
||||
const file = fileInput.files[0];
|
||||
const file = fileInput.files[0]
|
||||
|
||||
if(!file)
|
||||
{
|
||||
alert("파일을 선택해주세요.");
|
||||
if(!file){
|
||||
alert('파일을 선택하세요');
|
||||
return;
|
||||
}
|
||||
|
||||
// form 만들어 전송
|
||||
const formData = new FormData()
|
||||
formData.append("file", file)
|
||||
const formData = new FormData();
|
||||
formData.append("file",file);
|
||||
|
||||
const response = await fetch("/api/reg/question", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
const response = await fetch("/api/rag/upload",{
|
||||
method:"POST",
|
||||
body:formData
|
||||
})
|
||||
// 전송 후 answer 도착 시 answer 화면에 보여주기
|
||||
const answer = await response.json()
|
||||
document.querySelector('#answer').textContent = answer.message
|
||||
document.querySelector('#result').textContent = answer.message
|
||||
}
|
||||
|
||||
document.querySelector("#askBtn").addEventListener("click", rag_ask)
|
||||
async function rag_ask() {
|
||||
// 사용자가 질문 입력 시 질문을 서버로 전송
|
||||
const question = document.querySelector('#question').value
|
||||
|
||||
const response = await fetch("/api/rag/question", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({question : question})
|
||||
})
|
||||
// 전송 후 answer 도착 시 answer 화면에 보여주기
|
||||
// const answer = await response.json()
|
||||
// document.querySelector('#answer_result').textContent = answer.message
|
||||
|
||||
// stream 방식
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let answer = "";
|
||||
|
||||
while(true){
|
||||
const { value, done } = await reader.read();
|
||||
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
answer += decoder.decode(value);
|
||||
document.querySelector('#answer_result').textContent = answer;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,12 @@
|
||||
</div>
|
||||
<hr>
|
||||
<div id="result"></div>
|
||||
<hr>
|
||||
<div>
|
||||
<input type="text" name="question" id="question">
|
||||
<button id="askBtn">질문</button>
|
||||
</div>
|
||||
<div id="answer_result"></div>
|
||||
<script src="{{ url_for('static', path='js/index.js')}}"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user