import json import traceback from datetime import datetime import pytz from dateutil import parser as date_parser from api_utils import api_get_extremes, api_get_current_tide from config import GEMINI_API_KEY, STATION_NAMES from supabase_utils import get_supabase_client # Gemini 연동 확인 try: import google.generativeai as genai GEMINI_AVAILABLE = True except ImportError: GEMINI_AVAILABLE = False print("Gemini (google-generativeai) 패키지가 설치되지 않았습니다.") def parse_intent_with_llm(message: str) -> dict: """LLM을 사용해 사용자 질문에서 의도를 분석하고 JSON으로 반환""" if not GEMINI_API_KEY: return {"error": "Gemini API 키가 설정되지 않았습니다."} prompt = f""" 당신은 사용자의 자연어 질문을 분석하여 JSON 객체로 변환하는 전문가입니다. 질문에서 '관측소 이름', '원하는 정보', '시작 시간', '종료 시간'을 추출해주세요. 현재 시간은 {datetime.now(pytz.timezone('Asia/Seoul')).strftime('%Y-%m-%d %H:%M:%S')} KST 입니다. - '원하는 정보'는 '특정 시간 조위' 또는 '구간 조위' 중 하나여야 합니다. - '시작 시간'과 '종료 시간'은 'YYYY-MM-DD HH:MM:SS' 형식으로 변환해주세요. - 단일 시간이면 시작과 종료 시간을 동일하게 설정하고, 구간이면 그에 맞게 설정하세요. - 관측소 이름이 없으면 '인천'을 기본값으로 사용하세요. [사용자 질문]: {message} [JSON 출력]: """ try: genai.configure(api_key=GEMINI_API_KEY) model = genai.GenerativeModel('gemini-1.5-flash', generation_config={"response_mime_type": "application/json"}) response = model.generate_content(prompt) return json.loads(response.text) except Exception as e: return {"error": f"LLM 의도 분석 중 오류 발생: {e}"} def retrieve_context_from_db(intent: dict) -> str: """분석된 의도를 바탕으로 데이터베이스에서 정보 검색""" supabase = get_supabase_client() if not supabase: return "데이터베이스에 연결할 수 없습니다." if "error" in intent: return f"의도 분석에 실패했습니다: {intent['error']}" station_name = intent.get("관측소 이름", "인천") start_time_str = intent.get("시작 시간") end_time_str = intent.get("종료 시간") station_id = next((sid for sid, name in STATION_NAMES.items() if name == station_name), "DT_0001") if not start_time_str or not end_time_str: return "질문에서 시간 정보를 찾을 수 없습니다." try: start_time = date_parser.parse(start_time_str) end_time = date_parser.parse(end_time_str) start_query_str = start_time.strftime('%Y-%m-%d %H:%M:%S') end_query_str = end_time.strftime('%Y-%m-%d %H:%M:%S') result = supabase.table('tide_predictions')\ .select('*')\ .eq('station_id', station_id)\ .gte('predicted_at', start_query_str)\ .lte('predicted_at', end_query_str)\ .order('predicted_at')\ .execute() if result.data: info_text = f"'{station_name}'의 '{start_time_str}'부터 '{end_time_str}'까지 조위 정보입니다.\n\n" if len(result.data) > 10: levels = [d['final_tide_level'] for d in result.data] max_level = max(levels) min_level = min(levels) info_text += f"- 최고 조위: {max_level:.1f}cm\n- 최저 조위: {min_level:.1f}cm" else: for d in result.data: time_kst = date_parser.parse(d['predicted_at']).strftime('%H:%M') info_text += f"- {time_kst}: 최종 조위 {d['final_tide_level']:.1f}cm (잔차 {d['predicted_residual']:.1f}cm)\n" return info_text else: return "해당 기간의 예측 데이터를 찾을 수 없습니다. '통합 조위 예측' 탭에서 먼저 예측을 실행해주세요." except Exception as e: return f"데이터 검색 중 오류 발생: {traceback.format_exc()}" def process_chatbot_query_with_llm(message: str, history: list) -> str: """최종 RAG 파이프라인""" intent = parse_intent_with_llm(message) retrieved_data = retrieve_context_from_db(intent) prompt = f"""당신은 친절한 해양 조위 정보 전문가입니다. 주어진 [검색된 데이터]를 바탕으로 사용자의 [질문]에 대해 자연스러운 문장으로 답변해주세요. [검색된 데이터]: {retrieved_data} [사용자 질문]: {message} [답변]:""" try: genai.configure(api_key=GEMINI_API_KEY) model = genai.GenerativeModel('gemini-1.5-flash') response = model.generate_content(prompt) return response.text except Exception as e: return f"Gemini 답변 생성 중 오류가 발생했습니다: {e}"