NaraRAG_Chroma / app.py
kiikiik's picture
Update app.py
c0c77ab verified
import os
from dotenv import load_dotenv
load_dotenv()
import streamlit as st
import time
import base64
import uuid
import tempfile
from langchain_upstage import UpstageEmbeddings, ChatUpstage
from langchain_chroma import Chroma
from langchain_core.messages import HumanMessage, SystemMessage
if "id" not in st.session_state:
st.session_state.id = uuid.uuid4()
st.session_state.file_cache = {}
session_id = st.session_state.id
client = None
def reset_chat():
st.session_state.messages = []
st.session_state.context = None
DB_PATH = '/content/drive/MyDrive/db/db' # Vectorstore DB ์šฉ๋Ÿ‰๋ฌธ์ œ๋กœ ์—…๋กœ๋“œ ๋ถˆ๊ฐ€ (> 1GB)
vectorstore = Chroma(persist_directory=DB_PATH, embedding_function=UpstageEmbeddings(model="solar-embedding-1-large"))
chat = ChatUpstage(upstage_api_key = 'up_psZn40aDVMRiIBdkzCx5ImcZqzez5')
from langchain.retrievers.multi_query import MultiQueryRetriever
retriever = MultiQueryRetriever.from_llm(
retriever=vectorstore.as_retriever(), llm=chat)
# 1) ์ฑ—๋ด‡์— '๊ธฐ์–ต'์„ ์ž…ํžˆ๊ธฐ ์œ„ํ•œ ์ฒซ๋ฒˆ์งธ ๋‹จ๊ณ„
# ์ด์ „ ๋ฉ”์‹œ์ง€๋“ค๊ณผ ์ตœ์‹  ์‚ฌ์šฉ์ž ์งˆ๋ฌธ์„ ๋ถ„์„ํ•ด, ๋ฌธ๋งฅ์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ์—†์ด ํ˜ผ์ž์„œ๋งŒ ๋ดค์„ ๋•Œ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ์งˆ๋ฌธ์„ ๋‹ค์‹œ ๊ตฌ์„ฑํ•จ.
# ์ฆ‰ ์ƒˆ๋กœ ๋“ค์–ด์˜จ ๊ทธ ์งˆ๋ฌธ ์ž์ฒด์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‹ค์‹œ ์žฌํŽธ์„ฑ (llm ์˜ˆ์ „ ๋Œ€ํ™”๋ฅผ ๊ธฐ์–ตํ•ด์„œ ๋Œ€ํ™”๋ฅผ ์žฌ๊ตฌ์„ฑ)
from langchain.chains import create_history_aware_retriever
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
contextualize_q_system_prompt = "When there are older conversations and more recent user questions, these questions may be related to previous conversations. In this case, change the question to a question that can be understood independently without needing to know the content of the conversation. You don't have to answer the question, just reformulate it if necessary or leave it as is."
# MessagePlaceHolder: 'chat_history' ์ž…๋ ฅ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด์ „ ๋ฉ”์„ธ์ง€ ๊ธฐ๋ก๋“ค์„ ํ”„๋กฌํ”„ํŠธ์— ํฌํ•จ์‹œํ‚ด.
# ์ฆ‰, ํ”„๋กฌํ”„ํŠธ, ๋ฉ”์„ธ์ง€ ๊ธฐ๋ก(๋ฌธ๋งฅ ์ •๋ณด), ์‚ฌ์šฉ์ž์˜ ์งˆ๋ฌธ์œผ๋กœ ํ”„๋กฌํ”„ํŠธ๊ฐ€ ๊ตฌ์„ฑ๋จ.
contextualize_q_prompt = ChatPromptTemplate.from_messages(
[
('system', contextualize_q_system_prompt),
MessagesPlaceholder('chat_history'),
('human', '{input}'),
]
)
# ์ด๋ฅผ ํ† ๋Œ€๋กœ ๋ฉ”์„ธ์ง€ ๊ธฐ๋ก์„ ๊ธฐ์–ตํ•˜๋Š” retriever๋ฅผ ์ƒ์„ฑ.
history_aware_retriever = create_history_aware_retriever(
chat, retriever, contextualize_q_prompt
)
# 2) ์ฒด์ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์„œ๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ๋Š” retriever ์ฒด์ธ ์ƒ์„ฑ
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
qa_system_prompt = """
You are an intelligent assistant helping the members of the Korean National Assembly with questions related to law and policy. You must answer politely. Read the given questions carefully and give the answer in Korean ONLY using the following pieces of the context. AGAIN, WRITE YOUR ANSWER ONLY BASED ON THE CONTEXT FROM THE DATABASE AND DON'T SEARCH ON THE INTERNET.
DO NOT TRY TO MAKE UP AN ANSWER:
ย - If the answer to the question cannot be determined from the context alone, say "I cannot determine the answer to that.".
ย - If the context is empty, just say "I do not know the answer to that.".
Context: {context} """
qa_prompt = ChatPromptTemplate.from_messages(
[
('system', qa_system_prompt),
MessagesPlaceholder('chat_history'),
('human','{input}'+' ๋‹ต๋ณ€์€ ๊ตฌ์ฒด์ ์œผ๋กœ ์ตœ์‹  ์ •๋ณด๋ถ€ํ„ฐ ์‹œ๊ฐ„์˜ ํ๋ฆ„์— ๋”ฐ๋ผ ์ž‘์„ฑํ•ด์ค˜. ๊ทธ๋ฆฌ๊ณ  ๋‹ต๋ณ€ํ•  ๋•Œ metadata์— ์žˆ๋Š” source๋ฅผ ํ•จ๊ป˜ ์ œ๊ณตํ•ด์ค˜.'),
]
)
question_answer_chain = create_stuff_documents_chain(chat, qa_prompt)
# ๊ฒฐ๊ณผ๊ฐ’์€ input, chat_history, context, answer ํฌํ•จํ•จ.
rag_chain = create_retrieval_chain(history_aware_retriever, question_answer_chain)
# history_aware_retriever ๋Œ€์‹  ์ผ๋ฐ˜ retriever๋กœ ๋‹ค์‹œ ์‹œ๋„
# ์›น์‚ฌ์ดํŠธ ์ œ๋ชฉ
st.title("๊ตญํšŒ ํšŒ์˜๋ก ๊ธฐ๋ฐ˜ ์ฑ—๋ด‡ ์„œ๋น„์Šค :orange[NaraRAG] ๐Ÿ“œโš–๏ธ")
if 'messages' not in st.session_state:
st.session_state['messages'] = [{'role': 'assistant',
'content': '์•ˆ๋…•ํ•˜์„ธ์š”! ๊ตญํšŒ ํšŒ์˜๋ก์— ๊ด€ํ•ด ๊ถ๊ธˆํ•œ ๊ฒƒ์ด ์žˆ์œผ๋ฉด ์–ธ์ œ๋“  ๋ฌผ์–ด๋ด์ฃผ์„ธ์š” ๐Ÿ˜Š'}]
# ๋Œ€ํ™” ๋‚ด์šฉ์„ ๊ธฐ๋กํ•˜๊ธฐ ์œ„ํ•ด ์…‹์—…
# Streamlit ํŠน์„ฑ์ƒ ํ™œ์„ฑํ™”ํ•˜์ง€ ์•Š์œผ๋ฉด ๋‚ด์šฉ์ด ๋‹ค ๋‚ ์•„๊ฐ.
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# ํ”„๋กฌํ”„ํŠธ ๋น„์šฉ์ด ๋„ˆ๋ฌด ๋งŽ์ด ์†Œ์š”๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด
MAX_MESSAGES_BEFORE_DELETION = 4
# ์›น์‚ฌ์ดํŠธ์—์„œ ์œ ์ €์˜ ์ธํ’‹์„ ๋ฐ›๊ณ  ์œ„์—์„œ ๋งŒ๋“  AI ์—์ด์ „ํŠธ ์‹คํ–‰์‹œ์ผœ์„œ ๋‹ต๋ณ€ ๋ฐ›๊ธฐ
if prompt := st.chat_input("Ask a question!"):
# ์œ ์ €๊ฐ€ ๋ณด๋‚ธ ์งˆ๋ฌธ์ด๋ฉด ์œ ์ € ์•„์ด์ฝ˜๊ณผ ์งˆ๋ฌธ ๋ณด์—ฌ์ฃผ๊ธฐ
# ๋งŒ์•ฝ ํ˜„์žฌ ์ €์žฅ๋œ ๋Œ€ํ™” ๋‚ด์šฉ ๊ธฐ๋ก์ด 4๊ฐœ๋ณด๋‹ค ๋งŽ์œผ๋ฉด ์ž๋ฅด๊ธฐ
if len(st.session_state.messages) >= MAX_MESSAGES_BEFORE_DELETION:
# Remove the first two messages
del st.session_state.messages[0]
del st.session_state.messages[0]
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
# AI๊ฐ€ ๋ณด๋‚ธ ๋‹ต๋ณ€์ด๋ฉด AI ์•„์ด์ฝ˜์ด๋ž‘ LLM ์‹คํ–‰์‹œ์ผœ์„œ ๋‹ต๋ณ€ ๋ฐ›๊ณ  ์ŠคํŠธ๋ฆฌ๋ฐํ•ด์„œ ๋ณด์—ฌ์ฃผ๊ธฐ
with st.chat_message("assistant"):
message_placeholder = st.empty()
full_response = ""
result = rag_chain.invoke({"input": prompt, "chat_history": st.session_state.messages})
# ์ฆ๊ฑฐ์ž๋ฃŒ ๋ณด์—ฌ์ฃผ๊ธฐ
with st.expander("Evidence context"):
st.write( result['context'])
for chunk in result["answer"].split(" "):
full_response += chunk + " "
time.sleep(0.2)
message_placeholder.markdown(full_response + "โ–Œ")
message_placeholder.markdown(full_response)
st.session_state.messages.append({"role": "assistant", "content": full_response})
print("_______________________")
print(st.session_state.messages)