Claude
Add HuggingFace Spaces support with Gradio UI
f47651a unverified
"""
HuggingFace Spaces์šฉ Gradio ์•ฑ
Financial RAG with Metacognitive Agent
"""
import gradio as gr
import os
import sys
from loguru import logger
import asyncio
from typing import Dict, Tuple
# ๋กœ๊น… ์„ค์ •
logger.remove()
logger.add(
sys.stdout,
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <level>{message}</level>",
level="INFO"
)
# ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ๋ฅผ Python ๊ฒฝ๋กœ์— ์ถ”๊ฐ€
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from app.metacognitive_agent import MetaCognitiveAgent
from app.rag_pipeline import RAGPipeline
from services.vector_store import VectorStore
from services.embedder import Embedder
from utils.config import settings
# ๊ธ€๋กœ๋ฒŒ ๋ณ€์ˆ˜
rag_pipeline = None
def setup_vector_db():
"""๋ฒกํ„ฐ DB ์ž๋™ ์„ค์ • (์—†์œผ๋ฉด ๋‹ค์šด๋กœ๋“œ ๋˜๋Š” ์ƒ์„ฑ)"""
db_path = settings.chroma_persist_directory
# ๋ฒกํ„ฐ DB๊ฐ€ ์ด๋ฏธ ์กด์žฌํ•˜๊ณ  ๋น„์–ด์žˆ์ง€ ์•Š์€์ง€ ํ™•์ธ
if os.path.exists(db_path):
if os.listdir(db_path):
logger.info("โœ… Vector DB already exists. Skipping setup.")
return True
logger.info("๐Ÿ“ฅ Vector DB not found. Setting up...")
os.makedirs(db_path, exist_ok=True)
# ์˜ต์…˜ 1: GitHub Release์—์„œ ๋‹ค์šด๋กœ๋“œ ์‹œ๋„
try:
import urllib.request
import tarfile
release_url = "https://github.com/csjjin2025/Hallucination_and_Deception_for_financial_RAG/releases/download/v1.0/chroma_db.tar.gz"
tar_path = "./data/chroma_db.tar.gz"
logger.info(f"Attempting to download from {release_url}...")
urllib.request.urlretrieve(release_url, tar_path)
# ํŒŒ์ผ ํฌ๊ธฐ ํ™•์ธ
file_size = os.path.getsize(tar_path)
if file_size > 1000:
logger.info(f"๐Ÿ“ฆ Extracting vector DB ({file_size} bytes)...")
with tarfile.open(tar_path, 'r:gz') as tar:
tar.extractall(path='./data/')
os.remove(tar_path)
logger.info("โœ… Vector DB downloaded and extracted!")
return True
else:
logger.warning(f"Downloaded file too small ({file_size} bytes)")
os.remove(tar_path)
except Exception as e:
logger.warning(f"Failed to download from Release: {e}")
# ์˜ต์…˜ 2: ํ…Œ์ŠคํŠธ DB ์ƒ์„ฑ
try:
logger.info("โš ๏ธ Creating test DB with sample data...")
import subprocess
result = subprocess.run(
["python", "scripts/quick_setup_test_db.py"],
capture_output=True,
text=True,
timeout=300
)
if result.returncode == 0:
logger.info("โœ… Test DB created successfully!")
return True
else:
logger.error(f"Test DB creation failed: {result.stderr}")
return False
except Exception as e:
logger.error(f"Failed to create test DB: {e}")
return False
def initialize_rag_system():
"""RAG ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™”"""
global rag_pipeline
try:
logger.info("=" * 80)
logger.info("๐Ÿš€ Financial RAG ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™” ์ค‘...")
logger.info("=" * 80)
# 0. Vector DB ์ž๋™ ์„ค์ •
logger.info("0๏ธโƒฃ Vector DB ์„ค์ • ํ™•์ธ ์ค‘...")
if not setup_vector_db():
logger.error("โŒ Vector DB ์„ค์ • ์‹คํŒจ")
return False
# 1. Vector Store ์ดˆ๊ธฐํ™”
logger.info("1๏ธโƒฃ Vector Store ๋กœ๋”ฉ ์ค‘...")
vector_store = VectorStore(
persist_directory=settings.chroma_persist_directory,
collection_name=settings.collection_name
)
doc_count = vector_store.collection.count()
logger.info(f"โœ… Vector Store ๋กœ๋”ฉ ์™„๋ฃŒ ({doc_count}๊ฐœ ๋ฌธ์„œ)")
# 2. Embedder ์ดˆ๊ธฐํ™”
logger.info("2๏ธโƒฃ Embedder ์ดˆ๊ธฐํ™” ์ค‘...")
embedder = Embedder(
model_type=settings.embedding_model,
model_name=settings.embedding_model_name,
openai_api_key=settings.openai_api_key,
cohere_api_key=settings.cohere_api_key
)
logger.info(f"โœ… Embedder ์ดˆ๊ธฐํ™” ์™„๋ฃŒ ({embedder.get_embedding_dimension()}์ฐจ์›)")
# 3. Metacognitive Agent ์ดˆ๊ธฐํ™”
logger.info("3๏ธโƒฃ Metacognitive Agent ์ดˆ๊ธฐํ™” ์ค‘...")
agent = MetaCognitiveAgent(api_key=settings.anthropic_api_key)
logger.info(f"โœ… Agent ์ดˆ๊ธฐํ™” ์™„๋ฃŒ ({agent.model})")
# 4. RAG Pipeline ์ƒ์„ฑ
logger.info("4๏ธโƒฃ RAG Pipeline ์ƒ์„ฑ ์ค‘...")
rag_pipeline = RAGPipeline(
vector_store=vector_store,
embedder=embedder,
metacognitive_agent=agent
)
logger.info("โœ… RAG Pipeline ์ƒ์„ฑ ์™„๋ฃŒ")
logger.info("=" * 80)
logger.info("โœจ ์‹œ์Šคํ…œ ์ค€๋น„ ์™„๋ฃŒ!")
logger.info(f"๐Ÿ“š Vector DB: {doc_count}๊ฐœ ๋ฌธ์„œ")
logger.info(f"๐Ÿค– Model: {agent.model}")
logger.info("=" * 80)
return True
except Exception as e:
logger.error(f"โŒ ์ดˆ๊ธฐํ™” ์‹คํŒจ: {str(e)}")
import traceback
logger.error(traceback.format_exc())
return False
def format_sources(sources: list) -> str:
"""์ถœ์ฒ˜ ๋ฌธ์„œ ํฌ๋งทํŒ…"""
if not sources:
return "์ถœ์ฒ˜ ๋ฌธ์„œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."
formatted = "### ๐Ÿ“š ์ฐธ๊ณ  ๋ฌธ์„œ\n\n"
for idx, source in enumerate(sources[:3], 1): # ์ƒ์œ„ 3๊ฐœ๋งŒ ํ‘œ์‹œ
similarity = source.get('similarity', 0) * 100
filename = source.get('source_filename', 'unknown')
text = source.get('text', '')[:300] # ์•ž 300์ž๋งŒ
formatted += f"**{idx}. {filename}** (์œ ์‚ฌ๋„: {similarity:.1f}%)\n"
formatted += f"> {text}...\n\n"
return formatted
def format_metacognition(metacognition: Dict) -> str:
"""๋ฉ”ํƒ€์ธ์ง€ ๊ณผ์ • ํฌ๋งทํŒ…"""
if not metacognition:
return ""
history = metacognition.get('thinking_history', [])
iterations = metacognition.get('iterations', 0)
formatted = f"\n\n### ๐Ÿง  ๋ฉ”ํƒ€์ธ์ง€ ๊ณผ์ • ({iterations}ํšŒ ๋ฐ˜๋ณต)\n\n"
for idx, step in enumerate(history, 1):
stage = step.get('stage', 'unknown')
content = step.get('content', '')
stage_emoji = {
'planning': '๐Ÿ“‹',
'monitoring': '๐Ÿ‘๏ธ',
'evaluation': 'โœ…',
'revision': '๐Ÿ”„'
}.get(stage, '๐Ÿ’ญ')
formatted += f"**{stage_emoji} {stage.capitalize()}**\n"
formatted += f"{content}\n\n"
return formatted
async def process_query_async(question: str, top_k: int, enable_metacognition: bool) -> Tuple[str, str]:
"""๋น„๋™๊ธฐ ์ฟผ๋ฆฌ ์ฒ˜๋ฆฌ"""
if not rag_pipeline:
return "โŒ ์‹œ์Šคํ…œ์ด ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.", ""
if not question.strip():
return "โŒ ์งˆ๋ฌธ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", ""
try:
logger.info(f"๐Ÿ“ ์งˆ๋ฌธ: {question}")
# RAG ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ์ฟผ๋ฆฌ ์ฒ˜๋ฆฌ
result = await rag_pipeline.query(
question=question,
top_k=top_k,
enable_metacognition=enable_metacognition
)
# ๋‹ต๋ณ€ ํฌ๋งทํŒ…
answer = result.get('answer', '๋‹ต๋ณ€์„ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.')
sources = result.get('sources', [])
metacognition = result.get('metacognition', None)
# ์ถœ๋ ฅ ๊ตฌ์„ฑ
main_output = f"## ๐Ÿ’ฌ ๋‹ต๋ณ€\n\n{answer}\n\n"
main_output += format_sources(sources)
# ๋ฉ”ํƒ€์ธ์ง€ ๊ณผ์ • (๋ณ„๋„ ํƒญ)
meta_output = format_metacognition(metacognition) if metacognition else "๋ฉ”ํƒ€์ธ์ง€๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค."
logger.info("โœ… ๋‹ต๋ณ€ ์ƒ์„ฑ ์™„๋ฃŒ")
return main_output, meta_output
except Exception as e:
error_msg = f"โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
logger.error(error_msg)
import traceback
logger.error(traceback.format_exc())
return error_msg, ""
def process_query(question: str, top_k: int, enable_metacognition: bool) -> Tuple[str, str]:
"""Gradio์šฉ ๋™๊ธฐ ๋ž˜ํผ"""
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
return loop.run_until_complete(process_query_async(question, top_k, enable_metacognition))
finally:
loop.close()
# Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ
def create_interface():
"""Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ"""
with gr.Blocks(theme=gr.themes.Soft(), title="Financial RAG with Metacognitive Agent") as demo:
gr.Markdown("""
# ๐Ÿ’ผ Financial RAG System
### ๋ฉ”ํƒ€์ธ์ง€ ์—์ด์ „ํŠธ ๊ธฐ๋ฐ˜ ๊ธˆ์œต/๊ฒฝ์ œ ์งˆ์˜์‘๋‹ต ์‹œ์Šคํ…œ
์ด ์‹œ์Šคํ…œ์€ ๊ธˆ์œต/๊ฒฝ์ œ ๋…ผ๋ฌธ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์งˆ๋ฌธ์— ๋‹ต๋ณ€ํ•ฉ๋‹ˆ๋‹ค.
๋ฉ”ํƒ€์ธ์ง€ ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”ํ•˜๋ฉด ๋” ๊นŠ์ด ์žˆ๋Š” ์‚ฌ๊ณ  ๊ณผ์ •์„ ๊ฑฐ์ณ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
""")
with gr.Row():
with gr.Column(scale=2):
question_input = gr.Textbox(
label="๐Ÿ’ฌ ์งˆ๋ฌธ์„ ์ž…๋ ฅํ•˜์„ธ์š”",
placeholder="์˜ˆ: ํฌํŠธํด๋ฆฌ์˜ค ๋‹ค๊ฐํ™”๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?",
lines=3
)
with gr.Row():
top_k_slider = gr.Slider(
minimum=1,
maximum=10,
value=5,
step=1,
label="๐Ÿ” ๊ฒ€์ƒ‰ํ•  ๋ฌธ์„œ ๊ฐœ์ˆ˜"
)
metacognition_check = gr.Checkbox(
label="๐Ÿง  ๋ฉ”ํƒ€์ธ์ง€ ํ™œ์„ฑํ™”",
value=True,
info="๋” ๊นŠ์ด ์žˆ๋Š” ์‚ฌ๊ณ  ๊ณผ์ • (์ฒ˜๋ฆฌ ์‹œ๊ฐ„ ์ฆ๊ฐ€)"
)
submit_btn = gr.Button("๐Ÿš€ ์งˆ๋ฌธํ•˜๊ธฐ", variant="primary", size="lg")
gr.Markdown("""
### ๐Ÿ’ก ์‚ฌ์šฉ ํŒ
- **๋ฉ”ํƒ€์ธ์ง€ ํ™œ์„ฑํ™”**: Planning โ†’ Monitoring โ†’ Evaluation โ†’ Revision ๊ณผ์ •์„ ๊ฑฐ์ณ ์‹ ์ค‘ํ•œ ๋‹ต๋ณ€ ์ƒ์„ฑ
- **๋ฉ”ํƒ€์ธ์ง€ ๋น„ํ™œ์„ฑํ™”**: ๋น ๋ฅธ ๋‹ต๋ณ€ ์ƒ์„ฑ
- **๊ฒ€์ƒ‰ ๋ฌธ์„œ ๊ฐœ์ˆ˜**: ๋งŽ์„์ˆ˜๋ก ๋” ๋งŽ์€ ์ •๋ณด๋ฅผ ์ฐธ๊ณ ํ•˜์ง€๋งŒ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ ์ฆ๊ฐ€
""")
with gr.Column(scale=3):
with gr.Tabs():
with gr.Tab("๐Ÿ“ ๋‹ต๋ณ€ ๋ฐ ์ถœ์ฒ˜"):
answer_output = gr.Markdown(label="๋‹ต๋ณ€")
with gr.Tab("๐Ÿง  ๋ฉ”ํƒ€์ธ์ง€ ๊ณผ์ •"):
metacognition_output = gr.Markdown(label="์‚ฌ๊ณ  ๊ณผ์ •")
# ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
submit_btn.click(
fn=process_query,
inputs=[question_input, top_k_slider, metacognition_check],
outputs=[answer_output, metacognition_output]
)
# Enter ํ‚ค๋กœ๋„ ์ œ์ถœ
question_input.submit(
fn=process_query,
inputs=[question_input, top_k_slider, metacognition_check],
outputs=[answer_output, metacognition_output]
)
gr.Markdown("""
---
### ๐Ÿ“Œ ์‹œ์Šคํ…œ ์ •๋ณด
- **๋ชจ๋ธ**: Claude 3.5 Sonnet
- **์ž„๋ฒ ๋”ฉ**: sentence-transformers/all-MiniLM-L6-v2
- **๋ฒกํ„ฐ DB**: ChromaDB
""")
return demo
# ๋ฉ”์ธ ์‹คํ–‰
if __name__ == "__main__":
# ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™”
logger.info("์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™” ์‹œ์ž‘...")
success = initialize_rag_system()
if not success:
logger.error("์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™” ์‹คํŒจ. ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.")
sys.exit(1)
# Gradio ์•ฑ ์‹คํ–‰
demo = create_interface()
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False
)