|
""" |
|
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 |
|
|
|
|
|
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 = 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]: |
|
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 |
|
|
|
|
|
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): |
|
|
|
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 |
|
) |