4g3gf / feature_focus_blog.py
ssboost's picture
Create feature_focus_blog.py
7c34f04 verified
import os
import random
import re
import requests
import logging
import tempfile
from bs4 import BeautifulSoup
from datetime import datetime
from zoneinfo import ZoneInfo
import html
from PIL import Image
from urllib.request import urlopen
import markdown2
import gradio as gr
# ๋กœ๊น… ์„ค์ • (INFO ๋ ˆ๋ฒจ)
logging.basicConfig(level=logging.INFO)
# ์ƒ์ˆ˜ ์ •์˜
TARGET_CHAR_LENGTH = 4000
MIN_SECTION_LENGTH = 600
MAX_TOKENS = 15000
TEMPERATURE = 0.75
TOP_P = 0.95
# API ๊ด€๋ จ ์„ค์ •
gemini_api_key = os.getenv("GEMINI_API_KEY")
# --- Google Gemini SDK ์ดˆ๊ธฐํ™” ---
from google import genai
from google.genai import types
client = genai.Client(api_key=gemini_api_key)
# -------------------------------
# ๊ธฐ๋ณธ ๋„์šฐ๋ฏธ ํ•จ์ˆ˜๋“ค
# -------------------------------
def remove_unwanted_phrases(text):
"""๋ถˆํ•„์š”ํ•œ ํ‘œํ˜„ ์ œ๊ฑฐ ํ•จ์ˆ˜"""
unwanted_phrases = [
'์—ฌ๋Ÿฌ๋ถ„', '์ตœ๊ทผ', '๋งˆ์ง€๋ง‰์œผ๋กœ', '๊ฒฐ๋ก ์ ์œผ๋กœ', '๊ฒฐ๊ตญ',
'์ข…ํ•ฉ์ ์œผ๋กœ', '๋”ฐ๋ผ์„œ', '๋งˆ๋ฌด๋ฆฌ', '๋์œผ๋กœ', '์š”์•ฝ',
'ํ•œ ์ค„ ์š”์•ฝ', '์ •๋ฆฌํ•˜์ž๋ฉด', '์ด์ •๋ฆฌ', '๊ธ€์„ ๋งˆ์น˜๋ฉฐ',
'์ด์ƒ์œผ๋กœ', '์ถ”์ฒœ๋“œ๋ฆฝ๋‹ˆ๋‹ค', '์ฐธ๊ณ ํ•˜์„ธ์š”', '๋„์›€์ด ๋˜์…จ๊ธธ',
'์ข‹์€ ํ•˜๋ฃจ ๋˜์„ธ์š”', '๋‹ค์Œ ๊ธ€์—์„œ', '๋„์›€์ด ๋˜์—ˆ๊ธธ',
'์ฆ๊ฑฐ์šด ํ•˜๋ฃจ ๋˜์„ธ์š”', '๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค'
]
# ๋ฌธ๋‹จ๋ณ„๋กœ ๋‚˜๋ˆ„์–ด ์ฒ˜๋ฆฌ
lines = text.split('\n')
result_lines = []
for line in lines:
if "๋‹ค์Œ ์„น์…˜์—์„œ๋Š”" in line:
parts = line.split("๋‹ค์Œ ์„น์…˜์—์„œ๋Š”")
if parts[0].strip():
result_lines.append(parts[0].strip())
else:
# ๋ถˆํ•„์š”ํ•œ ํ‘œํ˜„ ์ œ๊ฑฐ (๊ตฌ๋‘์  ํฌํ•จ)
for phrase in unwanted_phrases:
# ๋ถˆํ•„์š”ํ•œ ํ‘œํ˜„ ์•ž๋’ค์˜ ๊ตฌ๋‘์ ๊ณผ ๊ณต๋ฐฑ๊นŒ์ง€ ํฌํ•จํ•˜์—ฌ ์ œ๊ฑฐ
pattern = rf'(\b{re.escape(phrase)}\b[\s,.!?]*)|([,.!?]*\b{re.escape(phrase)}\b)'
line = re.sub(pattern, '', line)
# ๋ฌธ์žฅ ๋‚ด ์ž”์—ฌ ๊ณต๋ฐฑ ๋ฐ ๊ตฌ๋‘์  ์ •๋ฆฌ
line = re.sub(r'\s{2,}', ' ', line) # ์—ฐ์† ๊ณต๋ฐฑ ์ œ๊ฑฐ
line = line.strip() # ์•ž๋’ค ๊ณต๋ฐฑ ์ œ๊ฑฐ
result_lines.append(line)
return '\n'.join(result_lines)
def convert_to_html(text):
"""๋งˆํฌ๋‹ค์šด ํ˜•์‹์„ HTML๋กœ ๋ณ€ํ™˜ - ๊ฐœ์„ ๋œ ๋ฒ„์ „"""
# ์ œ๋ชฉ ํ˜•์‹ ๋ณ€ํ™˜ (# -> h1, ## -> h2, ๋“ฑ)
for i in range(6, 0, -1):
pattern = r'^' + r'#' * i + r'\s+(.+)$'
text = re.sub(pattern, r'<h' + str(i) + r'>\1</h' + str(i) + r'>', text, flags=re.MULTILINE)
# ๋ณผ๋“œ์ฒด ๋ณ€ํ™˜ (**text** -> <strong>text</strong>)
text = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', text)
# ์ดํƒค๋ฆญ์ฒด ๋ณ€ํ™˜ (*text* -> <em>text</em>)
text = re.sub(r'\*([^*]+?)\*', r'<em>\1</em>', text)
# ๋ชฉ๋ก ๋ณ€ํ™˜ (- item -> <li>item</li>)
lines = text.split('\n')
in_list = False
result_lines = []
for line in lines:
list_match = re.match(r'^[\*\-+]\s+(.+)$', line)
if list_match:
if not in_list:
result_lines.append('<ul>')
in_list = True
result_lines.append(f'<li>{list_match.group(1)}</li>')
else:
if in_list:
result_lines.append('</ul>')
in_list = False
# ๋‹จ๋ฝ ์ฒ˜๋ฆฌ
if line.strip() and not re.match(r'^<h[1-6]>|<ul>|<li>|<\/ul>|<\/li>', line):
result_lines.append(f'<p>{line}</p>')
elif not line.strip():
result_lines.append('<br>')
else:
result_lines.append(line)
if in_list:
result_lines.append('</ul>')
# ์ตœ์ข… HTML ๊ฒฐํ•ฉ
html_content = '\n'.join(result_lines)
# HTML ์Šคํƒ€์ผ ์ ์šฉ (์ œ๋ชฉ๊ณผ ์†Œ์žฌ๋ชฉ์— Bold ๋ฐ ํฐํŠธ ์ง€์ •)
styled_html = f"""
<div style="font-family: 'Pretendard', Arial, sans-serif; color: #333; line-height: 1.6;">
<style>
h1 {{
font-size: 1.8em;
margin-top: 1.5em;
margin-bottom: 0.8em;
color: #111;
font-weight: bold;
font-family: 'Pretendard', Arial, sans-serif;
}}
h2 {{
font-size: 1.5em;
margin-top: 1.3em;
margin-bottom: 0.7em;
color: #222;
font-weight: bold;
font-family: 'Pretendard', Arial, sans-serif;
}}
h3 {{
font-size: 1.3em;
margin-top: 1.1em;
margin-bottom: 0.6em;
color: #333;
font-weight: bold;
font-family: 'Pretendard', Arial, sans-serif;
}}
p {{
margin-bottom: 1em;
line-height: 1.7;
font-size: 1.05em;
}}
strong {{
font-weight: bold;
color: #000;
}}
em {{
font-style: italic;
}}
ul {{
margin-left: 1.5em;
margin-bottom: 1em;
}}
li {{
margin-bottom: 0.5em;
}}
</style>
{html_content}
</div>
"""
return styled_html
def post_process_blog(blog_content, style="์นœ๊ทผํ•œ"):
"""๋ธ”๋กœ๊ทธ ์ปจํ…์ธ  ํ›„์ฒ˜๋ฆฌ ํ•จ์ˆ˜"""
try:
# ๋งˆํฌ๋‹ค์šด ์ œ๊ฑฐ (# ํ˜•์‹์˜ ์ œ๋ชฉ ์ œ์™ธ)
blog_content = re.sub(r'^\s*[\*\-+]\s+', '- ', blog_content, flags=re.MULTILINE) # ๊ธ€๋จธ๋ฆฌ ๊ธฐํ˜ธ ํ†ต์ผ
# ์Šคํƒ€์ผ์— ๋”ฐ๋ฅธ ์–ดํˆฌ ์กฐ์ •
if style == "์นœ๊ทผํ•œ":
blog_content = re.sub(r'([๊ฐ€-ํžฃ]+)๊ณ ์š”', r'\1๊ตฌ์š”', blog_content)
blog_content = re.sub(r'๋‹ต๋‹ˆ๋‹ค', '์–ด์š”', blog_content)
blog_content = re.sub(r'์˜€๋‹ต๋‹ˆ๋‹ค', '์˜€์–ด์š”', blog_content)
blog_content = re.sub(r'ํ–ˆ๋‹ต๋‹ˆ๋‹ค', 'ํ–ˆ์–ด์š”', blog_content)
blog_content = re.sub(r'์Šต๋‹ˆ๋‹ค', '์š”', blog_content)
blog_content = re.sub(r'ํ•ฉ๋‹ˆ๋‹ค', 'ํ•ด์š”', blog_content)
blog_content = re.sub(r'๋ฉ๋‹ˆ๋‹ค', '๋ผ์š”', blog_content)
blog_content = re.sub(r'์ž…๋‹ˆ๋‹ค', '์ด์—์š”', blog_content)
# ํ•ต์‹ฌ๊ธฐ๋Šฅ ์ „๋ฌธ์šฉ์–ด ๊ฐ•ํ™” ๋ฐ ๊ตฌ์ฒด์  ์ˆ˜์น˜ ๊ฐ•์กฐ
tech_terms = [
(r'์„ฑ๋Šฅ\b(?!\s*๋ถ„์„|\s*ํ…Œ์ŠคํŠธ|\s*์ธก์ •)', r'๊ธฐ์ˆ ์  ์„ฑ๋Šฅ'),
(r'์†๋„\b(?!\s*์ธก์ •|\s*ํ…Œ์ŠคํŠธ)', r'์ฒ˜๋ฆฌ ์†๋„'),
(r'ํ™”๋ฉด\b(?!\s*ํฌ๊ธฐ|\s*๋ฐ๊ธฐ)', r'๋””์Šคํ”Œ๋ ˆ์ด'),
(r'์นด๋ฉ”๋ผ\b(?!\s*๋ชจ๋“ˆ|\s*์„ผ์„œ)', r'์ด๋ฏธ์ง€ ์„ผ์„œ ์‹œ์Šคํ…œ'),
(r'๋ฐฐํ„ฐ๋ฆฌ\b(?!\s*์šฉ๋Ÿ‰|\s*์ˆ˜๋ช…)', r'์ „๋ ฅ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ'),
(r'์‚ฌ์šฉ\b(?!\s*๋ฐฉ๋ฒ•|\s*์„ค๋ช…|\s*์‚ฌ๋ก€)', r'์šด์šฉ'),
(r'์ข‹๋‹ค\b(?!\s*๊ณ )', r'ํšจ์œจ์ ์ด๋‹ค'),
(r'๋น ๋ฅด๋‹ค\b', r'๊ณ ์„ฑ๋Šฅ์ด๋‹ค')
]
for pattern, replacement in tech_terms:
blog_content = re.sub(pattern, replacement, blog_content)
# ๋ถ„์„์  ํ‘œํ˜„ ๊ฐ•ํ™”
blog_content = re.sub(r'์ œ ์ƒ๊ฐ์—๋Š”', r'๋ถ„์„ ๊ฒฐ๊ณผ์— ๋”ฐ๋ฅด๋ฉด', blog_content)
blog_content = re.sub(r'์ œ๊ฐ€ ๋ดค์„ ๋•Œ', r'๊ธฐ์ˆ ์  ๊ด€์ ์—์„œ', blog_content)
blog_content = re.sub(r'๋А๋‚Œ์ด ๋“ค์–ด์š”', r'ํ™•์ธ๋ฉ๋‹ˆ๋‹ค', blog_content)
# ์ˆ˜์น˜์™€ ๋‹จ์œ„ ์‚ฌ์ด์— ๊ณต๋ฐฑ ์ถ”๊ฐ€ ๋ฐ ๊ฐ•์กฐ
def add_space_to_numbers(match):
number, unit = match.groups()
return f"{number} {unit}"
blog_content = re.sub(r'(\d+(?:\.\d+)?)([๊ฐ€-ํžฃ]+)', add_space_to_numbers, blog_content)
# ์ฃผ์š” ์ˆซ์ž ๋ฐ์ดํ„ฐ ๋ณผ๋“œ ์ฒ˜๋ฆฌ
blog_content = re.sub(r'(\d+(?:\.\d+)?(?:\s*%|\s*dB|\s*Hz|\s*์‹œ๊ฐ„|\s*mAh|\s*GB|\s*MB|\s*TB|\s*MP))', r'**\1**', blog_content)
# ๊ณผ์žฅ๋œ ํ‘œํ˜„ ์ •๋ฆฌ
exaggerated_expressions = [
(r'ํ•„์ˆ˜์ ์ธ', r'์ค‘์š”ํ•œ'),
(r'ํ˜๋ช…์ ์ธ', r'์ฃผ๋ชฉํ• ๋งŒํ•œ'),
(r'๋†€๋ผ์šด', r'์ฃผ๋ชฉํ•  ๋งŒํ•œ'),
(r'๊ธฐ์ ์˜', r'ํšจ๊ณผ์ ์ธ'),
(r'์ตœ๊ณ ์˜', r'์šฐ์ˆ˜ํ•œ'),
(r'์„ธ๊ณ„์ ์ธ', r'์ฃผ์š”'),
(r'์™„๋ฒฝํ•œ', r'์šฐ์ˆ˜ํ•œ'),
(r'๊ทน์ ์ธ', r'์ƒ๋‹นํ•œ'),
(r'๋ฌดํ•œํ•œ', r'๋‹ค์–‘ํ•œ'),
(r'์ ˆ๋Œ€์ ์ธ', r'์ค‘์š”ํ•œ'),
(r'ํ˜์‹ ์ ์ธ', r'์ƒˆ๋กœ์šด'),
(r'ํ™˜์ƒ์ ์ธ', r'์šฐ์ˆ˜ํ•œ'),
(r'๊ทผ๋ณธ์ ์ธ', r'๊ธฐ๋ณธ์ ์ธ'),
(r'ํš๊ธฐ์ ์ธ', r'์ค‘์š”ํ•œ'),
(r'์ „๋ก€์—†๋Š”', r'ํŠน๋ณ„ํ•œ'),
(r'์••๋„์ ์ธ', r'๋‘๋“œ๋Ÿฌ์ง„'),
(r'๊ธฐ๊ฐ€ ๋ง‰ํžŒ', r'ํšจ๊ณผ์ ์ธ'),
(r'๋ํŒ์™•', r'์ตœ์ƒ์œ„ ๋ชจ๋ธ')
]
for pattern, replacement in exaggerated_expressions:
blog_content = re.sub(r'\b' + pattern + r'\b', replacement, blog_content, flags=re.IGNORECASE)
# ์ฐธ๊ณ ๊ธ€ ๊ด€๋ จ ํ‘œํ˜„ ์ •๋ฆฌ
blog_content = re.sub(r'์ฐธ๊ณ ๊ธ€์— ๋”ฐ๋ฅด๋ฉด', r'๊ธฐ์ˆ  ๋ถ„์„์— ๋”ฐ๋ฅด๋ฉด', blog_content)
blog_content = re.sub(r'์ฐธ๊ณ ๊ธ€', r'๊ธฐ์ˆ  ๋ฌธ์„œ', blog_content)
return blog_content
except Exception as e:
logging.error(f"๋ธ”๋กœ๊ทธ ๊ธ€ ํ›„์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
return blog_content
def generate_outline(category, style, references1, references2, references3):
"""ํ•ต์‹ฌ๊ธฐ๋Šฅ ์„ ๋ณ„ ํ•จ์ˆ˜ - ๊ฐ„๋‹จํ•œ ์„ค๋ช… ์ถ”๊ฐ€"""
try:
# ์ฐธ๊ณ ๊ธ€ ์ •๋ณด ์ค€๋น„
references = [
references1.strip() if references1.strip() else "์ฐธ๊ณ  ์ž๋ฃŒ ์—†์Œ",
references2.strip() if references2.strip() else "์ฐธ๊ณ  ์ž๋ฃŒ ์—†์Œ",
references3.strip() if references3.strip() else "์ฐธ๊ณ  ์ž๋ฃŒ ์—†์Œ"
]
# ์˜๋ฏธ ์žˆ๋Š” ์ฐธ๊ณ ๊ธ€๋งŒ ํ•„ํ„ฐ๋ง
meaningful_refs = [ref for ref in references if ref != "์ฐธ๊ณ  ์ž๋ฃŒ ์—†์Œ"]
if not meaningful_refs:
return ["์ฐธ๊ณ  ์ž๋ฃŒ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค"] * 5
# ์Šคํƒ€์ผ ํ”„๋กฌํ”„ํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ
style_prompt = get_style_prompt(style)
# ๋ชจ๋“  ์ฐธ๊ณ ๊ธ€ ๊ฒฐํ•ฉ
combined_refs = "\n\n".join(meaningful_refs)
# LLM์— ํ•ต์‹ฌ๊ธฐ๋Šฅ ์ถ”์ถœ ์š”์ฒญ
extract_prompt = f"""
[ํ•ต์‹ฌ๊ธฐ๋Šฅ ์„ ๋ณ„ ์š”์ฒญ]
์ œ๊ณต๋œ ์ฐธ๊ณ ๊ธ€์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•˜๊ณ  ํ•ต์‹ฌ์ ์ธ ๊ธฐ๋Šฅ 5๊ฐ€์ง€๋ฅผ ์„ ๋ณ„ํ•˜๊ณ  ๊ฐ๊ฐ์— ๊ฐ„๋‹จํ•œ ์„ค๋ช…์„ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.
์ฐธ๊ณ ๊ธ€:
{combined_refs}
[์‹œ์Šคํ…œ ์—ญํ• ]
๋‹น์‹ ์€ ์ˆ˜๋…„๊ฐ„์˜ ๊ฒฝํ—˜์„ ๊ฐ€์ง„ ์ƒํ’ˆ ๊ธฐ๋Šฅ ์ „๋ฌธ ๋ถ„์„๊ฐ€์ž…๋‹ˆ๋‹ค. ์ œํ’ˆ์˜ ๋‹จ์ผ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์„ ์‹ฌ์ธต์ ์œผ๋กœ ๋ถ„์„ํ•˜๊ณ  ๋‹ค์–‘ํ•œ ์ธก๋ฉด์—์„œ ํ‰๊ฐ€ํ•˜์—ฌ ๋งŽ์€ ๋…์ž๋“ค์˜ ์‹ ๋ขฐ๋ฅผ ๋ฐ›๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
[๋ถ„์„ ๋‹จ๊ณ„]
1. ์ฐธ๊ณ  ์ž๋ฃŒ 3๊ฐœ๋ฅผ ์ฒ ์ €ํžˆ ๋ถ„์„ํ•˜์—ฌ ์ œํ’ˆ์˜ ๋‹จ์ผ ํ•ต์‹ฌ ๊ธฐ๋Šฅ ์‹๋ณ„
2. ์„ ์ •ํ•œ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์˜ 5๊ฐ€์ง€ ์ฃผ์š” ์ธก๋ฉด ํŒŒ์•… (์„ฑ๋Šฅ, ์‚ฌ์šฉ์„ฑ, ํšจ์œจ์„ฑ, ๊ธฐ์ˆ ์  ํŠน์ง•, ํ™œ์šฉ ๊ฐ€์น˜ ๋“ฑ)
3. ์„ ์ •ํ•œ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์ด ์ œํ’ˆ ์ „์ฒด์—์„œ ๊ฐ–๋Š” ์ค‘์š”๋„์™€ ์ฐจ๋ณ„์„ฑ ํ‰๊ฐ€
[์•„์›ƒ๋ผ์ธ ๊ตฌ์„ฑ ์›์น™]
1. ๋ณธ๋ก (5๊ฐœ) - ์ฐธ๊ณ  ์ž๋ฃŒ ๋ถ„์„์„ ํ†ตํ•ด ๋ฐœ๊ฒฌํ•œ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์˜ 5๊ฐ€์ง€ ์ค‘์š” ์ธก๋ฉด์„ ๋‹ด์€ ์†Œ์ œ๋ชฉ
- ํ•ต์‹ฌ ๊ธฐ๋Šฅ์˜ ๊ธฐ์ˆ ์  ์›๋ฆฌ์™€ ์ž‘๋™ ๋ฉ”์ปค๋‹ˆ์ฆ˜
- ํ•ต์‹ฌ ๊ธฐ๋Šฅ์˜ ์‹ค์ œ ์„ฑ๋Šฅ ๋ฐ ์ธก์ • ๋ฐ์ดํ„ฐ ๋ถ„์„
- ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ์ธก๋ฉด์—์„œ์˜ ๊ธฐ๋Šฅ ํ‰๊ฐ€
- ๊ฒฝ์Ÿ ์ œํ’ˆ๊ณผ์˜ ํ•ด๋‹น ๊ธฐ๋Šฅ ๋น„๊ต ๋ถ„์„
- ํ•ต์‹ฌ ๊ธฐ๋Šฅ์˜ ์‹ค์ƒํ™œ ํ™œ์šฉ ๊ฐ€์น˜์™€ ํ•œ๊ณ„์ 
- (์œ„ ํ•ญ๋ชฉ๋“ค์€ ์„ ์ •๋œ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์— ๋”ฐ๋ผ ์œ ์—ฐํ•˜๊ฒŒ ์กฐ์ •)
[ํ•ต์‹ฌ ์ง€์นจ]
1. ์™„์ „ํžˆ ํ•œ๊ตญ์–ด๋กœ๋งŒ ์ž‘์„ฑํ•  ๊ฒƒ
2. ์†Œ์ œ๋ชฉ์€ ์ตœ๋Œ€ 30์ž ์ด๋‚ด๋กœ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ž‘์„ฑ
3. ์„ ์ •๋œ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์˜ ์ค‘์š” ์ธก๋ฉด์„ ๋ช…ํ™•ํžˆ ๋“œ๋Ÿฌ๋‚ด๋Š” ํ‘œํ˜„ ์‚ฌ์šฉ (์˜ˆ: "์ •๋ฐ€ ์ธก์ • ์‹คํ—˜์œผ๋กœ ๋ณธ ์„ฑ๋Šฅ ํ•œ๊ณ„", "์ผ์ƒ ํ™˜๊ฒฝ์—์„œ์˜ ๊ธฐ๋Šฅ ์•ˆ์ •์„ฑ ๋ถ„์„")
4. ๊ธฐ์ˆ ์  ์ •ํ™•์„ฑ๊ณผ ์‹ฌ์ธต์  ๋ถ„์„์ด ์†Œ์ œ๋ชฉ์— ๋ฐ˜์˜๋˜๋„๋ก ๊ตฌ์„ฑ
5. ํ‚ค์›Œ๋“œ๋Š” ์†Œ์ œ๋ชฉ ๊ฒฐ์ •์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์Œ (๋ณธ๋ฌธ ์ž‘์„ฑ ์‹œ ์ฐธ๊ณ ์‚ฌํ•ญ์œผ๋กœ๋งŒ ํ™œ์šฉ)
6. ๋ณธ๋ก  5๊ฐœ ํ•ญ๋ชฉ๋งŒ์œผ๋กœ ๊ตฌ์„ฑ (๋„์ž…๋ถ€์™€ ๊ฒฐ๋ก  ๋ถˆํ•„์š”)
7. ๋‹ค์–‘ํ•œ ์ œํ’ˆ ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹จ์ผ ๊ธฐ๋Šฅ ๋ถ„์„์— ์œ ์—ฐํ•˜๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑ
8. ํŠน์ˆ˜๋ฌธ์ž(**, :, #, ## ๋“ฑ)๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋ง๊ณ  ์ผ๋ฐ˜ ํ…์ŠคํŠธ๋กœ๋งŒ ์ž‘์„ฑํ•˜์„ธ์š”.
[์ถœ๋ ฅ ํ˜•์‹]
1. ์ฐธ๊ณ  ์ž๋ฃŒ ๋ถ„์„์„ ํ†ตํ•ด ์„ ์ •๋œ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์˜ 5๊ฐ€์ง€ ์ค‘์š” ์ธก๋ฉด์„ ํŒŒ์•…ํ•˜์—ฌ ์ž์œ ๋กญ๊ฒŒ ์•„์›ƒ๋ผ์ธ ๊ตฌ์„ฑ
2. ๋ฐ˜๋“œ์‹œ ๋ณธ๋ก  5๊ฐœ ํ•ญ๋ชฉ์œผ๋กœ๋งŒ ๊ตฌ์„ฑํ•  ๊ฒƒ:(๊ฐ ํ•ญ๋ชฉ๋‹น 1๋ฒˆ ์—”ํ„ฐ๋ฅผ ์ ์šฉํ•˜์—ฌ ๋นˆ์นธ์ด ๋‚˜์˜ค์ง€ ์•Š๋„๋กํ•˜๋ผ.)
- ๋ณธ๋ก 1: [ํ•ต์‹ฌ ๊ธฐ๋Šฅ์˜ ๊ธฐ์ˆ ์  ์›๋ฆฌ/์ž‘๋™ ๋ฉ”์ปค๋‹ˆ์ฆ˜ ๊ด€๋ จ ์ œ๋ชฉ]
- ๋ณธ๋ก 2: [ํ•ต์‹ฌ ๊ธฐ๋Šฅ์˜ ์„ฑ๋Šฅ/์ธก์ • ๋ฐ์ดํ„ฐ ๊ด€๋ จ ์ œ๋ชฉ]
- ๋ณธ๋ก 3: [์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ์ธก๋ฉด์˜ ๊ธฐ๋Šฅ ํ‰๊ฐ€ ๊ด€๋ จ ์ œ๋ชฉ]
- ๋ณธ๋ก 4: [๊ฒฝ์Ÿ ์ œํ’ˆ๊ณผ์˜ ๊ธฐ๋Šฅ ๋น„๊ต ๊ด€๋ จ ์ œ๋ชฉ]
- ๋ณธ๋ก 5: [์‹ค์ƒํ™œ ํ™œ์šฉ ๊ฐ€์น˜/ํ•œ๊ณ„์  ๊ด€๋ จ ์ œ๋ชฉ]
3. ์†Œ์ œ๋ชฉ์€ ์„ ์ •๋œ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์˜ ํŠน์„ฑ์— ๋งž๊ฒŒ ์ž์œ ๋กญ๊ฒŒ ๊ตฌ์„ฑ
4. ํ‚ค์›Œ๋“œ์— ๋งž์ถ”์ง€ ๋ง๊ณ , ์ฐธ๊ณ  ์ž๋ฃŒ ๋ถ„์„์„ ํ†ตํ•ด ๋ฐœ๊ฒฌํ•œ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์˜ ์ค‘์š” ์ธก๋ฉด ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌ์„ฑ
5. ์˜ˆ์‹œ ํ˜•์‹ (์ฐธ๊ณ ์šฉ์ผ ๋ฟ, ๋‚ด์šฉ์€ ์„ ์ •๋œ ํ•ต์‹ฌ ๊ธฐ๋Šฅ๊ณผ ์ฐธ๊ณ  ์ž๋ฃŒ์— ๋”ฐ๋ผ ์™„์ „ํžˆ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Œ):
- ๋ณธ๋ก 1: [ํ•ต์‹ฌ ๊ธฐ๋Šฅ์˜ ๊ธฐ์ˆ ์  ์›๋ฆฌ/๋ฉ”์ปค๋‹ˆ์ฆ˜ ๊ด€๋ จ ์ œ๋ชฉ]
- ๋ณธ๋ก 2: [์‹ค์ธก ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ/์„ฑ๋Šฅ ๋ฐ์ดํ„ฐ ๊ด€๋ จ ์ œ๋ชฉ]
- ๋ณธ๋ก 3: [์‹ค์‚ฌ์šฉ ํ™˜๊ฒฝ์—์„œ์˜ ์‚ฌ์šฉ์„ฑ/ํšจ์œจ์„ฑ ๊ด€๋ จ ์ œ๋ชฉ]
- ๋ณธ๋ก 4: [ํƒ€ ์ œํ’ˆ ๋™์ผ ๊ธฐ๋Šฅ๊ณผ์˜ ์ฐจ๋ณ„์  ๊ด€๋ จ ์ œ๋ชฉ]
- ๋ณธ๋ก 5: [๊ธฐ๋Šฅ์˜ ๋ฏธ๋ž˜ ๋ฐœ์ „ ๊ฐ€๋Šฅ์„ฑ/๊ฐœ์„ ์  ๊ด€๋ จ ์ œ๋ชฉ]
{style_prompt}
"""
# Gemini API ํ˜ธ์ถœ
outline_result = call_gemini_api(extract_prompt, temperature=0.3)
# ๊ฒฐ๊ณผ์—์„œ ํ•ต์‹ฌ๊ธฐ๋Šฅ ์ถ”์ถœ
features = []
for line in outline_result.strip().split('\n'):
if line.strip():
# ํŠน์ˆ˜๋ฌธ์ž์™€ ๋ถˆํ•„์š”ํ•œ ํ˜•์‹ ์ œ๊ฑฐ
clean_feature = re.sub(r'^\s*[-*#]\s*', '', line) # ๋ฒˆํ˜ธ, ๋ถˆ๋ฆฟ ์ œ๊ฑฐ
clean_feature = re.sub(r'\*\*|\*|##|#', '', clean_feature) # ํŠน์ˆ˜๋ฌธ์ž ์ œ๊ฑฐ
clean_feature = clean_feature.strip()
if clean_feature:
features.append(clean_feature)
# 5๊ฐœ๊ฐ€ ์•ˆ ๋˜๋ฉด ๋นˆ ๊ฐ’์œผ๋กœ ์ฑ„์šฐ๊ธฐ
while len(features) < 5:
features.append(f"ํŠน์ง• {len(features) + 1} - ์ถ”๊ฐ€ ์„ค๋ช… ํ•„์š”")
return features[:5] # ์ตœ๋Œ€ 5๊ฐœ๋งŒ ๋ฐ˜ํ™˜
except Exception as e:
logging.error(f"ํ•ต์‹ฌ๊ธฐ๋Šฅ ์„ ๋ณ„ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
return ["ํŠน์ง•์„ ์ถ”์ถœํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค"] * 5
def generate_blog_post(category, style, references1, references2, references3, selected_feature):
"""ํ•ต์‹ฌ๊ธฐ๋Šฅ์ง‘์ค‘ํ˜• ๋ธ”๋กœ๊ทธ ๊ธ€ ์ƒ์„ฑ ํ•จ์ˆ˜ - ์œ ์—ฐํ•œ ์†Œ์žฌ ๊ตฌ์„ฑ"""
try:
# ์ฐธ๊ณ ๊ธ€ ์ค€๋น„
references = [
references1.strip() if references1.strip() else "์ฐธ๊ณ  ์ž๋ฃŒ ์—†์Œ",
references2.strip() if references2.strip() else "์ฐธ๊ณ  ์ž๋ฃŒ ์—†์Œ",
references3.strip() if references3.strip() else "์ฐธ๊ณ  ์ž๋ฃŒ ์—†์Œ"
]
# ์˜๋ฏธ ์žˆ๋Š” ์ฐธ๊ณ ๊ธ€๋งŒ ํ•„ํ„ฐ๋ง
references = [ref for ref in references if ref != "์ฐธ๊ณ  ์ž๋ฃŒ ์—†์Œ"]
if not references:
return "<p>์ฐธ๊ณ  ์ž๋ฃŒ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์ตœ์†Œ ํ•˜๋‚˜ ์ด์ƒ์˜ ์ฐธ๊ณ  ์ž๋ฃŒ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.</p>"
if not selected_feature.strip():
return "<p>ํ•ต์‹ฌ๊ธฐ๋Šฅ์ด ์„ ํƒ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ•ต์‹ฌ๊ธฐ๋Šฅ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.</p>"
# ์Šคํƒ€์ผ ํ”„๋กฌํ”„ํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ
style_prompt = get_style_prompt(style)
# ๋ธ”๋กœ๊ทธ ๊ธ€ ์ƒ์„ฑ ํ”„๋กฌํ”„ํŠธ ๊ตฌ์„ฑ
blog_prompt = f"""
[ํ•ต์‹ฌ๊ธฐ๋Šฅ ์ง‘์ค‘ํ˜• ์ƒํ’ˆ๋ฆฌ๋ทฐ ์ž‘์„ฑ ์š”์ฒญ]
์„ ํƒํ•œ ํ•ต์‹ฌ๊ธฐ๋Šฅ: {selected_feature}
[๋ฆฌ๋ทฐ ์ž‘์„ฑ ํ˜•์‹]
1. ๋ฆฌ๋ทฐ๋Š” '๋„์ž…๋ถ€', '5๊ฐ€์ง€ ์†Œ์žฌ', '๋งˆ๋ฌด๋ฆฌ' ๊ตฌ์กฐ๋กœ ์ž‘์„ฑํ•˜์„ธ์š”.
2. ๋งˆํฌ๋‹ค์šด ํ˜•์‹์€ ์ตœ์†Œํ•œ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ , ๊ฐ€๋Šฅํ•œ ํ•œ ์ผ๋ฐ˜ ํ…์ŠคํŠธ๋กœ ์ž‘์„ฑํ•˜์„ธ์š”.
3. ๊ฐ ๋ถ€๋ถ„์€ ๋ช…ํ™•ํžˆ ๊ตฌ๋ถ„๋˜์–ด์•ผ ํ•˜๋ฉฐ, ์„œ์ˆ ํ˜• ๋ฌธ์žฅ์œผ๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ด์–ด์ง€๋„๋ก ์ž‘์„ฑํ•˜์„ธ์š”.
4. ๋‹จ๋ฝ์€ ์ ์ ˆํžˆ ๋‚˜๋ˆ„๋˜, ๋„ˆ๋ฌด ์งง์€ ๋‹จ๋ฝ์„ ๋งŽ์ด ๋งŒ๋“ค์ง€ ๋งˆ์„ธ์š”.
[๋ฆฌ๋ทฐ ๋‚ด์šฉ ๊ตฌ์กฐ]
1. ๋„์ž…๋ถ€ (์ „์ฒด์˜ 10%)
- ์„ ํƒํ•œ ํ•ต์‹ฌ๊ธฐ๋Šฅ์˜ ์ค‘์š”์„ฑ๊ณผ ํŠน์ง•์„ ๊ฐ„๋žตํžˆ ์†Œ๊ฐœ
- ์ด ๊ธฐ๋Šฅ์ด ์ƒํ’ˆ์—์„œ ์–ด๋–ค ๊ฐ€์น˜๋ฅผ ์ œ๊ณตํ•˜๋Š”์ง€ ์„ค๋ช…
- ๋งˆ์ง€๋ง‰ ๋ฌธ์žฅ์—์„œ ๋ณธ๋ฌธ์—์„œ ๋‹ค๋ฃฐ ๋‚ด์šฉ์„ ์˜ˆ๊ณ 
2. 5๊ฐ€์ง€ ์†Œ์žฌ (์ „์ฒด์˜ 80%)
- ์„ ํƒํ•œ ํ•ต์‹ฌ๊ธฐ๋Šฅ์— ๊ฐ€์žฅ ์ ํ•ฉํ•œ 5๊ฐ€์ง€ ์†Œ์žฌ๋ฅผ ์ž์œ ๋กญ๊ฒŒ ์„ ์ •ํ•˜์„ธ์š”
- ๊ฐ ์†Œ์žฌ๋Š” ๊ธฐ๋Šฅ์˜ ์„œ๋กœ ๋‹ค๋ฅธ ์ธก๋ฉด์„ ๋‹ค๋ฃจ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
- ์˜ˆ์‹œ ์†Œ์žฌ: ๊ธฐ์ˆ ์  ์›๋ฆฌ, ์ž‘๋™ ๋ฐฉ์‹, ์„ฑ๋Šฅ ๋ถ„์„, ๊ฒฝ์Ÿ ์ œํ’ˆ ๋น„๊ต, ํ™œ์šฉ ๋ฐฉ๋ฒ•, ์‚ฌ์šฉ ๊ฒฝํ—˜, ์„ค์ • ํŒ,
์—…๋ฐ์ดํŠธ ์ด๋ ฅ, ์‚ฐ์—… ํ‘œ์ค€๊ณผ์˜ ๋น„๊ต, ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค, ํ˜ธํ™˜์„ฑ, ํ•œ๊ณ„์ ๊ณผ ๊ฐœ์„  ๋ฐฉํ–ฅ ๋“ฑ
- ๊ฐ ์†Œ์žฌ๋Š” ๋น„์Šทํ•œ ๋ถ„๋Ÿ‰์œผ๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์—ฐ๊ฒฐ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
3. ๋งˆ๋ฌด๋ฆฌ (์ „์ฒด์˜ 10%)
- ์ด ๊ธฐ๋Šฅ์˜ ์ข…ํ•ฉ์  ํ‰๊ฐ€์™€ ๊ฐ€์น˜
- ์–ด๋–ค ์œ ํ˜•์˜ ์‚ฌ์šฉ์ž์—๊ฒŒ ํŠนํžˆ ์œ ์šฉํ•œ์ง€
- ํ•ต์‹ฌ๊ธฐ๋Šฅ๊ณผ ์ œํ’ˆ ์ „์ฒด์— ๋Œ€ํ•œ ์ตœ์ข… ๊ฒฌํ•ด
[์ค‘์š” ์ž‘์„ฑ ์ง€์นจ]
1. ์ „์ฒด ๊ธ€์€ ์ตœ์†Œ 4000์ž ์ด์ƒ์œผ๋กœ ์ž‘์„ฑํ•˜์„ธ์š”.
2. ๊ฐ ์†Œ์žฌ๋Š” ์ตœ์†Œ 600์ž ์ด์ƒ ์ž‘์„ฑํ•˜๊ณ , ์„œ๋กœ ์œ ๊ธฐ์ ์œผ๋กœ ์—ฐ๊ฒฐ๋˜๊ฒŒ ํ•˜์„ธ์š”.
3. ์ œ๋ชฉ์€ ๋”ฐ๋กœ ์‚ฌ์šฉํ•˜์ง€ ๋ง๊ณ , ๋„์ž…๋ถ€, ์†Œ์žฌ, ๋งˆ๋ฌด๋ฆฌ๋ฅผ ํ•˜๋‚˜์˜ ์—ฐ๊ฒฐ๋œ ๊ธ€๋กœ ์ž‘์„ฑํ•˜์„ธ์š”.
4. ๊ธฐ์ˆ ์  ์ •ํ™•์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์„ ํƒํ•œ ํ•ต์‹ฌ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์‹ฌ์ธต์  ๋ถ„์„๊ณผ ๊ตฌ์ฒด์ ์ธ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜์„ธ์š”.
5. ๋งˆ์ผ€ํŒ…์  ๊ณผ์žฅ ํ‘œํ˜„๋ณด๋‹ค๋Š” ์‚ฌ์‹ค์ ์ด๊ณ  ๋ถ„์„์ ์ธ ํ‘œํ˜„์„ ์‚ฌ์šฉํ•˜์„ธ์š”.
6. ๋ถˆํ•„์š”ํ•œ ๋ฐ˜๋ณต์ด๋‚˜ ์žฅํ™ฉํ•œ ์„ค๋ช…์€ ํ”ผํ•˜๊ณ , ํ•ต์‹ฌ ์ •๋ณด์™€ ํ†ต์ฐฐ์„ ๊ฐ•์กฐํ•˜์„ธ์š”.
7. ์ „์ฒด ๊ธ€์˜ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๊ณ , ๋ฌธ๋‹จ ๊ฐ„ ์ž์—ฐ์Šค๋Ÿฌ์šด ํ๋ฆ„์„ ๋งŒ๋“œ์„ธ์š”.
8. ์†Œ์žฌ๋Š” ํ•ต์‹ฌ๊ธฐ๋Šฅ์˜ ์„ฑ๊ฒฉ์— ๋งž๊ฒŒ ๊ฐ€์žฅ ์ ์ ˆํ•œ ๊ฒƒ์„ ์ž์œ ๋กญ๊ฒŒ ์„ ์ •ํ•˜์„ธ์š”.
์ฐธ๊ณ ๊ธ€:
{references[0]}
{references[1] if len(references) > 1 else ""}
{references[2] if len(references) > 2 else ""}
{style_prompt}
"""
# Gemini API ํ˜ธ์ถœ
logging.info("ํ•ต์‹ฌ๊ธฐ๋Šฅ ์ง‘์ค‘ํ˜• ๋ธ”๋กœ๊ทธ ๊ธ€ ์ƒ์„ฑ ์‹œ์ž‘")
blog_content = call_gemini_api(blog_prompt, temperature=0.7)
logging.info(f"์ƒ์„ฑ๋œ ์›๋ณธ ๊ธ€ ๊ธธ์ด: {len(blog_content)}")
# ํ›„์ฒ˜๋ฆฌ
processed_content = post_process_blog(blog_content, style)
# ๊ธ€์ž ์ˆ˜ ์ฒดํฌ
char_count = len(processed_content)
logging.info(f"์ฒ˜๋ฆฌ ํ›„ ๋ธ”๋กœ๊ทธ ๊ธ€ ๊ธ€์ž ์ˆ˜: {char_count}")
# ๊ธ€์ž ์ˆ˜๊ฐ€ ๋ชฉํ‘œ์— ๋ฏธ๋‹ฌํ•˜๋ฉด ํ™•์žฅ ์‹œ๋„
if char_count < TARGET_CHAR_LENGTH:
logging.info(f"๊ธ€์ž ์ˆ˜ ๋ถ€์กฑ ({char_count} < {TARGET_CHAR_LENGTH}), ํ™•์žฅ ์‹œ๋„")
expansion_prompt = f"""
[ํ•ต์‹ฌ๊ธฐ๋Šฅ ๋ถ„์„ ํ™•์žฅ ์š”์ฒญ]
ํ˜„์žฌ ๊ธ€์€ ๋ชฉํ‘œ ๊ธ€์ž์ˆ˜์ธ 4000์ž์— ๋ฏธ์น˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ๊ธ€์ž์ˆ˜๋Š” ์•ฝ {char_count}์ž์ž…๋‹ˆ๋‹ค.
์„ ํƒํ•œ ํ•ต์‹ฌ๊ธฐ๋Šฅ: {selected_feature}
[ํ™•์žฅ ์ง€์นจ]
1. ์›๋ž˜ ๊ธ€์˜ ๊ตฌ์กฐ(๋„์ž…๋ถ€, 5๊ฐ€์ง€ ์†Œ์žฌ, ๋งˆ๋ฌด๋ฆฌ)๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ ๋‚ด์šฉ์„ ํ™•์žฅํ•˜์„ธ์š”.
2. ๊ฐ ์†Œ์žฌ์— ๋” ๊ตฌ์ฒด์ ์ธ ์ •๋ณด, ์˜ˆ์‹œ, ๋ถ„์„ ๋‚ด์šฉ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”.
3. ์„œ์ˆ ์  ํ๋ฆ„์„ ์œ ์ง€ํ•˜๊ณ , ๋ถˆํ•„์š”ํ•œ ๋งˆํฌ๋‹ค์šด ์‚ฌ์šฉ์€ ํ”ผํ•˜์„ธ์š”.
4. ๊ธ€์˜ ์ „์ฒด ์ผ๊ด€์„ฑ๊ณผ ์‘์ง‘์„ฑ์„ ์œ ์ง€ํ•˜์„ธ์š”.
์›๋ณธ ๊ธ€:
{processed_content}
"""
# ํ™•์žฅ ์‹œ๋„
expanded_content = call_gemini_api(expansion_prompt, temperature=0.75)
processed_content = post_process_blog(expanded_content, style)
# ๋‹ค์‹œ ๊ธ€์ž ์ˆ˜ ์ฒดํฌ
char_count = len(processed_content)
logging.info(f"ํ™•์žฅ ํ›„ ๋ธ”๋กœ๊ทธ ๊ธ€ ๊ธ€์ž ์ˆ˜: {char_count}")
# HTML ๋ณ€ํ™˜
final_html = convert_to_html(processed_content)
return final_html
except Exception as e:
logging.error(f"๋ธ”๋กœ๊ทธ ๊ธ€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
return f"<p>๋ธ”๋กœ๊ทธ ๊ธ€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}</p>"
def get_style_prompt(style="์นœ๊ทผํ•œ"):
"""๋ธ”๋กœ๊ทธ ๊ธ€์˜ ์Šคํƒ€์ผ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋ฐ˜ํ™˜"""
prompts = {
"์นœ๊ทผํ•œ": """
[์นœ๊ทผํ•œ ํ•ต์‹ฌ๊ธฐ๋Šฅ ๋ฆฌ๋ทฐ ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ]
1. ํ†ค๊ณผ ์–ด์กฐ
- ๋Œ€ํ™”ํ•˜๋“ฏ ํŽธ์•ˆํ•˜๊ณ  ์นœ๊ทผํ•œ ๋งํˆฌ ์‚ฌ์šฉ (์˜ˆ: "์˜ค๋Š˜์€ ~์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ๊ฒŒ์š”")
- 1์ธ์นญ ์‹œ์ ์œผ๋กœ ์ง์ ‘ ์‚ฌ์šฉํ•œ ๊ฒฝํ—˜์„ ์ƒ์ƒํ•˜๊ฒŒ ํ‘œํ˜„
- ๊ตฌ์–ด์ฒด์™€ ์ผ์ƒ์ ์ธ ํ‘œํ˜„ ์‚ฌ์šฉํ•˜์—ฌ ์นœ๊ทผํ•จ ์œ ์ง€
2. ๋ฌธ์žฅ ๋ฐ ์–ดํˆฌ
- 'ํ•ด์š”์ฒด'๋กœ ์ž‘์„ฑ (์˜ˆ: "~ํ–ˆ์–ด์š”", "~์ธ ๊ฒƒ ๊ฐ™์•„์š”")
- ๋ฌธ์žฅ์€ ๊ธธ์ง€ ์•Š๊ฒŒ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์—ฐ๊ฒฐ
- ๊ธฐ์ˆ ์  ๋‚ด์šฉ๋„ ์‰ฝ๊ณ  ์ดํ•ดํ•˜๊ธฐ ํŽธํ•œ ํ‘œํ˜„์œผ๋กœ ์„ค๋ช…
3. ์ •๋ณด ์ „๋‹ฌ ๋ฐฉ์‹
- ๊ฐœ์ธ ๊ฒฝํ—˜๊ณผ ์ฒด๊ฐ์„ ์ค‘์‹ฌ์œผ๋กœ ์ •๋ณด ์ „๋‹ฌ
- ์ „๋ฌธ์ ์ธ ๋‚ด์šฉ๋„ ์ผ์ƒ์ ์ธ ๋น„์œ ์™€ ์˜ˆ์‹œ๋กœ ํ’€์–ด์„œ ์„ค๋ช…
- "์ œ๊ฐ€ ์‚ฌ์šฉํ•ด๋ณด๋‹ˆ~", "์‹ค์ œ๋กœ ๊ฒฝํ—˜ํ•ด๋ณด๋ฉด~"๊ณผ ๊ฐ™์€ ํ‘œํ˜„ ํ™œ์šฉ
- ๋…์ž์—๊ฒŒ ์ง์ ‘ ๋งํ•˜๋“ฏ ์ค‘๊ฐ„์ค‘๊ฐ„ "~ํ•˜์‹œ๋ฉด ์ข‹์•„์š”" ๊ฐ™์€ ์กฐ์–ธ ์ถ”๊ฐ€
""",
"์ผ๋ฐ˜": """
[์ผ๋ฐ˜์ ์ธ ํ•ต์‹ฌ๊ธฐ๋Šฅ ๋ฆฌ๋ทฐ ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ]
1. ํ†ค๊ณผ ์–ด์กฐ
- ๊ฐ๊ด€์ ์ด๊ณ  ์ค‘๋ฆฝ์ ์ธ ํ†ค ์œ ์ง€
- ์ง์ ‘์ ์ธ ๊ฒฝํ—˜๊ณผ ๊ฐ๊ด€์  ๋ฐ์ดํ„ฐ๋ฅผ ๊ท ํ˜• ์žˆ๊ฒŒ ํ™œ์šฉ
- ์กด๋Œ“๋ง ์‚ฌ์šฉํ•˜๋˜ ๋”ฑ๋”ฑํ•˜์ง€ ์•Š๊ฒŒ ํ‘œํ˜„
2. ๋ฌธ์žฅ ๋ฐ ์–ดํˆฌ
- 'ํ•ฉ๋‹ˆ๋‹ค์ฒด' ์‚ฌ์šฉ (์˜ˆ: "~ํ•ฉ๋‹ˆ๋‹ค", "~์ž…๋‹ˆ๋‹ค")
- ๋ช…ํ™•ํ•˜๊ณ  ๊ฐ„๊ฒฐํ•œ ๋ฌธ์žฅ ๊ตฌ์„ฑ
- ๋‚ด์šฉ์˜ ๋…ผ๋ฆฌ์  ํ๋ฆ„์„ ์ค‘์‹œ
3. ์ •๋ณด ์ „๋‹ฌ ๋ฐฉ์‹
- ์‚ฌ์‹ค๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ๋‚ด์šฉ ์ „๊ฐœ
- ๊ฐœ์ธ ๊ฒฝํ—˜๊ณผ ๊ฐ๊ด€์  ๋ถ„์„์„ ์ ์ ˆํžˆ ํ˜ผํ•ฉ
- ๋ถˆํ•„์š”ํ•œ ๊ณผ์žฅ์ด๋‚˜ ์ฃผ๊ด€์  ํ‰๊ฐ€ ์ตœ์†Œํ™”
- ์‹ค์šฉ์ ์ธ ๊ด€์ ์—์„œ ๊ธฐ๋Šฅ์˜ ์žฅ๋‹จ์  ๊ท ํ˜•์žˆ๊ฒŒ ์„œ์ˆ 
""",
"์ „๋ฌธ์ ์ธ": """
[์ „๋ฌธ์ ์ธ ํ•ต์‹ฌ๊ธฐ๋Šฅ ๋ฆฌ๋ทฐ ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ]
1. ํ†ค๊ณผ ์–ด์กฐ
- ์ „๋ฌธ์ ์ด๊ณ  ๋ถ„์„์ ์ธ ํ†ค ์‚ฌ์šฉ
- ๊ธฐ์ˆ ์  ๊นŠ์ด์™€ ์ •ํ™•์„ฑ ๊ฐ•์กฐ
- ์กด์ค‘๊ณผ ๊ถŒ์œ„๋ฅผ ๋А๋‚„ ์ˆ˜ ์žˆ๋Š” ํ‘œํ˜„ ์‚ฌ์šฉ
2. ๋ฌธ์žฅ ๋ฐ ์–ดํˆฌ
- 'ํ•ฉ๋‹ˆ๋‹ค์ฒด'๋กœ ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ ์ž‘์„ฑ
- ๋…ผ๋ฆฌ์ ์ด๊ณ  ์ฒด๊ณ„์ ์ธ ๋ฌธ์žฅ ๊ตฌ์„ฑ
- ์ „๋ฌธ ์šฉ์–ด๋ฅผ ์ ์ ˆํžˆ ํ™œ์šฉํ•˜๋˜ ํ•„์š”์‹œ ๊ฐ„๋žตํ•œ ์„ค๋ช… ์ œ๊ณต
3. ์ •๋ณด ์ „๋‹ฌ ๋ฐฉ์‹
- ๊ธฐ์ˆ ์  ์›๋ฆฌ์™€ ๋ฉ”์ปค๋‹ˆ์ฆ˜์— ๋Œ€ํ•œ ์‹ฌ์ธต ๋ถ„์„
- ๋ฒค์น˜๋งˆํฌ ๋ฐ์ดํ„ฐ์™€ ๊ตฌ์ฒด์  ์ˆ˜์น˜๋ฅผ ํ™œ์šฉํ•œ ๊ฐ๊ด€์  ํ‰๊ฐ€
- ๊ฒฝ์Ÿ ์ œํ’ˆ๊ณผ์˜ ์„ธ๋ถ€์ ์ธ ๊ธฐ์ˆ  ๋น„๊ต ์ œ๊ณต
- ๊ธฐ๋Šฅ์˜ ๊ธฐ์ˆ ์  ํ•œ๊ณ„์™€ ๋ฐœ์ „ ๊ฐ€๋Šฅ์„ฑ์— ๋Œ€ํ•œ ํ†ต์ฐฐ ์ œ์‹œ
"""
}
return prompts.get(style, prompts["์นœ๊ทผํ•œ"])
def call_gemini_api(prompt, temperature=TEMPERATURE, top_p=TOP_P):
"""Gemini API ํ˜ธ์ถœ ํ•จ์ˆ˜"""
try:
logging.info("Gemini API ํ˜ธ์ถœ ์‹œ์ž‘")
response = client.models.generate_content(
model="gemini-2.0-flash",
contents=[prompt],
config=types.GenerateContentConfig(
max_output_tokens=MAX_TOKENS,
temperature=temperature,
top_p=top_p
)
)
logging.info("Gemini API ํ˜ธ์ถœ ์™„๋ฃŒ")
return response.text.strip()
except Exception as e:
logging.error(f"Gemini API ํ˜ธ์ถœ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
return f"API ํ˜ธ์ถœ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
# API ํ•จ์ˆ˜๋“ค
def generate_outline_4(category, style, ref1, ref2, ref3):
features = generate_outline(category, style, ref1, ref2, ref3)
# 3๊ฐœ์˜ ๋ฌธ์ž์—ด ํŠœํ”Œ ๋ฐ˜ํ™˜
return (features[0], features[1], features[2])
def generate_blog_post_4(category, style, ref1, ref2, ref3, outline):
# outline์€ ์„ ํƒ๋œ ํ•ต์‹ฌ๊ธฐ๋Šฅ ์ด๋ฆ„
return generate_blog_post(category, style, ref1, ref2, ref3, outline)