"""
Square Theory Generator v12 - Enhanced Edition
=============================================
2025-05-28 | 품질 향상 및 기능 확장 버전
-------------------------------------------
주요 개선사항:
- 더 구체적인 프롬프트로 고품질 결과 생성
- 시각적 프리뷰 기능 추가
- CSV/JSON 내보내기 기능
- 재생성 및 즐겨찾기 기능
- 향상된 UI/UX
"""
import os
import json
import csv
import io
import gradio as gr
import openai
from openai import OpenAI
from datetime import datetime
from typing import List, Dict, Optional, Tuple
# -------------------------------------------------
# 0. OpenAI 클라이언트 설정
# -------------------------------------------------
if not os.getenv("OPENAI_API_KEY"):
raise EnvironmentError("OPENAI_API_KEY 환경 변수를 설정하세요.")
client = OpenAI()
# -------------------------------------------------
# 1. 향상된 프롬프트 및 유틸리티
# -------------------------------------------------
ENHANCED_SYSTEM_PROMPT = """
당신은 20년 경력의 한국 최고 카피라이터이자 브랜드 네이밍 전문가입니다.
Square Theory는 4개의 핵심 단어를 사각형 모서리에 배치하여 브랜드 정체성을 구축하는 방법론입니다.
사용자가 입력한 시드 단어(tl)를 기반으로 최대 20개의 고품질 제안을 생성하세요.
각 제안은 다음 원칙을 따라야 합니다:
1. **사각형 구성 (tl, tr, br, bl)**
- 각 단어는 공백 없는 단일 단어 (한글 1-2어절 또는 영어 1단어)
- tl(왼쪽상단): 시드 단어
- tr(오른쪽상단): tl과 대비되거나 보완하는 개념
- br(오른쪽하단): 긍정적 결과나 혜택
- bl(왼쪽하단): 감성적 가치나 경험
2. **카피 작성 원칙**
- top_phrase: 고객의 니즈나 문제를 짚는 공감형 문구
- bottom_phrase: 해결책이나 약속을 제시하는 행동 유도형 문구
- slogan: 브랜드 정체성을 한 문장으로 압축 (7-15자 권장)
3. **브랜드 네이밍**
- 기억하기 쉽고 발음이 자연스러운 이름
- 사각형의 핵심 가치를 반영
- 2-4음절 권장
4. **품질 기준**
- 독창성: 진부하지 않은 신선한 조합
- 일관성: 4개 단어가 유기적으로 연결
- 실용성: 실제 마케팅에 활용 가능
- 감성적 호소력: 타겟 고객의 마음을 움직임
JSON 배열로 반환하며, 품질 순으로 정렬하세요.
각 객체: {tl, tr, br, bl, top_phrase, bottom_phrase, slogan, brand, concept_score(1-10), target_audience}
"""
FALLBACK_MODELS = ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo"]
# 예시 시드 단어들
EXAMPLE_SEEDS = ["골든", "스마트", "그린", "프리미엄", "힐링", "파워", "퓨어", "드림"]
def _clean_json_response(text: str) -> str:
"""JSON 응답 정제"""
text = text.strip()
if text.startswith("```"):
lines = text.split("\n")
text = "\n".join(lines[1:-1]) if lines[-1] == "```" else "\n".join(lines[1:])
return text.strip()
def _validate_square_item(item: Dict) -> bool:
"""Square Theory 항목 검증"""
required_fields = ["tl", "tr", "br", "bl", "top_phrase", "bottom_phrase", "slogan", "brand"]
if not all(field in item for field in required_fields):
return False
# 단어 검증 (공백 없는 단일 단어)
for corner in ["tl", "tr", "br", "bl"]:
if " " in item[corner].strip():
return False
return True
def _call_llm_with_retry(seed: str, temperature: float = 0.8) -> List[Dict]:
"""LLM 호출 (재시도 로직 포함)"""
last_error = None
for model in FALLBACK_MODELS:
try:
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": ENHANCED_SYSTEM_PROMPT},
{"role": "user", "content": f"시드 단어: {seed}"}
],
temperature=temperature,
max_tokens=3000,
response_format={"type": "json_object"}
)
content = _clean_json_response(response.choices[0].message.content)
data = json.loads(content)
# 응답 형식 정규화
if "suggestions" in data:
results = data["suggestions"]
elif "items" in data:
results = data["items"]
elif isinstance(data, list):
results = data
else:
results = [data]
# 검증 및 필터링
valid_results = [item for item in results if _validate_square_item(item)]
if not valid_results:
raise ValueError("유효한 Square Theory 제안이 없습니다")
# 점수 기준 정렬 (없으면 순서 유지)
valid_results.sort(key=lambda x: x.get("concept_score", 0), reverse=True)
return valid_results[:20] # 최대 20개
except Exception as e:
last_error = e
continue
raise RuntimeError(f"모든 모델에서 실패: {last_error}")
# -------------------------------------------------
# 2. 시각화 함수
# -------------------------------------------------
def create_square_preview(item: Dict) -> str:
"""Square Theory 시각적 프리뷰 생성"""
return f"""
{item['tl']}
{item['tr']}
{item['bl']}
{item['br']}
{item['brand']}
"{item['slogan']}"
"""
# -------------------------------------------------
# 3. 메인 생성 함수
# -------------------------------------------------
def generate_squares(seed_word: str, temperature: float = 0.8) -> Tuple[str, List[Dict], str]:
"""Square Theory 제안 생성"""
seed_word = seed_word.strip()
if not seed_word:
return "⚠️ **시드 단어를 입력해 주세요.**", [], ""
try:
# LLM 호출
results = _call_llm_with_retry(seed_word, temperature)
# 마크다운 생성
markdown_parts = [
f"# 🎯 Square Theory 제안: '{seed_word}'",
f"*생성 시각: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*",
f"\n## 📊 총 {len(results)}개 제안\n"
]
# HTML 프리뷰 생성
preview_html = ""
for idx, item in enumerate(results, 1):
score = item.get('concept_score', 'N/A')
target = item.get('target_audience', '일반')
markdown_parts.append(f"""
### {idx}. {item['brand']} {'⭐' * min(int(score) if isinstance(score, (int, float)) else 5, 5)}
**🎯 타겟**: {target} | **점수**: {score}/10
**📝 메인 카피**
- 상단: *"{item['top_phrase']}"*
- 하단: *"{item['bottom_phrase']}"*
**💬 슬로건**
> {item['slogan']}
**🔲 Square 구성**
```
[{item['tl']}] ← → [{item['tr']}]
↑ ↓
[{item['bl']}] ← → [{item['br']}]
```
---
""")
# 상위 3개는 프리뷰 추가
if idx <= 3:
preview_html += create_square_preview(item)
return "\n".join(markdown_parts), results, preview_html
except Exception as e:
return f"❌ **오류 발생**: {str(e)}", [], ""
# -------------------------------------------------
# 4. 내보내기 함수
# -------------------------------------------------
def export_to_csv(results: List[Dict]) -> str:
"""CSV 파일 생성"""
if not results:
return None
output = io.StringIO()
fieldnames = ["순위", "브랜드명", "TL", "TR", "BR", "BL",
"상단문구", "하단문구", "슬로건", "점수", "타겟"]
writer = csv.DictWriter(output, fieldnames=fieldnames)
writer.writeheader()
for idx, item in enumerate(results, 1):
writer.writerow({
"순위": idx,
"브랜드명": item['brand'],
"TL": item['tl'],
"TR": item['tr'],
"BR": item['br'],
"BL": item['bl'],
"상단문구": item['top_phrase'],
"하단문구": item['bottom_phrase'],
"슬로건": item['slogan'],
"점수": item.get('concept_score', 'N/A'),
"타겟": item.get('target_audience', '일반')
})
return output.getvalue()
def export_to_json(results: List[Dict]) -> str:
"""JSON 파일 생성"""
if not results:
return None
export_data = {
"generated_at": datetime.now().isoformat(),
"total_count": len(results),
"results": results
}
return json.dumps(export_data, ensure_ascii=False, indent=2)
# -------------------------------------------------
# 5. Gradio UI
# -------------------------------------------------
# 전역 변수로 결과 저장
current_results = []
def generate_and_update(seed_word, temperature):
"""생성 및 UI 업데이트"""
global current_results
markdown, results, preview = generate_squares(seed_word, temperature)
current_results = results
# 내보내기 파일 생성
csv_content = export_to_csv(results) if results else None
json_content = export_to_json(results) if results else None
return markdown, preview, csv_content, json_content
# UI 구성
with gr.Blocks(title="Square Theory Generator v12", theme=gr.themes.Soft()) as demo:
gr.Markdown("""
# 🟧 Square Theory Generator v12
### 브랜드 아이덴티티를 위한 4-Corner 전략 도구
Square Theory는 4개의 핵심 단어를 사각형 모서리에 배치하여 브랜드의 정체성과 가치를 명확히 하는 방법론입니다.
""")
with gr.Row():
with gr.Column(scale=2):
seed_input = gr.Textbox(
label="🌱 시드 단어 (TL - 왼쪽 상단)",
placeholder="예: 골든, 스마트, 프리미엄...",
info="브랜드의 핵심이 될 단어를 입력하세요"
)
with gr.Row():
temperature_slider = gr.Slider(
minimum=0.1,
maximum=1.0,
value=0.8,
step=0.1,
label="창의성 레벨",
info="높을수록 더 창의적이고 다양한 결과"
)
generate_btn = gr.Button("🚀 Square Theory 생성", variant="primary", size="lg")
gr.Examples(
examples=[[seed] for seed in EXAMPLE_SEEDS],
inputs=seed_input,
label="예시 시드 단어"
)
with gr.Column(scale=1):
gr.Markdown("""
### 💡 사용 팁
**좋은 시드 단어:**
- 감정: 행복, 평화, 열정
- 가치: 혁신, 신뢰, 품질
- 특성: 스마트, 프리미엄, 에코
**Square 구조:**
```
[시드] ← → [대비/보완]
↑ ↓
[경험] ← → [결과/혜택]
```
""")
with gr.Tabs():
with gr.Tab("📄 결과"):
output_markdown = gr.Markdown()
with gr.Tab("🎨 시각적 프리뷰"):
preview_html = gr.HTML()
with gr.Tab("💾 내보내기"):
with gr.Row():
csv_file = gr.File(label="CSV 다운로드", visible=False)
json_file = gr.File(label="JSON 다운로드", visible=False)
export_info = gr.Markdown("""
생성된 결과는 자동으로 CSV와 JSON 형식으로 변환됩니다.
생성 버튼을 클릭한 후 파일이 나타납니다.
""")
# 이벤트 연결
def process_and_save(seed_word, temperature):
markdown, preview, csv_content, json_content = generate_and_update(seed_word, temperature)
# 파일 저장
csv_filename = json_filename = None
if csv_content:
csv_filename = f"square_theory_{seed_word}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
with open(csv_filename, 'w', encoding='utf-8-sig') as f:
f.write(csv_content)
if json_content:
json_filename = f"square_theory_{seed_word}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
with open(json_filename, 'w', encoding='utf-8') as f:
f.write(json_content)
return markdown, preview, csv_filename, json_filename
generate_btn.click(
fn=process_and_save,
inputs=[seed_input, temperature_slider],
outputs=[output_markdown, preview_html, csv_file, json_file]
)
gr.Markdown("""
---
### 📌 추가 개발 아이디어
1. **A/B 테스트 모드**: 동일 시드로 여러 버전 생성 후 비교
2. **협업 기능**: 생성된 결과에 팀원들이 투표/코멘트
3. **히스토리 관리**: 이전 생성 결과 저장 및 검색
4. **업종별 템플릿**: F&B, IT, 패션 등 업종별 최적화
5. **다국어 지원**: 영어, 중국어, 일본어 Square Theory
""")
if __name__ == "__main__":
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False
)