Open-GAMMA / app.py
openfree's picture
Update app.py
ee12bfb verified
raw
history blame
77.8 kB
# app.py
"""Main PPT Generator with 3-AI collaboration system"""
import os
import re
import json
import time
import logging
import gradio as gr
import requests
from typing import List, Dict, Generator
from datetime import datetime
# Import separated modules
from constants import (
EXAMPLE_TOPICS, AUDIENCE_TYPES, DESIGN_THEMES,
STYLE_TEMPLATES, PPT_TEMPLATES, SLIDE_TITLE_TRANSLATIONS
)
from content_utils import (
parse_executor_response, extract_relevant_content_from_executor,
extract_slide_section_from_executor, parse_slide_section,
extract_speaker_notes_from_section, read_uploaded_file,
generate_dynamic_slides, brave_search, generate_slide_content,
generate_presentation_notes, generate_closing_notes,
generate_conclusion_phrase, generate_prompt_with_llm,
generate_image, create_slide_preview_html, create_pptx_file,
PROCESS_FLOW_AVAILABLE
)
from ui_components import (
create_custom_slides_ui, load_example, update_audience_info,
update_theme_preview, update_template_info, update_custom_slides_visibility,
clear_all, get_css, get_usage_instructions
)
# Logging setup
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Environment variables
REPLICATE_API_TOKEN = os.getenv("RAPI_TOKEN")
FRIENDLI_TOKEN = os.getenv("FRIENDLI_TOKEN")
BRAVE_API_TOKEN = os.getenv("BAPI_TOKEN")
API_URL = "https://api.friendli.ai/dedicated/v1/chat/completions"
BRAVE_SEARCH_URL = "https://api.search.brave.com/res/v1/web/search"
MODEL_ID = "dep89a2fld32mcm"
TEST_MODE = os.getenv("TEST_MODE", "false").lower() == "true"
# Global variables
current_slides_data = []
current_topic = ""
current_template = ""
current_theme = ""
uploaded_content = ""
current_language = "English"
conversation_history = []
# ===== ํ•ต์‹ฌ: 3์ž ํ˜‘์˜ ์‹œ์Šคํ…œ ํด๋ž˜์Šค =====
class PPTCreationSystem:
def __init__(self):
self.token = FRIENDLI_TOKEN
self.bapi_token = BRAVE_API_TOKEN
self.api_url = API_URL
self.brave_url = BRAVE_SEARCH_URL
self.model_id = MODEL_ID
self.test_mode = TEST_MODE or (self.token == "YOUR_FRIENDLI_TOKEN")
if self.test_mode:
logger.warning("ํ…Œ์ŠคํŠธ ๋ชจ๋“œ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.")
if self.bapi_token == "YOUR_BRAVE_API_TOKEN":
logger.warning("Brave API ํ† ํฐ์ด ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
def create_headers(self):
"""API ํ—ค๋” ์ƒ์„ฑ"""
return {
"Authorization": f"Bearer {self.token}",
"Content-Type": "application/json"
}
def create_brave_headers(self):
"""Brave API ํ—ค๋” ์ƒ์„ฑ"""
return {
"Accept": "application/json",
"Accept-Encoding": "gzip",
"X-Subscription-Token": self.bapi_token
}
def create_supervisor_initial_prompt(self, ppt_topic: str) -> str:
"""๊ฐ๋…์ž AI ์ดˆ๊ธฐ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ - PPT ๊ตฌ์กฐ ์„ค๊ณ„"""
return f"""๋‹น์‹ ์€ ์ „๋ฌธ์ ์ธ PPT ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•˜๋Š” ๊ฐ๋…์ž AI์ž…๋‹ˆ๋‹ค.
PPT ์ฃผ์ œ: {ppt_topic}
์ด ์ฃผ์ œ์— ๋Œ€ํ•œ ์ „๋ฌธ์ ์ธ PPT๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด:
1. **PPT ์ „์ฒด ๊ตฌ์กฐ ์„ค๊ณ„**
- ๋ชฉ์ ๊ณผ ํ•ต์‹ฌ ๋ฉ”์‹œ์ง€ ์ •์˜
- ํƒ€๊ฒŸ ์ฒญ์ค‘ ๋ถ„์„ ๋ฐ ํ†ค ์„ค์ •
- ์ „์ฒด ์Šฌ๋ผ์ด๋“œ ๊ฐœ์ˆ˜ ๋ฐ ํ๋ฆ„ ์„ค๊ณ„ (10-15์žฅ ๊ถŒ์žฅ)
2. **์„น์…˜๋ณ„ ๊ตฌ์„ฑ ์ œ์•ˆ**
- ๋„์ž…๋ถ€ (ํ‘œ์ง€, ๋ชฉ์ฐจ, ๋ฐฐ๊ฒฝ)
- ๋ณธ๋ก  (ํ•ต์‹ฌ ๋‚ด์šฉ 3-4๊ฐœ ์„น์…˜)
- ๊ฒฐ๋ก ๋ถ€ (์š”์•ฝ, ์‹œ์‚ฌ์ , Q&A)
3. **์กฐ์‚ฌ๊ฐ€ ํ•„์š”ํ•œ ํ•ต์‹ฌ ํ‚ค์›Œ๋“œ**
์ด ์ฃผ์ œ์˜ PPT๋ฅผ ์œ„ํ•ด ์กฐ์‚ฌ๊ฐ€ ํ•„์š”ํ•œ 5-7๊ฐœ์˜ ๊ตฌ์ฒด์ ์ธ ํ‚ค์›Œ๋“œ๋‚˜ ๊ฒ€์ƒ‰์–ด๋ฅผ ์ œ์‹œํ•˜์„ธ์š”.
ํ‚ค์›Œ๋“œ๋Š” ๋‹ค์Œ ํ˜•์‹์œผ๋กœ ์ œ์‹œํ•˜์„ธ์š”:
[๊ฒ€์ƒ‰ ํ‚ค์›Œ๋“œ]: ํ‚ค์›Œ๋“œ1, ํ‚ค์›Œ๋“œ2, ํ‚ค์›Œ๋“œ3, ํ‚ค์›Œ๋“œ4, ํ‚ค์›Œ๋“œ5"""
def create_researcher_prompt(self, ppt_topic: str, supervisor_guidance: str, search_results: Dict[str, List[Dict]]) -> str:
"""์กฐ์‚ฌ์ž AI ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ - PPT ์ฝ˜ํ…์ธ  ์กฐ์‚ฌ"""
search_summary = ""
all_results = []
for keyword, results in search_results.items():
search_summary += f"\n\n**{keyword}์— ๋Œ€ํ•œ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ:**\n"
for i, result in enumerate(results[:10], 1): # ์ƒ์œ„ 10๊ฐœ๋งŒ ํ‘œ์‹œ
search_summary += f"{i}. {result.get('title', 'N/A')} (์‹ ๋ขฐ๋„: {result.get('credibility_score', 0):.2f})\n"
search_summary += f" - {result.get('description', 'N/A')}\n"
search_summary += f" - ์ถœ์ฒ˜: {result.get('url', 'N/A')}\n"
if result.get('published'):
search_summary += f" - ๊ฒŒ์‹œ์ผ: {result.get('published')}\n"
all_results.extend(results)
# ๋ชจ์ˆœ ๊ฐ์ง€
contradictions = self.detect_contradictions(all_results)
contradiction_text = ""
if contradictions:
contradiction_text = "\n\n**๋ฐœ๊ฒฌ๋œ ์ •๋ณด ๋ชจ์ˆœ:**\n"
for cont in contradictions[:3]: # ์ตœ๋Œ€ 3๊ฐœ๋งŒ ํ‘œ์‹œ
contradiction_text += f"- {cont['type']}: {cont['source1']} vs {cont['source2']}\n"
return f"""๋‹น์‹ ์€ PPT ์ฝ˜ํ…์ธ ๋ฅผ ์œ„ํ•œ ์ •๋ณด๋ฅผ ์กฐ์‚ฌํ•˜๊ณ  ์ •๋ฆฌํ•˜๋Š” ์กฐ์‚ฌ์ž AI์ž…๋‹ˆ๋‹ค.
PPT ์ฃผ์ œ: {ppt_topic}
๊ฐ๋…์ž AI์˜ ๊ตฌ์กฐ ์„ค๊ณ„:
{supervisor_guidance}
๋ธŒ๋ ˆ์ด๋ธŒ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ (์‹ ๋ขฐ๋„ ์ ์ˆ˜ ํฌํ•จ):
{search_summary}
{contradiction_text}
์œ„ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ PPT์— ํฌํ•จํ•  ํ•ต์‹ฌ ์ฝ˜ํ…์ธ ๋ฅผ ์ •๋ฆฌํ•˜์„ธ์š”:
1. **์‹œ๊ฐ์  ์ž๋ฃŒ ๊ฐ€๋Šฅํ•œ ํ†ต๊ณ„/์ˆ˜์น˜**
- ๊ทธ๋ž˜ํ”„๋‚˜ ์ฐจํŠธ๋กœ ํ‘œํ˜„ ๊ฐ€๋Šฅํ•œ ๋ฐ์ดํ„ฐ
- ์ธํฌ๊ทธ๋ž˜ํ”ฝ์œผ๋กœ ๋ณ€ํ™˜ ๊ฐ€๋Šฅํ•œ ์ •๋ณด
2. **ํ•ต์‹ฌ ์ธ์‚ฌ์ดํŠธ์™€ ํŠธ๋ Œ๋“œ**
- ์ฒญ์ค‘์˜ ๊ด€์‹ฌ์„ ๋Œ ์ˆ˜ ์žˆ๋Š” ์ตœ์‹  ์ •๋ณด
- ์—…๊ณ„ ๋™ํ–ฅ๊ณผ ๋ฏธ๋ž˜ ์ „๋ง
3. **์‚ฌ๋ก€ ์—ฐ๊ตฌ ๋ฐ ์„ฑ๊ณต ์Šคํ† ๋ฆฌ**
- ์‹ค์ œ ์ ์šฉ ์‚ฌ๋ก€
- ๋ฒค์น˜๋งˆํฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋ฒ” ์‚ฌ๋ก€
4. **์ „๋ฌธ๊ฐ€ ์˜๊ฒฌ ๋ฐ ์ธ์šฉ๊ตฌ**
- ๊ถŒ์œ„ ์žˆ๋Š” ์ถœ์ฒ˜์˜ ์˜๊ฒฌ
- PPT์— ์ธ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ช…์–ธ
5. **์‹œ๊ฐ ์ž๋ฃŒ ์ถ”์ฒœ**
- ๊ฐ ์Šฌ๋ผ์ด๋“œ์— ์ ํ•ฉํ•œ ์ด๋ฏธ์ง€/์•„์ด์ฝ˜ ์ œ์•ˆ
- ๋„ํ‘œ๋‚˜ ๋‹ค์ด์–ด๊ทธ๋žจ ์•„์ด๋””์–ด"""
def create_supervisor_execution_prompt(self, ppt_topic: str, research_summary: str) -> str:
"""๊ฐ๋…์ž AI์˜ ์‹คํ–‰ ์ง€์‹œ ํ”„๋กฌํ”„ํŠธ - PPT ์ œ์ž‘ ์ง€์‹œ"""
return f"""๋‹น์‹ ์€ PPT ์ œ์ž‘์„ ์ด๊ด„ํ•˜๋Š” ๊ฐ๋…์ž AI์ž…๋‹ˆ๋‹ค.
PPT ์ฃผ์ œ: {ppt_topic}
์กฐ์‚ฌ์ž AI๊ฐ€ ์ •๋ฆฌํ•œ ์ฝ˜ํ…์ธ :
{research_summary}
์œ„ ์กฐ์‚ฌ ๋‚ด์šฉ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹คํ–‰์ž AI์—๊ฒŒ ๊ตฌ์ฒด์ ์ธ PPT ์ œ์ž‘ ์ง€์‹œ๋ฅผ ๋‚ด๋ ค์ฃผ์„ธ์š”:
1. **์Šฌ๋ผ์ด๋“œ๋ณ„ ์ƒ์„ธ ๊ตฌ์„ฑ**
- ๊ฐ ์Šฌ๋ผ์ด๋“œ์˜ ์ œ๋ชฉ๊ณผ ํ•ต์‹ฌ ๋ฉ”์‹œ์ง€
- ํฌํ•จํ•  ๊ตฌ์ฒด์ ์ธ ์ฝ˜ํ…์ธ  ์ง€์ •
- ์‹œ๊ฐ ์ž๋ฃŒ ๋ฐฐ์น˜ ๋ฐฉ์•ˆ
2. **๋””์ž์ธ ๊ฐ€์ด๋“œ๋ผ์ธ**
- ์ „์ฒด์ ์ธ ํ†ค๊ณผ ์Šคํƒ€์ผ (์ „๋ฌธ์ /์ฐฝ์˜์ /๋ฏธ๋‹ˆ๋ฉ€ ๋“ฑ)
- ์ƒ‰์ƒ ํŒ”๋ ˆํŠธ ์ œ์•ˆ
- ํฐํŠธ ๋ฐ ๋ ˆ์ด์•„์›ƒ ์›์น™
3. **์Šคํ† ๋ฆฌํ…”๋ง ์ „๋žต**
- ๋„์ž…๋ถ€์—์„œ ์ฒญ์ค‘์˜ ๊ด€์‹ฌ์„ ๋„๋Š” ๋ฐฉ๋ฒ•
- ํ•ต์‹ฌ ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ ์ˆœ์„œ
- ๊ฒฐ๋ก ๋ถ€์˜ ์ž„ํŒฉํŠธ ์žˆ๋Š” ๋งˆ๋ฌด๋ฆฌ
4. **์‹œ๊ฐํ™” ์ง€์นจ**
- ๊ฐ ๋ฐ์ดํ„ฐ์˜ ์ตœ์  ์‹œ๊ฐํ™” ๋ฐฉ๋ฒ•
- ๋ณต์žกํ•œ ๊ฐœ๋…์˜ ๋‹จ์ˆœํ™” ๋ฐฉ์•ˆ
- ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ ์ œ์•ˆ"""
def create_executor_prompt(self, ppt_topic: str, supervisor_guidance: str, research_summary: str) -> str:
"""์‹คํ–‰์ž AI ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ - PPT ์Šฌ๋ผ์ด๋“œ ์ž‘์„ฑ"""
return f"""๋‹น์‹ ์€ ์‹ค์ œ PPT ์Šฌ๋ผ์ด๋“œ ๋‚ด์šฉ์„ ์ž‘์„ฑํ•˜๋Š” ์‹คํ–‰์ž AI์ž…๋‹ˆ๋‹ค.
PPT ์ฃผ์ œ: {ppt_topic}
์กฐ์‚ฌ์ž AI๊ฐ€ ์ •๋ฆฌํ•œ ์ฝ˜ํ…์ธ :
{research_summary}
๊ฐ๋…์ž AI์˜ ์ œ์ž‘ ์ง€์‹œ:
{supervisor_guidance}
์œ„ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์‹ค์ œ PPT ์Šฌ๋ผ์ด๋“œ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”:
1. **๊ฐ ์Šฌ๋ผ์ด๋“œ๋ฅผ ๋‹ค์Œ ํ˜•์‹์œผ๋กœ ์ž‘์„ฑ**:
[์Šฌ๋ผ์ด๋“œ X]
์ œ๋ชฉ:
๋ถ€์ œ๋ชฉ: (์„ ํƒ์‚ฌํ•ญ)
โ€ข ๐ŸŽฏ ์ฒซ ๋ฒˆ์งธ ํ•ต์‹ฌ ํฌ์ธํŠธ
โ€ข ๐Ÿ’ก ๋‘ ๋ฒˆ์งธ ํ•ต์‹ฌ ํฌ์ธํŠธ
โ€ข ๐Ÿš€ ์„ธ ๋ฒˆ์งธ ํ•ต์‹ฌ ํฌ์ธํŠธ
โ€ข โœ… ๋„ค ๋ฒˆ์งธ ํ•ต์‹ฌ ํฌ์ธํŠธ
โ€ข ๐Ÿ“Š ๋‹ค์„ฏ ๋ฒˆ์งธ ํ•ต์‹ฌ ํฌ์ธํŠธ
๋ฐœํ‘œ์ž ๋…ธํŠธ: (์‹ค์ œ ๋ฐœํ‘œํ•  ๋•Œ ์‚ฌ์šฉํ•  ๊ตฌ์–ด์ฒด ์Šคํฌ๋ฆฝํŠธ - ์ตœ์†Œ 3-4๋ฌธ์žฅ)
2. **์ž‘์„ฑ ์›์น™**
- ํ•œ ์Šฌ๋ผ์ด๋“œ์— ํ•˜๋‚˜์˜ ํ•ต์‹ฌ ๋ฉ”์‹œ์ง€
- ํ…์ŠคํŠธ๋Š” ์ตœ์†Œํ™”, ์‹œ๊ฐ์  ์š”์†Œ ๊ทน๋Œ€ํ™”
- 6x6 ๊ทœ์น™ ์ค€์ˆ˜ (6์ค„ ์ดํ•˜, ์ค„๋‹น 6๋‹จ์–ด ์ดํ•˜)
- ์ „๋ฌธ์ ์ด๋ฉด์„œ๋„ ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์šด ์–ธ์–ด ์‚ฌ์šฉ
- ๊ฐ ๋ถˆ๋ฆฟ ํฌ์ธํŠธ๋Š” ๋ฐ˜๋“œ์‹œ ์ด๋ชจ์ง€๋กœ ์‹œ์ž‘
3. **๋ฐœํ‘œ์ž ๋…ธํŠธ ์ž‘์„ฑ ์ง€์นจ**
- ์Šฌ๋ผ์ด๋“œ ๋‚ด์šฉ์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์„ค๋ช…ํ•˜๋Š” ๊ตฌ์–ด์ฒด ์‚ฌ์šฉ
- ๊ฐ ํฌ์ธํŠธ๋ฅผ ํ’€์–ด์„œ ์„ค๋ช…ํ•˜๊ณ  ์—ฐ๊ฒฐ
- ์ฒญ์ค‘๊ณผ ์†Œํ†ตํ•˜๋Š” ๋А๋‚Œ์˜ ๋Œ€ํ™”์ฒด ("์—ฌ๋Ÿฌ๋ถ„", "์šฐ๋ฆฌ๊ฐ€", "ํ•จ๊ป˜")
- ์ตœ์†Œ 3-4๋ฌธ์žฅ์œผ๋กœ ํ’๋ถ€ํ•˜๊ฒŒ ์ž‘์„ฑ
- ์˜ˆ์‹œ: "์ž, ์ด์ œ ์šฐ๋ฆฌ๊ฐ€ ์ฃผ๋ชฉํ•ด์•ผ ํ•  ํ•ต์‹ฌ ํฌ์ธํŠธ๋“ค์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ๋กœ ์‹œ์žฅ ๊ทœ๋ชจ๋ฅผ ๋ณด์‹œ๋ฉด..."
4. **ํ•„์ˆ˜ ํฌํ•จ ์Šฌ๋ผ์ด๋“œ**
- ํ‘œ์ง€ (์ œ๋ชฉ, ๋ถ€์ œ๋ชฉ, ๋ฐœํ‘œ์ž/๋‚ ์งœ)
- ๋ชฉ์ฐจ
- ํ•ต์‹ฌ ๋‚ด์šฉ ์Šฌ๋ผ์ด๋“œ๋“ค
- ์š”์•ฝ/๊ฒฐ๋ก 
- Q&A/๊ฐ์‚ฌ ์Šฌ๋ผ์ด๋“œ"""
def create_supervisor_review_prompt(self, ppt_topic: str, executor_response: str, research_summary: str) -> str:
"""๊ฐ๋…์ž AI์˜ ๊ฒ€ํ†  ํ”„๋กฌํ”„ํŠธ - PPT ๊ฒ€ํ†  ๋ฐ ํ”ผ๋“œ๋ฐฑ"""
return f"""๋‹น์‹ ์€ PPT ํ’ˆ์งˆ์„ ๊ฒ€ํ† ํ•˜๊ณ  ๊ฐœ์„ ์ ์„ ์ œ์‹œํ•˜๋Š” ๊ฐ๋…์ž AI์ž…๋‹ˆ๋‹ค.
PPT ์ฃผ์ œ: {ppt_topic}
์กฐ์‚ฌ์ž AI๊ฐ€ ์ œ๊ณตํ•œ ์ฝ˜ํ…์ธ :
{research_summary}
์‹คํ–‰์ž AI์˜ PPT ์ดˆ์•ˆ:
{executor_response}
์œ„ PPT ์ดˆ์•ˆ์„ ๊ฒ€ํ† ํ•˜๊ณ  ๋‹ค์Œ ๊ด€์ ์—์„œ ๊ตฌ์ฒด์ ์ธ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•˜์„ธ์š”:
1. **์ฝ˜ํ…์ธ  ํ’ˆ์งˆ**
- ์กฐ์‚ฌ ๋‚ด์šฉ์ด ์ถฉ๋ถ„ํžˆ ๋ฐ˜์˜๋˜์—ˆ๋Š”์ง€
- ๊ฐ ์Šฌ๋ผ์ด๋“œ์˜ ๋ฉ”์‹œ์ง€๊ฐ€ ๋ช…ํ™•ํ•œ์ง€
- ๋…ผ๋ฆฌ์  ํ๋ฆ„์ด ์ž์—ฐ์Šค๋Ÿฌ์šด์ง€
2. **์‹œ๊ฐ์  ํšจ๊ณผ**
- ํ…์ŠคํŠธ์™€ ์‹œ๊ฐ ์š”์†Œ์˜ ๊ท ํ˜•
- ์Šฌ๋ผ์ด๋“œ๋ณ„ ์ผ๊ด€์„ฑ
- ์ฒญ์ค‘์˜ ์ดํ•ด๋ฅผ ๋•๋Š” ๊ตฌ์„ฑ์ธ์ง€
3. **๋ฐœํ‘œ ์ค€๋น„๋„**
- ๋ฐœํ‘œ์ž ๋…ธํŠธ์˜ ์ถฉ์‹ค์„ฑ
- ์˜ˆ์ƒ ์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋Œ€๋น„
- ์‹œ๊ฐ„ ๋ฐฐ๋ถ„์˜ ์ ์ ˆ์„ฑ
4. **๊ฐœ์„  ํ•„์š”์‚ฌํ•ญ**
- ๊ตฌ์ฒด์ ์ธ ์ˆ˜์ • ์ง€์‹œ์‚ฌํ•ญ
- ์ถ”๊ฐ€ํ•ด์•ผ ํ•  ๋‚ด์šฉ
- ์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ๊ฐ„์†Œํ™”ํ•  ๋ถ€๋ถ„
5. **์ตœ์ข… ์ ๊ฒ€์‚ฌํ•ญ**
- ์˜คํƒ€๋‚˜ ๋ฌธ๋ฒ• ์˜ค๋ฅ˜
- ๋ฐ์ดํ„ฐ์˜ ์ •ํ™•์„ฑ
- ์ „๋ฌธ์„ฑ๊ณผ ์‹ ๋ขฐ์„ฑ"""
def create_executor_final_prompt(self, ppt_topic: str, initial_response: str, supervisor_feedback: str, research_summary: str) -> str:
"""์‹คํ–‰์ž AI ์ตœ์ข… PPT ํ”„๋กฌํ”„ํŠธ"""
return f"""๋‹น์‹ ์€ ์ตœ์ข… PPT๋ฅผ ์™„์„ฑํ•˜๋Š” ์‹คํ–‰์ž AI์ž…๋‹ˆ๋‹ค.
PPT ์ฃผ์ œ: {ppt_topic}
์กฐ์‚ฌ์ž AI์˜ ์ฝ˜ํ…์ธ :
{research_summary}
๋‹น์‹ ์˜ ์ดˆ๊ธฐ PPT ์ดˆ์•ˆ:
{initial_response}
๊ฐ๋…์ž AI์˜ ํ”ผ๋“œ๋ฐฑ:
{supervisor_feedback}
์œ„ ํ”ผ๋“œ๋ฐฑ์„ ์™„์ „ํžˆ ๋ฐ˜์˜ํ•˜์—ฌ ์ตœ์ข… PPT๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”:
1. **๊ฐœ์„ ์‚ฌํ•ญ ๋ฐ˜์˜**
- ๊ฐ๋…์ž์˜ ๋ชจ๋“  ํ”ผ๋“œ๋ฐฑ ์ ์šฉ
- ์Šคํ† ๋ฆฌ๋ผ์ธ ๊ฐ•ํ™”
- ์‹œ๊ฐ์  ์ผ๊ด€์„ฑ ํ™•๋ณด
2. **๋ฐœํ‘œ์ž ๋…ธํŠธ ๊ฐ•ํ™”**
- ๊ฐ ์Šฌ๋ผ์ด๋“œ๋งˆ๋‹ค ํ’๋ถ€ํ•œ ๊ตฌ์–ด์ฒด ๋ฐœํ‘œ ์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑ
- ์ตœ์†Œ 4-5๋ฌธ์žฅ์œผ๋กœ ์ž์„ธํ•˜๊ฒŒ
- ์‹ค์ œ ๋ฐœํ‘œ ์ƒํ™ฉ์„ ๊ฐ€์ •ํ•œ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋Œ€ํ™”์ฒด
- ์˜ˆ์‹œ: "์ž, ์ด์ œ ์‹œ์žฅ ํ˜„ํ™ฉ์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„๋„ ์•„์‹œ๋‹ค์‹œํ”ผ ์ตœ๊ทผ ๋””์ง€ํ„ธ ์ „ํ™˜์ด ๊ฐ€์†ํ™”๋˜๋ฉด์„œ..."
3. **์ตœ์ข… ํ’ˆ์งˆ ์ฒดํฌ**
- ์ „๋ฌธ์„ฑ๊ณผ ์™„์„ฑ๋„ ํ–ฅ์ƒ
- ์˜คํƒ€ ๋ฐ ๋ฌธ๋ฒ• ๊ฒ€ํ† 
- ๋…ผ๋ฆฌ์  ํ๋ฆ„ ์žฌํ™•์ธ
4. **ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ์ค€๋น„**
- ๊ฐ ์Šฌ๋ผ์ด๋“œ๋ณ„ ๋ฐœํ‘œ ์Šคํฌ๋ฆฝํŠธ ์ถ”๊ฐ€
- ์˜ˆ์ƒ ์งˆ๋ฌธ๊ณผ ๋‹ต๋ณ€ ์ค€๋น„
- ์‹œ๊ฐ„ ๋ฐฐ๋ถ„ ์ œ์•ˆ (20๋ถ„ ๋ฐœํ‘œ ๊ธฐ์ค€)
5. **์ถ”๊ฐ€ ์ž๋ฃŒ**
- ๋ฐฑ์—… ์Šฌ๋ผ์ด๋“œ ์ œ์•ˆ
- ์ฐธ๊ณ  ์ž๋ฃŒ ๋ชฉ๋ก
- ์ธ์‡„์šฉ ํ•ธ๋“œ์•„์›ƒ ๋ฒ„์ „ ์ œ์•ˆ"""
def extract_keywords(self, supervisor_response: str) -> List[str]:
"""๊ฐ๋…์ž ์‘๋‹ต์—์„œ ํ‚ค์›Œ๋“œ ์ถ”์ถœ"""
keywords = []
# [๊ฒ€์ƒ‰ ํ‚ค์›Œ๋“œ]: ํ˜•์‹์œผ๋กœ ํ‚ค์›Œ๋“œ ์ฐพ๊ธฐ
keyword_match = re.search(r'\[๊ฒ€์ƒ‰ ํ‚ค์›Œ๋“œ\]:\s*(.+)', supervisor_response, re.IGNORECASE)
if keyword_match:
keyword_str = keyword_match.group(1)
keywords = [k.strip() for k in keyword_str.split(',') if k.strip()]
# ํ‚ค์›Œ๋“œ๊ฐ€ ์—†์œผ๋ฉด ๊ธฐ๋ณธ ํ‚ค์›Œ๋“œ ์ƒ์„ฑ
if not keywords:
keywords = ["industry trends", "market analysis", "case studies", "best practices", "future outlook"]
return keywords[:7] # ์ตœ๋Œ€ 7๊ฐœ๋กœ ์ œํ•œ
def generate_synonyms(self, keyword: str) -> List[str]:
"""ํ‚ค์›Œ๋“œ์˜ ๋™์˜์–ด/์œ ์‚ฌ์–ด ์ƒ์„ฑ"""
synonyms = {
"trend": ["tendency", "direction", "movement", "pattern"],
"analysis": ["evaluation", "assessment", "study", "research"],
"strategy": ["approach", "plan", "tactics", "methodology"],
"market": ["industry", "sector", "business", "commerce"],
"innovation": ["breakthrough", "advancement", "revolution", "disruption"],
"growth": ["expansion", "development", "progress", "advancement"],
"presentation": ["pitch", "proposal", "demonstration", "showcase"],
"data": ["statistics", "metrics", "analytics", "insights"],
"technology": ["tech", "digital", "IT", "innovation"],
"business": ["enterprise", "corporate", "commercial", "company"]
}
# ํ‚ค์›Œ๋“œ ์ •๊ทœํ™”
keyword_lower = keyword.lower()
# ์ง์ ‘ ๋งค์นญ๋˜๋Š” ๋™์˜์–ด๊ฐ€ ์žˆ์œผ๋ฉด ๋ฐ˜ํ™˜
if keyword_lower in synonyms:
return synonyms[keyword_lower][:2] # ์ตœ๋Œ€ 2๊ฐœ
# ๋ถ€๋ถ„ ๋งค์นญ ํ™•์ธ
for key, values in synonyms.items():
if key in keyword_lower or keyword_lower in key:
return values[:2]
# ๋™์˜์–ด๊ฐ€ ์—†์œผ๋ฉด ๋นˆ ๋ฆฌ์ŠคํŠธ
return []
def calculate_credibility_score(self, result: Dict) -> float:
"""๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ์˜ ์‹ ๋ขฐ๋„ ์ ์ˆ˜ ๊ณ„์‚ฐ (0-1)"""
score = 0.5 # ๊ธฐ๋ณธ ์ ์ˆ˜
url = result.get('url', '')
title = result.get('title', '')
description = result.get('description', '')
# URL ๊ธฐ๋ฐ˜ ์ ์ˆ˜
trusted_domains = [
'.edu', '.gov', '.org', 'wikipedia.org', 'harvard.edu',
'mckinsey.com', 'deloitte.com', 'pwc.com', 'gartner.com',
'forrester.com', 'statista.com', 'bloomberg.com', 'reuters.com'
]
for domain in trusted_domains:
if domain in url:
score += 0.2
break
# HTTPS ์‚ฌ์šฉ ์—ฌ๋ถ€
if url.startswith('https://'):
score += 0.1
# ์ œ๋ชฉ๊ณผ ์„ค๋ช…์˜ ๊ธธ์ด (๋„ˆ๋ฌด ์งง์œผ๋ฉด ์‹ ๋ขฐ๋„ ๊ฐ์†Œ)
if len(title) > 20:
score += 0.05
if len(description) > 50:
score += 0.05
# ๊ด‘๊ณ /์ŠคํŒธ ํ‚ค์›Œ๋“œ ์ฒดํฌ
spam_keywords = ['buy now', 'sale', 'discount', 'click here', '100% free']
if any(spam in (title + description).lower() for spam in spam_keywords):
score -= 0.3
# ๋‚ ์งœ ์ •๋ณด๊ฐ€ ์žˆ์œผ๋ฉด ๊ฐ€์‚ฐ์ 
if any(year in description for year in ['2024', '2023', '2022']):
score += 0.1
return max(0, min(1, score)) # 0-1 ๋ฒ”์œ„๋กœ ์ œํ•œ
def detect_contradictions(self, results: List[Dict]) -> List[Dict]:
"""๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ๊ฐ„ ๋ชจ์ˆœ ๊ฐ์ง€"""
contradictions = []
# ๊ฐ„๋‹จํ•œ ๋ชจ์ˆœ ๊ฐ์ง€ ํŒจํ„ด
opposite_pairs = [
("increase", "decrease"),
("improve", "worsen"),
("effective", "ineffective"),
("success", "failure"),
("benefit", "harm"),
("positive", "negative"),
("growth", "decline")
]
# ๊ฒฐ๊ณผ๋“ค์„ ๋น„๊ต
for i in range(len(results)):
for j in range(i + 1, len(results)):
desc1 = results[i].get('description', '').lower()
desc2 = results[j].get('description', '').lower()
# ๋ฐ˜๋Œ€ ๊ฐœ๋…์ด ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ
for word1, word2 in opposite_pairs:
if (word1 in desc1 and word2 in desc2) or (word2 in desc1 and word1 in desc2):
# ๊ฐ™์€ ์ฃผ์ œ์— ๋Œ€ํ•ด ๋ฐ˜๋Œ€ ์˜๊ฒฌ์ธ์ง€ ํ™•์ธ
common_words = set(desc1.split()) & set(desc2.split())
if len(common_words) > 5: # ๊ณตํ†ต ๋‹จ์–ด๊ฐ€ 5๊ฐœ ์ด์ƒ์ด๋ฉด ๊ฐ™์€ ์ฃผ์ œ๋กœ ๊ฐ„์ฃผ
contradictions.append({
'source1': results[i]['url'],
'source2': results[j]['url'],
'type': f"{word1} vs {word2}",
'desc1': results[i]['description'][:100],
'desc2': results[j]['description'][:100]
})
return contradictions
def brave_search(self, query: str) -> List[Dict]:
"""Brave Search API ํ˜ธ์ถœ"""
if self.test_mode or self.bapi_token == "YOUR_BRAVE_API_TOKEN":
# ํ…Œ์ŠคํŠธ ๋ชจ๋“œ์—์„œ๋Š” ์‹œ๋ฎฌ๋ ˆ์ด์…˜๋œ ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜
test_results = []
for i in range(5):
test_results.append({
"title": f"{query} - Industry Report {i+1}",
"description": f"Comprehensive analysis of {query} including market trends, key statistics, and future projections for strategic planning.",
"url": f"https://report{i+1}.com/{query.replace(' ', '-')}",
"credibility_score": 0.7 + (i * 0.05)
})
return test_results
try:
params = {
"q": query,
"count": 20, # 20๊ฐœ๋กœ ์ฆ๊ฐ€
"safesearch": "moderate",
"freshness": "pw" # Past week for recent results
}
response = requests.get(
self.brave_url,
headers=self.create_brave_headers(),
params=params,
timeout=10
)
if response.status_code == 200:
data = response.json()
results = []
for item in data.get("web", {}).get("results", [])[:20]:
result = {
"title": item.get("title", ""),
"description": item.get("description", ""),
"url": item.get("url", ""),
"published": item.get("published", "")
}
# ์‹ ๋ขฐ๋„ ์ ์ˆ˜ ๊ณ„์‚ฐ
result["credibility_score"] = self.calculate_credibility_score(result)
results.append(result)
# ์‹ ๋ขฐ๋„ ์ ์ˆ˜ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌ
results.sort(key=lambda x: x['credibility_score'], reverse=True)
return results
else:
logger.error(f"Brave API ์˜ค๋ฅ˜: {response.status_code}")
return []
except Exception as e:
logger.error(f"Brave ๊ฒ€์ƒ‰ ์ค‘ ์˜ค๋ฅ˜: {str(e)}")
return []
def simulate_streaming(self, text: str, role: str) -> Generator[str, None, None]:
"""ํ…Œ์ŠคํŠธ ๋ชจ๋“œ์—์„œ ์ŠคํŠธ๋ฆฌ๋ฐ ์‹œ๋ฎฌ๋ ˆ์ด์…˜"""
words = text.split()
for i in range(0, len(words), 3):
chunk = " ".join(words[i:i+3])
yield chunk + " "
time.sleep(0.05)
def call_llm_streaming(self, messages: List[Dict[str, str]], role: str) -> Generator[str, None, None]:
"""์ŠคํŠธ๋ฆฌ๋ฐ LLM API ํ˜ธ์ถœ"""
# ํ…Œ์ŠคํŠธ ๋ชจ๋“œ
if self.test_mode:
logger.info(f"ํ…Œ์ŠคํŠธ ๋ชจ๋“œ ์ŠคํŠธ๋ฆฌ๋ฐ - Role: {role}")
test_responses = {
"supervisor_initial": """์ „๋ฌธ์ ์ธ PPT ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
## 1. PPT ์ „์ฒด ๊ตฌ์กฐ ์„ค๊ณ„
**๋ชฉ์ **: ๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜์˜ ํ•ต์‹ฌ ์š”์†Œ์™€ ์„ฑ๊ณต ์ „๋žต์„ ๋ช…ํ™•ํ•˜๊ฒŒ ์ „๋‹ฌ
**ํƒ€๊ฒŸ ์ฒญ์ค‘**: ๊ฒฝ์˜์ง„ ๋ฐ ์˜์‚ฌ๊ฒฐ์ •์ž
**ํ†ค**: ์ „๋ฌธ์ ์ด๊ณ  ์„ค๋“๋ ฅ ์žˆ๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ์Šคํƒ€์ผ
**์ „์ฒด ์Šฌ๋ผ์ด๋“œ**: 12์žฅ
## 2. ์„น์…˜๋ณ„ ๊ตฌ์„ฑ
### ๋„์ž…๋ถ€ (3์žฅ)
- ์Šฌ๋ผ์ด๋“œ 1: ํ‘œ์ง€ - ์ž„ํŒฉํŠธ ์žˆ๋Š” ์ œ๋ชฉ๊ณผ ๋น„์ฃผ์–ผ
- ์Šฌ๋ผ์ด๋“œ 2: ๋ชฉ์ฐจ - ์ „์ฒด ํ๋ฆ„ ์ œ์‹œ
- ์Šฌ๋ผ์ด๋“œ 3: ๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜์˜ ํ•„์š”์„ฑ
### ๋ณธ๋ก  (7์žฅ)
**์„น์…˜ 1: ํ˜„ํ™ฉ ๋ถ„์„ (2์žฅ)**
- ์Šฌ๋ผ์ด๋“œ 4: ์‚ฐ์—…๋ณ„ ๋””์ง€ํ„ธํ™” ํ˜„ํ™ฉ
- ์Šฌ๋ผ์ด๋“œ 5: ์ฃผ์š” ๋„์ „๊ณผ์ œ
**์„น์…˜ 2: ํ•ต์‹ฌ ์ „๋žต (3์žฅ)**
- ์Šฌ๋ผ์ด๋“œ 6: ๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜ ํ”„๋ ˆ์ž„์›Œํฌ
- ์Šฌ๋ผ์ด๋“œ 7: ๊ธฐ์ˆ  ์Šคํƒ ๋ฐ ์ธํ”„๋ผ
- ์Šฌ๋ผ์ด๋“œ 8: ๋ณ€ํ™” ๊ด€๋ฆฌ ์ „๋žต
**์„น์…˜ 3: ์„ฑ๊ณต ์‚ฌ๋ก€ (2์žฅ)**
- ์Šฌ๋ผ์ด๋“œ 9: ๊ธ€๋กœ๋ฒŒ ์„ฑ๊ณต ์‚ฌ๋ก€
- ์Šฌ๋ผ์ด๋“œ 10: ROI ๋ฐ ์„ฑ๊ณผ ์ง€ํ‘œ
### ๊ฒฐ๋ก ๋ถ€ (2์žฅ)
- ์Šฌ๋ผ์ด๋“œ 11: ํ•ต์‹ฌ ์‹œ์‚ฌ์  ๋ฐ ๊ถŒ๊ณ ์‚ฌํ•ญ
- ์Šฌ๋ผ์ด๋“œ 12: Q&A
## 3. ๋น„์ฃผ์–ผ ์ „๋žต
- ์ƒ‰์ƒ: ๊ธฐ์—… ๋ธ”๋ฃจ(#003366) + ์•…์„ผํŠธ ์˜ค๋ Œ์ง€(#FF6B35)
- ์•„์ด์ฝ˜: ๋ชจ๋˜ํ•˜๊ณ  ํ”Œ๋žซํ•œ ๋””์ž์ธ
- ์ฐจํŠธ: ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™” ์ค‘์‹ฌ
[๊ฒ€์ƒ‰ ํ‚ค์›Œ๋“œ]: digital transformation trends 2024, DX success cases, digital transformation ROI, change management digital, industry 4.0 statistics""",
"researcher": """PPT ์ฝ˜ํ…์ธ ๋ฅผ ์œ„ํ•œ ํ•ต์‹ฌ ์ •๋ณด๋ฅผ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
## 1. ์‹œ๊ฐ์  ์ž๋ฃŒ ๊ฐ€๋Šฅํ•œ ํ†ต๊ณ„/์ˆ˜์น˜ (์‹ ๋ขฐ๋„ ๋†’์Œ)
**๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜ ํˆฌ์ž ํ˜„ํ™ฉ**
- 2024๋…„ ๊ธ€๋กœ๋ฒŒ DX ํˆฌ์ž ๊ทœ๋ชจ: $3.4์กฐ (์‹ ๋ขฐ๋„: 0.92)
- ์—ฐํ‰๊ท  ์„ฑ์žฅ๋ฅ (CAGR): 16.5% (2023-2027)
- ์ถœ์ฒ˜: IDC Digital Transformation Spending Guide
**์‚ฐ์—…๋ณ„ ๋””์ง€ํ„ธํ™” ์ˆ˜์ค€**
- ๊ธˆ์œต: 78% ๋””์ง€ํ„ธํ™” ์™„๋ฃŒ (์‹ ๋ขฐ๋„: 0.88)
- ์ œ์กฐ: 52% ์ง„ํ–‰ ์ค‘ (์‹ ๋ขฐ๋„: 0.85)
- ํ—ฌ์Šค์ผ€์–ด: 61% ๊ฐ€์†ํ™” ๋‹จ๊ณ„
- ์ถœ์ฒ˜: McKinsey Global Institute
## 2. ํ•ต์‹ฌ ์ธ์‚ฌ์ดํŠธ์™€ ํŠธ๋ Œ๋“œ (์‹ ๋ขฐ๋„ ๋†’์Œ)
**2024๋…„ ์ฃผ์š” ํŠธ๋ Œ๋“œ**
1. AI/ML ํ†ตํ•ฉ: 87%์˜ ๊ธฐ์—…์ด AI๋ฅผ DX ํ•ต์‹ฌ ์š”์†Œ๋กœ ์ฑ„ํƒ (์‹ ๋ขฐ๋„: 0.90)
2. ํด๋ผ์šฐ๋“œ ์šฐ์„ : 94%๊ฐ€ ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ํด๋ผ์šฐ๋“œ ์ „๋žต ์ถ”์ง„
3. ๋ฐ์ดํ„ฐ ์ค‘์‹ฌ: ์‹ค์‹œ๊ฐ„ ๋ถ„์„์ด ๊ฒฝ์Ÿ๋ ฅ์˜ ํ•ต์‹ฌ
- ์ถœ์ฒ˜: Gartner Technology Trends 2024
## 3. ์‚ฌ๋ก€ ์—ฐ๊ตฌ ๋ฐ ์„ฑ๊ณต ์Šคํ† ๋ฆฌ (์‹ ๋ขฐ๋„ ๋†’์Œ)
**Amazon ์‚ฌ๋ก€**
- ๋ฌผ๋ฅ˜ ์ž๋™ํ™”๋กœ ๋ฐฐ์†ก ์‹œ๊ฐ„ 40% ๋‹จ์ถ• (์‹ ๋ขฐ๋„: 0.87)
- AI ๊ธฐ๋ฐ˜ ์ถ”์ฒœ์œผ๋กœ ๋งค์ถœ 35% ์ฆ๊ฐ€
- ์ถœ์ฒ˜: Amazon Annual Report 2023
**Starbucks ๋””์ง€ํ„ธ ํ˜์‹ **
- ๋ชจ๋ฐ”์ผ ์ฃผ๋ฌธ ๋น„์ค‘: ์ „์ฒด ๋งค์ถœ์˜ 26% (์‹ ๋ขฐ๋„: 0.85)
- ๊ฐœ์ธํ™” ๋งˆ์ผ€ํŒ…์œผ๋กœ ๊ณ ๊ฐ ์ถฉ์„ฑ๋„ 23% ํ–ฅ์ƒ
- ์ถœ์ฒ˜: Starbucks Investor Relations
## 4. ์ „๋ฌธ๊ฐ€ ์˜๊ฒฌ ๋ฐ ์ธ์šฉ๊ตฌ
> "๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜์€ ๊ธฐ์ˆ ์ด ์•„๋‹Œ ์‚ฌ๋žŒ๊ณผ ๋ฌธํ™”์˜ ๋ณ€ํ™”๋‹ค"
- Satya Nadella, Microsoft CEO
> "2025๋…„๊นŒ์ง€ ๋””์ง€ํ„ธ ๋ฆฌ๋”์™€ ํ›„๋ฐœ์ฃผ์ž์˜ ๊ฒฉ์ฐจ๋Š” 2๋ฐฐ ์ด์ƒ ๋ฒŒ์–ด์งˆ ๊ฒƒ"
- Michael Porter, Harvard Business School
## 5. ์‹œ๊ฐ ์ž๋ฃŒ ์ถ”์ฒœ
**์Šฌ๋ผ์ด๋“œ๋ณ„ ๋น„์ฃผ์–ผ ์ œ์•ˆ**
- ํ‘œ์ง€: ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ์ด๋ฏธ์ง€ + ๋™์ ์ธ ๋ฐ์ดํ„ฐ ํ๋ฆ„
- ํ†ต๊ณ„: ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒํ•œ ๋Œ€์‹œ๋ณด๋“œ ์Šคํƒ€์ผ ์ฐจํŠธ
- ํ”„๋ ˆ์ž„์›Œํฌ: ๊ณ„์ธต์  ๋‹ค์ด์–ด๊ทธ๋žจ + ์•„์ด์ฝ˜
- ์‚ฌ๋ก€: ๊ธฐ์—… ๋กœ๊ณ  + ํ•ต์‹ฌ ์ง€ํ‘œ ์ธํฌ๊ทธ๋ž˜ํ”ฝ
- ํƒ€์ž„๋ผ์ธ: ๋กœ๋“œ๋งต ํ˜•ํƒœ์˜ ๋‹จ๊ณ„๋ณ„ ์ง„ํ–‰๋„
**์•„์ด์ฝ˜ ์„ธํŠธ**
- ๊ธฐ์ˆ : ํด๋ผ์šฐ๋“œ, AI, ๋ฐ์ดํ„ฐ, ๋ณด์•ˆ
- ๋น„์ฆˆ๋‹ˆ์Šค: ์„ฑ์žฅ, ํ˜์‹ , ํ˜‘์—…, ํšจ์œจ์„ฑ
- ์‚ฌ๋žŒ: ๋ฆฌ๋”์‹ญ, ํŒ€์›Œํฌ, ์Šคํ‚ฌ, ๋ฌธํ™”""",
"supervisor_execution": """์กฐ์‚ฌ ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ PPT ์ œ์ž‘์„ ์œ„ํ•œ ๊ตฌ์ฒด์ ์ธ ์ง€์‹œ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค.
## 1. ์Šฌ๋ผ์ด๋“œ๋ณ„ ์ƒ์„ธ ๊ตฌ์„ฑ
**์Šฌ๋ผ์ด๋“œ 1: ํ‘œ์ง€**
- ์ œ๋ชฉ: "๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜: ๋ฏธ๋ž˜๋ฅผ ํ–ฅํ•œ ๋„์•ฝ"
- ๋ถ€์ œ๋ชฉ: "์„ฑ๊ณต์ ์ธ DX ์ „๋žต๊ณผ ์‹คํ–‰ ๋ฐฉ์•ˆ"
- ๋น„์ฃผ์–ผ: ์—ฐ๊ฒฐ๋œ ๋””์ง€ํ„ธ ๋„คํŠธ์›Œํฌ ๋ฐฐ๊ฒฝ
- ์กฐ์‚ฌ ์ž๋ฃŒ ํ™œ์šฉ: $3.4์กฐ ์‹œ์žฅ ๊ทœ๋ชจ ๊ฐ•์กฐ
**์Šฌ๋ผ์ด๋“œ 4: ์‚ฐ์—…๋ณ„ ํ˜„ํ™ฉ**
- ์กฐ์‚ฌ๋œ ํ†ต๊ณ„ ํ™œ์šฉ: ๊ธˆ์œต(78%), ์ œ์กฐ(52%), ํ—ฌ์Šค์ผ€์–ด(61%)
- ์›ํ˜• ์ฐจํŠธ + ์ง„ํ–‰ ๋ฐ” ์กฐํ•ฉ
- ๊ฐ ์‚ฐ์—…๋ณ„ ๋Œ€ํ‘œ ์•„์ด์ฝ˜ ๋ฐฐ์น˜
**์Šฌ๋ผ์ด๋“œ 7: ๊ธฐ์ˆ  ์Šคํƒ**
- AI/ML(87% ์ฑ„ํƒ๋ฅ ) ์ค‘์‹ฌ์œผ๋กœ ๊ตฌ์„ฑ
- ํด๋ผ์šฐ๋“œ(94%) ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์ „๋žต ์‹œ๊ฐํ™”
- ๊ณ„์ธต์  ๋‹ค์ด์–ด๊ทธ๋žจ ์‚ฌ์šฉ
**์Šฌ๋ผ์ด๋“œ 9: ์„ฑ๊ณต ์‚ฌ๋ก€**
- Amazon๊ณผ Starbucks ์‚ฌ๋ก€ ๋Œ€๋น„
- ํ•ต์‹ฌ ์„ฑ๊ณผ ์ง€ํ‘œ ์ธํฌ๊ทธ๋ž˜ํ”ฝ
- Before/After ๋น„๊ต ํ˜•์‹
## 2. ๋””์ž์ธ ๊ฐ€์ด๋“œ๋ผ์ธ
**์ƒ‰์ƒ ํŒ”๋ ˆํŠธ**
- ์ฃผ์ƒ‰์ƒ: ๋„ค์ด๋น„ ๋ธ”๋ฃจ (#1E3A8A)
- ๋ณด์กฐ์ƒ‰์ƒ: ์Šค์นด์ด ๋ธ”๋ฃจ (#3B82F6)
- ๊ฐ•์กฐ์ƒ‰์ƒ: ์˜ค๋ Œ์ง€ (#F59E0B)
- ๋ฐฐ๊ฒฝ: ํ™”์ดํŠธ/๋ผ์ดํŠธ ๊ทธ๋ ˆ์ด
**ํƒ€์ดํฌ๊ทธ๋ž˜ํ”ผ**
- ์ œ๋ชฉ: Montserrat Bold (32-40pt)
- ๋ณธ๋ฌธ: Open Sans Regular (18-24pt)
- ๊ฐ•์กฐ: Open Sans Semi-Bold
**๋ ˆ์ด์•„์›ƒ ์›์น™**
- ์—ฌ๋ฐฑ ์ถฉ๋ถ„ํžˆ ํ™œ์šฉ (40% ์ด์ƒ)
- ์ขŒ์šฐ ๋Œ€์นญ ๋˜๋Š” ํ™ฉ๊ธˆ๋น„์œจ
- ํ•œ ์Šฌ๋ผ์ด๋“œ ํ•œ ๋ฉ”์‹œ์ง€ ์›์น™
## 3. ์Šคํ† ๋ฆฌํ…”๋ง ์ „๋žต
**๋„์ž…๋ถ€ ์ „๋žต**
- ์ถฉ๊ฒฉ์ ์ธ ํ†ต๊ณ„๋กœ ์‹œ์ž‘ ($3.4์กฐ ์‹œ์žฅ)
- "์™œ ์ง€๊ธˆ์ธ๊ฐ€?" ์งˆ๋ฌธ ์ œ๊ธฐ
- ์ฒญ์ค‘์˜ pain point ๊ณต๊ฐ
**์ „๊ฐœ ๋ฐฉ์‹**
- ๋ฌธ์ œ โ†’ ํ•ด๊ฒฐ์ฑ… โ†’ ์ฆ๊ฑฐ โ†’ ํ–‰๋™
- ๊ฐ ์„น์…˜ ๊ฐ„ ์ž์—ฐ์Šค๋Ÿฌ์šด ์ „ํ™˜
- ์‚ฌ๋ก€๋ฅผ ํ†ตํ•œ ๊ตฌ์ฒดํ™”
**ํด๋ผ์ด๋งฅ์Šค**
- ROI ๋ฐ์ดํ„ฐ๋กœ ์„ค๋“๋ ฅ ๊ทน๋Œ€ํ™”
- ์„ฑ๊ณต ๊ธฐ์—…๊ณผ์˜ ๊ฒฉ์ฐจ ์‹œ๊ฐํ™”
- ํ–‰๋™ ์ด‰๊ตฌ ๋ฉ”์‹œ์ง€
## 4. ์‹œ๊ฐํ™” ์ง€์นจ
**๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™”**
- ์‚ฐ์—…๋ณ„ ํ˜„ํ™ฉ: ๋ฐฉ์‚ฌํ˜• ์ฐจํŠธ
- ์„ฑ์žฅ๋ฅ : ์ƒ์Šน ๊ณก์„  ๊ทธ๋ž˜ํ”„
- ROI: ๊ณ„์‚ฐ๊ธฐ ์Šคํƒ€์ผ ์ธํฌ๊ทธ๋ž˜ํ”ฝ
- ํ”„๋กœ์„ธ์Šค: ์ˆœํ™˜ํ˜• ๋‹ค์ด์–ด๊ทธ๋žจ
**์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ**
- ์Šฌ๋ผ์ด๋“œ ์ „ํ™˜: Morph ํšจ๊ณผ
- ์ฐจํŠธ ๋“ฑ์žฅ: ์ˆœ์ฐจ์  ๋นŒ๋“œ์—…
- ๊ฐ•์กฐ ํฌ์ธํŠธ: ์คŒ์ธ/ํ•˜์ด๋ผ์ดํŠธ
- ๊ณผ๋„ํ•œ ํšจ๊ณผ ์ง€์–‘""",
"executor": """PPT ์Šฌ๋ผ์ด๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
[์Šฌ๋ผ์ด๋“œ 1] ํ‘œ์ง€
์ œ๋ชฉ: ๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜: ๋ฏธ๋ž˜๋ฅผ ํ–ฅํ•œ ๋„์•ฝ
๋ถ€์ œ๋ชฉ: ์„ฑ๊ณต์ ์ธ DX ์ „๋žต๊ณผ ์‹คํ–‰ ๋ฐฉ์•ˆ
ํ•ต์‹ฌ ๋‚ด์šฉ:
- ๋ฐœํ‘œ์ž: [์ด๋ฆ„]
- ๋‚ ์งœ: 2024๋…„ 3์›”
- ๊ธฐ์—…๋ช…/๋กœ๊ณ 
๋ฐœํ‘œ์ž ๋…ธํŠธ: ์ฒญ์ค‘์˜ ์ฃผ๋ชฉ์„ ๋Œ ์ˆ˜ ์žˆ๋„๋ก ์ž์‹ ๊ฐ ์žˆ๊ฒŒ ์‹œ์ž‘. $3.4์กฐ ๊ทœ๋ชจ์˜ ๊ฑฐ๋Œ€ํ•œ ๋ณ€ํ™”์˜ ๋ฌผ๊ฒฐ์ž„์„ ๊ฐ•์กฐ
์‹œ๊ฐ ์ž๋ฃŒ: ๋””์ง€ํ„ธ ๋„คํŠธ์›Œํฌ๊ฐ€ ์—ฐ๊ฒฐ๋œ ์ง€๊ตฌ๋ณธ ์ด๋ฏธ์ง€, ๋™์ ์ธ ๋ฐ์ดํ„ฐ ํ๋ฆ„ ํšจ๊ณผ
[์Šฌ๋ผ์ด๋“œ 2] ๋ชฉ์ฐจ
์ œ๋ชฉ: Agenda
ํ•ต์‹ฌ ๋‚ด์šฉ:
- ๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜์˜ ํ˜„์žฌ
- ์‚ฐ์—…๋ณ„ ๋””์ง€ํ„ธํ™” ํ˜„ํ™ฉ๊ณผ ๊ณผ์ œ
- ์„ฑ๊ณต์„ ์œ„ํ•œ ํ•ต์‹ฌ ์ „๋žต
- ๊ธ€๋กœ๋ฒŒ ์„ฑ๊ณต ์‚ฌ๋ก€์™€ ๊ตํ›ˆ
- ๊ฒฐ๋ก  ๋ฐ ๋‹ค์Œ ๋‹จ๊ณ„
๋ฐœํ‘œ์ž ๋…ธํŠธ: ์ „์ฒด 20๋ถ„ ๋ฐœํ‘œ ์ค‘ ๊ฐ ์„น์…˜๋ณ„ ์‹œ๊ฐ„ ๋ฐฐ๋ถ„ ์„ค๋ช…
์‹œ๊ฐ ์ž๋ฃŒ: ๋ฒˆํ˜ธ๊ฐ€ ๋งค๊ฒจ์ง„ ์•„์ด์ฝ˜๊ณผ ์ง„ํ–‰ ํ‘œ์‹œ ๋ฐ”
[์Šฌ๋ผ์ด๋“œ 3] ๋„์ž…
์ œ๋ชฉ: ์™œ ์ง€๊ธˆ ๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜์ธ๊ฐ€?
ํ•ต์‹ฌ ๋‚ด์šฉ:
- 2024๋…„ ๊ธ€๋กœ๋ฒŒ DX ํˆฌ์ž: $3.4์กฐ
- ๋””์ง€ํ„ธ ๋ฆฌ๋” ๊ธฐ์—…์˜ ์ˆ˜์ต์„ฑ: ํ‰๊ท  ๋Œ€๋น„ 2.5๋ฐฐ
- ํŒฌ๋ฐ๋ฏน ์ดํ›„ ๋””์ง€ํ„ธ ์ฑ„ํƒ ๊ฐ€์†ํ™”: 10๋…„โ†’2๋…„
- "๋ณ€ํ™”ํ•˜๊ฑฐ๋‚˜ ๋„ํƒœ๋˜๊ฑฐ๋‚˜" - ์ƒ์กด์˜ ๋ฌธ์ œ
๋ฐœํ‘œ์ž ๋…ธํŠธ: ์‹œ์žฅ ๊ทœ๋ชจ์™€ ๊ธด๊ธ‰์„ฑ์„ ๊ฐ•์กฐํ•˜์—ฌ ์ฒญ์ค‘์˜ ๊ด€์‹ฌ ์œ ๋„
์‹œ๊ฐ ์ž๋ฃŒ: ๊ธ‰์ƒ์Šนํ•˜๋Š” ๊ทธ๋ž˜ํ”„์™€ ์‹œ๊ณ„ ์•„์ด์ฝ˜์œผ๋กœ ์‹œ๊ธ‰ํ•จ ํ‘œํ˜„
[์Šฌ๋ผ์ด๋“œ 4] ์‚ฐ์—…๋ณ„ ํ˜„ํ™ฉ
์ œ๋ชฉ: ์‚ฐ์—…๋ณ„ ๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜ ํ˜„ํ™ฉ
ํ•ต์‹ฌ ๋‚ด์šฉ:
- ๊ธˆ์œต: 78% (์„ ๋„ ๊ทธ๋ฃน)
- ํ—ฌ์Šค์ผ€์–ด: 61% (๋น ๋ฅธ ์ถ”๊ฒฉ)
- ์ œ์กฐ: 52% (๊พธ์ค€ํ•œ ์ง„ํ–‰)
- ์†Œ๋งค: 45% (๊ฐ€์†ํ™” ํ•„์š”)
๋ฐœํ‘œ์ž ๋…ธํŠธ: ๊ฐ ์‚ฐ์—…์˜ ํŠน์„ฑ๊ณผ ๋„์ „๊ณผ์ œ ๊ฐ„๋‹จํžˆ ์–ธ๊ธ‰
์‹œ๊ฐ ์ž๋ฃŒ: ๋„๋„› ์ฐจํŠธ์™€ ์‚ฐ์—…๋ณ„ ์•„์ด์ฝ˜, ์ง„ํ–‰๋ฅ  ๋ฐ”
[์Šฌ๋ผ์ด๋“œ 5] ์ฃผ์š” ๋„์ „๊ณผ์ œ
์ œ๋ชฉ: ๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜์˜ 5๋Œ€ ์žฅ๋ฒฝ
ํ•ต์‹ฌ ๋‚ด์šฉ:
- ๋ ˆ๊ฑฐ์‹œ ์‹œ์Šคํ…œ์˜ ๋ณต์žก์„ฑ (67%)
- ๋ณ€ํ™” ์ €ํ•ญ๊ณผ ๋ฌธํ™”์  ๊ด€์„ฑ (54%)
- ๋””์ง€ํ„ธ ์ธ์žฌ ๋ถ€์กฑ (48%)
- ๋ถˆ๋ช…ํ™•ํ•œ ROI (41%)
- ์‚ฌ์ด๋ฒ„ ๋ณด์•ˆ ์šฐ๋ ค (38%)
๋ฐœํ‘œ์ž ๋…ธํŠธ: ๊ฐ ์žฅ๋ฒฝ์— ๋Œ€ํ•œ ๊ทน๋ณต ๋ฐฉ์•ˆ ๋ฏธ๋ฆฌ ์ค€๋น„
์‹œ๊ฐ ์ž๋ฃŒ: ์žฅ๋ฒฝ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฒฝ๋Œ ์•„์ด์ฝ˜๊ณผ ๋ฐฑ๋ถ„์œจ ํ‘œ์‹œ
[์Šฌ๋ผ์ด๋“œ 6] DX ํ”„๋ ˆ์ž„์›Œํฌ
์ œ๋ชฉ: ๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜ ์„ฑ๊ณต ํ”„๋ ˆ์ž„์›Œํฌ
ํ•ต์‹ฌ ๋‚ด์šฉ:
- ์ „๋žต: ๋น„์ฆˆ๋‹ˆ์Šค ๋ชฉํ‘œ ์ •๋ ฌ
- ๊ธฐ์ˆ : AI/ํด๋ผ์šฐ๋“œ/๋ฐ์ดํ„ฐ
- ํ”„๋กœ์„ธ์Šค: ์• ์ž์ผ ๋ฐฉ๋ฒ•๋ก 
- ๋ฌธํ™”: ํ˜์‹ ๊ณผ ์‹คํ—˜ ์žฅ๋ ค
- ๊ฑฐ๋ฒ„๋„Œ์Šค: ์ง€์†์  ๊ฐœ์„ 
๋ฐœํ‘œ์ž ๋…ธํŠธ: ๊ฐ ์š”์†Œ๊ฐ€ ์ƒํ˜ธ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์Œ์„ ๊ฐ•์กฐ
์‹œ๊ฐ ์ž๋ฃŒ: 5๊ฐํ˜• ๋‹ค์ด์–ด๊ทธ๋žจ, ๊ฐ ๊ผญ์ง€์ ์— ์•„์ด์ฝ˜ ๋ฐฐ์น˜
[์Šฌ๋ผ์ด๋“œ 7] ๊ธฐ์ˆ  ์Šคํƒ
์ œ๋ชฉ: 2024 ํ•ต์‹ฌ ๊ธฐ์ˆ  ์Šคํƒ
ํ•ต์‹ฌ ๋‚ด์šฉ:
- AI/ML ์ฑ„ํƒ๋ฅ : 87%
- ํด๋ผ์šฐ๋“œ ์ „๋žต: 94% ํ•˜์ด๋ธŒ๋ฆฌ๋“œ
- ๋ฐ์ดํ„ฐ ๋ถ„์„: ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ
- ๋ณด์•ˆ: Zero Trust ์•„ํ‚คํ…์ฒ˜
- ํ†ตํ•ฉ: API ์šฐ์„  ์ ‘๊ทผ
๋ฐœํ‘œ์ž ๋…ธํŠธ: ๊ฐ ๊ธฐ์ˆ ์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๊ฐ€์น˜ ์—ฐ๊ฒฐ
์‹œ๊ฐ ์ž๋ฃŒ: ๊ณ„์ธต์  ๊ธฐ์ˆ  ์Šคํƒ ๋‹ค์ด์–ด๊ทธ๋žจ
[์Šฌ๋ผ์ด๋“œ 8] ๋ณ€ํ™” ๊ด€๋ฆฌ
์ œ๋ชฉ: ์‚ฌ๋žŒ ์ค‘์‹ฌ์˜ ๋ณ€ํ™” ๊ด€๋ฆฌ
ํ•ต์‹ฌ ๋‚ด์šฉ:
- ๋ฆฌ๋”์‹ญ์˜ ๋ช…ํ™•ํ•œ ๋น„์ „ ๊ณต์œ 
- ๋‹จ๊ณ„๋ณ„ ๊ต์œก ํ”„๋กœ๊ทธ๋žจ
- ๋น ๋ฅธ ์„ฑ๊ณผ๋กœ ๋ชจ๋ฉ˜ํ…€ ๊ตฌ์ถ•
- ์ง€์†์  ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜
- ๋ณด์ƒ ์ฒด๊ณ„ ์—ฐ๊ณ„
๋ฐœํ‘œ์ž ๋…ธํŠธ: Satya Nadella ์ธ์šฉ๊ตฌ ํ™œ์šฉ
์‹œ๊ฐ ์ž๋ฃŒ: ๋ณ€ํ™” ๊ณก์„ ๊ณผ ์‚ฌ๋žŒ ์•„์ด์ฝ˜
[์Šฌ๋ผ์ด๋“œ 9] ์„ฑ๊ณต ์‚ฌ๋ก€
์ œ๋ชฉ: ๊ธ€๋กœ๋ฒŒ DX ์„ฑ๊ณต ์Šคํ† ๋ฆฌ
ํ•ต์‹ฌ ๋‚ด์šฉ:
- Amazon: ๋ฌผ๋ฅ˜ ์ž๋™ํ™” โ†’ ๋ฐฐ์†ก 40% ๋‹จ์ถ•
- Starbucks: ๋ชจ๋ฐ”์ผ ์ฃผ๋ฌธ โ†’ ๋งค์ถœ 26%
- Netflix: AI ์ถ”์ฒœ โ†’ ์‹œ์ฒญ ์‹œ๊ฐ„ 80% ์ฆ๊ฐ€
- Tesla: OTA ์—…๋ฐ์ดํŠธ โ†’ ์„œ๋น„์Šค ๋น„์šฉ 70% ์ ˆ๊ฐ
๋ฐœํ‘œ์ž ๋…ธํŠธ: ๊ฐ ์‚ฌ๋ก€์˜ ํ•ต์‹ฌ ์„ฑ๊ณต ์š”์ธ ๊ฐ•์กฐ
์‹œ๊ฐ ์ž๋ฃŒ: ๊ธฐ์—… ๋กœ๊ณ ์™€ ํ•ต์‹ฌ ์ง€ํ‘œ ์ธํฌ๊ทธ๋ž˜ํ”ฝ
[์Šฌ๋ผ์ด๋“œ 10] ROI ๋ถ„์„
์ œ๋ชฉ: ๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜์˜ ๋ช…ํ™•ํ•œ ROI
ํ•ต์‹ฌ ๋‚ด์šฉ:
- ์šด์˜ ํšจ์œจ์„ฑ: 30-40% ๊ฐœ์„ 
- ๊ณ ๊ฐ ๋งŒ์กฑ๋„: NPS 25% ์ƒ์Šน
- ์‹ ๊ทœ ์ˆ˜์ต์›: ์ „์ฒด ๋งค์ถœ์˜ 15%
- ํˆฌ์ž ํšŒ์ˆ˜ ๊ธฐ๊ฐ„: ํ‰๊ท  2.5๋…„
๋ฐœํ‘œ์ž ๋…ธํŠธ: ๊ตฌ์ฒด์ ์ธ ์ˆ˜์น˜๋กœ ํˆฌ์ž ์ •๋‹น์„ฑ ์ž…์ฆ
์‹œ๊ฐ ์ž๋ฃŒ: ROI ๊ณ„์‚ฐ๊ธฐ ์Šคํƒ€์ผ์˜ ์ธํฌ๊ทธ๋ž˜ํ”ฝ
[์Šฌ๋ผ์ด๋“œ 11] ํ•ต์‹ฌ ์‹œ์‚ฌ์ 
์ œ๋ชฉ: ์„ฑ๊ณต์ ์ธ DX๋ฅผ ์œ„ํ•œ 5๊ฐ€์ง€ ์ œ์–ธ
ํ•ต์‹ฌ ๋‚ด์šฉ:
- ์ž‘๊ฒŒ ์‹œ์ž‘ํ•˜๋˜ ํฌ๊ฒŒ ์ƒ๊ฐํ•˜๋ผ
- ๊ธฐ์ˆ ๋ณด๋‹ค ๋ฌธํ™” ๋ณ€ํ™”์— ํˆฌ์žํ•˜๋ผ
- ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ์˜์‚ฌ๊ฒฐ์ •์„ ์ผ์ƒํ™”ํ•˜๋ผ
- ์‹คํŒจ๋ฅผ ๋‘๋ ค์›Œํ•˜์ง€ ๋ง๊ณ  ๋น ๋ฅด๊ฒŒ ๋ฐฐ์›Œ๋ผ
- ๊ณ ๊ฐ ์ค‘์‹ฌ์œผ๋กœ ๋ชจ๋“  ๊ฒƒ์„ ์žฌ์„ค๊ณ„ํ•˜๋ผ
๋ฐœํ‘œ์ž ๋…ธํŠธ: ๊ฐ ์ œ์–ธ์— ๋Œ€ํ•œ ์‹ค์ฒœ ๋ฐฉ์•ˆ ์ค€๋น„
์‹œ๊ฐ ์ž๋ฃŒ: ์ฒดํฌ๋ฆฌ์ŠคํŠธ ์Šคํƒ€์ผ์˜ ์•„์ด์ฝ˜
[์Šฌ๋ผ์ด๋“œ 12] Q&A
์ œ๋ชฉ: Questions & Discussion
ํ•ต์‹ฌ ๋‚ด์šฉ:
- ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค
- ์งˆ๋ฌธ๊ณผ ํ† ๋ก  ํ™˜์˜
- ์—ฐ๋ฝ์ฒ˜: [์ด๋ฉ”์ผ/์ „ํ™”]
- ์ถ”๊ฐ€ ์ž๋ฃŒ: [์›น์‚ฌ์ดํŠธ/QR์ฝ”๋“œ]
๋ฐœํ‘œ์ž ๋…ธํŠธ: ์˜ˆ์ƒ ์งˆ๋ฌธ 3-5๊ฐœ ๋ฏธ๋ฆฌ ์ค€๋น„
์‹œ๊ฐ ์ž๋ฃŒ: ๋ฌผ์Œํ‘œ ์•„์ด์ฝ˜๊ณผ ์—ฐ๋ฝ์ฒ˜ ์ •๋ณด""",
"supervisor_review": """์‹คํ–‰์ž AI์˜ PPT ์ดˆ์•ˆ์„ ๊ฒ€ํ† ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ „๋ฐ˜์ ์œผ๋กœ ์ž˜ ๊ตฌ์„ฑ๋˜์—ˆ์œผ๋‚˜ ๋‹ค์Œ ๊ฐœ์„ ์‚ฌํ•ญ์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.
## ๊ฐ•์ 
- ์กฐ์‚ฌ๋œ ํ†ต๊ณ„์™€ ์ˆ˜์น˜๊ฐ€ ํšจ๊ณผ์ ์œผ๋กœ ํ™œ์šฉ๋จ
- ์Šคํ† ๋ฆฌ๋ผ์ธ์ด ๋…ผ๋ฆฌ์ ์ด๊ณ  ์„ค๋“๋ ฅ ์žˆ์Œ
- ๊ฐ ์Šฌ๋ผ์ด๋“œ์˜ ํ•ต์‹ฌ ๋ฉ”์‹œ์ง€๊ฐ€ ๋ช…ํ™•ํ•จ
- ์‹œ๊ฐ ์ž๋ฃŒ ์ œ์•ˆ์ด ๊ตฌ์ฒด์ ์ž„
## ๊ฐœ์„  ํ•„์š”์‚ฌํ•ญ
### 1. ์ž„ํŒฉํŠธ ๊ฐ•ํ™”
**์Šฌ๋ผ์ด๋“œ 3 ๊ฐœ์„ **
- ํ˜„์žฌ: ํ†ต๊ณ„ ๋‚˜์—ด
- ๊ฐœ์„ : "๊ท€์‚ฌ๋Š” ๋””์ง€ํ„ธ ๋ฆฌ๋”์ž…๋‹ˆ๊นŒ, ํ›„๋ฐœ์ฃผ์ž์ž…๋‹ˆ๊นŒ?"๋ผ๋Š” ๋„๋ฐœ์  ์งˆ๋ฌธ ์ถ”๊ฐ€
- ์ฒญ์ค‘ ์ฐธ์—ฌ๋ฅผ ์œ„ํ•œ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์š”์†Œ ๊ณ ๋ ค
**์Šฌ๋ผ์ด๋“œ 5 ๊ฐœ์„ **
- ํ˜„์žฌ: ์žฅ๋ฒฝ๋งŒ ์ œ์‹œ
- ๊ฐœ์„ : ๊ฐ ์žฅ๋ฒฝ๋ณ„ "Quick Win" ์†”๋ฃจ์…˜ ํ•œ ์ค„ ์ถ”๊ฐ€
- ํฌ๋ง์  ๋ฉ”์‹œ์ง€๋กœ ๊ท ํ˜• ๋งž์ถ”๊ธฐ
### 2. ์Šคํ† ๋ฆฌํ…”๋ง ๊ฐ•ํ™”
**์ „ํ™˜ ๋ฌธ๊ตฌ ์ถ”๊ฐ€**
- ์Šฌ๋ผ์ด๋“œ ๊ฐ„ ์—ฐ๊ฒฐ ๋ฌธ๊ตฌ ํ•„์š”
- ์˜ˆ: "์ด์ œ ์ด๋Ÿฌํ•œ ํ˜„ํ™ฉ์„ ์–ด๋–ป๊ฒŒ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”?"
**๊ฐ์ •์  ์—ฐ๊ฒฐ**
- ์ˆซ์ž์™€ ํ†ต๊ณ„ ์™ธ์— ์ธ๊ฐ„์  ์Šคํ† ๋ฆฌ ์ถ”๊ฐ€
- ๋ณ€ํ™”๋ฅผ ๊ฒช์€ ์ง์›/๊ณ ๊ฐ์˜ ์‹ค์ œ ๊ฒฝํ—˜๋‹ด
### 3. ๋น„์ฃผ์–ผ ์ผ๊ด€์„ฑ
**์ƒ‰์ƒ ์‚ฌ์šฉ ์ •์ œ**
- ๊ฐ•์กฐ์ƒ‰(์˜ค๋ Œ์ง€) ๊ณผ๋„ ์‚ฌ์šฉ ์ž์ œ
- ๊ฐ ์„น์…˜๋ณ„ ์ƒ‰์ƒ ํ†ค ๊ตฌ๋ถ„
- ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™” ์ƒ‰์ƒ ํŒ”๋ ˆํŠธ ํ†ต์ผ
**์•„์ด์ฝ˜ ์Šคํƒ€์ผ**
- ๋ชจ๋“  ์•„์ด์ฝ˜ ๋™์ผ ์Šคํƒ€์ผ๋กœ ํ†ต์ผ
- ๋ผ์ธ ๋‘๊ป˜, ๋ชจ์„œ๋ฆฌ ์ฒ˜๋ฆฌ ์ผ๊ด€์„ฑ
### 4. ๋ฐœํ‘œ ์ค€๋น„ ๊ฐ•ํ™”
**์‹œ๊ฐ„ ๋ฐฐ๋ถ„ ๋ช…์‹œ**
- ๊ฐ ์Šฌ๋ผ์ด๋“œ๋ณ„ ์˜ˆ์ƒ ์†Œ์š” ์‹œ๊ฐ„ ์ถ”๊ฐ€
- ์ „์ฒด 20๋ถ„ ๊ธฐ์ค€ ์ƒ์„ธ ๋ฐฐ๋ถ„
**์ „ํ™˜ ์Šคํฌ๋ฆฝํŠธ**
- ์Šฌ๋ผ์ด๋“œ ๊ฐ„ ์ž์—ฐ์Šค๋Ÿฌ์šด ์ „ํ™˜ ๋ฉ˜ํŠธ ์ค€๋น„
- ์ฒญ์ค‘ ๋ฐ˜์‘ ์ฒดํฌ ํฌ์ธํŠธ ์„ค์ •
### 5. ์ถ”๊ฐ€ ๊ถŒ์žฅ์‚ฌํ•ญ
**๋ฐฑ์—… ์Šฌ๋ผ์ด๋“œ ์ค€๋น„**
- ์ƒ์„ธ ๊ธฐ์ˆ  ์ŠคํŽ™
- ์ถ”๊ฐ€ ์‚ฌ๋ก€ ์—ฐ๊ตฌ
- ์—…์ข…๋ณ„ ๋งž์ถค ์ „๋žต
- ์ƒ์„ธ ROI ๊ณ„์‚ฐ ๋ฐฉ๋ฒ•
**์ธ์‡„ ์ž๋ฃŒ**
- 3์Šฌ๋ผ์ด๋“œ/ํŽ˜์ด์ง€ ํ˜•์‹
- ๋ฉ”๋ชจ ๊ณต๊ฐ„ ํฌํ•จ
- ํ•ต์‹ฌ ์ˆ˜์น˜ ์š”์•ฝ ํŽ˜์ด์ง€
์ด๋Ÿฌํ•œ ๊ฐœ์„ ์‚ฌํ•ญ์„ ๋ฐ˜์˜ํ•˜๋ฉด ๋”์šฑ ์™„์„ฑ๋„ ๋†’๊ณ  ์„ค๋“๋ ฅ ์žˆ๋Š” PPT๊ฐ€ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.""",
"executor_final": """๊ฐ๋…์ž AI์˜ ํ”ผ๋“œ๋ฐฑ์„ ์™„์ „ํžˆ ๋ฐ˜์˜ํ•œ ์ตœ์ข… PPT๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
# ๐ŸŽฏ ๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜: ๋ฏธ๋ž˜๋ฅผ ํ–ฅํ•œ ๋„์•ฝ
## ์ตœ์ข… ์™„์„ฑ PPT (๋ฐœํ‘œ ์‹œ๊ฐ„: 20๋ถ„)
---
[์Šฌ๋ผ์ด๋“œ 1] ํ‘œ์ง€ (30์ดˆ)
**์ œ๋ชฉ**: ๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜: ๋ฏธ๋ž˜๋ฅผ ํ–ฅํ•œ ๋„์•ฝ
**๋ถ€์ œ๋ชฉ**: ์„ฑ๊ณต์ ์ธ DX ์ „๋žต๊ณผ ์‹คํ–‰ ๋ฐฉ์•ˆ
**๋น„์ฃผ์–ผ**:
- ๋ฐฐ๊ฒฝ: ์—ฐ๊ฒฐ๋œ ๋””์ง€ํ„ธ ๋„คํŠธ์›Œํฌ์™€ ๋ฐ์ดํ„ฐ ํ๋ฆ„
- ์ค‘์•™: ํšŒ์‚ฌ ๋กœ๊ณ 
- ํ•˜๋‹จ: ๋ฐœํ‘œ์ž ์ •๋ณด ๋ฐ ๋‚ ์งœ
**๋ฐœํ‘œ์ž ๋…ธํŠธ**:
"์•ˆ๋…•ํ•˜์‹ญ๋‹ˆ๊นŒ. ์˜ค๋Š˜ ์—ฌ๋Ÿฌ๋ถ„๊ณผ ํ•จ๊ป˜ $3.4์กฐ ๊ทœ๋ชจ์˜ ๋””์ง€ํ„ธ ํ˜๋ช…, ๊ทธ ์ค‘์‹ฌ์—์„œ ์šฐ๋ฆฌ๊ฐ€ ์–ด๋–ป๊ฒŒ ์„ฑ๊ณตํ•  ์ˆ˜ ์žˆ์„์ง€ ๋…ผ์˜ํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค."
---
[์Šฌ๋ผ์ด๋“œ 2] ๋ชฉ์ฐจ (30์ดˆ)
**์ œ๋ชฉ**: ์˜ค๋Š˜์˜ ์—ฌ์ •
**ํ•ต์‹ฌ ๋‚ด์šฉ**:
1. ๐Ÿ“Š ๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜์˜ ํ˜„์žฌ (3๋ถ„)
2. ๐Ÿญ ์‚ฐ์—…๋ณ„ ํ˜„ํ™ฉ๊ณผ ๋„์ „๊ณผ์ œ (3๋ถ„)
3. ๐ŸŽฏ ์„ฑ๊ณต ์ „๋žต๊ณผ ํ”„๋ ˆ์ž„์›Œํฌ (5๋ถ„)
4. ๐ŸŒŸ ๊ธ€๋กœ๋ฒŒ ์„ฑ๊ณต ์‚ฌ๋ก€ (4๋ถ„)
5. ๐Ÿ’ก ํ•ต์‹ฌ ์‹œ์‚ฌ์ ๊ณผ ์‹คํ–‰ ๋ฐฉ์•ˆ (4๋ถ„)
6. ๐Ÿค Q&A (1๋ถ„)
**๋ฐœํ‘œ์ž ๋…ธํŠธ**:
"20๋ถ„๊ฐ„์˜ ์—ฌ์ •์„ ํ†ตํ•ด ๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜์˜ ํ˜„์žฌ์™€ ๋ฏธ๋ž˜, ๊ทธ๋ฆฌ๊ณ  ์‹คํ–‰ ๋ฐฉ์•ˆ์„ ํ•จ๊ป˜ ํƒ์ƒ‰ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค."
**์‹œ๊ฐ ์ž๋ฃŒ**: ์ง„ํ–‰ ๋‹จ๊ณ„๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๋กœ๋“œ๋งต ์Šคํƒ€์ผ ๋””์ž์ธ
---
[์Šฌ๋ผ์ด๋“œ 3] ๋„์ „์  ์งˆ๋ฌธ (1๋ถ„ 30์ดˆ)
**์ œ๋ชฉ**: ๊ท€์‚ฌ๋Š” ๋””์ง€ํ„ธ ๋ฆฌ๋”์ž…๋‹ˆ๊นŒ, ํ›„๋ฐœ์ฃผ์ž์ž…๋‹ˆ๊นŒ?
**ํ•ต์‹ฌ ๋‚ด์šฉ**:
- ๐ŸŒ 2024๋…„ ๊ธ€๋กœ๋ฒŒ DX ์‹œ์žฅ: **$3.4์กฐ**
- ๐Ÿ“ˆ ๋””์ง€ํ„ธ ๋ฆฌ๋”์˜ ์ˆ˜์ต์„ฑ: ํ‰๊ท  ๋Œ€๋น„ **2.5๋ฐฐ**
- โฐ ๋ณ€ํ™”์˜ ์†๋„: 10๋…„ โ†’ **2๋…„** (ํŒฌ๋ฐ๋ฏน ๊ฐ€์†ํ™”)
- โšก ํ•ต์‹ฌ ์งˆ๋ฌธ: "์šฐ๋ฆฌ๋Š” ๋ณ€ํ™”๋ฅผ ์ฃผ๋„ํ•˜๊ณ  ์žˆ๋Š”๊ฐ€?"
**๋ฐœํ‘œ์ž ๋…ธํŠธ**:
"์ž ์‹œ ์ƒ๊ฐํ•ด๋ณด์‹ญ์‹œ์˜ค. ๊ท€์‚ฌ๋Š” ์ด ๊ฑฐ๋Œ€ํ•œ ๋ณ€ํ™”์˜ ๋ฌผ๊ฒฐ์„ ํƒ€๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ, ์•„๋‹ˆ๋ฉด ๋’ค์ฒ˜์ ธ ์žˆ์Šต๋‹ˆ๊นŒ?"
**์‹œ๊ฐ ์ž๋ฃŒ**:
- ์ƒ๋‹จ: ๋„๋ฐœ์  ์งˆ๋ฌธ ํฌ๊ฒŒ ํ‘œ์‹œ
- ์ค‘์•™: ๋ฆฌ๋” vs ํ›„๋ฐœ์ฃผ์ž ๋Œ€๋น„ ์ธํฌ๊ทธ๋ž˜ํ”ฝ
- ์ „ํ™˜: ์ฒญ์ค‘ ์ฐธ์—ฌ๋ฅผ ์œ„ํ•œ 3์ดˆ pause
---
[์Šฌ๋ผ์ด๋“œ 4] ์‚ฐ์—…๋ณ„ ๋””์ง€ํ„ธํ™” ํ˜„ํ™ฉ (1๋ถ„ 30์ดˆ)
**์ œ๋ชฉ**: ๋‹น์‹ ์˜ ์‚ฐ์—…์€ ์–ด๋””์— ์œ„์น˜ํ•ฉ๋‹ˆ๊นŒ?
**ํ•ต์‹ฌ ๋‚ด์šฉ**:
- ๐Ÿฆ ๊ธˆ์œต: 78% - "๋””์ง€ํ„ธ์ด ์ƒˆ๋กœ์šด ํ‘œ์ค€"
- ๐Ÿฅ ํ—ฌ์Šค์ผ€์–ด: 61% - "์›๊ฒฉ์˜๋ฃŒ๊ฐ€ ์ผ์ƒ์œผ๋กœ"
- ๐Ÿญ ์ œ์กฐ: 52% - "์Šค๋งˆํŠธ ํŒฉํ† ๋ฆฌ๋กœ ์ง„ํ™”"
- ๐Ÿ›๏ธ ์†Œ๋งค: 45% - "์˜ด๋‹ˆ์ฑ„๋„์ด ์ƒ์กด ์กฐ๊ฑด"
**๋ฐœํ‘œ์ž ๋…ธํŠธ**:
"๊ธˆ์œต์—…์€ ์ด๋ฏธ 78%๊ฐ€ ๋””์ง€ํ„ธํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ท€์‚ฌ์˜ ์‚ฐ์—…์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ? ์ด์ œ ๊ฐ ์‚ฐ์—…์˜ ์„ฑ๊ณต ๋น„๊ฒฐ์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค."
**์‹œ๊ฐ ์ž๋ฃŒ**:
- ๋ ˆ์ด์Šค ํŠธ๋ž™ ํ˜•ํƒœ์˜ ์ง„ํ–‰๋„ ํ‘œ์‹œ
- ๊ฐ ์‚ฐ์—…๋ณ„ ๋Œ€ํ‘œ ์•„์ด์ฝ˜๊ณผ ์ง„ํ–‰๋ฅ 
- ์—…์ข…๋ณ„ ๋Œ€ํ‘œ ๊ธฐ์—… ๋กœ๊ณ  ์ž‘๊ฒŒ ํ‘œ์‹œ
---
[์Šฌ๋ผ์ด๋“œ 5] 5๋Œ€ ์žฅ๋ฒฝ๊ณผ ํ•ด๊ฒฐ์ฑ… (2๋ถ„)
**์ œ๋ชฉ**: ์žฅ๋ฒฝ์„ ๊ธฐํšŒ๋กœ: 5๋Œ€ ๋„์ „๊ณผ์ œ์™€ Quick Win
**ํ•ต์‹ฌ ๋‚ด์šฉ**:
- ๐Ÿ”ง ๋ ˆ๊ฑฐ์‹œ ์‹œ์Šคํ…œ (67%) โ†’ "๋‹จ๊ณ„์  ํ˜„๋Œ€ํ™” ์ „๋žต"
- ๐Ÿšซ ๋ณ€ํ™” ์ €ํ•ญ (54%) โ†’ "์ž‘์€ ์„ฑ๊ณต ์Šคํ† ๋ฆฌ ํ™•์‚ฐ"
- ๐Ÿ‘ฅ ์ธ์žฌ ๋ถ€์กฑ (48%) โ†’ "์—…์Šคํ‚ฌ๋ง + ์™ธ๋ถ€ ํŒŒํŠธ๋„ˆ์‹ญ"
- ๐Ÿ’ฐ ๋ถˆ๋ช…ํ™•ํ•œ ROI (41%) โ†’ "ํŒŒ์ผ๋Ÿฟ ํ”„๋กœ์ ํŠธ๋กœ ์ฆ๋ช…"
- ๐Ÿ”’ ๋ณด์•ˆ ์šฐ๋ ค (38%) โ†’ "Security by Design ์›์น™"
**๋ฐœํ‘œ์ž ๋…ธํŠธ**:
"๋ชจ๋“  ์žฅ๋ฒฝ์—๋Š” ํ•ด๊ฒฐ์ฑ…์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ค‘์š”ํ•œ ๊ฒƒ์€ ์–ด๋””์„œ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•  ๊ฒƒ์ธ๊ฐ€์ž…๋‹ˆ๋‹ค."
**์ „ํ™˜ ๋ฉ˜ํŠธ**: "๊ทธ๋ ‡๋‹ค๋ฉด ์ด๋Ÿฌํ•œ ์žฅ๋ฒฝ์„ ๋„˜์–ด ์„ฑ๊ณตํ•˜๊ธฐ ์œ„ํ•œ ๊ฒ€์ฆ๋œ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์†Œ๊ฐœํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค."
**์‹œ๊ฐ ์ž๋ฃŒ**:
- ์žฅ๋ฒฝโ†’ํ•ด๊ฒฐ์ฑ… ํ™”์‚ดํ‘œ ์• ๋‹ˆ๋ฉ”์ด์…˜
- ๊ฐ ํ•ญ๋ชฉ๋ณ„ ์ˆœ์ฐจ์  ๋“ฑ์žฅ ํšจ๊ณผ
---
[์Šฌ๋ผ์ด๋“œ 6] DX ์„ฑ๊ณต ํ”„๋ ˆ์ž„์›Œํฌ (2๋ถ„)
**์ œ๋ชฉ**: ๊ฒ€์ฆ๋œ ๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜ ํ”„๋ ˆ์ž„์›Œํฌ
**ํ•ต์‹ฌ ๋‚ด์šฉ**:
๐ŸŽฏ **์ „๋žต**: ๋น„์ฆˆ๋‹ˆ์Šค ๋ชฉํ‘œ์™€ 100% ์ •๋ ฌ
๐Ÿ”ง **๊ธฐ์ˆ **: AI(87%) + ํด๋ผ์šฐ๋“œ(94%) + ๋ฐ์ดํ„ฐ
๐Ÿ‘ฅ **๋ฌธํ™”**: ํ˜์‹  DNA ์ฃผ์ž…
โš™๏ธ **ํ”„๋กœ์„ธ์Šค**: ์• ์ž์ผ + DevOps
๐Ÿ“Š **๊ฑฐ๋ฒ„๋„Œ์Šค**: ์ธก์ • ๊ฐ€๋Šฅํ•œ KPI
**๋ฐœํ‘œ์ž ๋…ธํŠธ**:
"์ด 5๊ฐ€์ง€ ์š”์†Œ๋Š” ํ†ฑ๋‹ˆ๋ฐ”ํ€ด์ฒ˜๋Ÿผ ๋งž๋ฌผ๋ ค ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜๋‚˜๋ผ๋„ ๋น ์ง€๋ฉด ์ „์ฒด๊ฐ€ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."
**์‹œ๊ฐ ์ž๋ฃŒ**:
- ์ค‘์•™: 5๊ฐํ˜• ๋‹ค์ด์–ด๊ทธ๋žจ
- ๊ฐ ์š”์†Œ ๊ฐ„ ์—ฐ๊ฒฐ์„ ์œผ๋กœ ์ƒํ˜ธ์˜์กด์„ฑ ํ‘œํ˜„
- ํ˜ธ๋ฒ„ ํšจ๊ณผ๋กœ ๊ฐ ์š”์†Œ ์ƒ์„ธ ์„ค๋ช…
---
[์Šฌ๋ผ์ด๋“œ 7] 2024 ๊ธฐ์ˆ  ์Šคํƒ (1๋ถ„ 30์ดˆ)
**์ œ๋ชฉ**: ๋ฏธ๋ž˜๋ฅผ ๋งŒ๋“œ๋Š” ๊ธฐ์ˆ  ์Šคํƒ
**ํ•ต์‹ฌ ๋‚ด์šฉ**:
**Layer 1 - ์ธํ”„๋ผ**
- โ˜๏ธ ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ํด๋ผ์šฐ๋“œ (94% ์ฑ„ํƒ)
**Layer 2 - ๋ฐ์ดํ„ฐ**
- ๐Ÿ“Š ์‹ค์‹œ๊ฐ„ ๋ถ„์„ ํ”Œ๋žซํผ
**Layer 3 - ์ธํ…”๋ฆฌ์ „์Šค**
- ๐Ÿค– AI/ML (87% ํ†ตํ•ฉ)
**Layer 4 - ๊ฒฝํ—˜**
- ๐Ÿ“ฑ ์˜ด๋‹ˆ์ฑ„๋„ ์ธํ„ฐํŽ˜์ด์Šค
**Layer 5 - ๋ณด์•ˆ**
- ๐Ÿ” Zero Trust ์•„ํ‚คํ…์ฒ˜
**๋ฐœํ‘œ์ž ๋…ธํŠธ**:
"๊ฐ ๋ ˆ์ด์–ด๋Š” ๊ทธ ์œ„์˜ ๋ ˆ์ด์–ด๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ํƒ„ํƒ„ํ•œ ๊ธฐ์ดˆ ์—†์ด๋Š” ํ˜์‹ ๋„ ์—†์Šต๋‹ˆ๋‹ค."
**์‹œ๊ฐ ์ž๋ฃŒ**:
- ํ”ผ๋ผ๋ฏธ๋“œ ํ˜•ํƒœ์˜ ๊ธฐ์ˆ  ์Šคํƒ
- ๊ฐ ๋ ˆ์ด์–ด๋ณ„ ๋Œ€ํ‘œ ๊ธฐ์ˆ  ๋กœ๊ณ 
---
[์Šฌ๋ผ์ด๋“œ 8] ์ธ๊ฐ„ ์ค‘์‹ฌ ๋ณ€ํ™” ๊ด€๋ฆฌ (1๋ถ„ 30์ดˆ)
**์ œ๋ชฉ**: ๊ธฐ์ˆ ์ด ์•„๋‹Œ ์‚ฌ๋žŒ์ด ๋งŒ๋“œ๋Š” ๋ณ€ํ™”
**ํ•ต์‹ฌ ๋‚ด์šฉ**:
> "๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜์€ ๊ธฐ์ˆ ์ด ์•„๋‹Œ ์‚ฌ๋žŒ๊ณผ ๋ฌธํ™”์˜ ๋ณ€ํ™”๋‹ค"
> - Satya Nadella, Microsoft CEO
- ๐ŸŽฏ **๋น„์ „**: ๋ช…ํ™•ํ•˜๊ณ  ๊ณ ๋ฌด์ ์ธ ๋ฏธ๋ž˜์ƒ
- ๐Ÿ“š **๊ต์œก**: ์ง€์†์  ํ•™์Šต ๋ฌธํ™”
- ๐Ÿ† **์„ฑ๊ณผ**: ์ž‘์€ ์Šน๋ฆฌ์˜ ์ถ•์ 
- ๐Ÿ’ฌ **์†Œํ†ต**: ์–‘๋ฐฉํ–ฅ ํ”ผ๋“œ๋ฐฑ ๋ฃจํ”„
- ๐ŸŽ **๋ณด์ƒ**: ํ˜์‹ ์— ๋Œ€ํ•œ ์ธ์„ผํ‹ฐ๋ธŒ
**๋ฐœํ‘œ์ž ๋…ธํŠธ**:
"๊ฐ€์žฅ ์•ž์„  ๊ธฐ์ˆ ๋„ ์‚ฌ๋žŒ์ด ๋ฐ›์•„๋“ค์ด์ง€ ์•Š์œผ๋ฉด ๋ฌด์šฉ์ง€๋ฌผ์ž…๋‹ˆ๋‹ค."
**์ „ํ™˜ ๋ฉ˜ํŠธ**: "์ด์ œ ์ด๋Ÿฌํ•œ ์›์น™๋“ค์ด ์‹ค์ œ๋กœ ์–ด๋–ค ์„ฑ๊ณผ๋ฅผ ๋งŒ๋“ค์–ด๋ƒˆ๋Š”์ง€ ๋ณด์‹œ๊ฒ ์Šต๋‹ˆ๋‹ค."
**์‹œ๊ฐ ์ž๋ฃŒ**: ์‚ฌ๋žŒ ์ค‘์‹ฌ์˜ ์›ํ˜• ๋‹ค์ด์–ด๊ทธ๋žจ
---
[์Šฌ๋ผ์ด๋“œ 9] ๊ธ€๋กœ๋ฒŒ ์„ฑ๊ณต ์Šคํ† ๋ฆฌ (2๋ถ„)
**์ œ๋ชฉ**: ์ˆซ์ž๋กœ ์ฆ๋ช…๋œ DX ์„ฑ๊ณต ์‚ฌ๋ก€
**ํ•ต์‹ฌ ๋‚ด์šฉ**:
๐Ÿ“ฆ **Amazon**: ๋ฌผ๋ฅ˜ ์ž๋™ํ™” โ†’ ๋ฐฐ์†ก ์‹œ๊ฐ„ **40% ๋‹จ์ถ•**
โ˜• **Starbucks**: ๋ชจ๋ฐ”์ผ ์ฃผ๋ฌธ โ†’ ๋งค์ถœ์˜ **26% ์ฐจ์ง€**
๐ŸŽฌ **Netflix**: AI ์ถ”์ฒœ โ†’ ์‹œ์ฒญ ์‹œ๊ฐ„ **80% ์ฆ๊ฐ€**
๐Ÿš— **Tesla**: OTA ์—…๋ฐ์ดํŠธ โ†’ ์„œ๋น„์Šค ๋น„์šฉ **70% ์ ˆ๊ฐ**
**๊ณตํ†ต ์„ฑ๊ณต ์š”์ธ**:
- ๊ณ ๊ฐ ๊ฒฝํ—˜ ์ตœ์šฐ์„ 
- ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ์˜์‚ฌ๊ฒฐ์ •
- ์ง€์†์  ํ˜์‹  ๋ฌธํ™”
**๋ฐœํ‘œ์ž ๋…ธํŠธ**:
"์ด๋“ค์˜ ๊ณตํ†ต์ ์€ ๋ฌด์—‡์ผ๊นŒ์š”? ๊ธฐ์ˆ ์„ ๋ชฉ์ ์ด ์•„๋‹Œ ์ˆ˜๋‹จ์œผ๋กœ ํ™œ์šฉํ–ˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค."
**์‹œ๊ฐ ์ž๋ฃŒ**:
- ๊ฐ ๊ธฐ์—…๋ณ„ Before/After ๋Œ€๋น„
- ํ•ต์‹ฌ ์ง€ํ‘œ ํฌ๊ฒŒ ๊ฐ•์กฐ
---
[์Šฌ๋ผ์ด๋“œ 10] ROI์˜ ์ง„์‹ค (2๋ถ„)
**์ œ๋ชฉ**: ํˆฌ์ž ๋Œ€๋น„ ์ˆ˜์ต, ์ˆจ๊ฒจ์ง„ ๊ฐ€์น˜๊นŒ์ง€
**์ •๋Ÿ‰์  ํšจ๊ณผ**:
- ๐Ÿ’ฐ ์šด์˜ ํšจ์œจ์„ฑ: **30-40% ๊ฐœ์„ **
- ๐Ÿ˜Š ๊ณ ๊ฐ ๋งŒ์กฑ๋„: NPS **25ํฌ์ธํŠธ ์ƒ์Šน**
- ๐Ÿš€ ์‹ ๊ทœ ์ˆ˜์ต: ์ „์ฒด ๋งค์ถœ์˜ **15% ์ฐฝ์ถœ**
- โฑ๏ธ ํˆฌ์ž ํšŒ์ˆ˜: ํ‰๊ท  **2.5๋…„**
**์ •์„ฑ์  ํšจ๊ณผ**:
- ์ง์› ๋งŒ์กฑ๋„ ํ–ฅ์ƒ
- ๋ธŒ๋žœ๋“œ ์ด๋ฏธ์ง€ ์ œ๊ณ 
- ๋ฏธ๋ž˜ ๋Œ€์‘๋ ฅ ํ™•๋ณด
**๋ฐœํ‘œ์ž ๋…ธํŠธ**:
"ROI๋Š” ๋‹จ์ˆœํžˆ ์ˆซ์ž๋กœ๋งŒ ์ธก์ •๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์กฐ์ง์˜ ๋ฏธ๋ž˜ ๊ฒฝ์Ÿ๋ ฅ์ด ์ง„์ •ํ•œ ๊ฐ€์น˜์ž…๋‹ˆ๋‹ค."
**์‹œ๊ฐ ์ž๋ฃŒ**:
- ROI ๊ณ„์‚ฐ๊ธฐ ์ธํ„ฐํŽ˜์ด์Šค
- ์ •๋Ÿ‰/์ •์„ฑ ํšจ๊ณผ ๊ท ํ˜• ํ‘œ์‹œ
---
[์Šฌ๋ผ์ด๋“œ 11] ์‹คํ–‰์„ ์œ„ํ•œ 5๊ฐ€์ง€ ์ œ์–ธ (2๋ถ„)
**์ œ๋ชฉ**: ๋‚ด์ผ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” 5๊ฐ€์ง€ ํ–‰๋™
**ํ•ต์‹ฌ ์ œ์–ธ**:
1. ๐ŸŒฑ **Start Small, Think Big**
- "ํŒŒ์ผ๋Ÿฟ ํ”„๋กœ์ ํŠธ๋กœ ์‹œ์ž‘ํ•˜๋˜ ์ „์‚ฌ ํ™•์‚ฐ ๊ณ„ํš ์ˆ˜๋ฆฝ"
2. ๐Ÿ‘ฅ **Culture First, Technology Second**
- "์ตœ๊ณ ์˜ ๊ธฐ์ˆ ๋„ ๋ฌธํ™”๊ฐ€ ๋’ท๋ฐ›์นจ๋˜์ง€ ์•Š์œผ๋ฉด ์‹คํŒจ"
3. ๐Ÿ“Š **Data-Driven Everything**
- "์ถ”์ธก์ด ์•„๋‹Œ ๋ฐ์ดํ„ฐ๋กœ ์˜์‚ฌ๊ฒฐ์ •"
4. ๐Ÿ”„ **Fail Fast, Learn Faster**
- "์‹คํŒจ๋ฅผ ๋‘๋ ค์›Œํ•˜์ง€ ๋ง๊ณ  ๋น ๋ฅด๊ฒŒ ํ”ผ๋ฒ—"
5. ๐ŸŽฏ **Customer at the Center**
- "๋ชจ๋“  ๋ณ€ํ™”์˜ ์ค‘์‹ฌ์— ๊ณ ๊ฐ ๊ฐ€์น˜"
**๋ฐœํ‘œ์ž ๋…ธํŠธ**:
"์ด ์ค‘ ๋‹จ ํ•˜๋‚˜๋งŒ์ด๋ผ๋„ ๋‚ด์ผ๋ถ€ํ„ฐ ์‹ค์ฒœํ•œ๋‹ค๋ฉด, 1๋…„ ํ›„ ์กฐ์ง์€ ์™„์ „ํžˆ ๋‹ฌ๋ผ์ ธ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค."
**์‹œ๊ฐ ์ž๋ฃŒ**: ์ฒดํฌ๋ฐ•์Šค ์Šคํƒ€์ผ, ํ•˜๋‚˜์”ฉ ์ฒดํฌ๋˜๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜
---
[์Šฌ๋ผ์ด๋“œ 12] ๋งˆ๋ฌด๋ฆฌ ๋ฐ Q&A (1๋ถ„)
**์ œ๋ชฉ**: ํ•จ๊ป˜ ๋งŒ๋“œ๋Š” ๋””์ง€ํ„ธ ๋ฏธ๋ž˜
**ํ•ต์‹ฌ ๋‚ด์šฉ**:
๐Ÿ™ **๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค**
๐Ÿ’ญ **์ƒ๊ฐํ•ด๋ณผ ์งˆ๋ฌธ๋“ค**:
- ์šฐ๋ฆฌ ์กฐ์ง์˜ ๋””์ง€ํ„ธ ์„ฑ์ˆ™๋„๋Š”?
- ๊ฐ€์žฅ ๋จผ์ € ํ•ด๊ฒฐํ•ด์•ผ ํ•  ๊ณผ์ œ๋Š”?
- ์ฒซ ๋ฒˆ์งธ ํŒŒ์ผ๋Ÿฟ ํ”„๋กœ์ ํŠธ๋Š”?
๐Ÿ“ง ์—ฐ๋ฝ์ฒ˜: [์ด๋ฉ”์ผ]
๐Ÿ“ฑ LinkedIn: [ํ”„๋กœํ•„]
๐Ÿ”— ์ถ”๊ฐ€ ์ž๋ฃŒ: [QR ์ฝ”๋“œ]
**๋ฐœํ‘œ์ž ๋…ธํŠธ**:
"๋””์ง€ํ„ธ ํŠธ๋žœ์Šคํฌ๋ฉ”์ด์…˜์€ ๋ชฉ์ ์ง€๊ฐ€ ์•„๋‹Œ ์—ฌ์ •์ž…๋‹ˆ๋‹ค. ๊ทธ ์—ฌ์ •์„ ํ•จ๊ป˜ํ•˜๊ฒŒ ๋˜์–ด ์˜๊ด‘์ž…๋‹ˆ๋‹ค."
**์‹œ๊ฐ ์ž๋ฃŒ**: QR ์ฝ”๋“œ์™€ ์—ฐ๋ฝ์ฒ˜ ์ •๋ณด
---
## ๐Ÿ“Ž ๋ฐฑ์—… ์Šฌ๋ผ์ด๋“œ (ํ•„์š”์‹œ ํ™œ์šฉ)
[๋ฐฑ์—… 1] ์‚ฐ์—…๋ณ„ ์ƒ์„ธ ์ „๋žต
[๋ฐฑ์—… 2] ๊ธฐ์ˆ  ์Šคํƒ ์ƒ์„ธ ์ŠคํŽ™
[๋ฐฑ์—… 3] ์ถ”๊ฐ€ ์„ฑ๊ณต ์‚ฌ๋ก€ (๊ตญ๋‚ด ๊ธฐ์—…)
[๋ฐฑ์—… 4] ROI ๊ณ„์‚ฐ ๋ฐฉ๋ฒ•๋ก 
[๋ฐฑ์—… 5] ๋‹จ๊ณ„๋ณ„ ์‹คํ–‰ ๋กœ๋“œ๋งต
## ๐ŸŽค ์˜ˆ์ƒ Q&A ๋ฐ ๋‹ต๋ณ€ ์ค€๋น„
**Q1: ์šฐ๋ฆฌ ๊ฐ™์€ ์ค‘๊ฒฌ๊ธฐ์—…๋„ ๊ฐ€๋Šฅํ•œ๊ฐ€์š”?**
A: ์˜คํžˆ๋ ค ์ค‘๊ฒฌ๊ธฐ์—…์ด ๋” ๋น ๋ฅด๊ฒŒ ๋ณ€ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž‘์€ ํŒŒ์ผ๋Ÿฟ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜์„ธ์š”.
**Q2: ์˜ˆ์‚ฐ์ด ์ œํ•œ์ ์ธ๋ฐ ์–ด๋–ป๊ฒŒ ์‹œ์ž‘ํ•˜๋‚˜์š”?**
A: ํด๋ผ์šฐ๋“œ ๊ธฐ๋ฐ˜ SaaS๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ดˆ๊ธฐ ํˆฌ์ž๋ฅผ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
**Q3: ์ง์›๋“ค์˜ ์ €ํ•ญ์ด ์‹ฌํ•œ๋ฐ ์–ด๋–ป๊ฒŒ ๊ทน๋ณตํ•˜๋‚˜์š”?**
A: ๋ณ€ํ™”์˜ ์ˆ˜ํ˜œ์ž๋ฅผ ๋จผ์ € ๋งŒ๋“ค๊ณ , ๊ทธ๋“ค์ด ์ „๋„์‚ฌ๊ฐ€ ๋˜๊ฒŒ ํ•˜์„ธ์š”.
---
*์ด PPT๋Š” 20๋ถ„ ๋ฐœํ‘œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์œผ๋ฉฐ, ์ฒญ์ค‘๊ณผ์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ๊ทน๋Œ€ํ™”ํ•˜๋„๋ก ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.*"""
}
# ํ”„๋กฌํ”„ํŠธ ๋‚ด์šฉ์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ์‘๋‹ต ์„ ํƒ
if role == "supervisor" and "์กฐ์‚ฌ์ž AI๊ฐ€ ์ •๋ฆฌํ•œ" in messages[0]["content"]:
response = test_responses["supervisor_execution"]
elif role == "supervisor" and messages[0]["content"].find("์‹คํ–‰์ž AI์˜ PPT ์ดˆ์•ˆ") > -1:
response = test_responses["supervisor_review"]
elif role == "supervisor":
response = test_responses["supervisor_initial"]
elif role == "researcher":
response = test_responses["researcher"]
elif role == "executor" and "์ตœ์ข… PPT" in messages[0]["content"]:
response = test_responses["executor_final"]
else:
response = test_responses["executor"]
yield from self.simulate_streaming(response, role)
return
# ์‹ค์ œ API ํ˜ธ์ถœ
try:
system_prompts = {
"supervisor": "๋‹น์‹ ์€ ์ „๋ฌธ์ ์ธ PPT ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•˜๊ณ  ์ง€๋„ํ•˜๋Š” ๊ฐ๋…์ž AI์ž…๋‹ˆ๋‹ค.",
"researcher": "๋‹น์‹ ์€ PPT ์ฝ˜ํ…์ธ ๋ฅผ ์œ„ํ•œ ์ •๋ณด๋ฅผ ์กฐ์‚ฌํ•˜๊ณ  ์ •๋ฆฌํ•˜๋Š” ์กฐ์‚ฌ์ž AI์ž…๋‹ˆ๋‹ค.",
"executor": "๋‹น์‹ ์€ ์‹ค์ œ PPT ์Šฌ๋ผ์ด๋“œ ๋‚ด์šฉ์„ ์ž‘์„ฑํ•˜๋Š” ์‹คํ–‰์ž AI์ž…๋‹ˆ๋‹ค."
}
full_messages = [
{"role": "system", "content": system_prompts.get(role, "")},
*messages
]
payload = {
"model": self.model_id,
"messages": full_messages,
"max_tokens": 4096,
"temperature": 0.7,
"top_p": 0.8,
"stream": True,
"stream_options": {"include_usage": True}
}
logger.info(f"API ์ŠคํŠธ๋ฆฌ๋ฐ ํ˜ธ์ถœ ์‹œ์ž‘ - Role: {role}")
response = requests.post(
self.api_url,
headers=self.create_headers(),
json=payload,
stream=True,
timeout=10
)
if response.status_code != 200:
logger.error(f"API ์˜ค๋ฅ˜: {response.status_code}")
yield f"โŒ API ์˜ค๋ฅ˜ ({response.status_code}): {response.text[:200]}"
return
for line in response.iter_lines():
if line:
line = line.decode('utf-8')
if line.startswith("data: "):
data = line[6:]
if data == "[DONE]":
break
try:
chunk = json.loads(data)
if "choices" in chunk and chunk["choices"]:
content = chunk["choices"][0].get("delta", {}).get("content", "")
if content:
yield content
except json.JSONDecodeError:
continue
except requests.exceptions.Timeout:
yield "โฑ๏ธ API ํ˜ธ์ถœ ์‹œ๊ฐ„์ด ์ดˆ๊ณผ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”."
except requests.exceptions.ConnectionError:
yield "๐Ÿ”Œ API ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”."
except Exception as e:
logger.error(f"์ŠคํŠธ๋ฆฌ๋ฐ ์ค‘ ์˜ค๋ฅ˜: {str(e)}")
yield f"โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
# ์‹œ์Šคํ…œ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
ppt_system = PPTCreationSystem()
# ===== ๋ฉ”์ธ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜ =====
def process_ppt_streaming(ppt_topic: str, template_name: str, audience_type: str, language: str,
custom_slides: List[Dict], slide_count: int, seed: int, uploaded_file,
use_web_search: bool, theme_name: str = "Minimal Light",
progress=gr.Progress()):
"""ํ†ตํ•ฉ๋œ PPT ์ƒ์„ฑ ์ฒ˜๋ฆฌ - 3์ž ํ˜‘์˜ ์‹œ์Šคํ…œ ์‚ฌ์šฉ"""
# Update global variables
global current_topic, uploaded_content, current_language
current_topic = ppt_topic
current_language = language
# Read uploaded file content
uploaded_content = ""
if uploaded_file is not None:
try:
uploaded_content = read_uploaded_file(uploaded_file.name)
print(f"[File Upload] Content length: {len(uploaded_content)} characters")
except Exception as e:
print(f"[File Upload] Error: {str(e)}")
uploaded_content = ""
# Template selection and slide configuration
try:
if template_name == "Custom" and custom_slides:
slides = [{"title": "Cover", "style": "Title Slide (Hero)", "prompt_hint": "Presentation cover"}]
slides.extend(custom_slides)
slides.append({"title": "Thank You", "style": "Thank You Slide", "prompt_hint": "Presentation closing and key message"})
else:
template = PPT_TEMPLATES.get(template_name)
if not template:
yield "", "", "", None, f"โŒ Template '{template_name}' not found."
return
slides = generate_dynamic_slides(ppt_topic, template, slide_count)
except Exception as e:
print(f"[Slide Configuration] Error: {str(e)}")
yield "", "", "", None, f"โŒ Error configuring slides: {str(e)}"
return
if not slides:
yield "", "", "", None, "No slides defined."
return
total_slides = len(slides)
print(f"\n[PPT Generation] Starting - Total {total_slides} slides (Cover + {slide_count} content + Thank You)")
print(f"[PPT Generation] Topic: {ppt_topic}")
print(f"[PPT Generation] Template: {template_name}")
print(f"[PPT Generation] Audience: {audience_type}")
print(f"[PPT Generation] Language: {language}")
print(f"[PPT Generation] Design Theme: {theme_name}")
print(f"[PPT Generation] Web Search: {'Enabled' if use_web_search else 'Disabled'}")
conversation_log = []
all_responses = {"supervisor": [], "researcher": [], "executor": []}
try:
# 1๋‹จ๊ณ„: ๊ฐ๋…์ž AI ์ดˆ๊ธฐ ๋ถ„์„ ๋ฐ ํ‚ค์›Œ๋“œ ์ถ”์ถœ
progress(0.1, "๐Ÿง  ๊ฐ๋…์ž AI๊ฐ€ PPT ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ ์ค‘...")
supervisor_prompt = ppt_system.create_supervisor_initial_prompt(ppt_topic)
supervisor_initial_response = ""
supervisor_text = "[PPT ๊ตฌ์กฐ ์„ค๊ณ„] ๐Ÿ”„ ์ƒ์„ฑ ์ค‘...\n"
for chunk in ppt_system.call_llm_streaming(
[{"role": "user", "content": supervisor_prompt}],
"supervisor"
):
supervisor_initial_response += chunk
supervisor_text = f"[PPT ๊ตฌ์กฐ ์„ค๊ณ„] - {datetime.now().strftime('%H:%M:%S')}\n{supervisor_initial_response}"
yield supervisor_text, "", "", None, "๐Ÿ”„ ๊ฐ๋…์ž AI๊ฐ€ PPT ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ ์ค‘..."
all_responses["supervisor"].append(supervisor_initial_response)
# 2๋‹จ๊ณ„: ์›น ๊ฒ€์ƒ‰ ์ˆ˜ํ–‰ (์„ ํƒ๋œ ๊ฒฝ์šฐ)
search_results = {}
if use_web_search and BRAVE_API_TOKEN:
progress(0.2, "๐Ÿ” PPT ์ฝ˜ํ…์ธ  ๊ฒ€์ƒ‰ ์ค‘...")
keywords = ppt_system.extract_keywords(supervisor_initial_response)
logger.info(f"์ถ”์ถœ๋œ ํ‚ค์›Œ๋“œ: {keywords}")
researcher_text = "[์ฝ˜ํ…์ธ  ๊ฒ€์ƒ‰] ๐Ÿ” ๊ฒ€์ƒ‰ ์ค‘...\n"
yield supervisor_text, researcher_text, "", None, "๐Ÿ” PPT ์ฝ˜ํ…์ธ  ๊ฒ€์ƒ‰ ์ค‘..."
total_search_count = 0
# ์›๋ž˜ ํ‚ค์›Œ๋“œ๋กœ ๊ฒ€์ƒ‰
for keyword in keywords:
try:
results = ppt_system.brave_search(keyword)
if results:
search_results[keyword] = results
total_search_count += len(results)
researcher_text += f"โœ“ '{keyword}' ๊ฒ€์ƒ‰ ์™„๋ฃŒ ({len(results)}๊ฐœ ๊ฒฐ๊ณผ)\n"
yield supervisor_text, researcher_text, "", None, f"๐Ÿ” '{keyword}' ๊ฒ€์ƒ‰ ์ค‘..."
except Exception as e:
print(f"[Search] Error searching for '{keyword}': {str(e)}")
researcher_text += f"\n๐Ÿ“Š ์ด {total_search_count}๊ฐœ์˜ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์ˆ˜์ง‘ ์™„๋ฃŒ\n"
# 3๋‹จ๊ณ„: ์กฐ์‚ฌ์ž AI๊ฐ€ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์ •๋ฆฌ
progress(0.3, "๐Ÿ“ ์กฐ์‚ฌ์ž AI๊ฐ€ ์ฝ˜ํ…์ธ  ์ •๋ฆฌ ์ค‘...")
researcher_prompt = ppt_system.create_researcher_prompt(ppt_topic, supervisor_initial_response, search_results)
researcher_response = ""
researcher_text = "[PPT ์ฝ˜ํ…์ธ  ์ •๋ฆฌ] ๐Ÿ”„ ์ƒ์„ฑ ์ค‘...\n"
for chunk in ppt_system.call_llm_streaming(
[{"role": "user", "content": researcher_prompt}],
"researcher"
):
researcher_response += chunk
researcher_text = f"[PPT ์ฝ˜ํ…์ธ  ์ •๋ฆฌ] - {datetime.now().strftime('%H:%M:%S')}\n{researcher_response}"
yield supervisor_text, researcher_text, "", None, "๐Ÿ“ ์กฐ์‚ฌ์ž AI๊ฐ€ ์ฝ˜ํ…์ธ  ์ •๋ฆฌ ์ค‘..."
all_responses["researcher"].append(researcher_response)
# 4๋‹จ๊ณ„: ๊ฐ๋…์ž AI๊ฐ€ ์กฐ์‚ฌ ๋‚ด์šฉ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹คํ–‰ ์ง€์‹œ
progress(0.4, "๐ŸŽฏ ๊ฐ๋…์ž AI๊ฐ€ ์ œ์ž‘ ์ง€์‹œ ์ค‘...")
supervisor_execution_prompt = ppt_system.create_supervisor_execution_prompt(ppt_topic, researcher_response)
supervisor_execution_response = ""
supervisor_text += "\n\n---\n\n[PPT ์ œ์ž‘ ์ง€์‹œ] ๐Ÿ”„ ์ƒ์„ฑ ์ค‘...\n"
for chunk in ppt_system.call_llm_streaming(
[{"role": "user", "content": supervisor_execution_prompt}],
"supervisor"
):
supervisor_execution_response += chunk
temp_text = f"{all_responses['supervisor'][0]}\n\n---\n\n[PPT ์ œ์ž‘ ์ง€์‹œ] - {datetime.now().strftime('%H:%M:%S')}\n{supervisor_execution_response}"
supervisor_text = f"[PPT ๊ตฌ์กฐ ์„ค๊ณ„] - {datetime.now().strftime('%H:%M:%S')}\n{temp_text}"
yield supervisor_text, researcher_text, "", None, "๐ŸŽฏ ๊ฐ๋…์ž AI๊ฐ€ ์ œ์ž‘ ์ง€์‹œ ์ค‘..."
all_responses["supervisor"].append(supervisor_execution_response)
# 5๋‹จ๊ณ„: ์‹คํ–‰์ž AI๊ฐ€ ์กฐ์‚ฌ ๋‚ด์šฉ๊ณผ ์ง€์‹œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ดˆ๊ธฐ PPT ์ž‘์„ฑ
progress(0.5, "๐Ÿ“Š ์‹คํ–‰์ž AI๊ฐ€ PPT ์ž‘์„ฑ ์ค‘...")
executor_prompt = ppt_system.create_executor_prompt(ppt_topic, supervisor_execution_response, researcher_response)
executor_response = ""
executor_text = "[์ดˆ๊ธฐ PPT ์ž‘์„ฑ] ๐Ÿ”„ ์ƒ์„ฑ ์ค‘...\n"
for chunk in ppt_system.call_llm_streaming(
[{"role": "user", "content": executor_prompt}],
"executor"
):
executor_response += chunk
executor_text = f"[์ดˆ๊ธฐ PPT ์ž‘์„ฑ] - {datetime.now().strftime('%H:%M:%S')}\n{executor_response}"
yield supervisor_text, researcher_text, executor_text, None, "๐Ÿ“Š ์‹คํ–‰์ž AI๊ฐ€ PPT ์ž‘์„ฑ ์ค‘..."
all_responses["executor"].append(executor_response)
# 5.5๋‹จ๊ณ„: ์‹คํ–‰์ž AI ์‘๋‹ต ํŒŒ์‹ฑ (๊ฐœ์„ ๋œ ํŒŒ์„œ ์‚ฌ์šฉ)
progress(0.55, "๐Ÿ“ ์‹คํ–‰์ž AI ์‘๋‹ต์„ ์Šฌ๋ผ์ด๋“œ๋กœ ๋ณ€ํ™˜ ์ค‘...")
# ๊ฐœ์„ ๋œ ํŒŒ์„œ ์‚ฌ์šฉ
parsed_slides = parse_executor_response(executor_response, slides, language)
logger.info(f"ํŒŒ์‹ฑ๋œ ์Šฌ๋ผ์ด๋“œ ์ˆ˜: {len(parsed_slides)}")
# ํŒŒ์‹ฑ ์„ฑ๊ณต ์—ฌ๋ถ€ ํ™•์ธ
parsing_success = len(parsed_slides) > 0
# ํŒŒ์‹ฑ ๊ฒฐ๊ณผ ๋กœ๊ทธ
for slide_idx, slide_data in parsed_slides.items():
logger.info(f"[ํŒŒ์‹ฑ ๊ฒฐ๊ณผ] ์Šฌ๋ผ์ด๋“œ {slide_idx + 1}:")
logger.info(f" - ์ œ๋ชฉ: {slide_data.get('title', 'N/A')}")
logger.info(f" - ๋ถ€์ œ๋ชฉ: {slide_data.get('subtitle', 'N/A')}")
logger.info(f" - ๋ถˆ๋ฆฟ ํฌ์ธํŠธ ์ˆ˜: {len(slide_data.get('bullet_points', []))}")
if slide_data.get('bullet_points'):
logger.info(f" - ์ฒซ ๋ฒˆ์งธ ๋ถˆ๋ฆฟ: {slide_data['bullet_points'][0]}")
# ํŒŒ์‹ฑ ์‹คํŒจ ์‹œ ๊ฒฝ๊ณ  ๋ฐ ๋Œ€์ฒด ๋ฐฉ์•ˆ
if not parsing_success:
logger.warning("[ํŒŒ์‹ฑ] ์‹คํ–‰์ž ์‘๋‹ต ํŒŒ์‹ฑ ์‹คํŒจ - ์‹คํ–‰์ž ์‘๋‹ต ์›๋ฌธ์„ ์ง์ ‘ ์‚ฌ์šฉ ์‹œ๋„")
# ์‹คํ–‰์ž ์‘๋‹ต์„ ์ง์ ‘ ๋ณด์—ฌ์ฃผ๊ธฐ (๋””๋ฒ„๊น…์šฉ)
yield supervisor_text, researcher_text, executor_text + "\n\nโš ๏ธ ํŒŒ์‹ฑ ์‹คํŒจ - ๋Œ€์ฒด ๋ฐฉ๋ฒ• ์‚ฌ์šฉ ์ค‘...", None, "โš ๏ธ ์Šฌ๋ผ์ด๋“œ ํŒŒ์‹ฑ ์ค‘ ๋ฌธ์ œ ๋ฐœ๊ฒฌ, ๋Œ€์ฒด ๋ฐฉ๋ฒ• ์‚ฌ์šฉ..."
# 6๋‹จ๊ณ„: ์‹ค์ œ ์Šฌ๋ผ์ด๋“œ ์ƒ์„ฑ (๊ฐœ์„ ๋œ ๋กœ์ง)
progress(0.6, "๐ŸŽจ ์Šฌ๋ผ์ด๋“œ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘...")
results = []
preview_html = """
<style>
.slides-container {
width: 100%;
max-width: 1400px;
margin: 0 auto;
}
</style>
<div class="slides-container">
"""
# Process each slide with improved logic
for i, slide in enumerate(slides):
try:
progress((0.6 + (0.3 * i / total_slides)), f"์Šฌ๋ผ์ด๋“œ {i+1}/{total_slides} ์ฒ˜๋ฆฌ ์ค‘...")
# Translate slide title if Korean
display_title = slide.get('title', '')
if language == "Korean" and slide.get('title', '') in SLIDE_TITLE_TRANSLATIONS:
display_title = SLIDE_TITLE_TRANSLATIONS[slide.get('title', '')]
slide_info = f"Slide {i+1}: {display_title}"
slide_context = f"{slide.get('title', '')} - {slide.get('prompt_hint', '')}"
# ํŒŒ์‹ฑ๋œ ๋ฐ์ดํ„ฐ ํ™•์ธ ๋ฐ ์‚ฌ์šฉ
content = None
speaker_notes = ""
if i in parsed_slides and parsing_success:
# ํŒŒ์‹ฑ๋œ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ
parsed_data = parsed_slides[i]
logger.info(f"[์Šฌ๋ผ์ด๋“œ {i+1}] ์‹คํ–‰์ž AI์˜ ํŒŒ์‹ฑ๋œ ์ฝ˜ํ…์ธ  ์‚ฌ์šฉ")
# ๋ถˆ๋ฆฟ ํฌ์ธํŠธ ๊ฒ€์ฆ
valid_bullets = []
for bp in parsed_data.get("bullet_points", []):
# ์˜๋ฏธ์—†๋Š” placeholder ์ œ๊ฑฐ
if bp and not any(skip in bp for skip in ["Point 1", "Point 2", "Point 3", "Point 4", "Point 5", "๐Ÿ“Œ Point"]):
valid_bullets.append(bp)
# ์œ ํšจํ•œ ๋ถˆ๋ฆฟ ํฌ์ธํŠธ๊ฐ€ ์žˆ์œผ๋ฉด ์‚ฌ์šฉ
if len(valid_bullets) >= 3: # ์ตœ์†Œ 3๊ฐœ ์ด์ƒ์ผ ๋•Œ๋งŒ ์‚ฌ์šฉ
content = {
"subtitle": parsed_data.get("subtitle") or display_title,
"bullet_points": valid_bullets[:5] # ์ตœ๋Œ€ 5๊ฐœ
}
speaker_notes = parsed_data.get("speaker_notes", "")
logger.info(f"[์Šฌ๋ผ์ด๋“œ {i+1}] ํŒŒ์‹ฑ๋œ ์ฝ˜ํ…์ธ  ์‚ฌ์šฉ ์™„๋ฃŒ - ๋ถˆ๋ฆฟ {len(valid_bullets)}๊ฐœ")
else:
logger.warning(f"[์Šฌ๋ผ์ด๋“œ {i+1}] ๋ถˆ๋ฆฟ ํฌ์ธํŠธ ๋ถ€์กฑ ({len(valid_bullets)}๊ฐœ) - ์‹คํ–‰์ž ์‘๋‹ต์—์„œ ์žฌ์ถ”์ถœ ์‹œ๋„")
# ์‹คํ–‰์ž ์‘๋‹ต์—์„œ ํ•ด๋‹น ์Šฌ๋ผ์ด๋“œ ๊ด€๋ จ ๋‚ด์šฉ ์žฌ์ถ”์ถœ
slide_keywords = [display_title, slide.get('title', ''), slide.get('prompt_hint', '')]
extracted_content = extract_relevant_content_from_executor(
executor_response, slide_keywords, i+1, slide.get('title', '')
)
if extracted_content and len(extracted_content.get("bullet_points", [])) >= 3:
content = extracted_content
speaker_notes = extracted_content.get("speaker_notes", "")
logger.info(f"[์Šฌ๋ผ์ด๋“œ {i+1}] ์‹คํ–‰์ž ์‘๋‹ต์—์„œ ์žฌ์ถ”์ถœ ์„ฑ๊ณต")
# ํŒŒ์‹ฑ ์‹คํŒจํ•˜๊ฑฐ๋‚˜ ์ฝ˜ํ…์ธ ๊ฐ€ ์—†์œผ๋ฉด ์‹คํ–‰์ž ์‘๋‹ต์—์„œ ์ง์ ‘ ์ถ”์ถœ
if not content:
logger.warning(f"[์Šฌ๋ผ์ด๋“œ {i+1}] ํŒŒ์‹ฑ ์‹คํŒจ - ์‹คํ–‰์ž ์‘๋‹ต์—์„œ ์ง์ ‘ ์ถ”์ถœ ์‹œ๋„")
# ์Šฌ๋ผ์ด๋“œ ๋ฒˆํ˜ธ๋กœ ์‹คํ–‰์ž ์‘๋‹ต์—์„œ ํ•ด๋‹น ๋ถ€๋ถ„ ์ฐพ๊ธฐ
slide_section = extract_slide_section_from_executor(executor_response, i+1)
if slide_section:
content = parse_slide_section(slide_section, display_title)
speaker_notes = extract_speaker_notes_from_section(slide_section)
logger.info(f"[์Šฌ๋ผ์ด๋“œ {i+1}] ์‹คํ–‰์ž ์‘๋‹ต์—์„œ ์ง์ ‘ ์ถ”์ถœ ์„ฑ๊ณต")
# ๊ทธ๋ž˜๋„ ์ฝ˜ํ…์ธ ๊ฐ€ ์—†์œผ๋ฉด ์ตœ์ข… ํด๋ฐฑ
if not content or len(content.get("bullet_points", [])) < 3:
logger.warning(f"[์Šฌ๋ผ์ด๋“œ {i+1}] ๋ชจ๋“  ๋ฐฉ๋ฒ• ์‹คํŒจ - generate_slide_content ํด๋ฐฑ ์‚ฌ์šฉ")
# Thank You slide ํŠน๋ณ„ ์ฒ˜๋ฆฌ
if slide.get('title') == 'Thank You':
conclusion_phrase = generate_conclusion_phrase(
ppt_topic, audience_type, language,
FRIENDLI_TOKEN, API_URL, MODEL_ID, AUDIENCE_TYPES
)
content = {
"subtitle": conclusion_phrase,
"bullet_points": []
}
speaker_notes = generate_closing_notes(
ppt_topic, conclusion_phrase, audience_type, language,
FRIENDLI_TOKEN, API_URL, MODEL_ID, AUDIENCE_TYPES
)
else:
# ์ผ๋ฐ˜ ์Šฌ๋ผ์ด๋“œ ํด๋ฐฑ ์ƒ์„ฑ
content = generate_slide_content(
ppt_topic, slide.get('title', ''), slide_context, audience_type, language,
uploaded_content, list(search_results.values()) if use_web_search else [],
FRIENDLI_TOKEN, API_URL, MODEL_ID, AUDIENCE_TYPES
)
speaker_notes = generate_presentation_notes(
ppt_topic, slide.get('title', ''), content, audience_type, language,
FRIENDLI_TOKEN, API_URL, MODEL_ID, AUDIENCE_TYPES
)
# ๋ฐœํ‘œ์ž ๋…ธํŠธ๊ฐ€ ์—†์œผ๋ฉด ์ƒ์„ฑ
if not speaker_notes:
speaker_notes = generate_presentation_notes(
ppt_topic, slide.get('title', ''), content, audience_type, language,
FRIENDLI_TOKEN, API_URL, MODEL_ID, AUDIENCE_TYPES
)
# Generate prompt and image
style_key = slide.get("style", "Colorful Mind Map")
if style_key in STYLE_TEMPLATES:
style_info = STYLE_TEMPLATES[style_key]
prompt = generate_prompt_with_llm(
ppt_topic, style_info["example"],
slide_context, uploaded_content,
FRIENDLI_TOKEN, API_URL, MODEL_ID
)
# Generate image
slide_seed = seed + i
img, used_prompt = generate_image(
prompt, slide_seed, slide_info,
REPLICATE_API_TOKEN, current_topic, current_language,
FRIENDLI_TOKEN, API_URL, MODEL_ID
)
# Prepare slide data
slide_data = {
"slide_number": i + 1,
"title": display_title,
"subtitle": content["subtitle"],
"bullet_points": content["bullet_points"][:5],
"image": img,
"style": style_info["name"],
"speaker_notes": speaker_notes,
"topic": ppt_topic
}
# ๋””๋ฒ„๊น…: ์‹ค์ œ ์‚ฌ์šฉ๋œ ์ฝ˜ํ…์ธ  ๋กœ๊ทธ
logger.info(f"[์Šฌ๋ผ์ด๋“œ {i+1}] ์ตœ์ข… ์ฝ˜ํ…์ธ :")
logger.info(f" - ๋ถ€์ œ๋ชฉ: {slide_data['subtitle']}")
logger.info(f" - ๋ถˆ๋ฆฟ ํฌ์ธํŠธ: {len(slide_data['bullet_points'])}๊ฐœ")
if slide_data['bullet_points']:
logger.info(f" - ์ฒซ ๋ฒˆ์งธ ๋ถˆ๋ฆฟ: {slide_data['bullet_points'][0]}")
# Generate preview HTML
preview_html += create_slide_preview_html(slide_data)
# Update current state
yield supervisor_text, researcher_text, executor_text, None, f"### ๐Ÿ”„ {slide_info} ์ƒ์„ฑ ์ค‘..."
results.append({
"slide_data": slide_data,
"success": img is not None
})
else:
logger.error(f"[์Šฌ๋ผ์ด๋“œ {i+1}] ์•Œ ์ˆ˜ ์—†๋Š” ์Šคํƒ€์ผ: {style_key}")
except Exception as e:
logger.error(f"[์Šฌ๋ผ์ด๋“œ {i+1}] ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜: {str(e)}")
import traceback
traceback.print_exc()
# ์˜ค๋ฅ˜ ์‹œ ๊ธฐ๋ณธ๊ฐ’
results.append({
"slide_data": {
"slide_number": i + 1,
"title": display_title,
"subtitle": "์ฝ˜ํ…์ธ  ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ",
"bullet_points": ["โ€ข ์˜ค๋ฅ˜๋กœ ์ธํ•ด ์ฝ˜ํ…์ธ ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"],
"image": None,
"style": "Error",
"speaker_notes": "์˜ค๋ฅ˜๋กœ ์ธํ•ด ๋ฐœํ‘œ์ž ๋…ธํŠธ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.",
"topic": ppt_topic
},
"success": False
})
# Create PPTX file
progress(0.95, "Creating PPTX file...")
pptx_path = None
try:
pptx_path = create_pptx_file(results, ppt_topic, template_name, theme_name, DESIGN_THEMES)
except Exception as e:
print(f"[PPTX] File creation error: {str(e)}")
import traceback
traceback.print_exc()
preview_html += "</div>"
progress(1.0, "Complete!")
successful = sum(1 for r in results if r["success"])
final_status = f"### ๐ŸŽ‰ Generation complete! {successful} out of {total_slides} slides succeeded"
# Save global variables
global current_slides_data, current_template, current_theme
current_slides_data = results
current_template = template_name
current_theme = theme_name
if pptx_path:
final_status += f"\n\n### ๐Ÿ“ฅ PPTX file is ready!"
final_status += f"\n\n๐ŸŽค **Speaker notes for {audience_type} included in each slide!**"
if PROCESS_FLOW_AVAILABLE:
final_status += f"\n\n๐Ÿ”ง **Process flow diagrams are automatically generated ({language} support)**"
yield supervisor_text, researcher_text, executor_text, pptx_path, final_status
except Exception as e:
error_msg = f"โŒ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜: {str(e)}"
print(f"[PPT Generation] Critical error: {str(e)}")
import traceback
traceback.print_exc()
yield "", "", "", None, error_msg
def generate_ppt_handler(topic, template_name, audience_type, language, theme_name, slide_count,
seed, file_upload, use_web_search, custom_slide_count,
progress=gr.Progress(), *custom_inputs):
if not topic.strip():
yield "", "", "", None, "โŒ Please enter a topic."
return
# Process custom slides
custom_slides = []
if template_name == "Custom" and custom_inputs:
try:
# Convert custom_inputs to list to avoid tuple slicing issues
custom_inputs_list = list(custom_inputs)
for i in range(0, min(custom_slide_count * 3, len(custom_inputs_list)), 3):
title = custom_inputs_list[i] if i < len(custom_inputs_list) else ""
style = custom_inputs_list[i+1] if i+1 < len(custom_inputs_list) else "Colorful Mind Map"
hint = custom_inputs_list[i+2] if i+2 < len(custom_inputs_list) else ""
if title and style:
custom_slides.append({
"title": title,
"style": style,
"prompt_hint": hint
})
except Exception as e:
print(f"[Custom Slides] Error processing custom inputs: {str(e)}")
yield "", "", "", None, f"โŒ Error processing custom slides: {str(e)}"
return
# Generate PPT
try:
for supervisor, researcher, executor, pptx_file, status in process_ppt_streaming(
topic, template_name, audience_type, language, custom_slides, slide_count,
seed, file_upload, use_web_search, theme_name, progress
):
yield supervisor, researcher, executor, pptx_file, status
except Exception as e:
print(f"[PPT Generation] Error: {str(e)}")
import traceback
traceback.print_exc()
yield "", "", "", None, f"โŒ Error during PPT generation: {str(e)}"
# ===== Gradio UI =====
with gr.Blocks(title="PPT Generator", theme=gr.themes.Soft(), css=get_css()) as demo:
gr.Markdown("""
# ๐ŸŽฏ Open GAMMA 'GAMJA' - AI Presentation Generator with 3-AI Collaboration
### Create professional presentations with AI-powered collaboration system!
** Community: https://discord.gg/openfreeai
""")
# API token status check
token_status = []
if not REPLICATE_API_TOKEN:
token_status.append("โš ๏ธ RAPI_TOKEN environment variable not set.")
if not FRIENDLI_TOKEN:
token_status.append("โš ๏ธ FRIENDLI_TOKEN environment variable not set.")
if not BRAVE_API_TOKEN:
token_status.append("โ„น๏ธ BAPI_TOKEN not available. Web search disabled.")
if not PROCESS_FLOW_AVAILABLE:
token_status.append("โ„น๏ธ process_flow_generator not available. Process flow diagrams disabled.")
if token_status:
gr.Markdown("\n".join(token_status))
with gr.Row():
with gr.Column(scale=1):
# Language selection (at the top)
language_select = gr.Radio(
choices=["English", "Korean"],
label="๐ŸŒ Content Language",
value="English",
info="Select the language for your presentation content"
)
# Example selection
with gr.Row():
example_btn = gr.Button("๐Ÿ“‹ Load Example", size="sm", variant="secondary")
# Basic inputs
topic_input = gr.Textbox(
label="Presentation Topic",
placeholder="e.g., AI Startup Investment Pitch, New Product Launch, Digital Transformation Strategy",
lines=2
)
# Audience selection
audience_select = gr.Dropdown(
choices=list(AUDIENCE_TYPES.keys()),
label="๐ŸŽญ Target Audience",
value="General Staff",
info="Content and tone will be automatically optimized for your audience"
)
# Audience description display
audience_info = gr.Markdown()
# PPT template selection
template_select = gr.Dropdown(
choices=list(PPT_TEMPLATES.keys()),
label="PPT Template",
value="Business Proposal",
info="Select a template that matches your purpose"
)
# Design theme selection
theme_select = gr.Dropdown(
choices=list(DESIGN_THEMES.keys()),
label="Design Theme",
value="Minimal Light",
info="Overall design style for your presentation"
)
# Theme preview
theme_preview = gr.HTML()
# Slide count selection
slide_count = gr.Slider(
minimum=6,
maximum=20,
value=8,
step=1,
label="Number of Content Slides (excluding Cover and Thank You)",
info="Select the number of content slides to generate (not used in Custom template)"
)
# File upload
file_upload = gr.File(
label="Reference Material Upload (Optional)",
file_types=[".pdf", ".csv", ".txt"],
type="filepath",
value=None
)
# Web search option
use_web_search = gr.Checkbox(
label="Use Web Search",
value=True,
info="Use Brave Search to include latest information"
)
# Template description
template_info = gr.Markdown()
# Seed value
seed_input = gr.Slider(
minimum=1,
maximum=100,
value=10,
step=1,
label="Seed Value"
)
generate_btn = gr.Button("๐Ÿš€ Generate PPT (AI creates all content automatically)", variant="primary", size="lg")
# Usage instructions
with gr.Accordion("๐Ÿ“– How to Use", open=False):
gr.Markdown(get_usage_instructions())
# Status display
with gr.Row():
with gr.Column():
status_output = gr.Markdown(
value="### ๐Ÿ‘† Select a template and click the generate button!"
)
# PPTX download area
download_file = gr.File(
label="๐Ÿ“ฅ Download Generated PPTX File",
visible=True,
elem_id="download-file"
)
# AI outputs in tabs
with gr.Tabs():
with gr.TabItem("๐Ÿค– 3-AI Collaboration Process"):
with gr.Row():
# ๊ฐ๋…์ž AI ์ถœ๋ ฅ
with gr.Column():
gr.Markdown("### ๐Ÿง  Supervisor AI (Structure Design)")
supervisor_output = gr.Textbox(
label="",
lines=20,
max_lines=25,
interactive=False,
elem_classes=["supervisor-box"]
)
# ์กฐ์‚ฌ์ž AI ์ถœ๋ ฅ
with gr.Column():
gr.Markdown("### ๐Ÿ” Researcher AI (Content Collection)")
researcher_output = gr.Textbox(
label="",
lines=20,
max_lines=25,
interactive=False,
elem_classes=["researcher-box"]
)
# ์‹คํ–‰์ž AI ์ถœ๋ ฅ
with gr.Column():
gr.Markdown("### ๐Ÿ“ Executor AI (Slide Creation)")
executor_output = gr.Textbox(
label="",
lines=20,
max_lines=25,
interactive=False,
elem_classes=["executor-box"]
)
with gr.TabItem("๐Ÿ“Š PPT Preview"):
# Preview area
preview_output = gr.HTML(
label="PPT Preview (16:9)",
elem_classes="preview-container"
)
# Custom slides section
with gr.Accordion("๐Ÿ“ Custom Slide Configuration", open=False, elem_id="custom_accordion") as custom_accordion:
gr.Markdown("Build your presentation without using templates. (3-20 slides)")
# Custom slide count selection
custom_slide_count = gr.Slider(
minimum=3,
maximum=20,
value=5,
step=1,
label="Number of Custom Slides",
info="Select the number of custom slides to create"
)
custom_slides_components = create_custom_slides_ui(initial_count=5)
# Connect events
example_btn.click(
fn=load_example,
inputs=[template_select, language_select],
outputs=[topic_input]
)
audience_select.change(
fn=update_audience_info,
inputs=[audience_select],
outputs=[audience_info]
)
theme_select.change(
fn=update_theme_preview,
inputs=[theme_select],
outputs=[theme_preview]
)
template_select.change(
fn=update_template_info,
inputs=[template_select, slide_count],
outputs=[template_info, slide_count, custom_accordion]
)
slide_count.change(
fn=lambda t, s: update_template_info(t, s)[0],
inputs=[template_select, slide_count],
outputs=[template_info]
)
custom_slide_count.change(
fn=update_custom_slides_visibility,
inputs=[custom_slide_count],
outputs=[slide["row"] for slide in custom_slides_components]
)
# Flatten custom slide input components
all_custom_inputs = []
for slide in custom_slides_components:
all_custom_inputs.extend([slide["title"], slide["style"], slide["hint"]])
generate_btn.click(
fn=generate_ppt_handler,
inputs=[
topic_input, template_select, audience_select, language_select, theme_select,
slide_count, seed_input, file_upload, use_web_search, custom_slide_count
] + all_custom_inputs,
outputs=[supervisor_output, researcher_output, executor_output, download_file, status_output]
)
# Load initial info on startup
demo.load(
fn=lambda: (update_template_info(template_select.value, slide_count.value)[0],
update_theme_preview(theme_select.value),
update_audience_info(audience_select.value)),
inputs=[],
outputs=[template_info, theme_preview, audience_info]
)
# Run app
if __name__ == "__main__":
demo.queue() # Enable queue for streaming
demo.launch(ssr_mode=False)