oorr_4-1 / app.py
magictreee's picture
Update app.py
ee03c74 verified
import gradio as gr
import random
import os
import re
import openai
from fpdf import FPDF
from datetime import datetime
from zoneinfo import ZoneInfo
from sklearn.feature_extraction.text import CountVectorizer
# OpenAI API ํด๋ผ์ด์–ธํŠธ ์„ค์ •
openai.api_key = os.getenv("OPENAI_API_KEY")
def call_api(content, system_message, max_tokens, temperature, top_p):
try:
messages = [
{"role": "system", "content": system_message},
{"role": "user", "content": content}
]
response = openai.ChatCompletion.create(
model="gpt-4o-mini",
messages=messages,
max_tokens=max_tokens,
temperature=temperature,
top_p=top_p,
request_timeout=50
)
return response.choices[0].message['content']
except Exception as e:
print(f"API ํ˜ธ์ถœ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
raise
def analyze_info(data):
return f"์„ ํƒํ•œ ์นดํ…Œ๊ณ ๋ฆฌ: {data['category']}\n์„ ํƒํ•œ ํฌ์ŠคํŒ… ์Šคํƒ€์ผ: {data['style']}\n์ฐธ๊ณ  ๊ธ€1: {data['references1']}\n์ฐธ๊ณ  ๊ธ€2: {data['references2']}\n์ฐธ๊ณ  ๊ธ€3: {data['references3']}"
def generate_outline(category, style, references1, references2, references3):
data = {
'category': category,
'style': style,
'references1': references1,
'references2': references2,
'references3': references3
}
full_content = analyze_info(data)
system_prompt = get_outline_prompt(data['category']) + "\n\n" + get_style_prompt(data['style'])
modified_text = call_api(full_content, system_prompt, 2000, 0.7, 0.95)
# ํ•ต์‹ฌ๊ธฐ๋Šฅ์„ ๋ถ„๋ฆฌํ•˜์—ฌ ๋ฆฌ์ŠคํŠธ๋กœ ๋ฐ˜ํ™˜
features = re.findall(r'ํ•ต์‹ฌ๊ธฐ๋Šฅ : (.+)', modified_text)
return features[:3] # ์ตœ๋Œ€ 3๊ฐœ์˜ ํ•ต์‹ฌ๊ธฐ๋Šฅ๋งŒ ๋ฐ˜ํ™˜
def remove_unwanted_phrases(text):
unwanted_phrases = [
'์—ฌ๋Ÿฌ๋ถ„', '์ตœ๊ทผ', '๋งˆ์ง€๋ง‰์œผ๋กœ', '๊ฒฐ๋ก ์ ์œผ๋กœ', '๊ฒฐ๊ตญ',
'์ข…ํ•ฉ์ ์œผ๋กœ', '๋”ฐ๋ผ์„œ', '๋งˆ๋ฌด๋ฆฌ', '์š”์•ฝ'
]
words = re.findall(r'\S+|\n', text)
result_words = [word for word in words if not any(phrase in word for phrase in unwanted_phrases)]
return ' '.join(result_words).replace(' \n ', '\n').replace(' \n', '\n').replace('\n ', '\n')
def generate_blog_post(category, style, references1, references2, references3, outline):
try:
data = {
'category': category,
'style': style,
'references1': references1,
'references2': references2,
'references3': references3,
'outline': outline
}
system_prompt = get_blog_post_prompt(data['category'])
style_prompt = get_style_prompt(data['style'])
user_prompt = f"""
์นดํ…Œ๊ณ ๋ฆฌ: {data['category']}
ํฌ์ŠคํŒ… ์Šคํƒ€์ผ: {data['style']}
ํ•ต์‹ฌ๊ธฐ๋Šฅ: {data['outline']}
์ฐธ๊ณ ๊ธ€1: {data['references1']}
์ฐธ๊ณ ๊ธ€2: {data['references2']}
์ฐธ๊ณ ๊ธ€3: {data['references3']}
"""
blog_content = call_api(
user_prompt,
system_prompt + "\n" + style_prompt,
5000,
0.7,
0.95
)
filtered_post = remove_unwanted_phrases(blog_content)
filtered_post = filtered_post.lstrip()
html_post = convert_to_html(filtered_post)
return html_post
except Exception as e:
print(f"๊ธ€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
return ""
def convert_to_html(text):
lines = text.split('\n')
html_lines = []
for line in lines:
line = line.strip()
if line.startswith('####'):
html_lines.append(f"<h4>{line[4:].strip()}</h4>")
elif line.startswith('###'):
html_lines.append(f"<h3>{line[3:].strip()}</h3>")
elif line.startswith('##'):
html_lines.append(f"<h2>{line[2:].strip()}</h2>")
elif line.startswith('#'):
html_lines.append(f"<h1>{line[1:].strip()}</h1>")
elif line.startswith('- '): # ๋ฆฌ์ŠคํŠธ ์•„์ดํ…œ
html_lines.append(f"<li>{line[2:]}</li>")
elif line: # ์ผ๋ฐ˜ ํ…์ŠคํŠธ (๋นˆ ์ค„ ์ œ์™ธ)
# '**'๋กœ ๊ฐ์‹ธ์ง„ ๋ถ€๋ถ„์„ <strong> ํƒœ๊ทธ๋กœ ๋ณ€ํ™˜
line = re.sub(r'\*\*(.*?)\*\*', r'<strong>\1</strong>', line)
html_lines.append(f"<p>{line}</p>")
else: # ๋นˆ ์ค„
html_lines.append("<br>")
html_content = f"""
<div style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
{"".join(html_lines)}
</div>
"""
return html_content
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 get_outline_prompt(category):
if (category == "ํ•ต์‹ฌ๊ธฐ๋Šฅ์ง‘์ค‘ํ˜•"):
return """
[์ƒํ’ˆ๋ฆฌ๋ทฐ ์†Œ์ฃผ์ œ(Outline) ์ƒ์„ฑ ๊ทœ์น™]
[๊ธฐ๋ณธ๊ทœ์น™]
1. ๋ฐ˜๋“œ์‹œ ํ•œ๊ตญ์–ด(ํ•œ๊ธ€)๋กœ ์ž‘์„ฑํ•˜๋ผ
2. ๋ฐ˜๋“œ์‹œ ์ƒํ’ˆ์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ 3๊ฐœ๋งŒ ์„ ์ •ํ•˜๋ผ
- ํ•ต์‹ฌ๊ธฐ๋Šฅ์€ ์ฐธ๊ณ ๊ธ€์„ ๋ถ„์„ํ•˜์—ฌ ์„ ์ •ํ•˜๋ผ
4. ๋ฐ˜๋“œ์‹œ ํ•ต์‹ฌ ๊ธฐ๋Šฅ 3๊ฐœ์— ๋Œ€ํ•œ ํฅ๋ฏธ๋กœ์šด ์†Œ์ œ๋ชฉ๋งŒ ์ž‘์„ฑํ•˜๋ผ(์ ˆ๋Œ€ ๋‚ด์šฉ ์ž‘์„ฑ ๊ธˆ์ง€)
5. ๋ฐ˜๋“œ์‹œ ๋งˆํฌ๋‹ค์šด ํ˜•์‹์ด ์•„๋‹Œ ์ˆœ์ˆ˜ํ•œ ํ…์ŠคํŠธ๋กœ ์ถœ๋ ฅํ•˜๋ผ
6. ์†Œ์ œ๋ชฉ์€ ๋ฐ˜๋“œ์‹œ 30์ž ์ด๋‚ด๋กœ ์ž‘์„ฑํ•˜๋ผ
[์ถœ๋ ฅ์˜ˆ์‹œ]
ํ•ต์‹ฌ๊ธฐ๋Šฅ : ์„ ์ •ํ•œ ํ•ต์‹ฌ๊ธฐ๋Šฅ1์˜ ์†Œ์ œ๋ชฉ
ํ•ต์‹ฌ๊ธฐ๋Šฅ : ์„ ์ •ํ•œ ํ•ต์‹ฌ๊ธฐ๋Šฅ2์˜ ์†Œ์ œ๋ชฉ
ํ•ต์‹ฌ๊ธฐ๋Šฅ : ์„ ์ •ํ•œ ํ•ต์‹ฌ๊ธฐ๋Šฅ3์˜ ์†Œ์ œ๋ชฉ
"""
def get_blog_post_prompt(category):
if (category == "ํ•ต์‹ฌ๊ธฐ๋Šฅ์ง‘์ค‘ํ˜•"):
return """
[์ƒํ’ˆ๋ฆฌ๋ทฐ ์ฝ˜ํ…์ธ  ์ƒ์„ฑ ๊ทœ์น™]
[๊ธฐ๋ณธ๊ทœ์น™]
1. ๋ฐ˜๋“œ์‹œ ํ•œ๊ตญ์–ด(ํ•œ๊ธ€)๋กœ ์ž‘์„ฑํ•˜๋ผ
2. ๋ฐ˜๋“œ์‹œ ์ฃผ์–ด์ง„ ํ•ต์‹ฌ๊ธฐ๋Šฅ 1๊ฐœ์˜ ์ฃผ์ œ์— ๋Œ€ํ•ด์„œ๋งŒ ์ž‘์„ฑํ•˜๋ผ
- ์ ˆ๋Œ€๋กœ ์ฃผ์–ด์ง„ ํ•ต์‹ฌ๊ธฐ๋Šฅ 1๊ฐœ์˜ ์ฃผ์ œ ์ด์™ธ์˜ ๋‚ด์šฉ์„ ์ž‘์„ฑํ•˜์ง€ ๋ง ๊ฒƒ
3. ์ฃผ์–ด์ง„ ํ•ต์‹ฌ ๊ธฐ๋Šฅ 1๊ฐœ์— ๋Œ€ํ•œ ํŠน์žฅ์ , ๊ฐ์ข… ์ •๋ณด, ํŒ ๋“ฑ์„ ์ž์„ธํžˆ ์„ค๋ช…ํ•˜๋ผ
4. ์ „๋ฌธ ์šฉ์–ด๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž๋„ ์ดํ•ด๊ฐ€ ๋˜๋„๋ก ํ’€์–ด์„œ ์„ค๋ช…ํ•˜๋ผ
5. ๋งˆํฌ๋‹ค์šด ํ˜•์‹์œผ๋กœ ์ž‘์„ฑํ•˜๋ผ๋ผ
[ํ…์ŠคํŠธ ์ž‘์„ฑ ๊ทœ์น™]
1. ๋ฐ˜๋“œ์‹œ ์ž…๋ ฅ๋œ ์ฐธ๊ณ ๊ธ€ ์•ˆ์—์„œ ํ•ต์‹ฌ๊ธฐ๋Šฅ 1๊ฐœ์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ์ž‘์„ฑ
2. ์ ˆ๋Œ€๋กœ ์ฃผ์–ด์ง„ ํ•ต์‹ฌ๊ธฐ๋Šฅ 1๊ฐœ์˜ ์ฃผ์ œ ์ด์™ธ์˜ ๋‚ด์šฉ์„ ์ž‘์„ฑํ•˜์ง€ ๋ง ๊ฒƒ
3. ์ œ๊ณต๋œ ์ฐธ๊ณ ๊ธ€์˜ ์–ดํˆฌ๋ฅผ ๋ฐ˜์˜ํ•˜๋˜, ์ ˆ๋Œ€๋กœ ํ•œ ๋ฌธ์žฅ ์ด์ƒ ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅํ•˜์ง€ ๋ง ๊ฒƒ
4. ์‰ฝ๊ฒŒ ์ฝํž ์ˆ˜ ์žˆ๋„๋ก ์‰ฌ์šด ์–ดํœ˜๋กœ ์ž‘์„ฑ
5. ํ•ต์‹ฌ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์†Œ๋น„์ž ํƒ€๊ฒŸ์„ ๋ถ„์„ํ•˜์—ฌ ์ž‘์„ฑ
6. ์ฃผ์–ด์ง„ ํ•ต์‹ฌ๊ธฐ๋Šฅ 1๊ฐœ์— ๋งž๋Š” ์ŠคํŽ™, ๊ธฐ๋Šฅ, ์„ฑ๋Šฅ, ์žฅ๋‹จ์ , ๊ฐ€๊ฒฉ ๋Œ€๋น„ ์„ฑ๋Šฅ(๊ฐ€์„ฑ๋น„), ํŒ, ์ฃผ์˜์‚ฌํ•ญ ๋“ฑ์„ ์ž‘์„ฑ
7. ์ฃผ์–ด์ง„ ํ•ต์‹ฌ๊ธฐ๋Šฅ 1๊ฐœ์™€ ๊ด€๋ จ๋œ ๊ตฌ์ฒด์ ์ธ ์ˆ˜์น˜(๋ฐ์ดํ„ฐ)๊ฐ€ ์žˆ๋‹ค๋ฉด ๋ฐ˜์˜ํ•˜๋ผ
8. ๋ฐ˜๋“œ์‹œ ํ•ต์‹ฌ๊ธฐ๋Šฅ 1๊ฐœ์— ๋Œ€ํ•œ ์ฝ˜ํ…์ธ ๋งŒ 1800์ž ์ด์ƒ ์ž‘์„ฑํ•˜๋ผ
"""
def get_style_prompt(style):
prompts = {
"์นœ๊ทผํ•œ": """
[์นœ๊ทผํ•œ ํฌ์ŠคํŒ… ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ]
1. ํ†ค๊ณผ ์–ด์กฐ
- ๋Œ€ํ™”ํ•˜๋“ฏ ํŽธ์•ˆํ•˜๊ณ  ์นœ๊ทผํ•œ ๋งํˆฌ ์‚ฌ์šฉ
2. ๋ฌธ์žฅ ๋ฐ ์–ดํˆฌ
- ๋ฐ˜๋“œ์‹œ 'ํ•ด์š”์ฒด'๋กœ ์ž‘์„ฑ, ์ ˆ๋Œ€ '์Šต๋‹ˆ๋‹ค'์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋ง ๊ฒƒ.
- '~์š”'๋กœ ๋๋‚˜๋„๋ก ์ž‘์„ฑ, '~๋‹ค'๋กœ ๋๋‚˜์ง€ ์•Š๊ฒŒ ํ•˜๋ผ
- ๊ตฌ์–ด์ฒด ํ‘œํ˜„ ์‚ฌ์šฉ (์˜ˆ: "~ํ–ˆ์–ด์š”", "~์ธ ๊ฒƒ ๊ฐ™์•„์š”")
3. ์šฉ์–ด ๋ฐ ์„ค๋ช… ๋ฐฉ์‹
- ์ „๋ฌธ ์šฉ์–ด ๋Œ€์‹  ์‰ฌ์šด ๋‹จ์–ด๋กœ ํ’€์–ด์„œ ์„ค๋ช…
- ๋น„์œ ๋‚˜ ์€์œ ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋ณต์žกํ•œ ๊ฐœ๋… ์„ค๋ช…
- ์ˆ˜์‚ฌ์˜๋ฌธ๋ฌธ ํ™œ์šฉํ•˜์—ฌ ๋…์ž์™€ ์†Œํ†ตํ•˜๋Š” ๋Š๋‚Œ ์ฃผ๊ธฐ
์ฃผ์˜์‚ฌํ•ญ: ๋„ˆ๋ฌด ๊ฐ€๋ฒผ์šด ํ†ค์€ ์ง€์–‘ํ•˜๊ณ , ์ฃผ์ œ์˜ ์ค‘์š”์„ฑ์„ ํ•ด์น˜์ง€ ์•Š๋Š” ์„ ์—์„œ ์นœ๊ทผํ•จ ์œ ์ง€
(์˜ˆ์‹œ: ์ž‡๋‹˜๋“ค~ ์˜ค๋ ˆ์˜ค ์ฝ”์นด์ฝœ๋ผ๋ง›์ด์ƒˆ๋กœ ์ถœ์‹œ๊ฐ€ ๋๋‹ค๋Š”๊ฑฐ ์•Œ๊ณ  ๊ณ„์…จ๋‚˜์š”?!ใ…Ž ์˜ค๋ ˆ์˜ค ์ฝ”์นด์ฝœ๋ผ๋ง›์€ ์–ด๋–ค์ง€ ์†”์งํ‰๊ณผ๊ตฌ๋งค์ •๋ณด, ๊ฐ€๊ฒฉ, ์นผ๋กœ๋ฆฌ ๋“ฑ์— ๋Œ€ํ•ด ์ž์„ธ~ ํžˆ ์ ์–ด๋ณด๋„๋ก ํ• ๊ป˜์š”! ์˜ค๋ ˆ์˜ค๋ฅผ ์ข‹์•„ํ•˜๋Š” ์•„๋“ค์—๊ฒŒ๊ฐ„์‹์œผ๋กœ ์˜ค๋ ˆ์˜ค ์ฝ”์นด์ฝœ๋ผ๋ง›์„ ์คฌ๋”๋‹ˆ๋ง›์žˆ๋‹ค๊ณ  ์ข‹์•„ํ•˜๋”๋ผ๊ตฌ์š”. ์ฝœ๋ผํ–ฅ์ด ๋‚˜์„œ ๋” ๋งˆ์Œ์— ๋“ ๋‹ค๋ฉฐใ…Ž๊ฐœ์ธ์ ์œผ๋กœ๋Š” ๋ณ„ โญ๏ธโญ๏ธโญ๏ธ.์š”๊ฑด ๊ฐœ์ธ์ฐจ๊ฐ€ ์žˆ์„๊ฑฐ ๊ฐ™์•„์š”~)
""",
"์ผ๋ฐ˜": """
#์ผ๋ฐ˜์ ์ธ ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŒ… ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ
1. ํ†ค๊ณผ ์–ด์กฐ
- ์ค‘๋ฆฝ์ ์ด๊ณ  ๊ฐ๊ด€์ ์ธ ํ†ค ์œ ์ง€
- ์ ์ ˆํ•œ ์กด๋Œ“๋ง ์‚ฌ์šฉ (์˜ˆ: "~ํ•ฉ๋‹ˆ๋‹ค", "~์ž…๋‹ˆ๋‹ค")
2. ๋‚ด์šฉ ๊ตฌ์กฐ ๋ฐ ์ „๊ฐœ
- ๋ช…ํ™•ํ•œ ์ฃผ์ œ ์ œ์‹œ๋กœ ์‹œ์ž‘
- ๋…ผ๋ฆฌ์ ์ธ ์ˆœ์„œ๋กœ ์ •๋ณด ์ „๊ฐœ
- ์ฃผ์š” ํฌ์ธํŠธ๋ฅผ ๊ฐ•์กฐํ•˜๋Š” ์†Œ์ œ๋ชฉ ํ™œ์šฉ
- ์ ์ ˆํ•œ ๊ธธ์ด์˜ ๋‹จ๋ฝ์œผ๋กœ ๊ตฌ์„ฑ
3. ์šฉ์–ด ๋ฐ ์„ค๋ช… ๋ฐฉ์‹
- ์ผ๋ฐ˜์ ์œผ๋กœ ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์šด ์šฉ์–ด ์„ ํƒ
- ํ•„์š”์‹œ ๊ฐ„๋‹จํ•œ ์„ค๋ช… ์ถ”๊ฐ€
- ๊ฐ๊ด€์ ์ธ ์ •๋ณด ์ œ๊ณต์— ์ค‘์ 
4. ํ…์ŠคํŠธ ๊ตฌ์กฐํ™”
- ๋ถˆ๋ฆฟ ํฌ์ธํŠธ๋‚˜ ๋ฒˆํ˜ธ ๋งค๊ธฐ๊ธฐ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ •๋ณด ๊ตฌ์กฐํ™”
- ์ค‘์š”ํ•œ ์ •๋ณด๋Š” ๊ตต์€ ๊ธ€์”จ๋‚˜ ๊ธฐ์šธ์ž„๊ผด๋กœ ๊ฐ•์กฐ
5. ๋…์ž ์ƒํ˜ธ์ž‘์šฉ
- ์ ์ ˆํžˆ ๋…์ž์˜ ์ƒ๊ฐ์„ ๋ฌป๋Š” ์งˆ๋ฌธ ํฌํ•จ
- ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋Š” ํ‚ค์›Œ๋“œ ์ œ์‹œ
6. ๋งˆ๋ฌด๋ฆฌ
- ์ฃผ์š” ๋‚ด์šฉ ๊ฐ„๋‹จํžˆ ์š”์•ฝ
- ์ถ”๊ฐ€ ์ •๋ณด์— ๋Œ€ํ•œ ์•ˆ๋‚ด ์ œ๊ณต
์ฃผ์˜์‚ฌํ•ญ: ๋„ˆ๋ฌด ๋”ฑ๋”ฑํ•˜๊ฑฐ๋‚˜ ์ง€๋ฃจํ•˜์ง€ ์•Š๋„๋ก ๊ท ํ˜• ์œ ์ง€
์˜ˆ์‹œ: "์ตœ๊ทผ ํ™˜๊ฒฝ ๋ฌธ์ œ๊ฐ€ ๋Œ€๋‘๋˜๋ฉด์„œ '์ œ๋กœ ์›จ์ด์ŠคํŠธ' ๋ผ์ดํ”„์Šคํƒ€์ผ์— ๋Œ€ํ•œ ๊ด€์‹ฌ์ด ๋†’์•„์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ œ๋กœ ์›จ์ด์ŠคํŠธ๋ž€ ์ผ์ƒ์ƒํ™œ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์“ฐ๋ ˆ๊ธฐ๋ฅผ ์ตœ์†Œํ™”ํ•˜๋Š” ์ƒํ™œ ๋ฐฉ์‹์„ ๋งํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ธ€์—์„œ๋Š” ์ œ๋กœ ์›จ์ด์ŠคํŠธ์˜ ๊ฐœ๋…, ์‹ค์ฒœ ๋ฐฉ๋ฒ•, ๊ทธ๋ฆฌ๊ณ  ๊ทธ ํšจ๊ณผ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋จผ์ € ์ œ๋กœ ์›จ์ด์ŠคํŠธ์˜ ์ •์˜๋ถ€ํ„ฐ ์‚ดํŽด๋ณด๋ฉด...
""",
"์ „๋ฌธ์ ์ธ": """
#์ „๋ฌธ์ ์ธ ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŒ… ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ
1. ํ†ค๊ณผ ๊ตฌ์กฐ
- ๊ณต์‹์ ์ด๊ณ  ํ•™์ˆ ์ ์ธ ํ†ค ์‚ฌ์šฉ
- ๊ฐ๊ด€์ ์ด๊ณ  ๋ถ„์„์ ์ธ ์ ‘๊ทผ ์œ ์ง€
- ๋ช…ํ™•ํ•œ ์„œ๋ก , ๋ณธ๋ก , ๊ฒฐ๋ก  ๊ตฌ์กฐ
- ์ฒด๊ณ„์ ์ธ ๋…ผ์  ์ „๊ฐœ
- ์„ธ๋ถ€ ์„น์…˜์„ ์œ„ํ•œ ๋ช…ํ™•ํ•œ ์†Œ์ œ๋ชฉ ์‚ฌ์šฉ
2. ๋‚ด์šฉ ๊ตฌ์„ฑ ๋ฐ ์ „๊ฐœ
- ๋ณต์žกํ•œ ๊ฐœ๋…์„ ์ •ํ™•ํžˆ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์žฅ ๊ตฌ์กฐ ์‚ฌ์šฉ
- ๋…ผ๋ฆฌ์  ์—ฐ๊ฒฐ์„ ์œ„ํ•œ ์ „ํ™˜์–ด ํ™œ์šฉ
- ํ•ด๋‹น ๋ถ„์•ผ์˜ ์ „๋ฌธ ์šฉ์–ด ์ ๊ทน ํ™œ์šฉ (ํ•„์š”์‹œ ๊ฐ„๋žตํ•œ ์„ค๋ช… ์ œ๊ณต)
- ์‹ฌ์ธต์ ์ธ ๋ถ„์„๊ณผ ๋น„ํŒ์  ์‚ฌ๊ณ  ์ „๊ฐœ
- ๋‹ค์–‘ํ•œ ๊ด€์  ์ œ์‹œ ๋ฐ ๋น„๊ต
3. ๋ฐ์ดํ„ฐ ๋ฐ ๊ทผ๊ฑฐ ํ™œ์šฉ
- ํ†ต๊ณ„, ์—ฐ๊ตฌ ๊ฒฐ๊ณผ, ์ „๋ฌธ๊ฐ€ ์˜๊ฒฌ ๋“ฑ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ์ถœ์ฒ˜ ์ธ์šฉ
- ํ•„์š”์‹œ ๊ฐ์ฃผ๋‚˜ ์ฐธ๊ณ ๋ฌธํ—Œ ๋ชฉ๋ก ํฌํ•จ
- ์ˆ˜์น˜ ๋ฐ์ดํ„ฐ๋Š” ํ…์ŠคํŠธ๋กœ ๋ช…ํ™•ํžˆ ์„ค๋ช…
4. ํ…์ŠคํŠธ ๊ตฌ์กฐํ™”
- ๋…ผ๋ฆฌ์  ๊ตฌ์กฐ๋ฅผ ๊ฐ•์กฐํ•˜๊ธฐ ์œ„ํ•ด ๋ฒˆํ˜ธ ๋งค๊ธฐ๊ธฐ ์‚ฌ์šฉ
- ํ•ต์‹ฌ ๊ฐœ๋…์ด๋‚˜ ์šฉ์–ด๋Š” ๊ธฐ์šธ์ž„๊ผด๋กœ ๊ฐ•์กฐ
- ๊ธด ์ธ์šฉ๋ฌธ์€ ๋“ค์—ฌ์“ฐ๊ธฐ๋กœ ๊ตฌ๋ถ„
5. ๋งˆ๋ฌด๋ฆฌ
- ํ•ต์‹ฌ ๋…ผ์  ์žฌ๊ฐ•์กฐ
- ํ–ฅํ›„ ์—ฐ๊ตฌ ๋ฐฉํ–ฅ์ด๋‚˜ ์‹ค๋ฌด์  ํ•จ์˜ ์ œ์‹œ
์ฃผ์˜์‚ฌํ•ญ: ์ „๋ฌธ์„ฑ์„ ์œ ์ง€ํ•˜๋˜, ์™„์ „ํžˆ ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ค์šด ์ˆ˜์ค€์€ ์ง€์–‘
์˜ˆ์‹œ: "๋ณธ ์—ฐ๊ตฌ์—์„œ๋Š” ์ธ๊ณต์ง€๋Šฅ(AI)์˜ ์œค๋ฆฌ์  ํ•จ์˜์— ๋Œ€ํ•ด ๊ณ ์ฐฐํ•œ๋‹ค. ํŠนํžˆ, ์ž์œจ์ฃผํ–‰ ์ž๋™์ฐจ์˜ ์˜์‚ฌ๊ฒฐ์ • ์•Œ๊ณ ๋ฆฌ์ฆ˜์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์œค๋ฆฌ์  ๋”œ๋ ˆ๋งˆ์— ์ดˆ์ ์„ ๋งž์ถ˜๋‹ค. Bonnefon et al. (2016)์˜ ์—ฐ๊ตฌ์— ๋”ฐ๋ฅด๋ฉด, ์ž์œจ์ฃผํ–‰ ์ฐจ๋Ÿ‰์˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ์ง๋ฉดํ•  ์ˆ˜ ์žˆ๋Š” ์œค๋ฆฌ์  ์„ ํƒ์˜ ๋ณต์žก์„ฑ์ด ์ง€์ ๋œ ๋ฐ” ์žˆ๋‹ค. ๋ณธ๊ณ ์—์„œ๋Š” ์ด๋Ÿฌํ•œ ์œค๋ฆฌ์  ๋”œ๋ ˆ๋งˆ๋ฅผ ์„ธ ๊ฐ€์ง€ ์ฃผ์š” ๊ด€์ ์—์„œ ๋ถ„์„ํ•œ๋‹ค: 1) ๊ณต๋ฆฌ์ฃผ์˜์  ์ ‘๊ทผ, 2) ์˜๋ฌด๋ก ์  ์ ‘๊ทผ, 3) ๋• ์œค๋ฆฌ์  ์ ‘๊ทผ. ๊ฐ ์ ‘๊ทผ๋ฒ•์˜ ์žฅ๋‹จ์ ์„ ๋น„๊ต ๋ถ„์„ํ•˜๊ณ , ์ด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ž์œจ์ฃผํ–‰ ์ฐจ๋Ÿ‰์˜ ์œค๋ฆฌ์  ์˜์‚ฌ๊ฒฐ์ • ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ œ์•ˆํ•˜๊ณ ์ž ํ•œ๋‹ค...
"""
}
return prompts.get(style, "ํฌ์ŠคํŒ… ์Šคํƒ€์ผ ํ”„๋กฌํ”„ํŠธ")
def split_titles(suggested_titles):
titles = suggested_titles.split('\n')
titles = [re.sub(r'^(1\.|2\.|3\.|4\.|5\.|6\.|7\.|8\.|9\.|10\.|## |# |\* |\*\* |\*\*\*)', '', title.strip()) for title in titles if title.strip()]
titles = titles[::-1] # ๋ฆฌ์ŠคํŠธ๋ฅผ ์—ญ์ˆœ์œผ๋กœ ์ •๋ ฌ
titles += [""] * (10 - len(titles)) # 10๊ฐœ๋ณด๋‹ค ์ ์œผ๋ฉด ๋นˆ ๋ฌธ์ž์—ด๋กœ ์ฑ„์šฐ๊ธฐ
return titles[:10] # ์ตœ๋Œ€ 10๊ฐœ์˜ ์ œ๋ชฉ๋งŒ ๋ฐ˜ํ™˜
class PDF(FPDF):
def __init__(self):
super().__init__(orientation='P', unit='mm', format='A4')
self.set_margins(10, 10, 10)
current_dir = os.path.dirname(__file__)
self.add_font("NanumGothic", "", os.path.join(current_dir, "NanumGothic.ttf"))
self.add_font("NanumGothic", "B", os.path.join(current_dir, "NanumGothicBold.ttf"))
self.add_font("NanumGothicExtraBold", "", os.path.join(current_dir, "NanumGothicExtraBold.ttf"))
self.add_font("NanumGothicLight", "", os.path.join(current_dir, "NanumGothicLight.ttf"))
def header(self):
self.set_font('NanumGothic', '', 10)
# ํ—ค๋” ๋‚ด์šฉ ์ถ”๊ฐ€ (ํ•„์š”ํ•œ ๊ฒฝ์šฐ)
def footer(self):
self.set_y(-15)
self.set_font('NanumGothic', '', 8)
self.cell(0, 10, f'Page {self.page_no()}', 0, 0, 'C')
def chapter_title(self, title):
self.set_font("NanumGothic", 'B', 12)
self.cell(0, 6, title, 0, 1, 'L')
self.ln(4)
def chapter_body(self, body):
self.set_font("NanumGothic", '', 11)
self.multi_cell(0, 5, body)
self.ln()
def print_chapter(self, title, body):
self.add_page()
self.chapter_title(title)
self.chapter_body(body)
def format_filename(text):
if not isinstance(text, str):
text = str(text) # ๋ฌธ์ž์—ด์ด ์•„๋‹Œ ๊ฒฝ์šฐ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
text = re.sub(r'[^\w\s-]', '', text)
return text[:50].strip()
def save_to_pdf(blog_post, outline):
try:
pdf = PDF()
pdf.add_page()
# HTML ํƒœ๊ทธ๋ฅผ ํŒŒ์‹ฑํ•˜๊ธฐ ์œ„ํ•œ ์ •๊ทœํ‘œํ˜„์‹
tag_pattern = re.compile(r'<(/?)(\w+)([^>]*)>')
# ํ˜„์žฌ ๋‚ ์งœ์™€ ์‹œ๊ฐ„์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค (๋Œ€ํ•œ๋ฏผ๊ตญ ์‹œ๊ฐ„ ๊ธฐ์ค€)
now = datetime.now(ZoneInfo("Asia/Seoul"))
date_str = now.strftime("%y%m%d")
time_str = now.strftime("%H%M")
# ์ฒซ ๋ฒˆ์งธ ์ œ๋ชฉ์„ ์ฐพ์•„ ํŒŒ์ผ๋ช…์œผ๋กœ ์‚ฌ์šฉ
filename = f"{date_str}_{time_str}_{format_filename(outline)}.pdf"
# HTML ๋‚ด์šฉ์„ ์ˆœํšŒํ•˜๋ฉฐ PDF์— ์ž‘์„ฑ
current_tag = ''
buffer = ''
for part in re.split(tag_pattern, blog_post):
# HTML ํƒœ๊ทธ๊ฐ€ ๋“ฑ์žฅํ•œ ๊ฒฝ์šฐ
if part in ['h1', 'h2', 'h3', 'p', 'strong', 'li', 'br']:
# ์ด์ „ ํƒœ๊ทธ์˜ ๋‚ด์šฉ์„ ์ฒ˜๋ฆฌ
if buffer:
if current_tag in ['h1', 'h2', 'h3']:
pdf.chapter_title(buffer.strip()) # ์†Œ์ œ๋ชฉ ์ฒ˜๋ฆฌ
elif current_tag == 'p':
pdf.chapter_body(buffer.strip()) # ์ผ๋ฐ˜ ๋ฌธ๋‹จ ์ฒ˜๋ฆฌ
elif current_tag == 'strong':
pdf.set_font("NanumGothic", 'B', 11) # ๊ตต์€ ๊ธ€์”จ ์ฒ˜๋ฆฌ
pdf.cell(0, 5, buffer.strip(), 0, 1)
pdf.set_font("NanumGothic", '', 11) # ๋‹ค์‹œ ๊ธฐ๋ณธ ํฐํŠธ๋กœ ๋ณ€๊ฒฝ
elif current_tag == 'li':
pdf.chapter_body("โ€ข " + buffer.strip()) # ๋ฆฌ์ŠคํŠธ ์•„์ดํ…œ ์ฒ˜๋ฆฌ
elif current_tag == 'br':
pdf.ln(5) # ์ค„๋ฐ”๊ฟˆ ์ฒ˜๋ฆฌ
# ํ˜„์žฌ ํƒœ๊ทธ์™€ ๋ฒ„ํผ ์ดˆ๊ธฐํ™”
buffer = ''
current_tag = part
# ์ข…๋ฃŒ ํƒœ๊ทธ๋Š” ๋ฌด์‹œ (div ํฌํ•จ)
elif part.startswith('/'):
continue
# ํ…์ŠคํŠธ ๋ถ€๋ถ„ ์ฒ˜๋ฆฌ
elif not tag_pattern.match(part) and part.strip() != 'div':
buffer += part
# ๋งˆ์ง€๋ง‰ ๋ฒ„ํผ ์ฒ˜๋ฆฌ
if buffer:
if current_tag in ['h1', 'h2', 'h3']:
pdf.chapter_title(buffer.strip()) # ์ œ๋ชฉ ํ˜•์‹์œผ๋กœ ์ฒ˜๋ฆฌ
elif current_tag == 'p':
pdf.chapter_body(buffer.strip()) # ๋ณธ๋ฌธ ํ˜•์‹์œผ๋กœ ์ฒ˜๋ฆฌ
# PDF ์ €์žฅ
print(f"Saving PDF as: {filename}")
pdf.output(filename, 'F') # 'F' ์˜ต์…˜ ์ถ”๊ฐ€
return filename
except Exception as e:
print(f"PDF ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
import traceback
traceback.print_exc() # ์ƒ์„ธํ•œ ์˜ค๋ฅ˜ ์ •๋ณด ์ถœ๋ ฅ
return None # ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ None ๋ฐ˜ํ™˜
def save_content_to_pdf(blog_post, outline):
pdf_filename = save_to_pdf(blog_post, outline)
if pdf_filename:
return pdf_filename
else:
return None
title = "ํ•ต์‹ฌ๊ธฐ๋Šฅ์ง‘์ค‘ํ˜•"
with gr.Blocks() as demo:
gr.Markdown(f"# ํ•ต์‹ฌ๊ธฐ๋Šฅ์ง‘์ค‘ํ˜•")
gr.Markdown("### 1๋‹จ๊ณ„: ํฌ์ŠคํŒ… ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์ง€์ •ํ•ด์ฃผ์„ธ์š”", elem_id="step-title")
category = gr.Radio(choices=["ํ•ต์‹ฌ๊ธฐ๋Šฅ์ง‘์ค‘ํ˜•"], label="ํฌ์ŠคํŒ… ์นดํ…Œ๊ณ ๋ฆฌ", value="ํ•ต์‹ฌ๊ธฐ๋Šฅ์ง‘์ค‘ํ˜•")
gr.Markdown("---\n\n")
gr.Markdown("### 2๋‹จ๊ณ„: ํฌ์ŠคํŒ… ์Šคํƒ€์ผ์„ ์„ ํƒํ•ด์ฃผ์„ธ์š”", elem_id="step-title")
style = gr.Radio(choices=["์นœ๊ทผํ•œ", "์ผ๋ฐ˜", "์ „๋ฌธ์ ์ธ"], label="ํฌ์ŠคํŒ… ์Šคํƒ€์ผ", value="์นœ๊ทผํ•œ")
gr.Markdown("---\n\n")
gr.Markdown("### 3๋‹จ๊ณ„: ์ฐธ๊ณ  ๊ธ€์„ ์ž…๋ ฅํ•˜์„ธ์š”", elem_id="step-title")
references1 = gr.Textbox(label="์ฐธ๊ณ  ๊ธ€ 1", placeholder="์ฐธ๊ณ ํ•  ๊ธ€์„ ๋ณต์‚ฌํ•˜์—ฌ ๋ถ™์—ฌ๋„ฃ์œผ์„ธ์š”", lines=10)
references2 = gr.Textbox(label="์ฐธ๊ณ  ๊ธ€ 2", placeholder="์ฐธ๊ณ ํ•  ๊ธ€์„ ๋ณต์‚ฌํ•˜์—ฌ ๋ถ™์—ฌ๋„ฃ์œผ์„ธ์š”", lines=10)
references3 = gr.Textbox(label="์ฐธ๊ณ  ๊ธ€ 3", placeholder="์ฐธ๊ณ ํ•  ๊ธ€์„ ๋ณต์‚ฌํ•˜์—ฌ ๋ถ™์—ฌ๋„ฃ์œผ์„ธ์š”", lines=10)
gr.Markdown("---\n\n")
gr.Markdown("### 4๋‹จ๊ณ„: ํ•ต์‹ฌ๊ธฐ๋Šฅ์„ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”", elem_id="step-title")
gr.HTML("<span style='color: grey;'>[๋‚˜์˜จ ๊ฒฐ๊ณผ๋ฅผ ์ˆ˜์ •ํ•ด์„œ ์‚ฌ์šฉํ•ด์ฃผ์„ธ์š”]</span>")
outline_generate_btn = gr.Button("ํ•ต์‹ฌ๊ธฐ๋Šฅ ์„ ์ •ํ•˜๊ธฐ")
outline_result1 = gr.Textbox(label="ํ•ต์‹ฌ๊ธฐ๋Šฅ 1", lines=2)
outline_result2 = gr.Textbox(label="ํ•ต์‹ฌ๊ธฐ๋Šฅ 2", lines=2)
outline_result3 = gr.Textbox(label="ํ•ต์‹ฌ๊ธฐ๋Šฅ 3", lines=2)
outline_input = gr.Textbox(label="์ž‘์„ฑํ•  ํ•ต์‹ฌ๊ธฐ๋Šฅ ์ฃผ์ œ ์ค‘์— 1๊ฐœ๋งŒ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”", placeholder="ํ•ต์‹ฌ๊ธฐ๋Šฅ 1๊ฐœ๋งŒ ์ž…๋ ฅํ•˜์„ธ์š”", lines=2)
outline_generate_btn.click(
fn=generate_outline,
inputs=[category, style, references1, references2, references3],
outputs=[outline_result1, outline_result2, outline_result3]
)
gr.Markdown("---\n\n")
gr.Markdown("### 5๋‹จ๊ณ„: ๊ธ€ ์ƒ์„ฑํ•˜๊ธฐ", elem_id="step-title")
gr.HTML("<span style='color: grey;'>[ํ•ต์‹ฌ๊ธฐ๋Šฅ์„ ํ™•์ธํ•˜์„ธ์š”]</span>")
generate_btn = gr.Button("๋ธ”๋กœ๊ทธ ๊ธ€ ์ƒ์„ฑํ•˜๊ธฐ")
html_output = gr.HTML(label="์ƒ์„ฑ๋œ ๋ธ”๋กœ๊ทธ ๊ธ€")
generate_btn.click(
fn=generate_blog_post,
inputs=[category, style, references1, references2, references3, outline_input],
outputs=[html_output],
show_progress=True
)
save_pdf_btn = gr.Button("PDF๋กœ ์ €์žฅํ•˜๊ธฐ")
pdf_output = gr.File(label="์ƒ์„ฑ๋œ PDF ํŒŒ์ผ")
save_pdf_btn.click(
fn=save_content_to_pdf,
inputs=[html_output, outline_input], # outline_input ์ถ”๊ฐ€
outputs=[pdf_output],
show_progress=True
)
demo.launch()
gr.HTML("""
<style>
#step-title {
font-size: 1.7em;
font-weight: bold;
}
</style>
""")