palisade / app.py
camelfilm's picture
Update app.py
cb165d9 verified
import gradio as gr
import os
import sys
import time
from typing import List, Tuple
from dotenv import load_dotenv
# src ํด๋”๋ฅผ Python ๊ฒฝ๋กœ์— ์ถ”๊ฐ€
sys.path.append(os.path.join(os.path.dirname(__file__), 'src'))
from src.embeddings import VehicleManualEmbeddings
from src.rag_chain import VehicleManualRAG
load_dotenv()
# ์ „์—ญ ๋ณ€์ˆ˜๋กœ RAG ์‹œ์Šคํ…œ ์ €์žฅ
rag_system = None
chat_history = []
def initialize_system():
"""RAG ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™”"""
global rag_system
if rag_system is not None:
return "์‹œ์Šคํ…œ์ด ์ด๋ฏธ ์ดˆ๊ธฐํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค."
try:
# ๊ฒฝ๋กœ ์„ค์ •
project_root = os.path.dirname(os.path.abspath(__file__))
index_path = os.path.join(project_root, "data", "faiss_index")
# ๋ฒกํ„ฐ ์ €์žฅ์†Œ ๋กœ๋“œ
print("๋ฒกํ„ฐ ์ธ๋ฑ์Šค ๋กœ๋”ฉ ์ค‘...")
embedder = VehicleManualEmbeddings()
vector_store = embedder.load_index()
# RAG ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™” (OpenAI ์‚ฌ์šฉ)
print("RAG ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™” ์ค‘...")
rag_system = VehicleManualRAG(vector_store, use_ollama=False)
return "์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™” ์™„๋ฃŒ! ์งˆ๋ฌธ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."
except Exception as e:
return f"์ดˆ๊ธฐํ™” ์‹คํŒจ: {str(e)}"
def answer_question(question: str, history: List[Tuple[str, str]]) -> Tuple[str, List[Tuple[str, str]]]:
"""
์งˆ๋ฌธ์— ๋‹ต๋ณ€ํ•˜๊ณ  ์ฑ„ํŒ… ๊ธฐ๋ก ์—…๋ฐ์ดํŠธ
Args:
question: ์‚ฌ์šฉ์ž ์งˆ๋ฌธ
history: ์ฑ„ํŒ… ๊ธฐ๋ก
Returns:
๋‹ต๋ณ€๊ณผ ์—…๋ฐ์ดํŠธ๋œ ์ฑ„ํŒ… ๊ธฐ๋ก
"""
global rag_system
if rag_system is None:
return "๋จผ์ € '์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™”' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด์ฃผ์„ธ์š”.", history
if not question.strip():
return "", history
try:
# RAG ์‹œ์Šคํ…œ์œผ๋กœ ๋‹ต๋ณ€ ์ƒ์„ฑ
result = rag_system.answer_question(question)
# ๋‹ต๋ณ€ ํฌ๋งทํŒ…
answer = result['answer']
source_pages = result.get('source_pages', [])
response_time = result.get('response_time', 0)
# ์ถœ์ฒ˜ ์ •๋ณด ์ถ”๊ฐ€
# if source_pages:
# answer += f"\n\n๐Ÿ“„ **์ถœ์ฒ˜**: ๋งค๋‰ด์–ผ {', '.join(map(str, source_pages[:3]))} ํŽ˜์ด์ง€"
#answer += f"\nโฑ๏ธ ์‘๋‹ต์‹œ๊ฐ„: {response_time:.2f}์ดˆ"
# ์ฑ„ํŒ… ๊ธฐ๋ก ์—…๋ฐ์ดํŠธ
history.append((question, answer))
return "", history
except Exception as e:
error_msg = f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
history.append((question, error_msg))
return "", history
def clear_chat():
"""์ฑ„ํŒ… ๊ธฐ๋ก ์ดˆ๊ธฐํ™”"""
return [], []
def create_demo():
"""Gradio ๋ฐ๋ชจ ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ"""
with gr.Blocks(title="๐Ÿš— ํŒฐ๋ฆฌ์„ธ์ด๋“œ ๋งค๋‰ด์–ผ AI ์–ด์‹œ์Šคํ„ดํŠธ", theme=gr.themes.Soft()) as demo:
# ํ—ค๋”
gr.Markdown("""
# ๐Ÿš— ํŒฐ๋ฆฌ์„ธ์ด๋“œ 2026 ๋งค๋‰ด์–ผ AI ์–ด์‹œ์Šคํ„ดํŠธ
ํ˜„๋Œ€ ํŒฐ๋ฆฌ์„ธ์ด๋“œ ์ฐจ๋Ÿ‰ ๋งค๋‰ด์–ผ์— ๋Œ€ํ•œ ์งˆ๋ฌธ์„ ์ž์—ฐ์–ด๋กœ ์ž…๋ ฅํ•˜์„ธ์š”.
AI๊ฐ€ ๋งค๋‰ด์–ผ์„ ๊ฒ€์ƒ‰ํ•˜์—ฌ ์ •ํ™•ํ•œ ๋‹ต๋ณ€์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
**์‚ฌ์šฉ ๋ฐฉ๋ฒ•:**
1. ๋จผ์ € '์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™”' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”
2. ์ฐจ๋Ÿ‰ ๊ด€๋ จ ์งˆ๋ฌธ์„ ์ž…๋ ฅํ•˜์„ธ์š”. ๋ฐ˜๋“œ์‹œ **์งˆ๋ฌธ**์˜ ํ˜•ํƒœ๋กœ ์ž…๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
3. Enter๋ฅผ ๋ˆ„๋ฅด๊ฑฐ๋‚˜ '์ „์†ก' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”
""")
# ์ดˆ๊ธฐํ™” ์„น์…˜
with gr.Row():
init_btn = gr.Button("๐Ÿš€ ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™”", variant="primary")
init_status = gr.Textbox(label="์ƒํƒœ", interactive=False)
# ์ฑ„ํŒ… ์ธํ„ฐํŽ˜์ด์Šค
chatbot = gr.Chatbot(
label="๋Œ€ํ™” ๋‚ด์—ญ",
height=400,
bubble_full_width=False
)
with gr.Row():
msg = gr.Textbox(
label="์งˆ๋ฌธ ์ž…๋ ฅ",
placeholder="์˜ˆ: ์—”์ง„์˜ค์ผ ๊ต์ฒด ์ฃผ๊ธฐ๋Š”? / ํƒ€์ด์–ด ๊ณต๊ธฐ์••์€? / ๊ฒฝ๊ณ ๋“ฑ์ด ์ผœ์กŒ์–ด์š”",
lines=2,
scale=4
)
send_btn = gr.Button("๐Ÿ“ค ์ „์†ก", variant="primary", scale=1)
# ์˜ˆ์‹œ ์งˆ๋ฌธ ๋ฒ„ํŠผ๋“ค
gr.Markdown("### ๐Ÿ’ก ์˜ˆ์‹œ ์งˆ๋ฌธ")
with gr.Row():
example_btns = [
gr.Button("์—”์ง„์˜ค์ผ ๊ต์ฒด ์ฃผ๊ธฐ", size="sm"),
gr.Button("ํƒ€์ด์–ด ์ ์ • ๊ณต๊ธฐ์••", size="sm"),
gr.Button("์™€์ดํผ ๊ต์ฒด ๋ฐฉ๋ฒ•", size="sm"),
gr.Button("๊ฒฝ๊ณ ๋“ฑ ๋Œ€์ฒ˜๋ฒ•", size="sm")
]
# ์ปจํŠธ๋กค ๋ฒ„ํŠผ
with gr.Row():
clear_btn = gr.Button("๐Ÿ—‘๏ธ ๋Œ€ํ™” ์ดˆ๊ธฐํ™”")
# ์ถ”๊ฐ€ ์ •๋ณด
with gr.Accordion("๐Ÿ“Š ์‹œ์Šคํ…œ ์ •๋ณด", open=False):
gr.Markdown("""
- **PDF**: LX3_2026_ko_KR.pdf (590ํŽ˜์ด์ง€)
- **์ฒญํฌ ์ˆ˜**: 1,329๊ฐœ
- **์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ**: paraphrase-multilingual-MiniLM-L12-v2
- **๋ฒกํ„ฐ DB**: FAISS
- **LLM**: GPT-3.5-turbo
- **ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„**: < 2์ดˆ
""")
# ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
init_btn.click(
fn=initialize_system,
outputs=init_status
)
# ๋ฉ”์‹œ์ง€ ์ „์†ก
msg.submit(answer_question, [msg, chatbot], [msg, chatbot])
send_btn.click(answer_question, [msg, chatbot], [msg, chatbot])
# ์˜ˆ์‹œ ์งˆ๋ฌธ ๋ฒ„ํŠผ๋“ค
example_questions = [
"์—”์ง„์˜ค์ผ ๊ต์ฒด ์ฃผ๊ธฐ๋Š” ์–ผ๋งˆ๋‚˜ ๋˜๋‚˜์š”?",
"ํƒ€์ด์–ด ์ ์ • ๊ณต๊ธฐ์••์€ ์–ผ๋งˆ์ธ๊ฐ€์š”?",
"์™€์ดํผ๋ฅผ ์–ด๋–ป๊ฒŒ ๊ต์ฒดํ•˜๋‚˜์š”?",
"๊ฒฝ๊ณ ๋“ฑ์ด ์ผœ์กŒ์„ ๋•Œ ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•˜๋‚˜์š”?"
]
for btn, question in zip(example_btns, example_questions):
btn.click(
lambda current_history, q=question: (q, current_history),
inputs=[chatbot],
outputs=[msg, chatbot]
)
# ๋Œ€ํ™” ์ดˆ๊ธฐํ™”
clear_btn.click(clear_chat, outputs=[chatbot, msg])
# ์ž๋™ ์ดˆ๊ธฐํ™”
demo.load(fn=initialize_system, outputs=init_status)
return demo
# ๋ฉ”์ธ ์‹คํ–‰
if __name__ == "__main__":
print("=" * 60)
print("๐Ÿš— ํŒฐ๋ฆฌ์„ธ์ด๋“œ ๋งค๋‰ด์–ผ AI ์–ด์‹œ์Šคํ„ดํŠธ ์‹œ์ž‘")
print("=" * 60)
# ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ฒดํฌ
if not os.getenv("OPENAI_API_KEY"):
print("\n๊ฒฝ๊ณ : OPENAI_API_KEY ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
print("์„ค์ • ๋ฐฉ๋ฒ•:")
print("Windows: set OPENAI_API_KEY=sk-...")
print("Mac/Linux: export OPENAI_API_KEY=sk-...")
# ์ง์ ‘ ์ž…๋ ฅ ๋ฐ›๊ธฐ
api_key = input("\nOpenAI API Key๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š” (sk-...): ").strip()
if api_key:
os.environ["OPENAI_API_KEY"] = api_key
print("API Key ์„ค์ • ์™„๋ฃŒ")
else:
print("API Key ์—†์ด๋Š” ๊ณ ๊ธ‰ ๋‹ต๋ณ€ ๊ธฐ๋Šฅ์ด ์ œํ•œ๋ฉ๋‹ˆ๋‹ค.")
# Gradio ๋ฐ๋ชจ ์‹คํ–‰
demo = create_demo()
print("\n์›น ์ธํ„ฐํŽ˜์ด์Šค ์‹œ์ž‘ ์ค‘...")
print("๋ธŒ๋ผ์šฐ์ €์—์„œ ์ž๋™์œผ๋กœ ์—ด๋ฆฝ๋‹ˆ๋‹ค.")
print("์ˆ˜๋™ ์ ‘์†: http://localhost:7860")
print("\n์ข…๋ฃŒ: Ctrl+C")
# ๋ฐ๋ชจ ์‹คํ–‰
demo.launch(
server_name="0.0.0.0", # ๋ชจ๋“  ๋„คํŠธ์›Œํฌ์—์„œ ์ ‘์† ๊ฐ€๋Šฅ
server_port=7860,
share=False, # True๋กœ ํ•˜๋ฉด ๊ณต๊ฐœ URL ์ƒ์„ฑ
inbrowser=True # ์ž๋™์œผ๋กœ ๋ธŒ๋ผ์šฐ์ € ์—ด๊ธฐ
)