import os import json import copy from datetime import datetime import pandas as pd import gradio as gr from openai import OpenAI from supabase import create_client from language_info import * # 시스템 프롬프트 정의 FIRST_PROMPT = """ 너는 유저의 질문을 분석하는 AI야! 유저의 질문을 바탕으로 주어진 조건에 맞도록 질문을 분석해줘! 조건 1. 아래 Task 리스트 중 1개의 작업 선택! Task : ["ReAsk", "FindProduct", "Recommandation", "Etc"] 조건 2. 만약, Task가 "FindProduct"라면, 원하는 "Product"를 "Option"으로 정의를 해줘! 조건 3. 만약, Task가 "Recommandation"라면, 유저의 조건을 "Condition"이라고 "Option"에 제공해줘! 조건 4. 만약, Task가 "ReAsk"라면, 유저의 질문에서 추가적으로 요구되는 조건을 "Condition"이라고 "Option"에 제공해줘! ** 조건 5. 화장품 관련 이야기를 제외한 다른 이야기는 답할 수 없다는 내용으로 답변을 해줘! ** ** 조건 6. 언어가 다르더라도 출력은 전부 "한국어"를 사용해서 출력해! 그리고 유저 입력에 대한 한국어 번역을 "Translation"으로 제공해!** OUTPUT 포맷 json { "Task":"...", "Option":{"...":"...", ..., key:val}, "Translation":"..." } """ # Supabase 로깅 함수 def log_to_supabase(supabase, question, answer, history): """Supabase에 채팅 로그를 저장하는 함수""" try: timestamp = datetime.now().isoformat() supabase.table("chat_log").insert({ "question": question, "answer": answer, "history": json.dumps(history, ensure_ascii=False), "timestamp": timestamp }).execute() return True except Exception as e: print(f"Error logging to Supabase: {e}") return False def local_RAG(model, message, client, vector_id): """로컬 검색 기반 생성 함수""" try: response = client.responses.create( model=model, input=message, tools=[{ "type": "file_search", "vector_store_ids": [vector_id], "max_num_results": 5 }] ) if len(response.output)==0: return "" if not response.output[1].content[0].annotations: return "" return response.output[1].content[0].text except Exception as e: print(f"Error in RAG: {e}") return "" def routing(messages, client, language): """사용자 질문 분석 및 라우팅 함수""" try: routing_messages = copy.deepcopy(messages) routing_messages[0]['content'] = FIRST_PROMPT response = client.chat.completions.create( model="gpt-4o", messages=routing_messages, response_format={"type": "json_object"} ) return json.loads(response.choices[0].message.content) except Exception as e: print(f"Error in routing: {e}") return {"Task": "Etc"} def generate_response(query_parsing, client, vector_id, user_message, chat_history, language): """쿼리 분석 결과에 따른 응답 생성 함수""" # 메시지 형식으로 변환 messages = [{"role": "system", "content": get_system_prompt(language)}] for user_msg, bot_msg in chat_history: if user_msg: messages.append({"role": "user", "content": user_msg}) if bot_msg: messages.append({"role": "assistant", "content": bot_msg}) if language!="한국어": messages.append({"role": "user", "content": user_message}) else: messages.append({"role": "user", "content": query_parsing["Translation"]}) # 쿼리 분석 결과가 없는 경우 if "Task" not in query_parsing: return get_text('error_understanding', language) # ReAsk: 추가 정보 요청 if query_parsing["Task"] == "ReAsk": if "Option" in query_parsing and "Condition" in query_parsing["Option"]: return f"{get_text('additional_info_needed', language)}{query_parsing['Option']['Condition']}" else: return get_text('request_more_info', language) # FindProduct: 제품 검색 if query_parsing["Task"] == "FindProduct": if "Option" not in query_parsing: return get_text('error_product_not_found', language) product = query_parsing['Option']["Product"] # 수파베이스에서 화장품 데이터 가져오기 try: product_list = supabase.table("cosmetics").select("*").execute().data except: # 실제 구현 시 데이터베이스 연결 필수 product_list = [] # 임시 빈 리스트 find_data = "" for item in product_list: if product in item["title"]: find_data += f"item : {item['title']}\tbrand : {item['brand']}\tmake : {item['maker']}\tsummary : {item['summary']}\n\n" if len(find_data) > 2: # 데이터를 찾았을 때 messages[-1]['content'] = messages[-1]['content'] + "\n\n<데이터>\n\n" + find_data + "\n\n\n\n" + messages[-1]['content'] response = client.chat.completions.create( model="gpt-4o", messages=messages ) return response.choices[0].message.content else: # 데이터를 찾지 못했을 때 RAG 시도 response = local_RAG("gpt-4o", user_message, client, vector_id) if response == "": # RAG도 실패했을 때 검색 모델 사용 response = client.chat.completions.create( model="gpt-4o-search-preview", messages=messages ) return response.choices[0].message.content return response # Recommendation: 제품 추천 if query_parsing["Task"] == "Recommandation": response = local_RAG("gpt-4o", user_message, client, vector_id) if response == "": # RAG 실패 시 검색 모델 사용 response = client.chat.completions.create( model="gpt-4o-search-preview", messages=messages ) return response.choices[0].message.content return response # Etc: 일반 응답 response = client.chat.completions.create( model="gpt-4o", messages=messages ) return response.choices[0].message.content def get_system_prompt(language): """언어에 맞는 시스템 프롬프트 생성""" return f""" 너는 한국의 화장품을 상담해 주는 AI야! 유저의 한국 화장품 관련 질문에 가장 적절한 답변을 해줘! 조건1 : 화장품과 관련되지 않은 질문은 대답하지 마. 조건2 : 주어진 language에 맞는 언어로 답변해줘! 조건3 : 유저가 별도의 데이터를 제공하면 (<데이터>... 형식), 제공된 데이터 내에서만 답변해줘. language: {language} """ # 현재 선택된 언어를 저장할 전역 변수 selected_language = "English" def chatbot(message, history, language_choice=None): """챗봇의 메인 함수""" global selected_language # language_choice가 제공되었으면 업데이트 if language_choice is not None: selected_language = language_choice # OpenAI API 키 및 환경 변수 설정 openai_api_key = os.getenv("OPENAI_API_KEY") vector_id = os.getenv("vector_id") # Supabase 설정 supabase_url = os.getenv("SUPABASE_URL") supabase_key = os.getenv("SUPABASE_KEY") supabase = None client = OpenAI(api_key=openai_api_key) # Supabase 클라이언트 생성 (있을 경우) if supabase_url and supabase_key: supabase = create_client(supabase_url, supabase_key) # 쿼리 분석 및 응답 생성 query_parsing = routing([{"role": "system", "content": ""}, {"role": "user", "content": message}], client, selected_language) response = generate_response(query_parsing, client, vector_id, message, history, selected_language) # Supabase 로깅 (해당 환경변수가 있을 경우) if supabase: chat_history = [] for user_msg, bot_msg in history: if user_msg: chat_history.append({"role": "user", "content": user_msg}) if bot_msg: chat_history.append({"role": "assistant", "content": bot_msg}) chat_history.append({"role": "user", "content": message}) chat_history.append({"role": "assistant", "content": response}) log_to_supabase(supabase, message, response, chat_history) return response def change_language(language_choice): """언어 변경 함수""" global selected_language selected_language = language_choice return language_choice # 메인 함수 def main(): """Gradio 인터페이스 설정 함수""" # 언어 설정 languages = ["English", "한국어", "Español", "中文", "日本語", "ภาษาไทย", "Tiếng Việt", "Bahasa Indonesia"] global selected_language selected_language = "English" current_dir = os.path.dirname(os.path.abspath(__file__)) image_path = os.path.join(current_dir, "image.png") # Gradio 인터페이스 구성 with gr.Blocks(css="footer {visibility: hidden}") as demo: gr.Markdown(f"# {get_text('title', selected_language)}") gr.Markdown(f"(Conversation logs are saved.)") with gr.Row(): language_dropdown = gr.Dropdown( choices=languages, value=selected_language, label=get_text("language_selector", selected_language) ) # 언어 상태를 저장할 state 객체 생성 lang_state = gr.State(value=selected_language) # 언어가 변경될 때 실행되는 함수 def update_language(new_lang, history): global selected_language selected_language = new_lang return new_lang, history # 챗봇의 메시지 전송 함수를 수정하여 현재 언어 상태를 전달 def handle_message(message, history, lang): return chatbot(message, history, lang) chatbot_interface = gr.ChatInterface( fn=handle_message, additional_inputs=[language_dropdown], title="", examples=[ ["이니스프리 그린티 제품 알려줘", "한국어"], ["แนะนำผลิตภัณฑ์ดูแลผิวที่ดีสำหรับผู้หญิง", "ภาษาไทย"], ["What products are effective for whitening?", "English"] ], ) # 언어 변경 시 이벤트 language_dropdown.change( fn=update_language, inputs=[language_dropdown, chatbot_interface.chatbot], outputs=[lang_state, chatbot_interface.chatbot] ) gr.Markdown(f"# Architecture") gr.Image(value=image_path, show_label=False, height=400, width=400) # Gradio 앱 실행 demo.launch() if __name__ == "__main__": main()