Spaces:
Running
Running
import gradio as gr | |
import os | |
import json | |
import requests | |
from datetime import datetime | |
import time | |
from typing import List, Dict, Any, Generator, Tuple | |
import logging | |
import re | |
# ๋ก๊น ์ค์ | |
logging.basicConfig(level=logging.INFO) | |
logger = logging.getLogger(__name__) | |
# ์ถ๊ฐ ์ํฌํธ | |
from bs4 import BeautifulSoup | |
from urllib.parse import urlparse | |
import urllib.request | |
# Gemini API ์ํฌํธ | |
try: | |
from google import genai | |
from google.genai import types | |
GEMINI_AVAILABLE = True | |
except ImportError: | |
GEMINI_AVAILABLE = False | |
logger.warning("Google Gemini API๊ฐ ์ค์น๋์ง ์์์ต๋๋ค. pip install google-genai๋ก ์ค์นํ์ธ์.") | |
# ํ๊ฒฝ ๋ณ์์์ ํ ํฐ ๊ฐ์ ธ์ค๊ธฐ | |
FRIENDLI_TOKEN = os.getenv("FRIENDLI_TOKEN", "YOUR_FRIENDLI_TOKEN") | |
BAPI_TOKEN = os.getenv("BAPI_TOKEN", "YOUR_BRAVE_API_TOKEN") | |
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", "YOUR_GEMINI_API_KEY") | |
API_URL = "https://api.friendli.ai/dedicated/v1/chat/completions" | |
BRAVE_SEARCH_URL = "https://api.search.brave.com/res/v1/web/search" | |
MODEL_ID = "dep89a2fld32mcm" | |
TEST_MODE = os.getenv("TEST_MODE", "false").lower() == "true" | |
# ์ ์ญ ๋ณ์ | |
conversation_history = [] | |
class WuxingLLMSystem: | |
"""์คํยท์ค์ ๊ธฐ๋ฐ ํ๋ ฅ์ LLM ์์คํ """ | |
def __init__(self): | |
self.token = FRIENDLI_TOKEN | |
self.bapi_token = BAPI_TOKEN | |
self.gemini_api_key = GEMINI_API_KEY | |
self.api_url = API_URL | |
self.brave_url = BRAVE_SEARCH_URL | |
self.model_id = MODEL_ID | |
self.test_mode = TEST_MODE or (self.token == "YOUR_FRIENDLI_TOKEN") | |
self.use_gemini = False | |
self.gemini_client = None | |
# ์คํ ์ญํ ์ ์ | |
self.wuxing_roles = { | |
"wood": { | |
"name": "๊ฐ๋ ๊ด (ํ์ฅ)", | |
"virtue": "ไป", | |
"element": "ๆจ", | |
"traits": "ํฌ์ฉยท์ฑ์ฅํ ๋ฆฌ๋", | |
"expertise": "๋น์ ์ ์, ํ ์กฐ์จ, ์ธ์ฌ ์ก์ฑ", | |
"color": "#10b981" # Green | |
}, | |
"fire": { | |
"name": "์ ๋ตยท๊ธฐํ ๋ฆฌ๋", | |
"virtue": "็พฉ", | |
"element": "็ซ", | |
"traits": "์ด์ ยท๊ฒฐ๋จ, ๊ฐ์ฒ ์ ์ ", | |
"expertise": "์คยท์ฅ๊ธฐ ๋ก๋๋งต, ์ฌ์ ๋ชจ๋ธ ์ค๊ณ, ๋ฆฌ์คํฌ-๋ณด์ ์๋๋ฆฌ์ค", | |
"color": "#ef4444" # Red | |
}, | |
"metal": { | |
"name": "์ํคํ ์ฒ & ํ์ค ์ฑ ์", | |
"virtue": "็ฆฎ", | |
"element": "้", | |
"traits": "๊ตฌ์กฐํยท์ ๋ฐ, ์์คํ ์ค๊ณ ๋ง์คํฐ", | |
"expertise": "๊ธฐ์ ยท๋ฐ์ดํฐ ์ํคํ ์ฒ, ํ์ค ์๋ฆฝ, ํ์งยทํ์ฅ์ฑ ๊ฒ์ฆ", | |
"color": "#f59e0b" # Gold | |
}, | |
"water": { | |
"name": "๋๊ตฌ ํ์ฉ R&D ์คํ์ ๋ฆฌ์คํธ", | |
"virtue": "ๆบ", | |
"element": "ๆฐด", | |
"traits": "๋ถ์ยทํธ๊ธฐ์ฌ, ITยทAI ๋๊ตฌ ์ ๋ฌธ๊ฐ", | |
"expertise": "์ต์ ๊ธฐ์ ยท์์ฅ ์กฐ์ฌ, ํ๋กํ ํ์ ๊ฐ๋ฐ, ์๋ํยท์์ฐ์ฑ ํด", | |
"color": "#3b82f6" # Blue | |
}, | |
"earth": { | |
"name": "์คํยท์ด์ยทํ์ง ๋ด๋น", | |
"virtue": "ไฟก", | |
"element": "ๅ", | |
"traits": "์ ๋ขฐยท์ฑ์ค, ์คํ๋ ฅ", | |
"expertise": "์ผ์ ยท์์ฐยท๋ฆฌ์์ค ๊ด๋ฆฌ, ์ด์ ์ต์ ํ, ํ์ง ๋ณด์ฆ", | |
"color": "#a855f7" # Purple | |
} | |
} | |
if self.test_mode: | |
logger.warning("ํ ์คํธ ๋ชจ๋๋ก ์คํ๋ฉ๋๋ค.") | |
def set_llm_mode(self, mode: str): | |
"""LLM ๋ชจ๋ ์ค์ """ | |
if mode == "commercial" and GEMINI_AVAILABLE and self.gemini_api_key != "YOUR_GEMINI_API_KEY": | |
self.use_gemini = True | |
if not self.gemini_client: | |
self.gemini_client = genai.Client(api_key=self.gemini_api_key) | |
logger.info("Gemini 2.5 Pro ๋ชจ๋๋ก ์ ํ๋์์ต๋๋ค.") | |
else: | |
self.use_gemini = False | |
logger.info("๊ธฐ๋ณธ LLM ๋ชจ๋๋ก ์ ํ๋์์ต๋๋ค.") | |
def create_headers(self): | |
"""API ํค๋ ์์ฑ""" | |
return { | |
"Authorization": f"Bearer {self.token}", | |
"Content-Type": "application/json" | |
} | |
def create_brave_headers(self): | |
"""Brave API ํค๋ ์์ฑ""" | |
return { | |
"Accept": "application/json", | |
"Accept-Encoding": "gzip", | |
"X-Subscription-Token": self.bapi_token | |
} | |
def create_wood_initial_prompt(self, user_query: str, search_results: Dict = None) -> str: | |
"""ๆจ(๊ฐ๋ ๊ด) ์ด๊ธฐ ํ๋กฌํํธ""" | |
search_info = "" | |
if search_results: | |
search_info = f"\n\n์น ๊ฒ์ ๊ฒฐ๊ณผ:\n{self._format_search_results(search_results)}" | |
return f"""๋น์ ์ ไป(์ธ์ํจ)์ ๋๋ชฉ์ ์ง๋ ๆจ์ ๊ธฐ์ด์ ๊ฐ์ง ๊ฐ๋ ๊ด์ ๋๋ค. | |
ํฌ์ฉ์ ์ด๊ณ ์ฑ์ฅ์งํฅ์ ์ธ ๋ฆฌ๋์ญ์ผ๋ก ํ์ ์ด๋๋๋ค. | |
์ฌ์ฉ์ ์ง๋ฌธ: {user_query} | |
{search_info} | |
ํ์ฅ์ผ๋ก์ ์ด ์ง๋ฌธ์ ๋ํด: | |
1. ์ ์ฒด์ ์ธ ๋น์ ๊ณผ ๋ฐฉํฅ์ฑ์ ์ ์ํ์ธ์ | |
2. ๊ฐ ํ์(็ซ, ๅ, ้, ๆฐด)์ ์ญํ ๊ณผ ๊ธฐ์ฌ ๋ฐฉํฅ์ ์ค๊ณํ์ธ์ | |
3. ์ฑ์ฅ๊ณผ ๋ฐ์ ์ ๊ด์ ์์ ํต์ฌ ๋ชฉํ๋ฅผ ์ค์ ํ์ธ์ | |
4. ํ ์ ์ฒด๊ฐ ์กฐํ๋กญ๊ฒ ํ๋ ฅํ ์ ์๋ ํ๋ ์์ํฌ๋ฅผ ์ ์ํ์ธ์ | |
5. ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ์ฐธ๊ณ ํ์ฌ ์ต์ ํธ๋ ๋์ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ฐ์ํ์ธ์ | |
[ํต์ฌ ํค์๋]: 5-7๊ฐ์ ์กฐ์ฌ๊ฐ ํ์ํ ํค์๋๋ฅผ ์ ์ํ์ธ์""" | |
def create_fire_strategy_prompt(self, user_query: str, wood_response: str, critic_feedback: str, search_results: Dict = None) -> str: | |
"""็ซ(์ ๋ต๊ธฐํ) ํ๋กฌํํธ""" | |
search_info = "" | |
if search_results: | |
search_info = f"\n\n์น ๊ฒ์ ๊ฒฐ๊ณผ:\n{self._format_search_results(search_results)}" | |
return f"""๋น์ ์ ็พฉ(์ ์๋ก์)์ ๋๋ชฉ์ ์ง๋ ็ซ์ ๊ธฐ์ด์ ๊ฐ์ง ์ ๋ตยท๊ธฐํ ๋ฆฌ๋์ ๋๋ค. | |
์ด์ ๊ณผ ๊ฒฐ๋จ๋ ฅ์ผ๋ก ํ์ ์ ์ธ ์ ๋ต์ ์๋ฆฝํฉ๋๋ค. | |
์ฌ์ฉ์ ์ง๋ฌธ: {user_query} | |
๊ฐ๋ ๊ด(ๆจ)์ ๋น์ : | |
{wood_response} | |
๋นํ์์ ํผ๋๋ฐฑ: | |
{critic_feedback} | |
{search_info} | |
์ ๋ต๊ธฐํ ๋ฆฌ๋๋ก์: | |
1. ์คยท์ฅ๊ธฐ ๋ก๋๋งต์ ๊ตฌ์ฒด์ ์ผ๋ก ์๋ฆฝํ์ธ์ | |
2. ํ์ ์ ์ธ ์ฌ์ ๋ชจ๋ธ์ ์ค๊ณํ์ธ์ | |
3. ๋ฆฌ์คํฌ์ ๊ธฐํ ๋ถ์์ ์ํํ์ธ์ | |
4. ๊ฒฝ์ ์ฐ์ ํ๋ณด ์ ๋ต์ ์ ์ํ์ธ์ | |
5. ๋นํ์์ ํผ๋๋ฐฑ์ ๋ฐ์ํ์ฌ ์ ๋ต์ ๋ณด์ํ์ธ์ | |
6. ํ์ํ ์ถ๊ฐ ๊ฒ์ ํค์๋๋ฅผ ์ ์ํ์ธ์: [์ถ๊ฐ ๊ฒ์]""" | |
def create_earth_execution_prompt(self, user_query: str, fire_response: str, critic_feedback: str, search_results: Dict = None) -> str: | |
"""ๅ(์คํ์ด์) ํ๋กฌํํธ""" | |
search_info = "" | |
if search_results: | |
search_info = f"\n\n์น ๊ฒ์ ๊ฒฐ๊ณผ:\n{self._format_search_results(search_results)}" | |
return f"""๋น์ ์ ไฟก(์ ๋ขฐ)์ ๋๋ชฉ์ ์ง๋ ๅ์ ๊ธฐ์ด์ ๊ฐ์ง ์คํยท์ด์ยทํ์ง ๋ด๋น์์ ๋๋ค. | |
์ฑ์คํจ๊ณผ ์คํ๋ ฅ์ผ๋ก ๊ณํ์ ํ์ค๋ก ๋ง๋ญ๋๋ค. | |
์ฌ์ฉ์ ์ง๋ฌธ: {user_query} | |
์ ๋ต๊ธฐํ(็ซ)์ ์ ๋ต: | |
{fire_response} | |
๋นํ์์ ํผ๋๋ฐฑ: | |
{critic_feedback} | |
{search_info} | |
์คํ์ด์ ๋ด๋น์๋ก์: | |
1. ๊ตฌ์ฒด์ ์ธ ์คํ ๊ณํ๊ณผ ์ผ์ ์ ์๋ฆฝํ์ธ์ | |
2. ํ์ํ ๋ฆฌ์์ค์ ์์ฐ์ ์ฐ์ ํ์ธ์ | |
3. ํ์ง ๊ด๋ฆฌ ๊ธฐ์ค๊ณผ ํ๋ก์ธ์ค๋ฅผ ์ ์ํ์ธ์ | |
4. ๋ฆฌ์คํฌ ๋์ ๊ณํ์ ์๋ฆฝํ์ธ์ | |
5. ์ฑ๊ณผ ์ธก์ ์งํ๋ฅผ ์ค์ ํ์ธ์ | |
6. ์ถ๊ฐ ์กฐ์ฌ๊ฐ ํ์ํ ์คํ ๊ด๋ จ ํค์๋: [์ถ๊ฐ ๊ฒ์]""" | |
def create_metal_architecture_prompt(self, user_query: str, earth_response: str, critic_feedback: str, search_results: Dict = None) -> str: | |
"""้(์ํคํ ์ฒ) ํ๋กฌํํธ""" | |
search_info = "" | |
if search_results: | |
search_info = f"\n\n์น ๊ฒ์ ๊ฒฐ๊ณผ:\n{self._format_search_results(search_results)}" | |
return f"""๋น์ ์ ็ฆฎ(์์ยท์ง์)์ ๋๋ชฉ์ ์ง๋ ้์ ๊ธฐ์ด์ ๊ฐ์ง ์ํคํ ์ฒ & ํ์ค ์ฑ ์์์ ๋๋ค. | |
์ ๋ฐํจ๊ณผ ๊ตฌ์กฐํ ๋ฅ๋ ฅ์ผ๋ก ์๋ฒฝํ ์์คํ ์ ์ค๊ณํฉ๋๋ค. | |
์ฌ์ฉ์ ์ง๋ฌธ: {user_query} | |
์คํ์ด์(ๅ)์ ๊ณํ: | |
{earth_response} | |
๋นํ์์ ํผ๋๋ฐฑ: | |
{critic_feedback} | |
{search_info} | |
์ํคํ ์ฒ ์ฑ ์์๋ก์: | |
1. ์ ์ฒด ์์คํ ์ ๊ธฐ์ ยท๋ฐ์ดํฐ ์ํคํ ์ฒ๋ฅผ ์ค๊ณํ์ธ์ | |
2. ์ฝ๋ฉ/APIยท๋ฐ์ดํฐยท๋ณด์ ํ์ค์ ์๋ฆฝํ์ธ์ | |
3. ํ์ฅ์ฑ๊ณผ ํธํ์ฑ์ ๊ณ ๋ คํ ํ๋ ์์ํฌ๋ฅผ ์ ์ํ์ธ์ | |
4. ํ์ง ๊ฒ์ฆ ์ฒด๊ณ์ ๊ธฐ์ค์ ์ ์ํ์ธ์ | |
5. ๊ธฐ์ ์ ์ ์ฝ์ฌํญ๊ณผ ํด๊ฒฐ๋ฐฉ์์ ์ ์ํ์ธ์ | |
6. ๊ธฐ์ ์ํคํ ์ฒ ๊ด๋ จ ์ถ๊ฐ ๊ฒ์: [์ถ๊ฐ ๊ฒ์]""" | |
def create_water_rd_prompt(self, user_query: str, metal_response: str, critic_feedback: str, search_results: Dict = None) -> str: | |
"""ๆฐด(R&D) ํ๋กฌํํธ""" | |
search_info = "" | |
if search_results: | |
search_info = f"\n\n์ต์ ๊ธฐ์ ์กฐ์ฌ ๊ฒฐ๊ณผ:\n{self._format_search_results(search_results)}" | |
return f"""๋น์ ์ ๆบ(์งํ)์ ๋๋ชฉ์ ์ง๋ ๆฐด์ ๊ธฐ์ด์ ๊ฐ์ง ๋๊ตฌ ํ์ฉ R&D ์คํ์ ๋ฆฌ์คํธ์ ๋๋ค. | |
๋ถ์๋ ฅ๊ณผ ํธ๊ธฐ์ฌ์ผ๋ก ์ต์ ๊ธฐ์ ์ ํ๊ตฌํ๊ณ ํ์ ํฉ๋๋ค. | |
์ฌ์ฉ์ ์ง๋ฌธ: {user_query} | |
์ํคํ ์ฒ(้)์ ์ค๊ณ: | |
{metal_response} | |
๋นํ์์ ํผ๋๋ฐฑ: | |
{critic_feedback} | |
{search_info} | |
R&D ์คํ์ ๋ฆฌ์คํธ๋ก์: | |
1. ์ต์ ๊ธฐ์ ํธ๋ ๋์ ๋๊ตฌ๋ฅผ ๋ถ์ํ์ธ์ | |
2. ํ์ ์ ์ธ ํ๋กํ ํ์ ๊ฐ๋ฐ ๋ฐฉ์์ ์ ์ํ์ธ์ | |
3. ์๋ํ์ ์์ฐ์ฑ ํฅ์ ๋๊ตฌ๋ฅผ ์ถ์ฒํ์ธ์ | |
4. ๊ธฐ์ ๋์ ์ ROI์ ์คํ ๊ฐ๋ฅ์ฑ์ ํ๊ฐํ์ธ์ | |
5. ํ ๊ต์ก๊ณผ ๊ธฐ์ ์ ํ ๊ณํ์ ์๋ฆฝํ์ธ์ | |
6. ๋ฏธ๋ ๊ธฐ์ ๊ด๋ จ ์ถ๊ฐ ๊ฒ์: [์ถ๊ฐ ๊ฒ์]""" | |
def create_wood_final_prompt(self, user_query: str, all_responses: Dict, all_critics: List) -> str: | |
"""ๆจ(๊ฐ๋ ๊ด) ์ต์ข ์ข ํฉ ํ๋กฌํํธ""" | |
return f"""๋น์ ์ ไป(์ธ์ํจ)์ ๋๋ชฉ์ ์ง๋ ๆจ์ ๊ธฐ์ด์ ๊ฐ์ง ๊ฐ๋ ๊ด์ ๋๋ค. | |
ํ ์ ์ฒด์ ์๊ฒฌ์ ์ข ํฉํ์ฌ ์ต์ข ๊ฒฐ์ ์ ๋ด๋ฆฝ๋๋ค. | |
์ฌ์ฉ์ ์ง๋ฌธ: {user_query} | |
ํ์๋ค์ ๊ธฐ์ฌ: | |
- ็ซ(์ ๋ต๊ธฐํ): {all_responses['fire']} | |
- ๅ(์คํ์ด์): {all_responses['earth']} | |
- ้(์ํคํ ์ฒ): {all_responses['metal']} | |
- ๆฐด(R&D): {all_responses['water']} | |
๋นํ์์ ํผ๋๋ฐฑ ์ด๋ ฅ: | |
{self._format_critic_history(all_critics)} | |
ํ์ฅ์ผ๋ก์ ์ต์ข ์ข ํฉ ๋ณด๊ณ ์๋ฅผ ์์ฑํ์ธ์: | |
1. ๊ฐ ํ์์ ๊ธฐ์ฌ๋ฅผ ํตํฉํ ์ข ํฉ ์๋ฃจ์ | |
2. ์คํ ์ฐ์ ์์์ ๋จ๊ณ๋ณ ๋ก๋๋งต | |
3. ์์ ์ฑ๊ณผ์ ์ฑ๊ณต ์งํ | |
4. ํ ์ ์ฒด์ ์๋์ง ์ฐฝ์ถ ๋ฐฉ์ | |
5. ์ง์์ ๊ฐ์ ๊ณผ ์ฑ์ฅ ๊ณํ | |
๋งํฌ๋ค์ด ํ์์ ํ์ฉํ์ฌ ์ ๋ฌธ์ ์ด๊ณ ์ฒด๊ณ์ ์ผ๋ก ์์ฑํ์ธ์.""" | |
def create_critic_prompt(self, stage: str, content: str, context: str = "", search_results: Dict = None) -> str: | |
"""์ค๋ฆฝ์ ๋นํ์ ํ๋กฌํํธ""" | |
search_info = "" | |
if search_results: | |
search_info = f"\n\n์ฐธ๊ณ ๊ฒ์ ๊ฒฐ๊ณผ:\n{self._format_search_results(search_results)}" | |
return f"""๋น์ ์ ์ค๋ฆฝ์ ์ด๊ณ ๋ ผ๋ฆฌ์ ์ธ ๋นํ์์ ๋๋ค. | |
ํธ๊ฒฌ ์์ด ํฉ๋ฆฌ์ ์ด๊ณ ๊ฑด์ค์ ์ธ ๋นํ์ ์ ๊ณตํฉ๋๋ค. | |
ํ์ฌ ๋จ๊ณ: {stage} | |
๋ถ์ ๋์: | |
{content} | |
{f"์ด์ ๋งฅ๋ฝ: {context}" if context else ""} | |
{search_info} | |
๋ค์ ๊ด์ ์์ ๋นํํ์ธ์: | |
1. ๋ ผ๋ฆฌ์ ์ผ๊ด์ฑ๊ณผ ํ๋น์ฑ | |
2. ์คํ ๊ฐ๋ฅ์ฑ๊ณผ ์ค์ฉ์ฑ | |
3. ๋๋ฝ๋ ์ค์ ์์ | |
4. ๊ฐ์ ๊ฐ๋ฅํ ๋ถ๋ถ | |
5. ๊ฐ์ ๊ณผ ์ฝ์ ์ ๊ท ํ์กํ ํ๊ฐ | |
6. ์ต์ ํธ๋ ๋์ ๋ชจ๋ฒ ์ฌ๋ก ๋๋น ํ๊ฐ | |
๊ฑด์ค์ ์ด๊ณ ๊ตฌ์ฒด์ ์ธ ํผ๋๋ฐฑ์ ์ ๊ณตํ๋, ๋ค์ ๋จ๊ณ ๋ด๋น์๊ฐ | |
๊ฐ์ ํ ์ ์๋ ์ค์ง์ ์ธ ์ ์์ ํฌํจํ์ธ์. | |
ํ์์ ์ถ๊ฐ ๊ฒ์์ด ํ์ํ ํค์๋๋ฅผ ์ ์ํ์ธ์: [๋นํ ๊ฒ์]""" | |
def _format_search_results(self, search_results: Dict) -> str: | |
"""๊ฒ์ ๊ฒฐ๊ณผ ํฌ๋งทํ """ | |
if not search_results: | |
return "๊ฒ์ ๊ฒฐ๊ณผ ์์" | |
formatted = "" | |
for keyword, results in search_results.items(): | |
formatted += f"\n**{keyword}:**\n" | |
for i, result in enumerate(results[:5], 1): | |
formatted += f"{i}. {result.get('title', 'N/A')} (์ ๋ขฐ๋: {result.get('credibility_score', 0):.2f})\n" | |
formatted += f" {result.get('description', 'N/A')[:150]}...\n" | |
return formatted | |
def _format_critic_history(self, critics: List) -> str: | |
"""๋นํ ์ด๋ ฅ ํฌ๋งทํ """ | |
if not critics: | |
return "๋นํ ์ด๋ ฅ ์์" | |
formatted = "" | |
stages = ["ๆจ ์ด๊ธฐ", "็ซ ์ ๋ต", "ๅ ์คํ", "้ ์ํคํ ์ฒ", "ๆฐด R&D"] | |
for i, critic in enumerate(critics): | |
if i < len(stages): | |
formatted += f"\n**{stages[i]} ๋จ๊ณ ๋นํ:**\n{critic}\n" | |
return formatted | |
def extract_keywords(self, response: str, keyword_marker: str = "[ํต์ฌ ํค์๋]") -> List[str]: | |
"""์๋ต์์ ํค์๋ ์ถ์ถ (๋ฒ์ฉ)""" | |
keywords = [] | |
# ๋ค์ํ ํค์๋ ๋ง์ปค ํจํด | |
patterns = [ | |
rf'\{keyword_marker}:\s*(.+)', | |
r'\[์ถ๊ฐ ๊ฒ์\]:\s*(.+)', | |
r'\[๋นํ ๊ฒ์\]:\s*(.+)' | |
] | |
for pattern in patterns: | |
match = re.search(pattern, response, re.IGNORECASE) | |
if match: | |
keyword_str = match.group(1) | |
new_keywords = [k.strip() for k in keyword_str.split(',') if k.strip()] | |
keywords.extend(new_keywords) | |
# ์ค๋ณต ์ ๊ฑฐ | |
keywords = list(dict.fromkeys(keywords)) | |
if not keywords: | |
# ๊ธฐ๋ณธ ํค์๋ ์์ฑ | |
keywords = ["best practices", "implementation", "case studies", "latest trends"] | |
return keywords[:7] | |
def calculate_credibility_score(self, result: Dict) -> float: | |
"""๊ฒ์ ๊ฒฐ๊ณผ ์ ๋ขฐ๋ ๊ณ์ฐ""" | |
score = 0.5 | |
url = result.get('url', '') | |
trusted_domains = ['.edu', '.gov', '.org', 'wikipedia.org', 'nature.com', | |
'ieee.org', 'acm.org', 'github.com'] | |
for domain in trusted_domains: | |
if domain in url: | |
score += 0.2 | |
break | |
if url.startswith('https://'): | |
score += 0.1 | |
if len(result.get('title', '')) > 20: | |
score += 0.05 | |
if len(result.get('description', '')) > 50: | |
score += 0.05 | |
spam_keywords = ['buy now', 'sale', 'discount', 'click here'] | |
if any(spam in (result.get('title', '') + result.get('description', '')).lower() | |
for spam in spam_keywords): | |
score -= 0.3 | |
return max(0, min(1, score)) | |
def brave_search(self, query: str) -> List[Dict]: | |
"""Brave Search API ํธ์ถ""" | |
if self.test_mode or self.bapi_token == "YOUR_BRAVE_API_TOKEN": | |
test_results = [] | |
for i in range(5): | |
test_results.append({ | |
"title": f"{query} - Best Practices {i+1}", | |
"description": f"Comprehensive guide on {query} with proven methodologies.", | |
"url": f"https://example{i+1}.com/{query.replace(' ', '-')}", | |
"credibility_score": 0.7 + (i * 0.05) | |
}) | |
return test_results | |
try: | |
params = { | |
"q": query, | |
"count": 10, | |
"safesearch": "moderate" | |
} | |
response = requests.get( | |
self.brave_url, | |
headers=self.create_brave_headers(), | |
params=params, | |
timeout=10 | |
) | |
if response.status_code == 200: | |
data = response.json() | |
results = [] | |
for item in data.get("web", {}).get("results", []): | |
result = { | |
"title": item.get("title", ""), | |
"description": item.get("description", ""), | |
"url": item.get("url", ""), | |
"credibility_score": self.calculate_credibility_score(item) | |
} | |
results.append(result) | |
results.sort(key=lambda x: x['credibility_score'], reverse=True) | |
return results | |
else: | |
logger.error(f"Brave API ์ค๋ฅ: {response.status_code}") | |
return [] | |
except Exception as e: | |
logger.error(f"Brave ๊ฒ์ ์ค ์ค๋ฅ: {str(e)}") | |
return [] | |
def call_gemini_streaming(self, messages: List[Dict[str, str]], role: str) -> Generator[str, None, None]: | |
"""Gemini API ์คํธ๋ฆฌ๋ฐ ํธ์ถ""" | |
if not self.gemini_client: | |
yield "โ Gemini API ํด๋ผ์ด์ธํธ๊ฐ ์ด๊ธฐํ๋์ง ์์์ต๋๋ค." | |
return | |
try: | |
contents = [] | |
for msg in messages: | |
if msg["role"] == "user": | |
contents.append(types.Content( | |
role="user", | |
parts=[types.Part.from_text(text=msg["content"])] | |
)) | |
generate_content_config = types.GenerateContentConfig( | |
temperature=0.7, | |
top_p=0.8, | |
max_output_tokens=4096, | |
response_mime_type="text/plain" | |
) | |
for chunk in self.gemini_client.models.generate_content_stream( | |
model="gemini-2.5-pro", | |
contents=contents, | |
config=generate_content_config, | |
): | |
if chunk.text: | |
yield chunk.text | |
except Exception as e: | |
logger.error(f"Gemini API ์ค๋ฅ: {str(e)}") | |
yield f"โ Gemini API ์ค๋ฅ: {str(e)}" | |
def call_llm_streaming(self, messages: List[Dict[str, str]], role: str) -> Generator[str, None, None]: | |
"""์คํธ๋ฆฌ๋ฐ LLM API ํธ์ถ""" | |
if self.use_gemini: | |
yield from self.call_gemini_streaming(messages, role) | |
return | |
if self.test_mode: | |
test_response = f"์ด๊ฒ์ {role} ์ญํ ์ ํ ์คํธ ์๋ต์ ๋๋ค.\n" | |
test_response += f"์ฌ์ฉ์ ์ง๋ฌธ์ ๋ํ {role}์ ๊ด์ ์์ ๋ถ์ํ ๋ด์ฉ์ ๋๋ค.\n" | |
test_response += "1. ์ฒซ ๋ฒ์งธ ํต์ฌ ํฌ์ธํธ\n2. ๋ ๋ฒ์งธ ํต์ฌ ํฌ์ธํธ\n3. ์ธ ๋ฒ์งธ ํต์ฌ ํฌ์ธํธ" | |
words = test_response.split() | |
for i in range(0, len(words), 3): | |
chunk = " ".join(words[i:i+3]) | |
yield chunk + " " | |
time.sleep(0.05) | |
return | |
try: | |
payload = { | |
"model": self.model_id, | |
"messages": messages, | |
"max_tokens": 4096, | |
"temperature": 0.7, | |
"stream": True | |
} | |
response = requests.post( | |
self.api_url, | |
headers=self.create_headers(), | |
json=payload, | |
stream=True, | |
timeout=10 | |
) | |
if response.status_code != 200: | |
yield f"โ API ์ค๋ฅ: {response.status_code}" | |
return | |
for line in response.iter_lines(): | |
if line: | |
line = line.decode('utf-8') | |
if line.startswith("data: "): | |
data = line[6:] | |
if data == "[DONE]": | |
break | |
try: | |
chunk = json.loads(data) | |
if "choices" in chunk and chunk["choices"]: | |
content = chunk["choices"][0].get("delta", {}).get("content", "") | |
if content: | |
yield content | |
except json.JSONDecodeError: | |
continue | |
except Exception as e: | |
logger.error(f"์คํธ๋ฆฌ๋ฐ ์ค ์ค๋ฅ: {str(e)}") | |
yield f"โ ์ค๋ฅ ๋ฐ์: {str(e)}" | |
# ์์คํ ์ธ์คํด์ค ์์ฑ | |
wuxing_system = WuxingLLMSystem() | |
def process_wuxing_query(user_query: str, llm_mode: str): | |
"""์คํ ๊ธฐ๋ฐ ์ฟผ๋ฆฌ ์ฒ๋ฆฌ""" | |
if not user_query: | |
return "", "", "", "", "", "", "", "โ ์ง๋ฌธ์ ์ ๋ ฅํด์ฃผ์ธ์." | |
wuxing_system.set_llm_mode(llm_mode) | |
all_responses = {} | |
all_critics = [] | |
all_search_results = {} | |
try: | |
# 0. ์ด๊ธฐ ๊ฒ์ ์ํ | |
initial_keywords = [user_query] + ["best practices", "latest trends", "case studies"] | |
wood_search_results = {} | |
status_text = "๐ ์ด๊ธฐ ์น ๊ฒ์ ์ํ ์ค..." | |
yield "", "", "", "", "", "", "", status_text | |
for keyword in initial_keywords[:3]: | |
results = wuxing_system.brave_search(keyword) | |
if results: | |
wood_search_results[keyword] = results | |
all_search_results[f"์ด๊ธฐ-{keyword}"] = results | |
# 1. ๆจ(๊ฐ๋ ๊ด) ์ด๊ธฐ ๋ถ์ | |
wood_prompt = wuxing_system.create_wood_initial_prompt(user_query, wood_search_results) | |
wood_response = "" | |
wood_text = "๐ณ **ๆจ - ๊ฐ๋ ๊ด** (ไป)\n๐ ๋ถ์ ์ค...\n" | |
for chunk in wuxing_system.call_llm_streaming( | |
[{"role": "user", "content": wood_prompt}], "wood" | |
): | |
wood_response += chunk | |
wood_text = f"๐ณ **ๆจ - ๊ฐ๋ ๊ด** (ไป)\n{wood_response}" | |
yield wood_text, "", "", "", "", "", "", "๐ณ ๊ฐ๋ ๊ด์ด ๋น์ ์ ์๋ฆฝ ์ค..." | |
all_responses['wood_initial'] = wood_response | |
# ํค์๋ ์ถ์ถ | |
keywords = wuxing_system.extract_keywords(wood_response) | |
# 2. ๆจ ๋นํ + ๋นํ์ ๊ฒ์ | |
critic_search = {} | |
for keyword in keywords[:2]: | |
results = wuxing_system.brave_search(f"{keyword} analysis") | |
if results: | |
critic_search[keyword] = results | |
all_search_results[f"๋นํ1-{keyword}"] = results | |
critic_prompt = wuxing_system.create_critic_prompt("ๆจ ์ด๊ธฐ ๋ถ์", wood_response, search_results=critic_search) | |
critic_response = "" | |
critic_text = "๐ **์ค๋ฆฝ์ ๋นํ์**\n[ๆจ ๋ถ์ ๋นํ] ๐ ๋นํ ์ค...\n" | |
for chunk in wuxing_system.call_llm_streaming( | |
[{"role": "user", "content": critic_prompt}], "critic" | |
): | |
critic_response += chunk | |
critic_text = f"๐ **์ค๋ฆฝ์ ๋นํ์**\n[ๆจ ๋ถ์ ๋นํ]\n{critic_response}" | |
yield wood_text, "", "", "", "", critic_text, "", "๐ ๋นํ์๊ฐ ๋ถ์ ์ค..." | |
all_critics.append(critic_response) | |
# 3. ็ซ(์ ๋ต๊ธฐํ) + ๊ฒ์ | |
# ๋นํ์๊ฐ ์ ์ํ ์ถ๊ฐ ํค์๋ ์ถ์ถ | |
critic_keywords = wuxing_system.extract_keywords(critic_response, "[๋นํ ๊ฒ์]") | |
fire_search = {} | |
for keyword in (keywords[:2] + critic_keywords[:2]): | |
results = wuxing_system.brave_search(f"{user_query} {keyword} strategy") | |
if results: | |
fire_search[keyword] = results | |
all_search_results[f"็ซ-{keyword}"] = results | |
fire_prompt = wuxing_system.create_fire_strategy_prompt(user_query, wood_response, critic_response, fire_search) | |
fire_response = "" | |
fire_text = "๐ฅ **็ซ - ์ ๋ตยท๊ธฐํ ๋ฆฌ๋** (็พฉ)\n๐ ์ ๋ต ์๋ฆฝ ์ค...\n" | |
for chunk in wuxing_system.call_llm_streaming( | |
[{"role": "user", "content": fire_prompt}], "fire" | |
): | |
fire_response += chunk | |
fire_text = f"๐ฅ **็ซ - ์ ๋ตยท๊ธฐํ ๋ฆฌ๋** (็พฉ)\n{fire_response}" | |
yield wood_text, fire_text, "", "", "", critic_text, "", "๐ฅ ์ ๋ต ์๋ฆฝ ์ค..." | |
all_responses['fire'] = fire_response | |
# 4. ็ซ ๋นํ | |
fire_keywords = wuxing_system.extract_keywords(fire_response, "[์ถ๊ฐ ๊ฒ์]") | |
critic_search = {} | |
for keyword in fire_keywords[:2]: | |
results = wuxing_system.brave_search(keyword) | |
if results: | |
critic_search[keyword] = results | |
all_search_results[f"๋นํ2-{keyword}"] = results | |
critic_prompt = wuxing_system.create_critic_prompt("็ซ ์ ๋ต๊ธฐํ", fire_response, wood_response, critic_search) | |
critic_response = "" | |
critic_text += "\n\n---\n\n[็ซ ์ ๋ต ๋นํ] ๐ ๋นํ ์ค...\n" | |
for chunk in wuxing_system.call_llm_streaming( | |
[{"role": "user", "content": critic_prompt}], "critic" | |
): | |
critic_response += chunk | |
temp_text = all_critics[0] + f"\n\n---\n\n[็ซ ์ ๋ต ๋นํ]\n{critic_response}" | |
critic_text = f"๐ **์ค๋ฆฝ์ ๋นํ์**\n[ๆจ ๋ถ์ ๋นํ]\n{temp_text}" | |
yield wood_text, fire_text, "", "", "", critic_text, "", "๐ ์ ๋ต ๋นํ ์ค..." | |
all_critics.append(critic_response) | |
# 5. ๅ(์คํ์ด์) | |
earth_search = {} | |
for keyword in ["implementation", "resource management", "quality assurance"]: | |
results = wuxing_system.brave_search(f"{user_query} {keyword}") | |
if results: | |
earth_search[keyword] = results | |
all_search_results[f"ๅ-{keyword}"] = results | |
earth_prompt = wuxing_system.create_earth_execution_prompt(user_query, fire_response, critic_response, earth_search) | |
earth_response = "" | |
earth_text = "๐๏ธ **ๅ - ์คํยท์ด์ยทํ์ง** (ไฟก)\n๐ ์คํ ๊ณํ ์๋ฆฝ ์ค...\n" | |
for chunk in wuxing_system.call_llm_streaming( | |
[{"role": "user", "content": earth_prompt}], "earth" | |
): | |
earth_response += chunk | |
earth_text = f"๐๏ธ **ๅ - ์คํยท์ด์ยทํ์ง** (ไฟก)\n{earth_response}" | |
yield wood_text, fire_text, earth_text, "", "", critic_text, "", "๐๏ธ ์คํ ๊ณํ ์๋ฆฝ ์ค..." | |
all_responses['earth'] = earth_response | |
# 6. ๅ ๋นํ | |
critic_search = {} | |
results = wuxing_system.brave_search(f"{user_query} execution challenges") | |
if results: | |
critic_search["execution"] = results | |
all_search_results["๋นํ3-execution"] = results | |
critic_prompt = wuxing_system.create_critic_prompt("ๅ ์คํ๊ณํ", earth_response, fire_response, critic_search) | |
critic_response = "" | |
for chunk in wuxing_system.call_llm_streaming( | |
[{"role": "user", "content": critic_prompt}], "critic" | |
): | |
critic_response += chunk | |
yield wood_text, fire_text, earth_text, "", "", critic_text, "", "๐ ์คํ ๋นํ ์ค..." | |
all_critics.append(critic_response) | |
# 7. ้(์ํคํ ์ฒ) | |
metal_search = {} | |
for keyword in ["architecture", "standards", "system design", "scalability"]: | |
results = wuxing_system.brave_search(f"{user_query} {keyword}") | |
if results: | |
metal_search[keyword] = results | |
all_search_results[f"้-{keyword}"] = results | |
metal_prompt = wuxing_system.create_metal_architecture_prompt(user_query, earth_response, critic_response, metal_search) | |
metal_response = "" | |
metal_text = "โ๏ธ **้ - ์ํคํ ์ฒ & ํ์ค** (็ฆฎ)\n๐ ์ํคํ ์ฒ ์ค๊ณ ์ค...\n" | |
for chunk in wuxing_system.call_llm_streaming( | |
[{"role": "user", "content": metal_prompt}], "metal" | |
): | |
metal_response += chunk | |
metal_text = f"โ๏ธ **้ - ์ํคํ ์ฒ & ํ์ค** (็ฆฎ)\n{metal_response}" | |
yield wood_text, fire_text, earth_text, metal_text, "", critic_text, "", "โ๏ธ ์ํคํ ์ฒ ์ค๊ณ ์ค..." | |
all_responses['metal'] = metal_response | |
# 8. ้ ๋นํ | |
critic_search = {} | |
results = wuxing_system.brave_search(f"{user_query} technical constraints") | |
if results: | |
critic_search["constraints"] = results | |
all_search_results["๋นํ4-constraints"] = results | |
critic_prompt = wuxing_system.create_critic_prompt("้ ์ํคํ ์ฒ", metal_response, earth_response, critic_search) | |
critic_response = "" | |
for chunk in wuxing_system.call_llm_streaming( | |
[{"role": "user", "content": critic_prompt}], "critic" | |
): | |
critic_response += chunk | |
yield wood_text, fire_text, earth_text, metal_text, "", critic_text, "", "๐ ์ํคํ ์ฒ ๋นํ ์ค..." | |
all_critics.append(critic_response) | |
# 9. ๆฐด(R&D) | |
water_search = {} | |
for keyword in ["innovation", "emerging technology", "R&D", "automation tools"]: | |
results = wuxing_system.brave_search(f"{user_query} {keyword}") | |
if results: | |
water_search[keyword] = results | |
all_search_results[f"ๆฐด-{keyword}"] = results | |
water_prompt = wuxing_system.create_water_rd_prompt(user_query, metal_response, critic_response, water_search) | |
water_response = "" | |
water_text = "๐ง **ๆฐด - R&D ์คํ์ ๋ฆฌ์คํธ** (ๆบ)\n๐ ํ์ ๋ฐฉ์ ์ฐ๊ตฌ ์ค...\n" | |
for chunk in wuxing_system.call_llm_streaming( | |
[{"role": "user", "content": water_prompt}], "water" | |
): | |
water_response += chunk | |
water_text = f"๐ง **ๆฐด - R&D ์คํ์ ๋ฆฌ์คํธ** (ๆบ)\n{water_response}" | |
yield wood_text, fire_text, earth_text, metal_text, water_text, critic_text, "", "๐ง ํ์ ์ฐ๊ตฌ ์ค..." | |
all_responses['water'] = water_response | |
# 10. ๆฐด ๋นํ | |
critic_search = {} | |
results = wuxing_system.brave_search(f"{user_query} future trends") | |
if results: | |
critic_search["trends"] = results | |
all_search_results["๋นํ5-trends"] = results | |
critic_prompt = wuxing_system.create_critic_prompt("ๆฐด R&D", water_response, metal_response, critic_search) | |
critic_response = "" | |
for chunk in wuxing_system.call_llm_streaming( | |
[{"role": "user", "content": critic_prompt}], "critic" | |
): | |
critic_response += chunk | |
yield wood_text, fire_text, earth_text, metal_text, water_text, critic_text, "", "๐ R&D ๋นํ ์ค..." | |
all_critics.append(critic_response) | |
# 11. ๆจ(๊ฐ๋ ๊ด) ์ต์ข ์ข ํฉ | |
wood_final_prompt = wuxing_system.create_wood_final_prompt(user_query, all_responses, all_critics) | |
wood_final_response = "" | |
wood_text += "\n\n---\n\n๐ณ **์ต์ข ์ข ํฉ ๋ณด๊ณ ์**\n๐ ์์ฑ ์ค...\n" | |
for chunk in wuxing_system.call_llm_streaming( | |
[{"role": "user", "content": wood_final_prompt}], "wood" | |
): | |
wood_final_response += chunk | |
temp_text = all_responses['wood_initial'] + f"\n\n---\n\n๐ณ **์ต์ข ์ข ํฉ ๋ณด๊ณ ์**\n{wood_final_response}" | |
wood_text = f"๐ณ **ๆจ - ๊ฐ๋ ๊ด** (ไป)\n{temp_text}" | |
yield wood_text, fire_text, earth_text, metal_text, water_text, critic_text, "", "๐ณ ์ต์ข ๋ณด๊ณ ์ ์์ฑ ์ค..." | |
# ์ต์ข ๋ณด๊ณ ์ ์์ฑ | |
final_report = f"""# ๐ ์คํ ํ๋ ฅ ์์คํ ์ต์ข ์ข ํฉ ๋ณด๊ณ ์ | |
## ๐ ์ฌ์ฉ์ ์ง๋ฌธ | |
> **{user_query}** | |
--- | |
## ๐ณ ํ์ฅ ์ต์ข ์ข ํฉ (ๆจ - ไป) | |
{wood_final_response} | |
--- | |
## ๐ ์คํ ํ์ ๊ธฐ์ฌ ์์ฝ | |
### ๐ฅ ์ ๋ตยท๊ธฐํ (็ซ - ็พฉ) | |
{fire_response[:500]}... | |
### ๐๏ธ ์คํยท์ด์ (ๅ - ไฟก) | |
{earth_response[:500]}... | |
### โ๏ธ ์ํคํ ์ฒ (้ - ็ฆฎ) | |
{metal_response[:500]}... | |
### ๐ง R&D ํ์ (ๆฐด - ๆบ) | |
{water_response[:500]}... | |
--- | |
## ๐ ์ค๋ฆฝ์ ๋นํ ํต์ฌ ํต์ฐฐ | |
### ์ฃผ์ ๊ฐ์ ์ฌํญ | |
{all_critics[-1][:400]}... | |
--- | |
## ๐ ์ฑ๊ณผ ์งํ | |
| ํญ๋ชฉ | ๋ด์ฉ | | |
|------|------| | |
| **์น ๊ฒ์ ์ํ** | {len(all_search_results)}๊ฐ ํค์๋ | | |
| **์์ง ์ ๋ณด๋** | {sum(len(r) for r in all_search_results.values())}๊ฐ ๊ฒฐ๊ณผ | | |
| **ํ๋ ฅ ๋จ๊ณ** | 11๋จ๊ณ (5์ธ + 5๋นํ + ์ข ํฉ) | | |
| **์ฌ์ฉ ๋ชจ๋ธ** | {'Gemini 2.5 Pro' if wuxing_system.use_gemini else '๊ธฐ๋ณธ LLM'} | | |
--- | |
## ๐ฏ ํต์ฌ ์ฑ๊ณต ์์ธ | |
1. **์กฐํ๋ก์ด ํ๋ ฅ**: ์คํ์ ์์ ๊ด๊ณ๋ฅผ ํตํ ์๋์ง ์ฐฝ์ถ | |
2. **๋ค๊ฐ์ ๊ฒํ **: 5๊ฐ์ง ์ ๋ฌธ์ฑ๊ณผ ์ค๋ฆฝ์ ๋นํ์ ๊ท ํ | |
3. **๋ฐ์ดํฐ ๊ธฐ๋ฐ**: {len(all_search_results)}ํ์ ์น ๊ฒ์์ ํตํ ์ต์ ์ ๋ณด ๋ฐ์ | |
4. **์คํ ๊ฐ๋ฅ์ฑ**: ๊ตฌ์ฒด์ ์ด๊ณ ๋จ๊ณ๋ณ ์ ๊ทผ ๋ฐฉ์ ์ ์ | |
--- | |
*์์ฑ ์๊ฐ: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}* | |
*์คํ ํ๋ ฅ ์์คํ - ไป็พฉ็ฆฎๆบไฟก์ ์กฐํ*""" | |
status_text = f"โ ์คํ ํ๋ ฅ ํ๋ก์ธ์ค ์๋ฃ! ({len(all_search_results)} ํค์๋, {sum(len(r) for r in all_search_results.values())} ๊ฒ์๊ฒฐ๊ณผ)" | |
yield wood_text, fire_text, earth_text, metal_text, water_text, critic_text, final_report, status_text | |
except Exception as e: | |
error_msg = f"โ ์ฒ๋ฆฌ ์ค ์ค๋ฅ: {str(e)}" | |
yield "", "", "", "", "", "", "", error_msg | |
def clear_wuxing(): | |
"""์ด๊ธฐํ""" | |
return "", "", "", "", "", "", "", "๐ ์ด๊ธฐํ๋์์ต๋๋ค." | |
# CSS ์คํ์ผ | |
css = """ | |
.gradio-container { | |
font-family: 'Arial', sans-serif; | |
} | |
.wood-box textarea { | |
border-left: 4px solid #10b981 !important; | |
background-color: #f0fdf4 !important; | |
} | |
.fire-box textarea { | |
border-left: 4px solid #ef4444 !important; | |
background-color: #fef2f2 !important; | |
} | |
.earth-box textarea { | |
border-left: 4px solid #a855f7 !important; | |
background-color: #faf5ff !important; | |
} | |
.metal-box textarea { | |
border-left: 4px solid #f59e0b !important; | |
background-color: #fffbeb !important; | |
} | |
.water-box textarea { | |
border-left: 4px solid #3b82f6 !important; | |
background-color: #eff6ff !important; | |
} | |
.critic-box textarea { | |
border-left: 4px solid #6b7280 !important; | |
background-color: #f9fafb !important; | |
} | |
.final-report-box { | |
border: 2px solid #10b981 !important; | |
border-radius: 8px !important; | |
padding: 20px !important; | |
background: linear-gradient(to bottom, #f0fdf4, #ffffff) !important; | |
margin-top: 10px !important; | |
font-size: 14px !important; | |
max-height: 800px !important; | |
overflow-y: auto !important; | |
line-height: 1.8 !important; | |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) !important; | |
} | |
.final-report-box h1 { | |
color: #065f46 !important; | |
font-size: 28px !important; | |
margin-bottom: 16px !important; | |
text-align: center !important; | |
font-weight: bold !important; | |
} | |
.final-report-box h2 { | |
color: #047857 !important; | |
font-size: 22px !important; | |
margin-top: 20px !important; | |
margin-bottom: 12px !important; | |
border-bottom: 2px solid #10b981 !important; | |
padding-bottom: 8px !important; | |
} | |
.final-report-box h3 { | |
color: #059669 !important; | |
font-size: 18px !important; | |
margin-top: 16px !important; | |
margin-bottom: 10px !important; | |
font-weight: 600 !important; | |
} | |
.final-report-box h4 { | |
color: #10b981 !important; | |
font-size: 16px !important; | |
margin-top: 12px !important; | |
margin-bottom: 8px !important; | |
} | |
.final-report-box table { | |
border-collapse: collapse !important; | |
width: 100% !important; | |
margin: 20px 0 !important; | |
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important; | |
} | |
.final-report-box th, .final-report-box td { | |
border: 1px solid #d1d5db !important; | |
padding: 12px 14px !important; | |
text-align: left !important; | |
} | |
.final-report-box th { | |
background-color: #065f46 !important; | |
font-weight: bold !important; | |
color: #ffffff !important; | |
text-transform: uppercase !important; | |
font-size: 13px !important; | |
letter-spacing: 0.5px !important; | |
} | |
.final-report-box tr:nth-child(even) { | |
background-color: #f9fafb !important; | |
} | |
.final-report-box tr:hover { | |
background-color: #f0fdf4 !important; | |
transition: background-color 0.2s !important; | |
} | |
.final-report-box code { | |
background-color: #ecfdf5 !important; | |
padding: 3px 8px !important; | |
border-radius: 4px !important; | |
font-family: 'Consolas', 'Monaco', monospace !important; | |
color: #047857 !important; | |
font-size: 13px !important; | |
border: 1px solid #10b981 !important; | |
} | |
.final-report-box pre { | |
background-color: #1e293b !important; | |
color: #e2e8f0 !important; | |
padding: 16px !important; | |
border-radius: 8px !important; | |
overflow-x: auto !important; | |
margin: 16px 0 !important; | |
font-size: 13px !important; | |
line-height: 1.5 !important; | |
} | |
.final-report-box pre code { | |
background-color: transparent !important; | |
color: #e2e8f0 !important; | |
padding: 0 !important; | |
border: none !important; | |
} | |
.final-report-box blockquote { | |
border-left: 4px solid #10b981 !important; | |
padding-left: 16px !important; | |
margin: 16px 0 !important; | |
color: #064e3b !important; | |
font-style: italic !important; | |
background-color: #f0fdf4 !important; | |
padding: 14px 16px !important; | |
border-radius: 0 8px 8px 0 !important; | |
} | |
.final-report-box ul, .final-report-box ol { | |
margin-left: 24px !important; | |
margin-bottom: 16px !important; | |
} | |
.final-report-box li { | |
margin-bottom: 8px !important; | |
line-height: 1.8 !important; | |
} | |
.final-report-box strong { | |
color: #065f46 !important; | |
font-weight: 600 !important; | |
} | |
.final-report-box em { | |
color: #047857 !important; | |
} | |
.final-report-box hr { | |
border: none !important; | |
border-top: 2px solid #10b981 !important; | |
margin: 28px 0 !important; | |
} | |
.final-report-box a { | |
color: #059669 !important; | |
text-decoration: underline !important; | |
} | |
.final-report-box a:hover { | |
color: #047857 !important; | |
} | |
h1 { | |
text-align: center; | |
color: #1f2937; | |
} | |
""" | |
# Gradio ์ธํฐํ์ด์ค | |
with gr.Blocks(title="์คํยท์ค์ ํ๋ ฅ ์์คํ ", theme=gr.themes.Soft(), css=css) as app: | |
gr.Markdown( | |
""" | |
# ๐ ์คํยท์ค์ ๊ธฐ๋ฐ ํ๋ ฅ์ LLM ์์คํ | |
### ๐ ํ๋ก์ธ์ค: ๆจโ๋นํโ็ซโ๋นํโๅโ๋นํโ้โ๋นํโๆฐดโ๋นํโๆจ(์ต์ข ) | |
| ์ญํ | ๋๋ชฉยท์คํ | ํต์ฌ ์ ๋ฌธ์ฑ | | |
|------|-----------|-------------| | |
| ๐ณ **๊ฐ๋ ๊ด** | ไปยทๆจ | ๋น์ ์ ์, ํ ์กฐ์จ, ์ธ์ฌ ์ก์ฑ | | |
| ๐ฅ **์ ๋ต๊ธฐํ** | ็พฉยท็ซ | ๋ก๋๋งต, ์ฌ์ ๋ชจ๋ธ, ๋ฆฌ์คํฌ ๋ถ์ | | |
| ๐๏ธ **์คํ์ด์** | ไฟกยทๅ | ์ผ์ ยท์์ฐ ๊ด๋ฆฌ, ํ์ง ๋ณด์ฆ | | |
| โ๏ธ **์ํคํ ์ฒ** | ็ฆฎยท้ | ์์คํ ์ค๊ณ, ํ์ค ์๋ฆฝ, ํ์ง ๊ฒ์ฆ | | |
| ๐ง **R&D** | ๆบยทๆฐด | ๊ธฐ์ ์กฐ์ฌ, ํ๋กํ ํ์ , ํ์ ๋๊ตฌ | | |
| ๐ **๋นํ์** | ์ค๋ฆฝ | ๋ ผ๋ฆฌ์ ยท๊ฑด์ค์ ํผ๋๋ฐฑ | | |
""" | |
) | |
with gr.Row(): | |
with gr.Column(scale=3): | |
llm_mode = gr.Radio( | |
choices=["default", "commercial"], | |
value="default", | |
label="LLM ๋ชจ๋", | |
info="commercial: Gemini 2.5 Pro ์ฌ์ฉ" | |
) | |
user_input = gr.Textbox( | |
label="์ง๋ฌธ ์ ๋ ฅ", | |
placeholder="์: ์ง์ ๊ฐ๋ฅํ ์ค๋งํธ์ํฐ ๊ตฌ์ถ ์ ๋ต์?", | |
lines=3 | |
) | |
with gr.Row(): | |
submit_btn = gr.Button("๐ ๋ถ์ ์์", variant="primary", scale=2) | |
clear_btn = gr.Button("๐๏ธ ์ด๊ธฐํ", scale=1) | |
with gr.Column(scale=1): | |
status_text = gr.Textbox( | |
label="์งํ ์ํ", | |
interactive=False, | |
value="๋๊ธฐ ์ค...", | |
lines=3 | |
) | |
# ์ต์ข ๋ณด๊ณ ์ ์น์ | |
with gr.Row(): | |
with gr.Column(): | |
gr.Markdown("### ๐ ์ต์ข ์ข ํฉ ๋ณด๊ณ ์") | |
final_report = gr.Markdown( | |
value="*์ต์ข ๋ณด๊ณ ์๊ฐ ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค.*", | |
elem_classes=["final-report-box"] | |
) | |
# ์คํ ์ถ๋ ฅ - 2x3 ๊ทธ๋ฆฌ๋ | |
with gr.Row(): | |
with gr.Column(): | |
gr.Markdown("### ๐ณ ๆจ - ๊ฐ๋ ๊ด (ไป)") | |
wood_output = gr.Textbox( | |
label="", | |
lines=10, | |
max_lines=15, | |
interactive=False, | |
elem_classes=["wood-box"] | |
) | |
with gr.Column(): | |
gr.Markdown("### ๐ฅ ็ซ - ์ ๋ตยท๊ธฐํ (็พฉ)") | |
fire_output = gr.Textbox( | |
label="", | |
lines=10, | |
max_lines=15, | |
interactive=False, | |
elem_classes=["fire-box"] | |
) | |
with gr.Row(): | |
with gr.Column(): | |
gr.Markdown("### ๐๏ธ ๅ - ์คํยท์ด์ (ไฟก)") | |
earth_output = gr.Textbox( | |
label="", | |
lines=10, | |
max_lines=15, | |
interactive=False, | |
elem_classes=["earth-box"] | |
) | |
with gr.Column(): | |
gr.Markdown("### โ๏ธ ้ - ์ํคํ ์ฒ (็ฆฎ)") | |
metal_output = gr.Textbox( | |
label="", | |
lines=10, | |
max_lines=15, | |
interactive=False, | |
elem_classes=["metal-box"] | |
) | |
with gr.Row(): | |
with gr.Column(): | |
gr.Markdown("### ๐ง ๆฐด - R&D (ๆบ)") | |
water_output = gr.Textbox( | |
label="", | |
lines=10, | |
max_lines=15, | |
interactive=False, | |
elem_classes=["water-box"] | |
) | |
with gr.Column(): | |
gr.Markdown("### ๐ ์ค๋ฆฝ์ ๋นํ์") | |
critic_output = gr.Textbox( | |
label="", | |
lines=10, | |
max_lines=15, | |
interactive=False, | |
elem_classes=["critic-box"] | |
) | |
# ์์ | |
gr.Examples( | |
examples=[ | |
"์ง์ ๊ฐ๋ฅํ ์ค๋งํธ์ํฐ ๊ตฌ์ถ์ ์ํ ์ข ํฉ ์ ๋ต์?", | |
"AI ๊ธฐ๋ฐ ํฌ์ค์ผ์ด ์๋น์ค ํ๋ซํผ ๊ฐ๋ฐ ๊ณํ์?", | |
"ํ์์ค๋ฆฝ ๋ฌ์ฑ์ ์ํ ๊ธฐ์ ์ ํ ๋ก๋๋งต์?", | |
"๋ฉํ๋ฒ์ค ๊ต์ก ํ๋ซํผ ๊ตฌ์ถ ๋ฐฉ์์?", | |
"๋ธ๋ก์ฒด์ธ ๊ธฐ๋ฐ ๊ณต๊ธ๋ง ๊ด๋ฆฌ ์์คํ ๋์ ์ ๋ต์?" | |
], | |
inputs=user_input, | |
label="๐ก ์์ ์ง๋ฌธ" | |
) | |
# ์ด๋ฒคํธ ํธ๋ค๋ฌ | |
submit_btn.click( | |
fn=process_wuxing_query, | |
inputs=[user_input, llm_mode], | |
outputs=[wood_output, fire_output, earth_output, metal_output, water_output, critic_output, final_report, status_text] | |
).then( | |
fn=lambda: "", | |
outputs=[user_input] | |
) | |
user_input.submit( | |
fn=process_wuxing_query, | |
inputs=[user_input, llm_mode], | |
outputs=[wood_output, fire_output, earth_output, metal_output, water_output, critic_output, final_report, status_text] | |
).then( | |
fn=lambda: "", | |
outputs=[user_input] | |
) | |
clear_btn.click( | |
fn=clear_wuxing, | |
outputs=[wood_output, fire_output, earth_output, metal_output, water_output, critic_output, final_report, status_text] | |
) | |
if __name__ == "__main__": | |
app.queue() | |
app.launch( | |
server_name="0.0.0.0", | |
server_port=7860, | |
share=True, | |
show_error=True | |
) |