Naming / app.py
openfree's picture
Update app.py
bc9d1cb verified
raw
history blame
32.5 kB
"""
Ultimate Brand Theory Generator - Unified Output Version
========================================================
2025-05-28 | 15๊ฐœ ์ด๋ก ์„ ํ†ตํ•ฉํ•œ ์ข…ํ•ฉ ๋ธŒ๋žœ๋“œ ์ƒ์„ฑ๊ธฐ (ํ†ต์ผ๋œ ์ถœ๋ ฅ ๊ตฌ์กฐ)
------------------------------------------------------------------------
"""
import os
import json
import gradio as gr
import openai
from openai import OpenAI
from datetime import datetime
from typing import List, Dict, Tuple, Optional
import random
# OpenAI ํด๋ผ์ด์–ธํŠธ
if not os.getenv("OPENAI_API_KEY"):
raise EnvironmentError("OPENAI_API_KEY ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•˜์„ธ์š”.")
client = OpenAI()
# ์ด๋ก ๋ณ„ ์„ค๋ช…
THEORY_DESCRIPTIONS = {
"square": "4๊ฐœ์˜ ๋‹จ์–ด๊ฐ€ ์˜๋ฏธ์  ๊ด€๊ณ„๋กœ ์—ฐ๊ฒฐ๋˜์–ด ์‚ฌ๊ฐํ˜•์„ ์ด๋ฃจ๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค. ๋Œ€๋ณ€์˜ ์ˆจ๊ฒจ์ง„ ์—ฐ๊ฒฐ์ด '์•„ํ•˜!' ๋ชจ๋จผํŠธ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.",
"blending": "๋‘ ๊ฐœ ์ด์ƒ์˜ ๊ฐœ๋…์„ ํ˜ผํ•ฉํ•˜์—ฌ ์ƒˆ๋กœ์šด ์˜๋ฏธ๋ฅผ ์ฐฝ์ถœํ•ฉ๋‹ˆ๋‹ค. Netflix(Net+Flix)์ฒ˜๋Ÿผ ํ˜์‹ ์ ์ธ ๊ฐœ๋…์„ ํƒ„์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.",
"sound": "์Œ์†Œ์™€ ์˜๋ฏธ ๊ฐ„์˜ ์—ฐ๊ด€์„ฑ์„ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค. 'i,e'๋Š” ๊ฐ€๋ณ๊ณ  ๋น ๋ฅธ ๋А๋‚Œ, 'o,u'๋Š” ๋ฌด๊ฒ๊ณ  ๋А๋ฆฐ ๋А๋‚Œ์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.",
"linguistic": "์–ธ์–ด๋ณ„ ์‚ฌ๊ณ ๋ฐฉ์‹ ์ฐจ์ด๋ฅผ ๊ณ ๋ คํ•œ ๊ธ€๋กœ๋ฒŒ ๋ธŒ๋žœ๋“œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋ฌธํ™”์  ๋‰˜์•™์Šค์™€ ํ˜„์ง€ํ™” ์ „๋žต์„ ๋ฐ˜์˜ํ•ฉ๋‹ˆ๋‹ค.",
"archetype": "Jung์˜ 12๊ฐ€์ง€ ๋ณดํŽธ์  ์›ํ˜•์„ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค. Hero(Nike), Creator(Apple)์ฒ˜๋Ÿผ ๋ฌด์˜์‹์  ๊ฐ์ • ์—ฐ๊ฒฐ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.",
"jobs": "๊ณ ๊ฐ์ด ํ•ด๊ฒฐํ•˜๋ ค๋Š” '์ผ'์— ์ดˆ์ ์„ ๋งž์ถฅ๋‹ˆ๋‹ค. ๊ธฐ๋Šฅ์ , ๊ฐ์ •์ , ์‚ฌํšŒ์  ์ฐจ์›์˜ ๋‹ˆ์ฆˆ๋ฅผ ํ†ตํ•ฉ์ ์œผ๋กœ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.",
"scamper": "7๊ฐ€์ง€ ์ฐฝ์˜์  ๊ธฐ๋ฒ•(๋Œ€์ฒด, ๊ฒฐํ•ฉ, ์ ์‘, ์ˆ˜์ •, ์šฉ๋„๋ณ€๊ฒฝ, ์ œ๊ฑฐ, ์—ญ์ „)์œผ๋กœ ํ˜์‹ ์ ์ธ ๋ธŒ๋žœ๋“œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.",
"design": "์ธ๊ฐ„ ์ค‘์‹ฌ ํ˜์‹ ์„ ์ถ”๊ตฌํ•ฉ๋‹ˆ๋‹ค. ๋ฐ”๋žŒ์งํ•จ(์ธ๊ฐ„), ์‹คํ˜„๊ฐ€๋Šฅ์„ฑ(๊ธฐ์ˆ ), ์ƒ์กด๊ฐ€๋Šฅ์„ฑ(๋น„์ฆˆ๋‹ˆ์Šค)์˜ ๊ต์ง‘ํ•ฉ์„ ์ฐพ์Šต๋‹ˆ๋‹ค.",
"biomimicry": "์ž์—ฐ์—์„œ ์˜๊ฐ์„ ๋ฐ›์€ ๋ธŒ๋žœ๋“œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. 38์–ต๋…„ ์ง„ํ™”์˜ ์ง€ํ˜œ๋ฅผ ๋ธŒ๋žœ๋”ฉ์— ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.",
"cognitive": "์ธ์ง€ ์ฒ˜๋ฆฌ๋ฅผ ์ตœ์†Œํ™”ํ•˜๋Š” ๋ธŒ๋žœ๋“œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. 1-3์Œ์ ˆ์˜ ์‰ฌ์šด ๋ฐœ์Œ์œผ๋กœ ์ฆ‰๊ฐ์  ์ธ์‹๊ณผ ๊ธฐ์–ต์„ ๋•์Šต๋‹ˆ๋‹ค.",
"vonrestorff": "๋…ํŠนํ•˜๊ณ  ๊ธฐ์–ต์— ๋‚จ๋Š” ๋ธŒ๋žœ๋“œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์นดํ…Œ๊ณ ๋ฆฌ ๊ด€์Šต์„ ์˜๋„์ ์œผ๋กœ ์œ„๋ฐ˜ํ•˜์—ฌ 30๋ฐฐ ๋” ์ž˜ ๊ธฐ์–ต๋˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.",
"network": "๋„คํŠธ์›Œํฌ ๊ฐ€์น˜๋ฅผ ๊ทน๋Œ€ํ™”ํ•˜๋Š” ๋ธŒ๋žœ๋“œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ๋งŽ์„์ˆ˜๋ก ๊ฐ€์น˜๊ฐ€ ์ฆ๊ฐ€ํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•ฉ๋‹ˆ๋‹ค.",
"memetics": "๋ฌธํ™”์ ์œผ๋กœ ๋ณต์ œ๋˜๊ณ  ์ง„ํ™”ํ•˜๋Š” ๋ธŒ๋žœ๋“œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋ฐˆ์ฒ˜๋Ÿผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํผ์ ธ๋‚˜๊ฐ€๋Š” ๋ฐ”์ด๋Ÿด ์š”์†Œ๋ฅผ ๋‚ด์žฌํ™”ํ•ฉ๋‹ˆ๋‹ค.",
"color": "์ƒ‰์ƒ ์—ฐ์ƒ๊ณผ ๊ฐ์ •์„ ํ™œ์šฉํ•œ ๋ธŒ๋žœ๋“œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋นจ๊ฐ•(์—ด์ •), ํŒŒ๋ž‘(์‹ ๋ขฐ), ์ดˆ๋ก(์ž์—ฐ) ๋“ฑ ์ƒ‰์ƒ ์‹ฌ๋ฆฌ๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.",
"gestalt": "์ง€๊ฐ ์›๋ฆฌ๋ฅผ ํ™œ์šฉํ•œ ๋ธŒ๋žœ๋“œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์ „์ฒด๊ฐ€ ๋ถ€๋ถ„์˜ ํ•ฉ๋ณด๋‹ค ํฌ๋‹ค๋Š” ์›์น™์œผ๋กœ ํ†ตํ•ฉ์  ๋ธŒ๋žœ๋“œ ๊ฒฝํ—˜์„ ์„ค๊ณ„ํ•ฉ๋‹ˆ๋‹ค."
}
# ํ†ต์ผ๋œ ๊ธฐ๋ณธ ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ
UNIFIED_BASE_PROMPT = """
๋‹น์‹ ์€ {theory_name} ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. {theory_description}
์‚ฌ์šฉ์ž ์ž…๋ ฅ(์—…์ข…/ํ‚ค์›Œ๋“œ)์„ ๋ฐ›์•„ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ†ต์ผ๋œ JSON ํ˜•์‹์˜ ๋ฐฐ์—ด์„ ์ƒ์„ฑํ•˜์„ธ์š”:
{{
"brands": [
{{
"core": {{
"brand_name": "๋ธŒ๋žœ๋“œ๋ช…",
"slogan": "์Šฌ๋กœ๊ฑด",
"core_value": "ํ•ต์‹ฌ ๊ฐ€์น˜",
"target_emotion": "๋ชฉํ‘œ ๊ฐ์ •",
"brand_personality": "๋ธŒ๋žœ๋“œ ์„ฑ๊ฒฉ"
}},
"visual": {{
"primary_color": "#HEX์ฝ”๋“œ",
"color_meaning": "์ƒ‰์ƒ ์˜๋ฏธ",
"visual_concept": "์‹œ๊ฐ์  ์ปจ์…‰",
"typography_style": "ํƒ€์ดํฌ๊ทธ๋ž˜ํ”ผ ์Šคํƒ€์ผ"
}},
"linguistic": {{
"pronunciation": "๋ฐœ์Œ ๊ฐ€์ด๋“œ",
"etymology": "์–ด์›/๊ตฌ์„ฑ",
"global_adaptability": "๊ธ€๋กœ๋ฒŒ ์ ์‘์„ฑ",
"memorable_factor": "๊ธฐ์–ต ์šฉ์ด์„ฑ ์š”์†Œ"
}},
"strategic": {{
"differentiation": "์ฐจ๋ณ„ํ™” ํฌ์ธํŠธ",
"market_positioning": "์‹œ์žฅ ํฌ์ง€์…”๋‹",
"growth_potential": "์„ฑ์žฅ ์ž ์žฌ๋ ฅ",
"implementation_ease": "์‹คํ–‰ ์šฉ์ด์„ฑ"
}},
"theory_specific": {{
{theory_specific_fields}
}},
"evaluation": {{
"creativity_score": 0-10,
"memorability_score": 0-10,
"relevance_score": 0-10,
"overall_effectiveness": "์ „์ฒด์ ์ธ ํšจ๊ณผ์„ฑ ์„ค๋ช…"
}}
}}
]
}}
๋ฐ˜๋“œ์‹œ ์œ ํšจํ•œ JSON ํ˜•์‹์œผ๋กœ ์‘๋‹ตํ•˜๊ณ , ๋ชจ๋“  ํ•„๋“œ๋ฅผ ์ฑ„์›Œ์ฃผ์„ธ์š”.
๊ฐ ์ด๋ก ์˜ ํŠน์„ฑ์„ theory_specific ์„น์…˜์— ๋‹ด๋˜, ๋‚˜๋จธ์ง€๋Š” ํ†ต์ผ๋œ ๊ตฌ์กฐ๋ฅผ ์œ ์ง€ํ•˜์„ธ์š”.
"""
# ์ด๋ก ๋ณ„ ํŠน์ˆ˜ ํ•„๋“œ ์ •์˜
THEORY_SPECIFIC_FIELDS = {
"square": """
"tl": "์™ผ์ชฝ์ƒ๋‹จ",
"tr": "์˜ค๋ฅธ์ชฝ์ƒ๋‹จ",
"bl": "์™ผ์ชฝํ•˜๋‹จ",
"br": "์˜ค๋ฅธ์ชฝํ•˜๋‹จ",
"top_edge": "์ƒ๋‹จ ๊ด€๊ณ„",
"bottom_edge": "ํ•˜๋‹จ ๊ด€๊ณ„",
"left_edge": "์™ผ์ชฝ ๊ด€๊ณ„",
"right_edge": "์˜ค๋ฅธ์ชฝ ๊ด€๊ณ„",
"diagonal_insight": "๋Œ€๊ฐ์„  ํ†ต์ฐฐ"
""",
"blending": """
"input_space1": "์ฒซ ๋ฒˆ์งธ ๊ฐœ๋…",
"input_space2": "๋‘ ๋ฒˆ์งธ ๊ฐœ๋…",
"generic_space": "๊ณตํ†ต ๊ตฌ์กฐ",
"blended_space": "ํ˜ผํ•ฉ๋œ ์ƒˆ๋กœ์šด ์˜๋ฏธ",
"emergent_properties": "์ฐฝ๋ฐœ์  ์†์„ฑ๋“ค",
"blend_ratio": "ํ˜ผํ•ฉ ๋น„์œจ"
""",
"sound": """
"phonetic_analysis": "์Œ์„ฑ ๋ถ„์„",
"sound_meaning": "์Œํ–ฅ์ด ์ „๋‹ฌํ•˜๋Š” ์˜๋ฏธ",
"vowel_consonant_ratio": "๋ชจ์Œ/์ž์Œ ๋น„์œจ",
"phoneme_emotion_map": "์Œ์†Œ-๊ฐ์ • ๋งคํ•‘",
"cross_linguistic_sound": "์–ธ์–ด๊ฐ„ ์Œํ–ฅ ์ผ๊ด€์„ฑ"
""",
"linguistic": """
"korean_adaptation": "ํ•œ๊ตญ์–ด ์ ์‘",
"english_meaning": "์˜์–ด ์˜๋ฏธ",
"cultural_considerations": "๋ฌธํ™”์  ๊ณ ๋ ค์‚ฌํ•ญ",
"avoid_meanings": "ํ”ผํ•ด์•ผ ํ•  ์˜๋ฏธ๋“ค",
"localization_strategy": "ํ˜„์ง€ํ™” ์ „๋žต"
""",
"archetype": """
"archetype": "์„ ํƒ๋œ ์›ํ˜•",
"archetype_traits": "์›ํ˜•์˜ ํŠน์ง•๋“ค",
"shadow_side": "๊ทธ๋ฆผ์ž ์ธก๋ฉด",
"mythology_reference": "์‹ ํ™”์  ์ฐธ์กฐ",
"customer_journey": "๊ณ ๊ฐ ์—ฌ์ • ์—ฐ๊ฒฐ"
""",
"jobs": """
"functional_job": "๊ธฐ๋Šฅ์  ์ผ",
"emotional_job": "๊ฐ์ •์  ์ผ",
"social_job": "์‚ฌํšŒ์  ์ผ",
"job_statement": "ํ•ต์‹ฌ Job ๋ฌธ์žฅ",
"outcome_metrics": "์„ฑ๊ณผ ์ง€ํ‘œ"
""",
"scamper": """
"scamper_technique": "์‚ฌ์šฉ๋œ ๊ธฐ๋ฒ•",
"original_concept": "์›๋ž˜ ๊ฐœ๋…",
"transformation": "๋ณ€ํ˜• ๊ณผ์ •",
"innovation_type": "ํ˜์‹  ์œ ํ˜•",
"disruption_level": "ํŒŒ๊ดด์  ํ˜์‹  ์ˆ˜์ค€"
""",
"design": """
"user_insight": "์‚ฌ์šฉ์ž ํ†ต์ฐฐ",
"pain_point": "ํ•ด๊ฒฐํ•˜๋Š” ๋ฌธ์ œ์ ",
"desirability": "๋ฐ”๋žŒ์งํ•จ (์ธ๊ฐ„)",
"feasibility": "์‹คํ˜„๊ฐ€๋Šฅ์„ฑ (๊ธฐ์ˆ )",
"viability": "์ƒ์กด๊ฐ€๋Šฅ์„ฑ (๋น„์ฆˆ๋‹ˆ์Šค)"
""",
"biomimicry": """
"natural_inspiration": "์ž์—ฐ์  ์˜๊ฐ์›",
"biomimetic_principle": "์ƒ์ฒด๋ชจ๋ฐฉ ์›๋ฆฌ",
"form_function": "ํ˜•ํƒœ์™€ ๊ธฐ๋Šฅ",
"sustainability_aspect": "์ง€์†๊ฐ€๋Šฅ์„ฑ ์ธก๋ฉด",
"adaptation_strategy": "์ ์‘ ์ „๋žต"
""",
"cognitive": """
"syllable_count": "์Œ์ ˆ ์ˆ˜",
"processing_ease": "์ฒ˜๋ฆฌ ์šฉ์ด์„ฑ ์ ์ˆ˜",
"memory_hooks": "๊ธฐ์–ต ๊ณ ๋ฆฌ",
"cognitive_fluency": "์ธ์ง€์  ์œ ์ฐฝ์„ฑ",
"attention_span_fit": "์ฃผ์˜๋ ฅ ์ ํ•ฉ๋„"
""",
"vonrestorff": """
"category_norm": "์นดํ…Œ๊ณ ๋ฆฌ ํ‘œ์ค€",
"deviation_strategy": "์ผํƒˆ ์ „๋žต",
"uniqueness_factors": "๋…ํŠน์„ฑ ์š”์†Œ๋“ค",
"attention_triggers": "์ฃผ์˜ ํŠธ๋ฆฌ๊ฑฐ",
"isolation_effect": "๊ณ ๋ฆฝ ํšจ๊ณผ ํ™œ์šฉ"
""",
"network": """
"network_type": "๋„คํŠธ์›Œํฌ ์œ ํ˜•",
"viral_coefficient": "๋ฐ”์ด๋Ÿด ๊ณ„์ˆ˜",
"sharing_ease": "๊ณต์œ  ์šฉ์ด์„ฑ",
"community_aspect": "์ปค๋ฎค๋‹ˆํ‹ฐ ์ธก๋ฉด",
"network_value": "๋„คํŠธ์›Œํฌ ๊ฐ€์น˜"
""",
"memetics": """
"meme_structure": "๋ฐˆ ๊ตฌ์กฐ",
"replication_ease": "๋ณต์ œ ์šฉ์ด์„ฑ",
"mutation_potential": "๋ณ€์ด ์ž ์žฌ๋ ฅ",
"cultural_fitness": "๋ฌธํ™”์  ์ ํ•ฉ๋„",
"transmission_channels": "์ „๋‹ฌ ์ฑ„๋„"
""",
"color": """
"color_palette": "์ƒ‰์ƒ ํŒ”๋ ˆํŠธ",
"emotional_response": "๊ฐ์ •์  ๋ฐ˜์‘",
"cultural_associations": "๋ฌธํ™”์  ์—ฐ์ƒ",
"industry_alignment": "์—…์ข… ์ •๋ ฌ",
"color_accessibility": "์ƒ‰์ƒ ์ ‘๊ทผ์„ฑ"
""",
"gestalt": """
"gestalt_principle": "ํ™œ์šฉ ์›์น™",
"visual_structure": "์‹œ๊ฐ์  ๊ตฌ์กฐ",
"perceptual_grouping": "์ง€๊ฐ์  ๊ทธ๋ฃนํ™”",
"figure_ground": "์ „๊ฒฝ-๋ฐฐ๊ฒฝ ๊ด€๊ณ„",
"closure_effect": "ํ์‡„ ํšจ๊ณผ"
"""
}
def create_theory_prompt(theory: str) -> str:
"""๊ฐ ์ด๋ก ๋ณ„ ํ†ต์ผ๋œ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ"""
theory_names = {
"square": "Square Theory",
"blending": "Conceptual Blending Theory",
"sound": "Sound Symbolism",
"linguistic": "Linguistic Relativity",
"archetype": "Jung์˜ Archetype Theory",
"jobs": "Jobs-to-be-Done Theory",
"scamper": "SCAMPER Method",
"design": "IDEO์˜ Design Thinking",
"biomimicry": "Biomimicry",
"cognitive": "Cognitive Load Theory",
"vonrestorff": "Von Restorff Effect",
"network": "Network Effects",
"memetics": "Memetics",
"color": "Color Psychology",
"gestalt": "Gestalt Theory"
}
return UNIFIED_BASE_PROMPT.format(
theory_name=theory_names[theory],
theory_description=THEORY_DESCRIPTIONS[theory],
theory_specific_fields=THEORY_SPECIFIC_FIELDS[theory]
)
def generate_by_theory(industry: str, keywords: str, theory: str, count: int = 3) -> Tuple[str, str]:
"""ํŠน์ • ์ด๋ก ์œผ๋กœ ๋ธŒ๋žœ๋“œ ์ƒ์„ฑ"""
if not industry or not keywords:
return "โš ๏ธ ์—…์ข…๊ณผ ํ‚ค์›Œ๋“œ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", ""
prompt = create_theory_prompt(theory)
user_input = f"""์—…์ข…: {industry}
ํ‚ค์›Œ๋“œ: {keywords}
์œ„ ์ •๋ณด๋กœ {count}๊ฐœ์˜ ๋ธŒ๋žœ๋“œ๋ฅผ ์ƒ์„ฑํ•˜์„ธ์š”.
๊ฐ ๋ธŒ๋žœ๋“œ๋Š” ํ†ต์ผ๋œ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๋˜, theory_specific ์„น์…˜์—๋Š” {theory} ์ด๋ก ์˜ ํŠน์„ฑ์„ ๋ฐ˜์˜ํ•˜์„ธ์š”."""
try:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": prompt},
{"role": "user", "content": user_input}
],
temperature=0.8,
max_tokens=2000,
response_format={"type": "json_object"}
)
content = response.choices[0].message.content
data = json.loads(content)
# ์‘๋‹ต ์ •๊ทœํ™”
if "brands" in data:
results = data["brands"]
else:
results = [data]
if not isinstance(results, list):
results = [results]
# ๋งˆํฌ๋‹ค์šด ์ƒ์„ฑ
markdown = generate_unified_markdown(theory, results, industry, keywords)
# HTML ์‹œ๊ฐํ™” ์ƒ์„ฑ
html = generate_unified_visualization(theory, results)
return markdown, html
except Exception as e:
error_msg = f"โŒ {theory} ์ด๋ก  ์˜ค๋ฅ˜: {str(e)}"
print(error_msg)
return error_msg, ""
def generate_unified_markdown(theory: str, results: List[Dict], industry: str, keywords: str) -> str:
"""ํ†ต์ผ๋œ ๋งˆํฌ๋‹ค์šด ์ƒ์„ฑ"""
theory_names = {
"square": "Square Theory",
"blending": "Conceptual Blending",
"sound": "Sound Symbolism",
"linguistic": "Linguistic Relativity",
"archetype": "Archetype Theory",
"jobs": "Jobs-to-be-Done",
"scamper": "SCAMPER Method",
"design": "Design Thinking",
"biomimicry": "Biomimicry",
"cognitive": "Cognitive Load Theory",
"vonrestorff": "Von Restorff Effect",
"network": "Network Effects",
"memetics": "Memetics",
"color": "Color Psychology",
"gestalt": "Gestalt Principles"
}
theory_icons = {
"square": "๐ŸŸฆ", "blending": "๐Ÿ”€", "sound": "๐Ÿ”Š", "linguistic": "๐ŸŒ",
"archetype": "๐ŸŽญ", "jobs": "โœ…", "scamper": "๐Ÿ”ง", "design": "๐Ÿ’ญ",
"biomimicry": "๐ŸŒฟ", "cognitive": "๐Ÿง ", "vonrestorff": "โšก", "network": "๐ŸŒ",
"memetics": "๐Ÿงฌ", "color": "๐ŸŽจ", "gestalt": "๐Ÿ‘๏ธ"
}
markdown = f"""# {theory_icons[theory]} {theory_names[theory]}
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 10px; margin-bottom: 20px;">
<h3 style="margin: 0 0 10px 0;">์ด๋ก  ๊ฐœ์š”</h3>
<p style="margin: 0; line-height: 1.6;">{THEORY_DESCRIPTIONS[theory]}</p>
</div>
**์—…์ข…**: {industry} | **ํ‚ค์›Œ๋“œ**: {keywords}
*์ƒ์„ฑ ์‹œ๊ฐ: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*
---
"""
for idx, result in enumerate(results, 1):
core = result.get('core', {})
visual = result.get('visual', {})
linguistic = result.get('linguistic', {})
strategic = result.get('strategic', {})
theory_specific = result.get('theory_specific', {})
evaluation = result.get('evaluation', {})
brand_name = core.get('brand_name', 'N/A')
slogan = core.get('slogan', 'N/A')
markdown += f"\n## {idx}. {brand_name}\n"
markdown += f"**์Šฌ๋กœ๊ฑด**: *\"{slogan}\"*\n\n"
# ํ•ต์‹ฌ ์ •๋ณด
markdown += f"""### ๐Ÿ“ ํ•ต์‹ฌ ์ •๋ณด
- **ํ•ต์‹ฌ ๊ฐ€์น˜**: {core.get('core_value', 'N/A')}
- **๋ชฉํ‘œ ๊ฐ์ •**: {core.get('target_emotion', 'N/A')}
- **๋ธŒ๋žœ๋“œ ์„ฑ๊ฒฉ**: {core.get('brand_personality', 'N/A')}
### ๐ŸŽจ ์‹œ๊ฐ์  ์ปจ์…‰
- **์ฃผ์š” ์ƒ‰์ƒ**: {visual.get('primary_color', '#000000')} - {visual.get('color_meaning', 'N/A')}
- **๋น„์ฃผ์–ผ ์ปจ์…‰**: {visual.get('visual_concept', 'N/A')}
- **ํƒ€์ดํฌ๊ทธ๋ž˜ํ”ผ**: {visual.get('typography_style', 'N/A')}
### ๐Ÿ—ฃ๏ธ ์–ธ์–ด์  ํŠน์„ฑ
- **๋ฐœ์Œ**: {linguistic.get('pronunciation', 'N/A')}
- **์–ด์›/๊ตฌ์„ฑ**: {linguistic.get('etymology', 'N/A')}
- **๊ธ€๋กœ๋ฒŒ ์ ์‘์„ฑ**: {linguistic.get('global_adaptability', 'N/A')}
### ๐ŸŽฏ ์ „๋žต์  ๊ฐ€์น˜
- **์ฐจ๋ณ„ํ™” ํฌ์ธํŠธ**: {strategic.get('differentiation', 'N/A')}
- **์‹œ์žฅ ํฌ์ง€์…”๋‹**: {strategic.get('market_positioning', 'N/A')}
- **์„ฑ์žฅ ์ž ์žฌ๋ ฅ**: {strategic.get('growth_potential', 'N/A')}
"""
# ์ด๋ก ๋ณ„ ํŠน์ˆ˜ ์ •๋ณด
if theory_specific:
markdown += f"\n### ๐Ÿ’ก {theory_names[theory]} ํŠน์„ฑ\n"
for key, value in theory_specific.items():
# ํ‚ค๋ฅผ ์ฝ๊ธฐ ์‰ฌ์šด ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜
display_key = key.replace('_', ' ').title()
markdown += f"- **{display_key}**: {value}\n"
# ํ‰๊ฐ€ ์ ์ˆ˜
markdown += f"""
### ๐Ÿ“Š ํ‰๊ฐ€
- **์ฐฝ์˜์„ฑ**: {'โญ' * int(evaluation.get('creativity_score', 0))} ({evaluation.get('creativity_score', 0)}/10)
- **๊ธฐ์–ต์„ฑ**: {'โญ' * int(evaluation.get('memorability_score', 0))} ({evaluation.get('memorability_score', 0)}/10)
- **๊ด€๋ จ์„ฑ**: {'โญ' * int(evaluation.get('relevance_score', 0))} ({evaluation.get('relevance_score', 0)}/10)
๐Ÿ’ฌ **์ „์ฒด ํ‰๊ฐ€**: {evaluation.get('overall_effectiveness', 'N/A')}
"""
markdown += "\n---\n"
return markdown
def generate_unified_visualization(theory: str, results: List[Dict]) -> str:
"""ํ†ต์ผ๋œ ์‹œ๊ฐํ™” ์ƒ์„ฑ"""
html_parts = []
for idx, result in enumerate(results, 1):
core = result.get('core', {})
visual = result.get('visual', {})
linguistic = result.get('linguistic', {})
strategic = result.get('strategic', {})
theory_specific = result.get('theory_specific', {})
evaluation = result.get('evaluation', {})
brand_name = core.get('brand_name', 'Brand')
slogan = core.get('slogan', '')
primary_color = visual.get('primary_color', '#667eea')
# ํ†ต์ผ๋œ ์นด๋“œ ๋ ˆ์ด์•„์›ƒ
html = f"""
<div style="max-width: 800px; margin: 30px auto; font-family: -apple-system, sans-serif;">
<div style="background: white; border-radius: 20px; box-shadow: 0 10px 40px rgba(0,0,0,0.1); overflow: hidden;">
<!-- ํ—ค๋” -->
<div style="background: linear-gradient(135deg, {primary_color} 0%, #2c3e50 100%); padding: 40px; color: white;">
<h2 style="margin: 0 0 10px 0; font-size: 2.5em;">{brand_name}</h2>
<p style="margin: 0; font-style: italic; font-size: 1.2em; opacity: 0.9;">"{slogan}"</p>
</div>
<!-- ๋ณธ๋ฌธ -->
<div style="padding: 40px;">
<!-- ํ‰๊ฐ€ ์ ์ˆ˜ -->
<div style="display: flex; justify-content: space-around; margin-bottom: 30px;">
<div style="text-align: center;">
<div style="font-size: 2em; color: {primary_color}; font-weight: bold;">
{evaluation.get('creativity_score', 0)}/10
</div>
<div style="color: #7f8c8d; margin-top: 5px;">์ฐฝ์˜์„ฑ</div>
</div>
<div style="text-align: center;">
<div style="font-size: 2em; color: {primary_color}; font-weight: bold;">
{evaluation.get('memorability_score', 0)}/10
</div>
<div style="color: #7f8c8d; margin-top: 5px;">๊ธฐ์–ต์„ฑ</div>
</div>
<div style="text-align: center;">
<div style="font-size: 2em; color: {primary_color}; font-weight: bold;">
{evaluation.get('relevance_score', 0)}/10
</div>
<div style="color: #7f8c8d; margin-top: 5px;">๊ด€๋ จ์„ฑ</div>
</div>
</div>
<!-- ํ•ต์‹ฌ ์ •๋ณด ๊ทธ๋ฆฌ๋“œ -->
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; margin-bottom: 30px;">
<div style="background: #f8f9fa; padding: 20px; border-radius: 10px;">
<h4 style="margin: 0 0 10px 0; color: {primary_color};">ํ•ต์‹ฌ ๊ฐ€์น˜</h4>
<p style="margin: 0; color: #555;">{core.get('core_value', 'N/A')}</p>
</div>
<div style="background: #f8f9fa; padding: 20px; border-radius: 10px;">
<h4 style="margin: 0 0 10px 0; color: {primary_color};">๋ชฉํ‘œ ๊ฐ์ •</h4>
<p style="margin: 0; color: #555;">{core.get('target_emotion', 'N/A')}</p>
</div>
<div style="background: #f8f9fa; padding: 20px; border-radius: 10px;">
<h4 style="margin: 0 0 10px 0; color: {primary_color};">์ฐจ๋ณ„ํ™” ํฌ์ธํŠธ</h4>
<p style="margin: 0; color: #555;">{strategic.get('differentiation', 'N/A')}</p>
</div>
<div style="background: #f8f9fa; padding: 20px; border-radius: 10px;">
<h4 style="margin: 0 0 10px 0; color: {primary_color};">๋ฐœ์Œ ๊ฐ€์ด๋“œ</h4>
<p style="margin: 0; color: #555;">{linguistic.get('pronunciation', 'N/A')}</p>
</div>
</div>
"""
# ์ด๋ก ๋ณ„ ํŠน์ˆ˜ ์‹œ๊ฐํ™” ์ถ”๊ฐ€
if theory == "square" and all(k in theory_specific for k in ['tl', 'tr', 'bl', 'br']):
html += visualize_square_specific(theory_specific, primary_color)
elif theory == "blending" and 'input_space1' in theory_specific:
html += visualize_blending_specific(theory_specific, primary_color)
elif theory == "color" and 'color_palette' in theory_specific:
html += visualize_color_specific(theory_specific, primary_color)
else:
# ๊ธฐ๋ณธ ์ด๋ก  ํŠน์„ฑ ํ‘œ์‹œ
html += f"""
<div style="background: linear-gradient(135deg, {primary_color}15 0%, {primary_color}05 100%); padding: 25px; border-radius: 15px; margin-top: 20px;">
<h4 style="margin: 0 0 15px 0; color: {primary_color};">์ด๋ก  ํŠน์„ฑ</h4>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
"""
for key, value in theory_specific.items():
display_key = key.replace('_', ' ').title()
html += f"""
<div>
<strong style="color: #555;">{display_key}:</strong><br>
<span style="color: #777;">{value}</span>
</div>
"""
html += """
</div>
</div>
"""
# ์ „์ฒด ํ‰๊ฐ€
html += f"""
<div style="background: #f8f9fa; padding: 20px; border-radius: 10px; margin-top: 20px;">
<h4 style="margin: 0 0 10px 0; color: {primary_color};">์ „์ฒด ํ‰๊ฐ€</h4>
<p style="margin: 0; color: #555; line-height: 1.6;">{evaluation.get('overall_effectiveness', 'N/A')}</p>
</div>
</div>
</div>
</div>
"""
html_parts.append(html)
return "\n".join(html_parts)
def visualize_square_specific(theory_specific: Dict, primary_color: str) -> str:
"""Square Theory ํŠน์ˆ˜ ์‹œ๊ฐํ™”"""
return f"""
<div style="background: #f8f9fa; padding: 30px; border-radius: 15px; margin-top: 20px;">
<h4 style="margin: 0 0 20px 0; color: {primary_color}; text-align: center;">Square ๊ตฌ์กฐ</h4>
<div style="position: relative; width: 100%; max-width: 400px; height: 300px; margin: 0 auto;">
<div style="position: absolute; top: 0; left: 0; background: white; color: {primary_color}; padding: 15px 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); font-weight: bold;">
{theory_specific.get('tl', '?')}
</div>
<div style="position: absolute; top: 0; right: 0; background: white; color: {primary_color}; padding: 15px 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); font-weight: bold;">
{theory_specific.get('tr', '?')}
</div>
<div style="position: absolute; bottom: 0; left: 0; background: white; color: {primary_color}; padding: 15px 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); font-weight: bold;">
{theory_specific.get('bl', '?')}
</div>
<div style="position: absolute; bottom: 0; right: 0; background: white; color: {primary_color}; padding: 15px 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); font-weight: bold;">
{theory_specific.get('br', '?')}
</div>
<!-- ์—ฐ๊ฒฐ์„ ๊ณผ ๊ด€๊ณ„ ํ‘œ์‹œ -->
<div style="position: absolute; top: 25px; left: 50%; transform: translateX(-50%); color: #7f8c8d; font-size: 0.9em;">
{theory_specific.get('top_edge', '')}
</div>
<div style="position: absolute; bottom: 25px; left: 50%; transform: translateX(-50%); color: #7f8c8d; font-size: 0.9em;">
{theory_specific.get('bottom_edge', '')}
</div>
<div style="position: absolute; top: 50%; left: 25px; transform: translateY(-50%) rotate(-90deg); color: #7f8c8d; font-size: 0.9em;">
{theory_specific.get('left_edge', '')}
</div>
<div style="position: absolute; top: 50%; right: 25px; transform: translateY(-50%) rotate(90deg); color: #7f8c8d; font-size: 0.9em;">
{theory_specific.get('right_edge', '')}
</div>
</div>
</div>
"""
def visualize_blending_specific(theory_specific: Dict, primary_color: str) -> str:
"""Conceptual Blending ํŠน์ˆ˜ ์‹œ๊ฐํ™”"""
return f"""
<div style="background: #f8f9fa; padding: 30px; border-radius: 15px; margin-top: 20px;">
<h4 style="margin: 0 0 20px 0; color: {primary_color}; text-align: center;">๊ฐœ๋… ํ˜ผํ•ฉ</h4>
<div style="display: flex; justify-content: center; align-items: center; gap: 20px; flex-wrap: wrap;">
<div style="text-align: center; padding: 20px; background: white; color: {primary_color}; border-radius: 50%; width: 120px; height: 120px; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
<div>
<strong>Input 1</strong><br>
<span style="font-size: 0.9em;">{theory_specific.get('input_space1', '')}</span>
</div>
</div>
<div style="font-size: 2em; color: {primary_color};">+</div>
<div style="text-align: center; padding: 20px; background: white; color: {primary_color}; border-radius: 50%; width: 120px; height: 120px; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
<div>
<strong>Input 2</strong><br>
<span style="font-size: 0.9em;">{theory_specific.get('input_space2', '')}</span>
</div>
</div>
<div style="font-size: 2em; color: {primary_color};">=</div>
<div style="text-align: center; padding: 20px; background: {primary_color}; color: white; border-radius: 20px; min-width: 150px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
<strong>Blend</strong><br>
<span style="font-size: 0.95em;">{theory_specific.get('blended_space', '')}</span>
</div>
</div>
</div>
"""
def visualize_color_specific(theory_specific: Dict, primary_color: str) -> str:
"""Color Psychology ํŠน์ˆ˜ ์‹œ๊ฐํ™”"""
palette = theory_specific.get('color_palette', primary_color)
colors = palette.split(',') if ',' in palette else [primary_color]
html = f"""
<div style="background: #f8f9fa; padding: 30px; border-radius: 15px; margin-top: 20px;">
<h4 style="margin: 0 0 20px 0; color: {primary_color}; text-align: center;">์ƒ‰์ƒ ํŒ”๋ ˆํŠธ</h4>
<div style="display: flex; justify-content: center; gap: 10px; flex-wrap: wrap;">
"""
for color in colors[:5]: # ์ตœ๋Œ€ 5๊ฐœ ์ƒ‰์ƒ๋งŒ ํ‘œ์‹œ
color = color.strip()
html += f"""
<div style="width: 80px; height: 80px; background: {color}; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);"></div>
"""
html += """
</div>
</div>
"""
return html
# Gradio UI
with gr.Blocks(
title="Ultimate Brand Theory Generator - Unified",
theme=gr.themes.Soft(),
css="""
.gradio-container {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.tab-nav button {
font-size: 0.9em !important;
padding: 10px 15px !important;
}
"""
) as demo:
gr.Markdown("""
<div style="text-align: center; padding: 30px 0;">
<h1 style="font-size: 2.5em; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 10px;">
๐ŸŒŸ Ultimate Brand Theory Generator
</h1>
<p style="font-size: 1.2em; color: #7f8c8d;">15๊ฐœ ์ด๋ก ์„ ํ™œ์šฉํ•œ ์ข…ํ•ฉ ๋ธŒ๋žœ๋“œ ์ƒ์„ฑ๊ธฐ (ํ†ต์ผ๋œ ์ถœ๋ ฅ ๊ตฌ์กฐ)</p>
</div>
""")
with gr.Row():
with gr.Column(scale=1, min_width=300):
gr.Markdown("""
<div style="background: #f8f9fa; padding: 20px; border-radius: 10px; margin-bottom: 20px;">
<h3 style="margin-top: 0;">๐Ÿ“ ๋ธŒ๋žœ๋“œ ์ •๋ณด ์ž…๋ ฅ</h3>
</div>
""")
industry_input = gr.Textbox(
label="๐Ÿญ ์—…์ข…",
placeholder="์˜ˆ: ์นดํŽ˜, ํ”ผํŠธ๋‹ˆ์Šค, ๊ต์œก, ๋ทฐํ‹ฐ...",
value="์นดํŽ˜/์ปคํ”ผ์ˆ"
)
keywords_input = gr.Textbox(
label="๐Ÿ”‘ ํ•ต์‹ฌ ํ‚ค์›Œ๋“œ",
placeholder="ํ”„๋ฆฌ๋ฏธ์—„, ํŽธ์•ˆํ•œ, ๋„์‹œ์ ์ธ, ์นœํ™˜๊ฒฝ...",
info="๋ธŒ๋žœ๋“œ๊ฐ€ ๋‹ด์•„์•ผ ํ•  ํ•ต์‹ฌ ๊ฐ€์น˜๋‚˜ ํŠน์ง•๋“ค",
lines=2
)
gr.Markdown("""
<div style="background: #e3f2fd; padding: 15px; border-radius: 8px; margin-top: 20px;">
<h4 style="margin-top: 0; color: #1976d2;">๐Ÿ”„ ํ†ต์ผ๋œ ์ถœ๋ ฅ ๊ตฌ์กฐ</h4>
<p style="margin: 10px 0;">๋ชจ๋“  ์ด๋ก ์—์„œ ๋™์ผํ•œ ๊ตฌ์กฐ๋กœ ๊ฒฐ๊ณผ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค:</p>
<ul style="margin: 5px 0; padding-left: 20px;">
<li><strong>ํ•ต์‹ฌ ์ •๋ณด</strong>: ๋ธŒ๋žœ๋“œ๋ช…, ์Šฌ๋กœ๊ฑด, ๊ฐ€์น˜, ์„ฑ๊ฒฉ</li>
<li><strong>์‹œ๊ฐ์  ์š”์†Œ</strong>: ์ƒ‰์ƒ, ๋น„์ฃผ์–ผ ์ปจ์…‰</li>
<li><strong>์–ธ์–ด์  ํŠน์„ฑ</strong>: ๋ฐœ์Œ, ๊ธ€๋กœ๋ฒŒ ์ ์‘์„ฑ</li>
<li><strong>์ „๋žต์  ๊ฐ€์น˜</strong>: ์ฐจ๋ณ„ํ™”, ํฌ์ง€์…”๋‹</li>
<li><strong>์ด๋ก ๋ณ„ ํŠน์„ฑ</strong>: ๊ฐ ์ด๋ก ์˜ ๊ณ ์œ  ์š”์†Œ</li>
<li><strong>ํ‰๊ฐ€ ์ ์ˆ˜</strong>: ์ฐฝ์˜์„ฑ, ๊ธฐ์–ต์„ฑ, ๊ด€๋ จ์„ฑ</li>
</ul>
</div>
""")
with gr.Column(scale=3):
# 15๊ฐœ ํƒญ ์ƒ์„ฑ
with gr.Tabs():
# ๊ฐ ์ด๋ก ๋ณ„ ํƒญ (์˜ˆ์‹œ๋กœ ๋ช‡ ๊ฐœ๋งŒ ํ‘œ์‹œ)
theories = [
("๐ŸŸฆ Square Theory", "square"),
("๐Ÿ”€ Conceptual Blending", "blending"),
("๐Ÿ”Š Sound Symbolism", "sound"),
("๐ŸŒ Linguistic Relativity", "linguistic"),
("๐ŸŽญ Archetype Theory", "archetype"),
("โœ… Jobs-to-be-Done", "jobs"),
("๐Ÿ”ง SCAMPER Method", "scamper"),
("๐Ÿ’ญ Design Thinking", "design"),
("๐ŸŒฟ Biomimicry", "biomimicry"),
("๐Ÿง  Cognitive Load", "cognitive"),
("โšก Von Restorff Effect", "vonrestorff"),
("๐ŸŒ Network Effects", "network"),
("๐Ÿงฌ Memetics", "memetics"),
("๐ŸŽจ Color Psychology", "color"),
("๐Ÿ‘๏ธ Gestalt Principles", "gestalt")
]
for tab_name, theory_key in theories:
with gr.Tab(tab_name):
with gr.Row():
btn = gr.Button(f"{tab_name}๋กœ ์ƒ์„ฑ", variant="primary", size="sm")
output = gr.Markdown()
visual = gr.HTML()
btn.click(
lambda i, k, t=theory_key: generate_by_theory(i, k, t),
inputs=[industry_input, keywords_input],
outputs=[output, visual]
)
gr.Markdown("""
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 15px; margin-top: 40px;">
<h3 style="margin-top: 0;">๐Ÿ“Š ํ†ต์ผ๋œ ํ‰๊ฐ€ ์‹œ์Šคํ…œ</h3>
<p style="margin: 15px 0;">๋ชจ๋“  ๋ธŒ๋žœ๋“œ๋Š” ๋‹ค์Œ 3๊ฐ€์ง€ ๊ธฐ์ค€์œผ๋กœ ํ‰๊ฐ€๋ฉ๋‹ˆ๋‹ค:</p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px;">
<div>
<h4>์ฐฝ์˜์„ฑ (Creativity)</h4>
<p style="margin: 5px 0;">๋…์ฐฝ์„ฑ๊ณผ ํ˜์‹ ์„ฑ์˜ ์ •๋„</p>
</div>
<div>
<h4>๊ธฐ์–ต์„ฑ (Memorability)</h4>
<p style="margin: 5px 0;">์‰ฝ๊ฒŒ ๊ธฐ์–ต๋˜๊ณ  ํšŒ์ƒ๋˜๋Š” ์ •๋„</p>
</div>
<div>
<h4>๊ด€๋ จ์„ฑ (Relevance)</h4>
<p style="margin: 5px 0;">์—…์ข…๊ณผ ํ‚ค์›Œ๋“œ์™€์˜ ์—ฐ๊ด€์„ฑ</p>
</div>
</div>
</div>
""")
if __name__ == "__main__":
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False
)