|
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 |
|
|
|
|
|
logging.basicConfig(level=logging.INFO) |
|
|
|
|
|
TARGET_CHAR_LENGTH = 4000 |
|
MIN_SECTION_LENGTH = 600 |
|
MAX_TOKENS = 15000 |
|
TEMPERATURE = 0.75 |
|
TOP_P = 0.95 |
|
|
|
|
|
gemini_api_key = os.getenv("GEMINI_API_KEY") |
|
|
|
|
|
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๋ก ๋ณํ - ๊ฐ์ ๋ ๋ฒ์ """ |
|
|
|
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 = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', text) |
|
|
|
|
|
text = re.sub(r'\*([^*]+?)\*', r'<em>\1</em>', text) |
|
|
|
|
|
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_content = '\n'.join(result_lines) |
|
|
|
|
|
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) |
|
|
|
|
|
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} |
|
""" |
|
|
|
|
|
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) |
|
|
|
|
|
while len(features) < 5: |
|
features.append(f"ํน์ง {len(features) + 1} - ์ถ๊ฐ ์ค๋ช
ํ์") |
|
|
|
return features[: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} |
|
""" |
|
|
|
|
|
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}") |
|
|
|
|
|
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)}" |
|
|
|
|
|
def generate_outline_4(category, style, ref1, ref2, ref3): |
|
features = generate_outline(category, style, ref1, ref2, ref3) |
|
|
|
return (features[0], features[1], features[2]) |
|
|
|
def generate_blog_post_4(category, style, ref1, ref2, ref3, outline): |
|
|
|
return generate_blog_post(category, style, ref1, ref2, ref3, outline) |