Update product_blog.py
Browse files- product_blog.py +556 -1
product_blog.py
CHANGED
|
@@ -1,2 +1,557 @@
|
|
| 1 |
import os
|
| 2 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import os
|
| 2 |
+
import random
|
| 3 |
+
import re
|
| 4 |
+
import requests
|
| 5 |
+
import logging
|
| 6 |
+
from bs4 import BeautifulSoup
|
| 7 |
+
import html
|
| 8 |
+
import markdown2
|
| 9 |
+
from dotenv import load_dotenv # μΆκ°λ λΆλΆ
|
| 10 |
+
|
| 11 |
+
# νκ²½ λ³μ λ‘λ
|
| 12 |
+
load_dotenv() # μΆκ°λ λΆλΆ
|
| 13 |
+
|
| 14 |
+
# λ‘κΉ
μ€μ
|
| 15 |
+
logging.basicConfig(
|
| 16 |
+
level=logging.INFO,
|
| 17 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
# μμ μ μ
|
| 21 |
+
TARGET_CHAR_LENGTH = 4000 # μ΅μ’
κΈμμ λͺ©ν (λ¬Έμ)
|
| 22 |
+
MIN_SECTION_LENGTH = 300 # κ° μμ λͺ© μλ μ΅μ κΈμμ (μ ν νκΈ°λ 300μλ‘ μ€μ )
|
| 23 |
+
MAX_TOKENS = 15000 # Gemini API μ΅λ ν ν° μ
|
| 24 |
+
TEMPERATURE = 0.85 # Gemini API μ¨λ κ°
|
| 25 |
+
TOP_P = 0.9 # Gemini API top_p κ°
|
| 26 |
+
|
| 27 |
+
# API μ€μ
|
| 28 |
+
API_BASE_URL = os.getenv("API_BASE_URL", "").rstrip('/')
|
| 29 |
+
API_KEY = os.getenv("API_KEY", "")
|
| 30 |
+
API_HEADERS = {
|
| 31 |
+
"x-api-key": API_KEY,
|
| 32 |
+
"content-type": "application/json"
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
# API ν€ μ€μ
|
| 36 |
+
def load_gemini_api_keys():
|
| 37 |
+
# μ¬λ¬ κ°μ API ν€ λ‘λ
|
| 38 |
+
api_keys = [
|
| 39 |
+
os.getenv("GEMINI_API_KEY_1", ""),
|
| 40 |
+
os.getenv("GEMINI_API_KEY_2", ""),
|
| 41 |
+
os.getenv("GEMINI_API_KEY_3", ""),
|
| 42 |
+
os.getenv("GEMINI_API_KEY_4", ""),
|
| 43 |
+
os.getenv("GEMINI_API_KEY_5", "")
|
| 44 |
+
]
|
| 45 |
+
|
| 46 |
+
# λΉ ν€ μ κ±°
|
| 47 |
+
api_keys = [key for key in api_keys if key]
|
| 48 |
+
|
| 49 |
+
# κΈ°λ³Έ ν€κ° μμΌλ©΄ GEMINI_API_KEY νκ²½λ³μ μ¬μ©
|
| 50 |
+
if not api_keys:
|
| 51 |
+
default_key = os.getenv("GEMINI_API_KEY")
|
| 52 |
+
if default_key:
|
| 53 |
+
api_keys.append(default_key)
|
| 54 |
+
|
| 55 |
+
if not api_keys:
|
| 56 |
+
raise ValueError("API ν€κ° μ€μ λμ§ μμμ΅λλ€. .env νμΌμ GEMINI_API_KEY λλ GEMINI_API_KEY_1~5λ₯Ό μΆκ°νμΈμ.")
|
| 57 |
+
|
| 58 |
+
logging.info(f"μ΄ {len(api_keys)}κ°μ API ν€κ° λ‘λλμμ΅λλ€.")
|
| 59 |
+
return api_keys
|
| 60 |
+
|
| 61 |
+
# λ§€λ² λλ€νκ² API ν€ μ ννλ ν¨μ
|
| 62 |
+
def get_random_gemini_client():
|
| 63 |
+
"""λλ€ API ν€λ‘ Gemini ν΄λΌμ΄μΈνΈ μ΄κΈ°ν λ° λ°ν"""
|
| 64 |
+
from google import genai
|
| 65 |
+
from google.genai import types
|
| 66 |
+
|
| 67 |
+
api_keys = load_gemini_api_keys()
|
| 68 |
+
if not api_keys:
|
| 69 |
+
raise ValueError("μ¬μ© κ°λ₯ν API ν€κ° μμ΅λλ€.")
|
| 70 |
+
|
| 71 |
+
# λλ€νκ² API ν€ μΈλ±μ€ μ ν
|
| 72 |
+
random_index = random.randint(0, len(api_keys) - 1)
|
| 73 |
+
selected_key = api_keys[random_index]
|
| 74 |
+
|
| 75 |
+
logging.info(f"λλ€ API ν€ μ ν: μΈλ±μ€ {random_index + 1}")
|
| 76 |
+
|
| 77 |
+
# Gemini ν΄λΌμ΄μΈνΈ μ΄κΈ°ν λ° λ°ν
|
| 78 |
+
return genai.Client(api_key=selected_key)
|
| 79 |
+
|
| 80 |
+
# --- Google Gemini SDK μ΄κΈ°ν μ½λ μ κ±° ---
|
| 81 |
+
# κΈ°μ‘΄ μ½λ μ£Όμ μ²λ¦¬:
|
| 82 |
+
# from google import genai
|
| 83 |
+
# from google.genai import types
|
| 84 |
+
# client = genai.Client(api_key=gemini_api_key)
|
| 85 |
+
|
| 86 |
+
# νμν λͺ¨λμ μ¬μ ν μν¬νΈ
|
| 87 |
+
from google import genai
|
| 88 |
+
from google.genai import types
|
| 89 |
+
|
| 90 |
+
def fetch_references(product):
|
| 91 |
+
"""APIλ₯Ό ν΅ν΄ μ°Έκ³ μν 리뷰 κ°μ Έμ€κΈ°"""
|
| 92 |
+
try:
|
| 93 |
+
if not product or not product.strip():
|
| 94 |
+
return ["κ²μ ν€μλλ₯Ό μ
λ ₯ν΄μ£ΌμΈμ."] * 3
|
| 95 |
+
|
| 96 |
+
encoded_keyword = requests.utils.quote(product.strip())
|
| 97 |
+
url = f"{API_BASE_URL}/search/{encoded_keyword}"
|
| 98 |
+
|
| 99 |
+
logging.info(f"API νΈμΆ URL: {url}")
|
| 100 |
+
logging.info(f"API ν€λ: {API_HEADERS}")
|
| 101 |
+
|
| 102 |
+
response = requests.get(url, headers=API_HEADERS)
|
| 103 |
+
logging.info(f"API μλ΅ μν: {response.status_code}")
|
| 104 |
+
logging.info(f"API μλ΅ λ΄μ©: {response.text}")
|
| 105 |
+
|
| 106 |
+
if response.ok:
|
| 107 |
+
result = response.json()
|
| 108 |
+
return [
|
| 109 |
+
result.get("reference1", "μ°Έκ³ κΈ1μ μ°Ύμ μ μμ΅λλ€."),
|
| 110 |
+
result.get("reference2", "μ°Έκ³ κΈ2μ μ°Ύμ μ μμ΅λλ€."),
|
| 111 |
+
result.get("reference3", "μ°Έκ³ κΈ3μ μ°Ύμ μ μμ΅λλ€.")
|
| 112 |
+
]
|
| 113 |
+
else:
|
| 114 |
+
return [f"API μ€λ₯: {response.text}"] * 3
|
| 115 |
+
except Exception as e:
|
| 116 |
+
return [f"μ°Έκ³ κΈ μμ§ μ€ μ€λ₯ λ°μ: {str(e)}"] * 3
|
| 117 |
+
|
| 118 |
+
def fetch_crawl_results(query):
|
| 119 |
+
"""APIλ₯Ό ν΅ν΄ μν κ²μ κ²°κ³Ό κ°μ Έμ€κΈ° (μ°Έκ³ κΈ 3κ°)"""
|
| 120 |
+
references = fetch_references(query)
|
| 121 |
+
return references[0], references[1], references[2]
|
| 122 |
+
|
| 123 |
+
def get_style_prompt(style="μΉκ·Όν"):
|
| 124 |
+
prompts = {
|
| 125 |
+
"μΉκ·Όν": """
|
| 126 |
+
[μΉκ·Όν ν¬μ€ν
μ€νμΌ κ°μ΄λ]
|
| 127 |
+
1. ν€κ³Ό μ΄μ‘°
|
| 128 |
+
- λννλ― νΈμνκ³ μΉκ·Όν λ§ν¬ μ¬μ©
|
| 129 |
+
2. λ¬Έμ₯ λ° μ΄ν¬
|
| 130 |
+
- λ°λμ 'ν΄μ체'λ‘ μμ±, μ λ 'μ΅λλ€'체 μ¬μ©νμ§ λ§ κ²
|
| 131 |
+
- '~μ'λ‘ λλλλ‘ μμ±, '~λ€'λ‘ λλμ§ μκ² ν κ²
|
| 132 |
+
- ꡬμ΄μ²΄ νν μ¬μ© (μ: "~νμ΄μ", "~μΈ κ² κ°μμ")
|
| 133 |
+
- μ΄λͺ¨ν°μ½μ μ¬μ©νμ§ λ§μΈμ
|
| 134 |
+
3. μ©μ΄ λ° μ€λͺ
λ°©μ
|
| 135 |
+
- μ λ¬Έ μ©μ΄ λμ μ¬μ΄ λ¨μ΄ μ¬μ©
|
| 136 |
+
- λΉμ λ μμ λ₯Ό νμ©ν΄ 볡μ‘ν κ°λ
μ€λͺ
|
| 137 |
+
4. λ
μμμ μνΈμμ©
|
| 138 |
+
- λ
μ μ견μ 묻거λ λκΈ λ
λ € 문ꡬ μ¬μ©
|
| 139 |
+
μ£Όμμ¬ν: λ무 κ°λ²Όμ΄ ν€μ μ§μνκ³ , μ£Όμ μ μ€μμ±μ μ μ§ν κ²
|
| 140 |
+
""",
|
| 141 |
+
"μΌλ°μ μΈ": """
|
| 142 |
+
#μΌλ°μ μΈ λΈλ‘κ·Έ ν¬μ€ν
μ€νμΌ κ°μ΄λ
|
| 143 |
+
1. ν€κ³Ό μ΄μ‘°
|
| 144 |
+
- μ€λ¦½μ μ΄κ³ κ°κ΄μ μΈ ν€ μ μ§
|
| 145 |
+
- μ μ ν μ‘΄λλ§ μ¬μ© (μ: "~ν©λλ€", "~μ
λλ€")
|
| 146 |
+
2. λ΄μ© ꡬ쑰 λ° μ κ°
|
| 147 |
+
- λͺ
νν μ£Όμ μ μλ‘ μμ
|
| 148 |
+
- λ
Όλ¦¬μ μΈ μμλ‘ μ 보 μ κ°
|
| 149 |
+
- μμ λͺ© νμ©νμ¬ μ£Όμ ν¬μΈνΈ κ°μ‘°
|
| 150 |
+
- μ μ ν κΈΈμ΄μ λ¨λ½ ꡬμ±
|
| 151 |
+
3. μ©μ΄ λ° μ€λͺ
λ°©μ
|
| 152 |
+
- μ½κ² μ΄ν΄ν μ μλ μ©μ΄ μ¬μ©
|
| 153 |
+
- νμνλ©΄ κ°λ¨ν μ€λͺ
μΆκ°
|
| 154 |
+
4. λ
μ μνΈμμ©
|
| 155 |
+
- λ
μ μ견μ΄λ μΆκ° μ 보λ₯Ό μν ν€μλ μ μ
|
| 156 |
+
5. λ§λ¬΄λ¦¬
|
| 157 |
+
- μ£Όμ λ΄μ© μμ½ λ° μΆκ° μ 보 μλ΄
|
| 158 |
+
μ£Όμμ¬ν: λ무 λ±λ±νμ§ μκ³ κ· ν μ μ§
|
| 159 |
+
""",
|
| 160 |
+
"μ λ¬Έμ μΈ": """
|
| 161 |
+
#μ λ¬Έμ μΈ λΈλ‘κ·Έ ν¬μ€ν
μ€νμΌ κ°μ΄λ
|
| 162 |
+
1. ν€κ³Ό ꡬ쑰
|
| 163 |
+
- 곡μμ μ΄κ³ νμ μ μΈ ν€ μ¬μ©
|
| 164 |
+
- κ°κ΄μ μ΄κ³ λΆμμ μΈ μ κ·Ό μ μ§
|
| 165 |
+
- λͺ
νν μλ‘ , λ³Έλ‘ , κ²°λ‘ κ΅¬μ‘°
|
| 166 |
+
- 체κ³μ μΈ λ
Όμ μ κ°
|
| 167 |
+
2. λ΄μ© κ΅¬μ± λ° μ κ°
|
| 168 |
+
- 볡μ‘ν κ°λ
μ μ νν μ λ¬νλ λ¬Έμ₯ ꡬ쑰 μ¬μ©
|
| 169 |
+
- μ λ¬Έ μ©μ΄ μ κ·Ή νμ© (κ°λ¨ν μ€λͺ
ν¬ν¨)
|
| 170 |
+
- μ¬μΈ΅μ μΈ λΆμκ³Ό λΉκ΅, νκ° μ κ°
|
| 171 |
+
3. λ°μ΄ν° λ° κ·Όκ±° νμ©
|
| 172 |
+
- ν΅κ³, μ°κ΅¬ κ²°κ³Ό, μ λ¬Έκ° μ견 λ± μ λ’°μ± μλ μΆμ² μΈμ©
|
| 173 |
+
- μμΉ λ°μ΄ν°λ ν
μ€νΈλ‘ λͺ
νν μ€λͺ
|
| 174 |
+
4. λ§λ¬΄λ¦¬
|
| 175 |
+
- ν΅μ¬ λ
Όμ μ¬κ°μ‘° λ° ν₯ν μ λ§ μ μ
|
| 176 |
+
μ£Όμμ¬ν: μ λ¬Έμ±μ μ μ§νλ μ§λμΉκ² μ΄λ ΅μ§ μκ² μμ±
|
| 177 |
+
"""
|
| 178 |
+
}
|
| 179 |
+
return prompts.get(style, prompts["μΉκ·Όν"])
|
| 180 |
+
|
| 181 |
+
def get_product_review_prompt():
|
| 182 |
+
prompts = [
|
| 183 |
+
"""
|
| 184 |
+
[μ€μ: μν νκΈ° λΈλ‘κ·Έ κΈ μμ± νμ κ·μΉ]
|
| 185 |
+
μ΄ κ·μΉμ λ°λμ λ°λ₯΄μΈμ. μ΄λ€ μν©μμλ μμΈλ μμ΅λλ€:
|
| 186 |
+
1. λ§ν¬λ€μ΄ λ¬Έλ²(**, *, #, -, 1., 2., 3.)μ μ λ μ¬μ©νμ§ λ§μΈμ.
|
| 187 |
+
2. λͺ¨λ μμ λͺ©μ λ²νΈ μμ΄ μΌλ° λ¬Έμ₯μΌλ‘ μμ±νμΈμ.
|
| 188 |
+
3. λͺ¨λ λͺ©λ‘μ λΆλ¦Ώμ΄λ λ²νΈ μμ΄ μμ°μ€λ¬μ΄ λ¬Έμ₯μΌλ‘ μμ νμΈμ.
|
| 189 |
+
4. "μ°Έκ³ κΈ", "μ°Έκ³ κΈμ λ°λ₯΄λ©΄" λ±μ ννμ μ¬μ©νμ§ λ§μΈμ.
|
| 190 |
+
5. λΈλ‘κ·Έ μμ±μκ° μ§μ κ²½ννκ³ λλ λ΄μ©λ§ μμ±νμΈμ.
|
| 191 |
+
6. κΈμ μ£Όμ λ λ°λμ μ£Όμ΄μ§ μ°Έκ³ κΈμμ μν μ 보λ₯Ό μ μ νμ¬ μμ±ν κ².
|
| 192 |
+
[λΈλ‘κ·Έ μμ± μ€νμΌ]
|
| 193 |
+
- μΉκ·Όνκ³ λννλ λ―ν μ΄ν¬ μ¬μ© ("~ν΄μ", "~λ€μ")
|
| 194 |
+
- κ°μΈ κ²½νμ μ€μ¬μΌλ‘ ν μ€ν 리ν
λ§ λ°©μ
|
| 195 |
+
- κ° λ¨λ½μ΄ μμ°μ€λ½κ² μ°κ²°λλ νλ¦
|
| 196 |
+
- μμν κ°κ°μ λ¬μ¬μ μμ§ν κ°μ νν
|
| 197 |
+
- μν μ 보(κ°κ²©, ꡬμ±, κΈ°λ₯, μ₯λ¨μ λ±)μ μ¬μ© κ²½νμ μμ§νκ² ν¬ν¨
|
| 198 |
+
"""
|
| 199 |
+
]
|
| 200 |
+
return random.choice(prompts)
|
| 201 |
+
|
| 202 |
+
def remove_unwanted_phrases(text):
|
| 203 |
+
unwanted_phrases = [
|
| 204 |
+
'μ¬λ¬λΆ', 'μ΅κ·Ό', 'λ§μ§λ§μΌλ‘', 'κ²°λ‘ μ μΌλ‘', 'κ²°κ΅',
|
| 205 |
+
'μ’
ν©μ μΌλ‘', 'λ°λΌμ', 'λ§λ¬΄λ¦¬', 'λμΌλ‘', 'μμ½'
|
| 206 |
+
]
|
| 207 |
+
words = re.findall(r'\S+|\n', text)
|
| 208 |
+
result_words = [word for word in words if not any(phrase in word for phrase in unwanted_phrases)]
|
| 209 |
+
return ' '.join(result_words).replace(' \n ', '\n').replace(' \n', '\n').replace('\n ', '\n')
|
| 210 |
+
|
| 211 |
+
def post_process_blog(blog_content, style="μΉκ·Όν"):
|
| 212 |
+
"""μμ±λ λΈλ‘κ·Έ κΈμ μ€νμΌμ λ§κ² νμ²λ¦¬ν©λλ€."""
|
| 213 |
+
try:
|
| 214 |
+
# λ§ν¬λ€μ΄ νμ, λ²νΈ λ° λΆλ¦Ώ λͺ©λ‘, ν€λ© μ κ±°
|
| 215 |
+
blog_content = re.sub(r'^\d+\.\s+', '', blog_content, flags=re.MULTILINE)
|
| 216 |
+
blog_content = re.sub(r'^[\*\-\β’]\s+', '', blog_content, flags=re.MULTILINE)
|
| 217 |
+
blog_content = re.sub(r'^#+\s+', '', blog_content, flags=re.MULTILINE)
|
| 218 |
+
|
| 219 |
+
if style == "μΉκ·Όν":
|
| 220 |
+
blog_content = re.sub(r'([κ°-ν£]+)κ³ μ', r'\1ꡬμ', blog_content)
|
| 221 |
+
blog_content = re.sub(r'λ΅λλ€', 'μ΄μ', blog_content)
|
| 222 |
+
blog_content = re.sub(r'μλ΅λλ€', 'μμ΄μ', blog_content)
|
| 223 |
+
blog_content = re.sub(r'νλ΅λλ€', 'νμ΄μ', blog_content)
|
| 224 |
+
blog_content = re.sub(r'μ΅λλ€', 'μ', blog_content)
|
| 225 |
+
blog_content = re.sub(r'ν©λλ€', 'ν΄μ', blog_content)
|
| 226 |
+
blog_content = re.sub(r'λ©λλ€', 'λΌμ', blog_content)
|
| 227 |
+
blog_content = re.sub(r'μ
λλ€', 'μ΄μμ', blog_content)
|
| 228 |
+
|
| 229 |
+
# κ³Όμ₯λ νν μ 리 (μν, λ§, κ²½ν λ±)
|
| 230 |
+
exaggerated_expressions = [
|
| 231 |
+
(r'μλ²½ν λμμΈ', r'μ’μ λμμΈ'),
|
| 232 |
+
(r'λλ¨ν ν¨κ³Ό', r'μ’μ ν¨κ³Ό'),
|
| 233 |
+
(r'νκΈ°μ μΈ', r'μ μ©ν'),
|
| 234 |
+
(r'νμ μ μΈ', r'μλ‘μ΄'),
|
| 235 |
+
(r'μ΅μμ', r'μ’μ'),
|
| 236 |
+
(r'λ―Έμμ ν₯μ°', r'λ§μλ'),
|
| 237 |
+
(r'μ
λ§μ μ¬λ‘μ‘λ', r'λ§μλ'),
|
| 238 |
+
(r'ν©νν λ§', r'λ§μλ'),
|
| 239 |
+
(r'λΉν λ° μλ λ§', r'νΉλ³ν λ§'),
|
| 240 |
+
(r'μ²μμ λ§', r'λ§μλ'),
|
| 241 |
+
(r'νκ° κΈ°μ΅νλ', r'κΈ°μ΅μ λ¨λ'),
|
| 242 |
+
(r'μ λ μμ μ μλ λ§', r'κΈ°μ΅μ λ¨λ λ§'),
|
| 243 |
+
(r'κΈ°κ° λ§ν λ§', r'λ§μλ'),
|
| 244 |
+
(r'ν©νκ²½', r'μ’μ κ²½ν'),
|
| 245 |
+
(r'κ°μ΄μ΄ λ²
μ°¨μ€λ₯΄λ', r'κΈ°λΆ μ’μ'),
|
| 246 |
+
(r'κΏκ°μ μκ°', r'μ’μ μκ°'),
|
| 247 |
+
(r'μμ μ μλ κ²½ν', r'κΈ°μ΅μ λ¨λ κ²½ν'),
|
| 248 |
+
(r'κ°λμ μΈ', r'μΈμμ μΈ'),
|
| 249 |
+
(r'λ§μμ μ¬λ‘μ‘λ', r'μΈμμ μΈ'),
|
| 250 |
+
(r'μνΌμ μΉμ νλ', r'νΈμν'),
|
| 251 |
+
(r'μ΅κ³ μ μκ°', r'μ’μ μκ°'),
|
| 252 |
+
(r'νμ κΈ°μ΅μ λ¨μ', r'κΈ°μ΅μ λ¨λ'),
|
| 253 |
+
(r'μλ²½ν', r'μ’μ'),
|
| 254 |
+
(r'νμμ μΈ', r'μ’μ'),
|
| 255 |
+
(r'μ΅μμ', r'μ’μ'),
|
| 256 |
+
(r'κ²½μ΄λ‘μ΄', r'λλΌμ΄'),
|
| 257 |
+
(r'λ§€νΉμ μΈ', r'μ’μ'),
|
| 258 |
+
(r'κ·Όμ¬ν', r'μ’μ'),
|
| 259 |
+
(r'μ λ§μ΄μ§', r''),
|
| 260 |
+
(r'κ·Έ μ체', r''),
|
| 261 |
+
(r'μλ²½νκ²', r'μ'),
|
| 262 |
+
(r'λ©μ§κ²', r'μ'),
|
| 263 |
+
(r'λ§ν κ²λ μμ΄', r''),
|
| 264 |
+
(r'κΈ°λ μ΄μμΌλ‘', r'μμ보λ€'),
|
| 265 |
+
(r'λΉκ΅ν μ μμ΄', r''),
|
| 266 |
+
(r'μμμ μ΄μνλ', r'μμ λ°μ')
|
| 267 |
+
]
|
| 268 |
+
for pattern, replacement in exaggerated_expressions:
|
| 269 |
+
blog_content = re.sub(pattern, replacement, blog_content, flags=re.IGNORECASE)
|
| 270 |
+
|
| 271 |
+
blog_content = re.sub(r'μ°Έκ³ κΈμ λ°λ₯΄λ©΄', r'μ κ° μκ² λ λ°λ‘λ', blog_content)
|
| 272 |
+
blog_content = re.sub(r'μ°Έκ³ κΈ', r'κ΄λ ¨ μ 보', blog_content)
|
| 273 |
+
|
| 274 |
+
# μ€νμΌλ³ 문체 μ§μλ₯Ό ν¬ν¨ν Gemini API ν둬ννΈ μμ± (νμ²λ¦¬ μ©λ)
|
| 275 |
+
prompt = f"""
|
| 276 |
+
λ€μ μν νκΈ° λΈλ‘κ·Έ κΈμ λ μμ°μ€λ½κ² λ³κ²½ν΄μ£ΌμΈμ:
|
| 277 |
+
|
| 278 |
+
μλ³Έ κΈ:
|
| 279 |
+
{blog_content}
|
| 280 |
+
|
| 281 |
+
λ³κ²½ μꡬμ¬ν:
|
| 282 |
+
1. λ§ν¬λ€μ΄ νμ, λ²νΈ, λΆλ¦Ώ νν λͺ¨λ μ κ±°νκ³ μμ°μ€λ¬μ΄ λ¬Έμ₯μΌλ‘ λ³κ²½
|
| 283 |
+
2. μμ λͺ©μ 5κ° μ΄νλ‘ μμ±νλ©°, κ° μμ λͺ© μλ λ΄μ©μ μ΅μ {MIN_SECTION_LENGTH}μ μ΄μμΌλ‘ μμΈνκ² μμ
|
| 284 |
+
3. "μ°Έκ³ κΈ", "μ°Έκ³ κΈμ λ°λ₯΄λ©΄" λ±μ νν μ κ±°
|
| 285 |
+
4. μν μ¬μ© κ²½ν, κ°μ, ν λ±μ μΆκ°νμ¬ λΆμ‘±ν λΆλΆ 보μ
|
| 286 |
+
5. μνμ ꡬ체μ μΈ μ 보(κ°κ²©, ꡬμ±, μ¬μ©λ², μ₯λ¨μ λ±)λ₯Ό μμ°μ€λ½κ² ν¬ν¨
|
| 287 |
+
6. μ€νμΌ: {style} (μμ°μ€λ¬μ΄ 문체 μ¬μ©)
|
| 288 |
+
"""
|
| 289 |
+
# λλ€ ν΄λΌμ΄μΈνΈ μ¬μ©μΌλ‘ λ³κ²½
|
| 290 |
+
client = get_random_gemini_client()
|
| 291 |
+
|
| 292 |
+
response = client.models.generate_content(
|
| 293 |
+
model="gemini-2.0-flash",
|
| 294 |
+
contents=[prompt],
|
| 295 |
+
config=types.GenerateContentConfig(
|
| 296 |
+
max_output_tokens=MAX_TOKENS,
|
| 297 |
+
temperature=0.7,
|
| 298 |
+
top_p=0.9
|
| 299 |
+
)
|
| 300 |
+
)
|
| 301 |
+
return response.text.strip()
|
| 302 |
+
except Exception as e:
|
| 303 |
+
logging.error(f"λΈλ‘κ·Έ κΈ νμ²λ¦¬ μ€ μ€λ₯ λ°μ: {str(e)}")
|
| 304 |
+
return blog_content
|
| 305 |
+
|
| 306 |
+
def format_blog_post(blog_post, query=""):
|
| 307 |
+
# λ΄μ© κ·Έλλ‘ μ μ§
|
| 308 |
+
blog_post = re.sub(r'^#+\s+', '', blog_post, flags=re.MULTILINE)
|
| 309 |
+
blog_post = re.sub(r'^\d+\.\s+', '', blog_post, flags=re.MULTILINE)
|
| 310 |
+
blog_post = re.sub(r'^[\*\-]\s+', '', blog_post, flags=re.MULTILINE)
|
| 311 |
+
|
| 312 |
+
lines = blog_post.split('\n')
|
| 313 |
+
formatted_lines = []
|
| 314 |
+
in_paragraph = False
|
| 315 |
+
first_line = True
|
| 316 |
+
title_found = False
|
| 317 |
+
|
| 318 |
+
first_non_empty_line = ""
|
| 319 |
+
for line in lines:
|
| 320 |
+
if line.strip():
|
| 321 |
+
first_non_empty_line = line.strip()
|
| 322 |
+
break
|
| 323 |
+
|
| 324 |
+
subtitle_patterns = [
|
| 325 |
+
r'^.{10,100}\?$',
|
| 326 |
+
r'^.{10,100}:$',
|
| 327 |
+
r'^.{5,50}μ [κ°-ν£\s]+$',
|
| 328 |
+
r'^[κ°-ν£\s]{5,50} [κ°-ν£\s]+$'
|
| 329 |
+
]
|
| 330 |
+
previous_line_empty = True
|
| 331 |
+
|
| 332 |
+
def optimize_title(title, max_length=60):
|
| 333 |
+
if ': ' in title and len(title) > max_length:
|
| 334 |
+
title = title.split(': ')[0]
|
| 335 |
+
if len(title) > max_length:
|
| 336 |
+
title = re.sub(r'\s*\([^)]*\)', '', title)
|
| 337 |
+
if len(title) > max_length and ',' in title:
|
| 338 |
+
title = title.split(',')[0]
|
| 339 |
+
if len(title) > max_length:
|
| 340 |
+
words = title.split()
|
| 341 |
+
shortened_title = []
|
| 342 |
+
current_length = 0
|
| 343 |
+
for word in words:
|
| 344 |
+
if current_length + len(word) + 1 <= max_length:
|
| 345 |
+
shortened_title.append(word)
|
| 346 |
+
current_length += len(word) + 1
|
| 347 |
+
else:
|
| 348 |
+
break
|
| 349 |
+
title = ' '.join(shortened_title)
|
| 350 |
+
endings_to_remove = ['κ·Έ', 'μ΄', 'μ', 'λ', 'μ΄λ', 'μ', 'κ³Ό', 'λλ', 'κ·Έλ¦¬κ³ ']
|
| 351 |
+
for ending in endings_to_remove:
|
| 352 |
+
if title.endswith(f" {ending}"):
|
| 353 |
+
title = title[:-len(ending)-1]
|
| 354 |
+
if len(title) < 20 and query:
|
| 355 |
+
title = f"{query} μ¬μ© νκΈ°"
|
| 356 |
+
return title
|
| 357 |
+
|
| 358 |
+
for i, line in enumerate(lines):
|
| 359 |
+
line = line.strip()
|
| 360 |
+
next_line_empty = (i + 1 >= len(lines)) or not lines[i+1].strip()
|
| 361 |
+
|
| 362 |
+
if not line:
|
| 363 |
+
if in_paragraph:
|
| 364 |
+
formatted_lines.append("</p>")
|
| 365 |
+
in_paragraph = False
|
| 366 |
+
formatted_lines.append("<br>")
|
| 367 |
+
previous_line_empty = True
|
| 368 |
+
else:
|
| 369 |
+
if first_line and len(line) > 5:
|
| 370 |
+
optimized_title = optimize_title(line)
|
| 371 |
+
formatted_lines.append(f'<h1 style="font-size: 1.8em; margin-bottom: 20px; font-weight: bold; color: #222;">{html.escape(optimized_title)}</h1>')
|
| 372 |
+
first_line = False
|
| 373 |
+
title_found = True
|
| 374 |
+
previous_line_empty = False
|
| 375 |
+
else:
|
| 376 |
+
is_subtitle = False
|
| 377 |
+
if any(re.match(pattern, line) for pattern in subtitle_patterns):
|
| 378 |
+
is_subtitle = True
|
| 379 |
+
elif previous_line_empty and next_line_empty and len(line) < 80:
|
| 380 |
+
is_subtitle = True
|
| 381 |
+
if is_subtitle:
|
| 382 |
+
if in_paragraph:
|
| 383 |
+
formatted_lines.append("</p>")
|
| 384 |
+
in_paragraph = False
|
| 385 |
+
formatted_lines.append(f'<h2 style="font-size: 1.3em; margin-top: 25px; margin-bottom: 15px; font-weight: bold; color: #333;">{html.escape(line)}</h2>')
|
| 386 |
+
else:
|
| 387 |
+
if not in_paragraph:
|
| 388 |
+
formatted_lines.append("<p>")
|
| 389 |
+
in_paragraph = True
|
| 390 |
+
content = html.escape(line)
|
| 391 |
+
bold_content = re.sub(r'\*\*(.*?)\*\*', r'<strong>\1</strong>', content)
|
| 392 |
+
formatted_lines.append(bold_content)
|
| 393 |
+
previous_line_empty = False
|
| 394 |
+
|
| 395 |
+
if in_paragraph:
|
| 396 |
+
formatted_lines.append("</p>")
|
| 397 |
+
|
| 398 |
+
if not title_found:
|
| 399 |
+
default_title = f"{query} μ¬μ© νκΈ°" if query else "μν νκΈ°"
|
| 400 |
+
if first_non_empty_line:
|
| 401 |
+
default_title = optimize_title(first_non_empty_line)
|
| 402 |
+
formatted_lines.insert(0, f'<h1 style="font-size: 1.8em; margin-bottom: 20px; font-weight: bold; color: #222;">{html.escape(default_title)}</h1>')
|
| 403 |
+
|
| 404 |
+
return '\n'.join(formatted_lines)
|
| 405 |
+
|
| 406 |
+
# Gemini API νΈμΆ ν¬νΌ ν¨μ μμ
|
| 407 |
+
def call_gemini_api(prompt):
|
| 408 |
+
# λ§€λ² μλ‘μ΄ λλ€ ν΄λΌμ΄μΈνΈ κ°μ Έμ€κΈ°
|
| 409 |
+
client = get_random_gemini_client()
|
| 410 |
+
|
| 411 |
+
response = client.models.generate_content(
|
| 412 |
+
model="gemini-2.0-flash",
|
| 413 |
+
contents=[prompt],
|
| 414 |
+
config=types.GenerateContentConfig(
|
| 415 |
+
max_output_tokens=MAX_TOKENS,
|
| 416 |
+
temperature=TEMPERATURE,
|
| 417 |
+
top_p=TOP_P
|
| 418 |
+
)
|
| 419 |
+
)
|
| 420 |
+
return response.text.strip()
|
| 421 |
+
|
| 422 |
+
def generate_blog_post(query, style="μΉκ·Όν"):
|
| 423 |
+
try:
|
| 424 |
+
# λͺ©ν κΈμμ μ€μ
|
| 425 |
+
target_char_length = TARGET_CHAR_LENGTH
|
| 426 |
+
|
| 427 |
+
# μ°Έκ³ κΈ κ°μ Έμ€κΈ° (API μ¬μ©)
|
| 428 |
+
ref1, ref2, ref3 = fetch_crawl_results(query)
|
| 429 |
+
style_prompt = get_style_prompt(style)
|
| 430 |
+
format_prompt = get_product_review_prompt()
|
| 431 |
+
|
| 432 |
+
# μ€νμΌ μΈλΆ μ§μμ¬ν κ°ν
|
| 433 |
+
style_specific_instructions = ""
|
| 434 |
+
if style == "μΉκ·Όν":
|
| 435 |
+
style_specific_instructions = """
|
| 436 |
+
μ΄ λΈλ‘κ·Έλ λ°λμ μΉκ·Όν λνμ²΄λ‘ μμ±λμ΄μΌ ν©λλ€.
|
| 437 |
+
- 'ν΄μ체' μ¬μ©: "~νμ΄μ", "~μΈ κ² κ°μμ", "~νλ€μ"
|
| 438 |
+
- 격μ체(μ: "~ν©λλ€", "~μ
λλ€") μ¬μ© κΈμ§
|
| 439 |
+
- λννλ― νΈμνκ² μμ±
|
| 440 |
+
"""
|
| 441 |
+
elif style == "μΌλ°μ μΈ":
|
| 442 |
+
style_specific_instructions = """
|
| 443 |
+
μ΄ λΈλ‘κ·Έλ λ°λμ μΌλ°μ μΈ μ‘΄λλ§μ²΄λ‘ μμ±λμ΄μΌ ν©λλ€.
|
| 444 |
+
- 'μ΅λλ€μ²΄' μ¬μ©: "~νμ΅λλ€", "~μ
λλ€", "~νμμ΅λλ€"
|
| 445 |
+
- κ°κ΄μ μ΄κ³ λͺ
ννκ² μμ ν κ²
|
| 446 |
+
"""
|
| 447 |
+
elif style == "μ λ¬Έμ μΈ":
|
| 448 |
+
style_specific_instructions = """
|
| 449 |
+
μ΄ λΈλ‘κ·Έλ λ°λμ μ λ¬Έμ μ΄κ³ λΆμμ μΈ μ΄ν¬λ‘ μμ±λμ΄μΌ ν©λλ€.
|
| 450 |
+
- 'μ΅λλ€μ²΄' μ¬μ©: "~νμ΅λλ€", "~μ
λλ€", "~νμμ΅λλ€"
|
| 451 |
+
- λ°μ΄ν°μ ꡬ체μ μΈ μ 보 ν¬ν¨
|
| 452 |
+
"""
|
| 453 |
+
|
| 454 |
+
# Phase 1: μ΄κΈ° μμ±
|
| 455 |
+
initial_prompt = f"""
|
| 456 |
+
μ£Όμ : {query} μν νκΈ°
|
| 457 |
+
μ°Έκ³ κΈ 1: {ref1}
|
| 458 |
+
μ°Έκ³ κΈ 2: {ref2}
|
| 459 |
+
μ°Έκ³ κΈ 3: {ref3}
|
| 460 |
+
λͺ©ν κΈμμ: {target_char_length}
|
| 461 |
+
{format_prompt}
|
| 462 |
+
μ€νμΌ κ°μ΄λ:
|
| 463 |
+
{style_prompt}
|
| 464 |
+
μ€νμΌ μΈλΆ μ§μμ¬ν:
|
| 465 |
+
{style_specific_instructions}
|
| 466 |
+
νΉλ³ μ§μμ¬ν:
|
| 467 |
+
1. κΈμ μ²μμ λͺ
ννκ³ λ§€λ ₯μ μΈ μ λͺ©μ ν¬ν¨ν κ² (μνλͺ
κ³Ό ν΅μ¬ νΉμ§ ν¬ν¨).
|
| 468 |
+
2. μ λͺ© μμ: "{query} μ¬μ© νκΈ°: [ν΅μ¬ νΉμ§/μ₯μ ]", "{query}, [νΉλ³ν μ ] μμ§ νκΈ°"
|
| 469 |
+
3. λ§ν¬λ€μ΄ λ¬Έλ²(#, *, -, 1., 2. λ±)μ μ¬μ©νμ§ μμ κ².
|
| 470 |
+
4. μμ λͺ©μ λ²νΈ μμ΄ μΌλ° λ¬Έμ₯μΌλ‘ μμ±νκ³ , μμ λͺ© μλ₯Ό 5κ° μ΄νλ‘ μ ν.
|
| 471 |
+
5. λͺ¨λ λͺ©λ‘μ λΆλ¦Ώμ΄λ λ²νΈ μμ΄ μμ°μ€λ¬μ΄ λ¬Έμ₯μΌλ‘ μμ .
|
| 472 |
+
6. "μ°Έκ³ κΈ" κ΄λ ¨ ννμ μ¬μ©νμ§ μμ κ².
|
| 473 |
+
7. μμ±μμ μ€οΏ½οΏ½ κ²½νκ³Ό κ°μμ λ°νμΌλ‘ μμ±.
|
| 474 |
+
8. μνμ μ 보(κ°κ²©, ꡬμ±, κΈ°λ₯, μ₯λ¨μ , μ¬μ©λ² λ±)λ₯Ό ν¬ν¨ν κ².
|
| 475 |
+
9. κΈμμλ μ΅μ {target_char_length}μ μ΄μμ΄μ΄μΌ ν¨.
|
| 476 |
+
"""
|
| 477 |
+
first_attempt = call_gemini_api(initial_prompt)
|
| 478 |
+
first_attempt_cleaned = remove_unwanted_phrases(first_attempt)
|
| 479 |
+
first_attempt_length = len(first_attempt_cleaned)
|
| 480 |
+
|
| 481 |
+
if first_attempt_length >= target_char_length:
|
| 482 |
+
final_post = post_process_blog(first_attempt_cleaned, style)
|
| 483 |
+
final_html = format_blog_post(final_post, query)
|
| 484 |
+
return final_html, ref1, ref2, ref3, first_attempt_length
|
| 485 |
+
|
| 486 |
+
# Phase 2: ν΄κ³ (Revision) μλ
|
| 487 |
+
longest_ref = max([ref1, ref2, ref3], key=len)
|
| 488 |
+
revision_prompt = f"""
|
| 489 |
+
μ΄μ κΈ:
|
| 490 |
+
{first_attempt_cleaned}
|
| 491 |
+
μ°Έκ³ κΈ: {longest_ref}
|
| 492 |
+
ν¬μ€ν
μ€νμΌ:
|
| 493 |
+
{style_prompt}
|
| 494 |
+
μ€νμΌ μΈλΆ μ§μμ¬ν:
|
| 495 |
+
{style_specific_instructions}
|
| 496 |
+
λ¬Έμ μ :
|
| 497 |
+
μμ±λ κΈμ΄ λͺ©ν κΈμμ {target_char_length}μμ λ―ΈμΉμ§ λͺ»νλ©°, κ° μμ λͺ© μλ λ΄μ©μ΄ λΆμ€ν©λλ€.
|
| 498 |
+
μ€μ μꡬμ¬ν:
|
| 499 |
+
1. μνλͺ
({query})κ³Ό ν΅μ¬ νΉμ§μ΄ ν¬ν¨λ λ§€λ ₯μ μΈ μ λͺ© μΆκ°.
|
| 500 |
+
2. κΈμμλ₯Ό μ΅μ {target_char_length}μ μ΄μμΌλ‘ λλ¦¬κ³ , κ° μΉμ
μ μμΈνκ² μμ ν κ².
|
| 501 |
+
3. λ§ν¬λ€μ΄ λ¬Έλ²(#, *, -, 1., 2. λ±)μ μ¬μ©νμ§ μμ κ².
|
| 502 |
+
4. μμ λͺ©μ λ²νΈ μμ΄ μμ±.
|
| 503 |
+
5. λͺ¨λ λͺ©λ‘μ λΆλ¦Ώμ΄λ λ²νΈ μμ΄ μμ°μ€λ½κ² μμ .
|
| 504 |
+
6. "μ°Έκ³ κΈ" κ΄λ ¨ νν μ¬μ© κΈμ§.
|
| 505 |
+
7. μμ±μμ μ€μ μ¬μ© κ²½νκ³Ό κ°μμ ν¬ν¨ν κ².
|
| 506 |
+
8. κ° μμ λͺ© μλ λ΄μ©μ μ΅μ {MIN_SECTION_LENGTH}μ μ΄μ μμ ν κ².
|
| 507 |
+
9. μμ λͺ© μλ 5κ° μ΄νλ‘ μ ν.
|
| 508 |
+
μμΈ λ³΄μ:
|
| 509 |
+
- μν λμμΈ, μ¬μ§, ꡬμ±ν λ± μΈκ΄μ λν μμΈν λ¬μ¬.
|
| 510 |
+
- ꡬ맀 κ³Όμ , μΈλ°μ±, μ¬μ© κ³Όμ λ± κ΅¬μ²΄μ μΈ μνΌμλ μΆκ°.
|
| 511 |
+
- κΈ°λ₯κ³Ό μ¬μ©λ², μ₯λ¨μ μ λν κ· ν μ‘ν νκ°.
|
| 512 |
+
- κ°κ²© λλΉ κ°μΉμ λν μμ§ν μ견.
|
| 513 |
+
"""
|
| 514 |
+
revised_attempt = call_gemini_api(revision_prompt)
|
| 515 |
+
revised_cleaned = remove_unwanted_phrases(revised_attempt)
|
| 516 |
+
final_post = post_process_blog(revised_cleaned, style)
|
| 517 |
+
final_html = format_blog_post(final_post, query)
|
| 518 |
+
|
| 519 |
+
soup = BeautifulSoup(final_html, 'html.parser')
|
| 520 |
+
actual_char_length = len(soup.get_text())
|
| 521 |
+
|
| 522 |
+
# Phase 3: νμ₯ (Expansion) μλ (κΈμμκ° λΆμ‘±ν κ²½μ°)
|
| 523 |
+
if actual_char_length < target_char_length * 0.8:
|
| 524 |
+
expansion_prompt = f"""
|
| 525 |
+
λ€μ μν νκΈ° λΈλ‘κ·Έ κΈμ λ΄μ©μ ν¬κ² νμ₯ν΄μ£ΌμΈμ:
|
| 526 |
+
μλ³Έ κΈ:
|
| 527 |
+
{final_post}
|
| 528 |
+
λ¬Έμ μ :
|
| 529 |
+
λͺ©ν κΈμμ {target_char_length}μμ λ―ΈμΉμ§ λͺ»νλ©°, λ΄μ©μ΄ λΆμ€ν©λλ€. νμ¬ κΈμμλ μ½ {actual_char_length}μμ
λλ€.
|
| 530 |
+
μ€νμΌ κ°μ΄λ:
|
| 531 |
+
{style_prompt}
|
| 532 |
+
μ€νμΌ μΈλΆ μ§μμ¬ν:
|
| 533 |
+
{style_specific_instructions}
|
| 534 |
+
μꡬμ¬ν:
|
| 535 |
+
1. λ°λμ μ λͺ©μ΄ μ μ§λκ±°λ μλ€λ©΄ "{query} μ¬μ© νκΈ°: [ν΅μ¬ νΉμ§/μ₯μ ]" ννλ‘ μ λͺ© μΆκ°.
|
| 536 |
+
2. κ° μμ λͺ© μλμ λ΄μ©μ μ΅μ {MIN_SECTION_LENGTH}μ μ΄μμΌλ‘ νμ₯ν κ².
|
| 537 |
+
3. μν μ¬μ© μ€ κ΅¬μ²΄μ μνΌμλ, μν©, κ°μ λ±μ μμΈνκ² μΆκ°ν κ².
|
| 538 |
+
4. λ§ν¬λ€μ΄ λ¬Έλ²μ μ¬μ©νμ§ μμ κ².
|
| 539 |
+
5. μμ λͺ© ꡬ쑰λ κ·Έλλ‘ μ μ§νλ, κ° μΉμ
λ΄μ©μ 3λ°° μ΄μ νλΆνκ² μμ±ν κ².
|
| 540 |
+
6. μνμ λͺ¨μ΅, μ±λ₯, μ¬μ©κ°, μ₯λ¨μ λ±μ μμΈν λ¬μ¬ν κ².
|
| 541 |
+
7. μ 체 κΈμμλ₯Ό μ΅μ {target_char_length}μ μ΄μ λ¬μ±ν κ².
|
| 542 |
+
8. μμ λͺ© μλ μ΅λ 5κ°λ‘ μ ν.
|
| 543 |
+
μμ:
|
| 544 |
+
- ꡬ맀 λ°°κ²½ λ° μΈλ°μ±, μ¬μ© κ³Όμ μ λν΄ κ΅¬μ²΄μ μΌλ‘ μμ .
|
| 545 |
+
- μν μ¬μ© ν λλ μ λ° κ°κ²© λλΉ κ°μΉ νκ°.
|
| 546 |
+
"""
|
| 547 |
+
expanded_attempt = call_gemini_api(expansion_prompt)
|
| 548 |
+
final_post = post_process_blog(expanded_attempt, style)
|
| 549 |
+
final_html = format_blog_post(final_post, query)
|
| 550 |
+
soup = BeautifulSoup(final_html, 'html.parser')
|
| 551 |
+
actual_char_length = len(soup.get_text())
|
| 552 |
+
|
| 553 |
+
return final_html, ref1, ref2, ref3, actual_char_length
|
| 554 |
+
|
| 555 |
+
except Exception as e:
|
| 556 |
+
logging.error(f"λΈλ‘κ·Έ κΈ μμ± μ€ μ€λ₯ λ°μ: {str(e)}")
|
| 557 |
+
return f"<p>λΈλ‘κ·Έ κΈ μμ± μ€ μ€λ₯ λ°μ: {str(e)}</p>", "", "", "", 0
|