Spaces:
Sleeping
Sleeping
| from openai import OpenAI | |
| import os | |
| from typing import List, Dict, Any | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| class OpenAIClient: | |
| def __init__(self, api_key: str = None): | |
| """ | |
| OpenAI ν΄λΌμ΄μΈνΈ μ΄κΈ°ν | |
| api_keyκ° Noneμ΄λ©΄ νκ²½λ³μ OPENAI_API_KEYμμ κ°μ Έμ΄ | |
| """ | |
| self.api_key = api_key or os.getenv("OPENAI_API_KEY") | |
| if not self.api_key: | |
| raise ValueError("OpenAI API keyκ° νμν©λλ€.") | |
| self.client = OpenAI(api_key=self.api_key) | |
| def generate_idea( | |
| self, contest_info: Dict[str, str], nodes_data: List[Dict[str, Any]] | |
| ) -> Dict[str, str]: | |
| """ | |
| 곡λͺ¨μ μ 보μ λ Έλ λ°μ΄ν°λ₯Ό κΈ°λ°μΌλ‘ μμ΄λμ΄ μμ± | |
| """ | |
| try: | |
| # λ Έλ μμ½ | |
| nodes_summary = self._format_nodes_for_prompt(nodes_data) | |
| prompt = self._create_prompt(contest_info, nodes_summary) | |
| # OpenAI GPT νΈμΆ | |
| response = self.client.chat.completions.create( | |
| model="gpt-4o", # λλ "gpt-4", "gpt-3.5-turbo" | |
| messages=[ | |
| { | |
| "role": "system", | |
| "content": """ | |
| λΉμ μ μμ΄λμμ΄μ μ λ¬Έκ°μ΄μ ν¬λ¦¬μμ΄ν°λΈ 컨μ€ν΄νΈμ λλ€. μ λ ₯μΌλ‘ μ£Όμ΄μ§λ λ€μ λ€ κ°μ§ μμλ₯Ό κ²°ν©ν΄, ꡬ체μ μ΄κ³ μ€ν κ°λ₯ν νμ μμ΄λμ΄λ₯Ό μ μν΄μΌ ν©λλ€. | |
| 1. λλ©μΈ(Domain): μμ΄λμμ΄μ μ μΆλ°μ μ΄ λλ λΆμΌ (μ: λμ , μλ£, λ², μμ λ±) | |
| 2. 컨ν μ€νΈ(Context): ν΄λΉ κ³Όμ λ₯Ό μνν΄μΌ νλ μ΄μ λ λ°°κ²½ μ€λͺ (μ: 곡λͺ¨μ μ£Όμ , μ£Όμ΅κΈ°κ΄μ λͺ©ν, μμ₯ λν₯) | |
| 3. μ΄κ·Έλμ΄ν°(Igniter): μμ΄λμ΄μ ν΅μ¬ λ°©ν₯μ±μ κ²°μ νλ ν€μλλ μ§λ¬Έ (μ: μ§μκ°λ₯μ± κ·Ήλν, λ°μ΄ν° λ―Όμ£Όν, μ¬μ©μ μ°Έμ¬ κ°ν λ±) | |
| 4. λ Έλ(Nodes): μ¬μ©μμ κ²½ν, νλ‘μ νΈ μ¬λ‘, κΈ°μ μ€ν λ± Connecting the Dotsλ₯Ό μν μμ° μ»¬λ μ | |
| """, | |
| }, | |
| {"role": "user", "content": prompt}, | |
| ], | |
| temperature=0.7, | |
| max_tokens=5000, | |
| ) | |
| generated_text = response.choices[0].message.content | |
| idea = self._parse_generated_idea(generated_text, contest_info) | |
| return idea | |
| except Exception as e: | |
| return { | |
| "error": f"μμ΄λμ΄ μμ± μ€ μ€λ₯: {str(e)}", | |
| "ai_name": "ChatGPT", | |
| "title": "μ€λ₯", | |
| "overview": "μμ΄λμ΄ μμ± μ€ν¨", | |
| "problem": "", | |
| "solution": "", | |
| "implementation": "", | |
| "expected_effect": "", | |
| "rationale": "", | |
| } | |
| def _format_nodes_for_prompt(self, nodes_data: List[Dict[str, Any]]) -> str: | |
| """λ Έλ λ°μ΄ν°λ₯Ό λ¬Έμμ΄λ‘ ν¬λ§·ν """ | |
| if not nodes_data: | |
| return "κΈ°μ‘΄ νλ‘μ νΈ μ 보 μμ." | |
| formatted = [] | |
| for i, node in enumerate(nodes_data, 1): | |
| formatted.append( | |
| f""" | |
| νλ‘μ νΈ {i}: | |
| - μ λͺ©: {node.get('title', 'μ λͺ© μμ')} | |
| - μ€λͺ : {node.get('description', 'μ€λͺ μμ')} | |
| - ν λνΈ: {node.get('tenant', 'λ―Έμ§μ ')} | |
| - νκ·Έ: {', '.join(node.get('tags', []))} | |
| """ | |
| ) | |
| return "\n".join(formatted) | |
| def _create_prompt(self, contest_info: Dict[str, str], nodes_summary: str) -> str: | |
| """ChatGPTμ© ν둬ννΈ κ΅¬μ±""" | |
| return f""" | |
| λ€μ μ λ ₯κ°μ μ°Έκ³ ν΄μ κ°μ₯ μ΅κ³ μ μμ΄λμ΄λ₯Ό **1κ°μ§** μ μν΄μ£ΌμΈμ. | |
| κ° μμ΄λμ΄λ λλ©μΈ μ€μ¬μΌλ‘ 컨ν μ€νΈΒ·μ΄κ·Έλμ΄ν°Β·λ Έλλ₯Ό κ²°ν©νμ¬ μμ±ν©λλ€. | |
| γνκ² κ³΅λͺ¨μ μ 보γ | |
| - 곡λͺ¨μ μ λͺ©: {contest_info.get('title', '')} | |
| - λλ©μΈ: {contest_info.get('theme', '')} | |
| - 컨ν μ€νΈ: {contest_info.get('description', '')} | |
| - μ΄κ·Έλμ΄ν°: {contest_info.get('context', '')} | |
| γconnecting the dotsμ μν λ Έλ μ 보γ | |
| - λ Έλ: {nodes_summary} | |
| μμ) | |
| - λλ©μΈ: μ€λ§νΈ ν | |
| - 컨ν μ€νΈ: λλ¦ΌλΆ μ£Όμ΅ βμΉνκ²½ μ€λ§νΈ λμ 곡λͺ¨μ β, μ νμ λ°°μΆ μ°μμ¬λ‘ λ°κ΅΄ | |
| - μ΄κ·Έλμ΄ν°: βAIλ‘ ν μ κ±΄κ° μ€μκ° λͺ¨λν°λ§β | |
| - λ Έλ: OpenCV κΈ°λ° μ΄λ―Έμ§ λΆμ, AWS RDS λμ보λ κ°λ° κ²½ν, IoT μΌμ λ€νΈμν¬ κ΅¬μΆ κ²½ν | |
| μλ νμμ λ§μΆ° μλ΅ν΄μ£ΌμΈμ: | |
| μ λͺ©: [μμ΄λμ΄ μ λͺ©] | |
| κ°μ: [κ°λ¨ν μκ°] | |
| λ¬Έμ μμ: [ν΄κ²°νκ³ μ νλ λ¬Έμ ] | |
| μ루μ : [ꡬ체μ μΈ ν΄κ²° λ°©μ] | |
| ꡬνλ°©μ: [κΈ°μ μ ꡬν λλ μ€ν κ³νμ λ¨κ³λ³λ‘ λμ΄ (1. 2. 3. νν λλ - ννλ‘)] | |
| κΈ°λν¨κ³Ό: [μμ μ±κ³Ό λλ ν¨κ³Όλ₯Ό νλͺ©λ³λ‘ λμ΄ (- ννλ‘ μμ±)] | |
| κ·Όκ±°: [μμ 곡λͺ¨μ μ 보μ λ Έλλ€μ΄ μ΄λ»κ² μ°κ²°λμ΄ μ΄ μμ΄λμ΄κ° λμΆλμλμ§λ₯Ό connecting the dots κ΄μ μμ λ Όλ¦¬μ λ¨κ³λ³λ‘ μ€λͺ (- ννλ‘ μμ±)] | |
| """ | |
| def _parse_generated_idea( | |
| self, generated_text: str, contest_info: Dict[str, str] | |
| ) -> Dict[str, str]: | |
| """AI μμ± κ²°κ³Όλ₯Ό ꡬ쑰νλ ννλ‘ νμ±""" | |
| idea = { | |
| "ai_name": "ChatGPT", | |
| "title": "", | |
| "overview": "", | |
| "problem": "", | |
| "solution": "", | |
| "implementation": "", | |
| "expected_effect": "", | |
| "rationale": "", | |
| "contest_info": contest_info, | |
| "raw_response": generated_text, | |
| } | |
| try: | |
| # λ κ°κ±΄ν νμ±μ μν ν€μλ λ§€ν | |
| keyword_mappings = { | |
| "μ λͺ©": "title", | |
| "κ°μ": "overview", | |
| "λ¬Έμ μμ": "problem", | |
| "μ루μ ": "solution", | |
| "ꡬνλ°©μ": "implementation", | |
| "κΈ°λν¨κ³Ό": "expected_effect", | |
| "κ·Όκ±°": "rationale", | |
| } | |
| lines = generated_text.split("\n") | |
| current_key = None | |
| for line in lines: | |
| line = line.strip() | |
| if not line: # λΉ μ€ κ±΄λλ°κΈ° | |
| continue | |
| # ν€μλ κ²μ (μ½λ‘ ν¬ν¨) | |
| found_key = False | |
| for keyword, key in keyword_mappings.items(): | |
| if ( | |
| line.startswith(f"{keyword}:") | |
| or line.startswith(f"**{keyword}:**") | |
| or line.startswith(f"#{keyword}") | |
| ): | |
| current_key = key | |
| # μ½λ‘ μ΄ν λ΄μ© μΆμΆ | |
| content = line.split(":", 1)[1].strip() if ":" in line else "" | |
| idea[current_key] = content | |
| found_key = True | |
| break | |
| # ν€μλκ° λ°κ²¬λμ§ μμκ³ νμ¬ ν€κ° μμΌλ©΄ λ΄μ© μΆκ° | |
| if not found_key and current_key and line: | |
| if idea[current_key]: | |
| idea[current_key] += " " + line | |
| else: | |
| idea[current_key] = line | |
| except Exception as e: | |
| print(f"[νμ±μ€λ₯] {e}") | |
| print(f"[μλ³Έμλ΅] {generated_text}") | |
| idea["title"] = "νμ± μ€ν¨" | |
| idea["overview"] = f"κ²°κ³Ό νμ±μ μ€ν¨νμ΅λλ€. μ€λ₯: {str(e)}" | |
| # νμ± ν λΉ νλκ° μλμ§ νμΈ λ° λλ²κΉ | |
| empty_fields = [ | |
| key | |
| for key, value in idea.items() | |
| if key not in ["contest_info", "raw_response", "ai_name"] and not value | |
| ] | |
| if empty_fields: | |
| print(f"[κ²½κ³ ] λΉ νλ λ°κ²¬: {empty_fields}") | |
| print(f"[μλ³Έμλ΅ μΌλΆ] {generated_text[:500]}...") | |
| # μ΅μνμ μ λͺ©μ ν보 | |
| if not idea.get("title"): | |
| idea["title"] = "μ λͺ© μμ" | |
| # ꡬνλ°©μ, κΈ°λν¨κ³Ό, κ·Όκ±° νλ κ°λ μ± κ°μ (νλͺ©λ³ κ°ν μΆκ°) | |
| if idea.get("implementation"): | |
| idea["implementation"] = format_list_text(idea["implementation"]) | |
| if idea.get("expected_effect"): | |
| idea["expected_effect"] = format_list_text(idea["expected_effect"]) | |
| if idea.get("rationale"): | |
| idea["rationale"] = format_list_text(idea["rationale"]) | |
| return idea | |
| def format_list_text(text: str) -> str: | |
| """ν μ€νΈμ κ°λ μ±μ κ°μ νμ¬ κ° νλͺ©λ³λ‘ κ°ν μΆκ°""" | |
| if not text: | |
| return text | |
| import re | |
| # λ¨Όμ ν μ€νΈλ₯Ό μ 리 | |
| text = text.strip() | |
| # "- " ν¨ν΄μ΄ μμΌλ©΄ μ°μ μ²λ¦¬ | |
| if "- " in text and not text.startswith("- "): | |
| # "- " μμ κ°ν μΆκ° (첫 λ²μ§Έ νλͺ© μ μΈ) | |
| formatted_text = re.sub(r"([^.\n])\s*(-\s)", r"\1\n\2", text) | |
| else: | |
| # "- " ν¨ν΄μ΄ μκ±°λ μ΄λ―Έ 첫 λ²μ§Έκ° "- "λ‘ μμνλ©΄ μ«μ ν¨ν΄λ μ²λ¦¬ | |
| formatted_text = text | |
| # "1. ", "2. " λ±μ ν¨ν΄ μμ κ°ν μΆκ° (첫 λ²μ§Έ νλͺ© μ μΈ) | |
| formatted_text = re.sub(r"([^.\n])(\d+\.\s)", r"\1\n\2", formatted_text) | |
| # "- " λ‘ μμνλ νλͺ©λ€ μ²λ¦¬ | |
| formatted_text = re.sub(r"([^.\n])\s*(-\s)", r"\1\n\2", formatted_text) | |
| # "β’ " λ‘ μμνλ νλͺ©λ€ μ²λ¦¬ | |
| formatted_text = re.sub(r"([^.\n])\s*(β’\s)", r"\1\n\2", formatted_text) | |
| # μ²μμ κ°νμ΄ μΆκ°λ κ²½μ° μ κ±° | |
| if formatted_text.startswith("\n"): | |
| formatted_text = formatted_text[1:] | |
| return formatted_text | |
| def format_implementation_text(text: str) -> str: | |
| """ꡬνλ°©μ ν μ€νΈμ κ°λ μ±μ κ°μ νμ¬ κ° νλͺ©λ³λ‘ κ°ν μΆκ° (νμ νΈνμ±)""" | |
| return format_list_text(text) | |
| # μ¬μ© μμ | |
| def create_openai_client(api_key: str = None) -> OpenAIClient: | |
| return OpenAIClient(api_key) | |