File size: 10,022 Bytes
1271db4 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
"""
ν
μ€νΈ μ²λ¦¬ κ΄λ ¨ μ νΈλ¦¬ν° ν¨μ λͺ¨μ
- ν
μ€νΈ λΆλ¦¬ λ° μ μ
- ν€μλ μΆμΆ
- Gemini API ν€ ν΅ν© κ΄λ¦¬ μ μ©
"""
import re
import google.generativeai as genai
import os
import logging
import api_utils # API ν€ ν΅ν© κ΄λ¦¬λ₯Ό μν μν¬νΈ
# λ‘κΉ
μ€μ
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger.addHandler(handler)
# ===== Gemini λͺ¨λΈ κ΄λ¦¬ ν¨μλ€ =====
def get_gemini_model():
"""api_utilsμμ Gemini λͺ¨λΈ κ°μ Έμ€κΈ° (ν΅ν© κ΄λ¦¬)"""
try:
model = api_utils.get_gemini_model()
if model:
logger.info("Gemini λͺ¨λΈ λ‘λ μ±κ³΅ (api_utils ν΅ν© κ΄λ¦¬)")
return model
else:
logger.warning("μ¬μ© κ°λ₯ν Gemini API ν€κ° μμ΅λλ€.")
return None
except Exception as e:
logger.error(f"Gemini λͺ¨λΈ λ‘λ μ€ν¨: {e}")
return None
# ν
μ€νΈ λΆλ¦¬ λ° μ μ ν¨μ
def clean_and_split(text, only_korean=False):
"""ν
μ€νΈλ₯Ό λΆλ¦¬νκ³ μ μ νλ ν¨μ"""
text = re.sub(r"[()\[\]-]", " ", text)
text = text.replace("/", " ")
if only_korean:
# νκΈλ§ μΆμΆ μ΅μ
μ΄ μΌμ§ κ²½μ°
# 곡백μ΄λ μΌνλ‘ κ΅¬λΆν λ€ νκΈλ§ μΆμΆ
words = re.split(r"[ ,]", text)
cleaned = []
for word in words:
word = word.strip()
# νκΈλ§ λ¨κΈ°κ³ λ€λ₯Έ λ¬Έμλ μ κ±°
word = re.sub(r"[^κ°-ν£]", "", word)
if word and len(word) >= 1: # λΉ λ¬Έμμ΄μ΄ μλκ³ 1κΈμ μ΄μμΈ κ²½μ°λ§ μΆκ°
cleaned.append(word)
else:
# νκΈλ§ μΆμΆ μ΅μ
μ΄ κΊΌμ§ κ²½μ° - λ¨μ΄ ν΅μ§Έλ‘ μ²λ¦¬
# 곡백과 μΌνλ‘ κ΅¬λΆνμ¬ λ¨μ΄ μ 체λ₯Ό μ μ§
words = re.split(r"[,\s]+", text)
cleaned = []
for word in words:
word = word.strip()
if word and len(word) >= 1: # λΉ λ¬Έμμ΄μ΄ μλκ³ 1κΈμ μ΄μμΈ κ²½μ°λ§ μΆκ°
cleaned.append(word)
return cleaned
def filter_keywords_with_gemini(pairs, gemini_model=None):
"""Gemini AIλ₯Ό μ¬μ©νμ¬ ν€μλ μ‘°ν© νν°λ§ (κ°μ λ²μ ) - API ν€ ν΅ν© κ΄λ¦¬"""
if gemini_model is None:
# api_utilsμμ Gemini λͺ¨λΈ κ°μ Έμ€κΈ°
gemini_model = get_gemini_model()
if gemini_model is None:
logger.error("Gemini λͺ¨λΈμ κ°μ Έμ¬ μ μμ΅λλ€. λͺ¨λ ν€μλλ₯Ό μ μ§ν©λλ€.")
# μμ νκ² μ²λ¦¬: λͺ¨λ ν€μλλ₯Ό μ μ§
all_keywords = set()
for pair in pairs:
for keyword in pair:
all_keywords.add(keyword)
return list(all_keywords)
# λͺ¨λ ν€μλλ₯Ό λͺ©λ‘μΌλ‘ μΆμΆ (μ κ±°λ ν€μλ νμΈμ©)
all_keywords = set()
for pair in pairs:
for keyword in pair:
all_keywords.add(keyword)
# λ무 λ§μ μμ΄ μμΌλ©΄ μ ν
max_pairs = 50 # μ΅λ 50κ° μλ§ μ²λ¦¬
pairs_to_process = list(pairs)[:max_pairs] if len(pairs) > max_pairs else pairs
logger.info(f"νν°λ§ν ν€μλ μ: μ΄ {len(pairs)}κ° μ€ {len(pairs_to_process)}κ° μ²λ¦¬")
# 보μμ μΈ ν둬ννΈ μ¬μ© - ν€μλ μ κ±° μ΅μν
prompt = (
"λ€μμ μλΉμκ° κ²μν κ°λ₯μ±μ΄ μλ ν€μλ μ λͺ©λ‘μ
λλ€.\n"
"κ° μμ κ°μ λ¨μ΄ μ‘°ν©μ΄μ§λ§ μμλ§ λ€λ₯Έ κ²½μ°μ
λλ€ (μ: μμ§μ€μ§μ΄ vs μ€μ§μ΄μμ§).\n\n"
"μλμ κΈ°μ€μ λ°λΌ κ° μμμ λ μμ°μ€λ¬μ΄ ν€μλλ₯Ό μ νν΄μ£ΌμΈμ:\n"
"1. μλΉμκ° μΌμμ μΌλ‘ μ¬μ©νλ μμ°μ€λ¬μ΄ ννμ μ°μ μ ννμΈμ.\n"
"2. λ ν€μλκ° λͺ¨λ μμ°μ€λ½κ±°λ μλ―Έκ° μ½κ° λ€λ₯΄λ€λ©΄, λ°λμ λ λ€ μ μ§νμΈμ.\n"
"3. νμ€ν λΉμμ°μ€λ½κ±°λ μ΄μν κ²½μ°μλ§ μ κ±°νμΈμ.\n"
"4. λΆνμ€ν κ²½μ°μλ λ°λμ ν€μλλ₯Ό μ μ§νμΈμ.\n"
"5. μ«μλ μμ΄κ° ν¬ν¨λ ν€μλλ νκΈ λ©μΈ ν€μλκ° μμͺ½μ μ€λ ννλ₯Ό μ ννμΈμ. (μ: '10kg μ€μ§μ΄' λ³΄λ€ 'μ€μ§μ΄ 10kg' μ ν)\n"
"6. κ²μλμ΄ 0μΈ ν€μλλΌλ μΌμμ μΈ ννμ΄λΌλ©΄ κ°λ₯ν μ μ§νμΈμ. λͺ
λ°±νκ² λΉμ μμ μΈ ννλ§ μ κ±°νμΈμ.\n\n"
"μ£Όμ: κΈ°λ³Έμ μΌλ‘ λλΆλΆμ ν€μλλ₯Ό μ μ§νκ³ , λ§€μ° λͺ
ννκ² λΉμμ°μ€λ¬μ΄ κ²λ§ μ κ±°νμΈμ.\n\n"
"κ²°κ³Όλ λ€μ νμμΌλ‘ μ 곡ν΄μ£ΌμΈμ:\n"
"- μ νλ ν€μλ (μ΄μ : μμ°μ€λ¬μ΄ ννμ΄κΈ° λλ¬Έ)\n"
"- μ νλ ν€μλ1, μ νλ ν€μλ2 (μ΄μ : λ λ€ μμ°μ€λ½κ³ μλ―Έκ° μ‘°κΈ λ€λ¦)\n\n"
)
# ν€μλ μ λͺ©λ‘
formatted = "\n".join([f"- {a}, {b}" for a, b in pairs_to_process])
full_prompt = prompt + formatted
try:
# νμμμ μΆκ°
logger.info(f"Gemini API νΈμΆ μμ - {len(pairs_to_process)}κ° ν€μλ μ μ²λ¦¬ μ€...")
# μλ΅ λ°κΈ° (νμμμ κΈ°λ₯μ΄ μμΌλ©΄ μΆκ°)
response = gemini_model.generate_content(full_prompt)
logger.info("Gemini API μλ΅ μ±κ³΅")
lines = response.text.strip().split("\n")
# μ νλ ν€μλ μΆμΆ (μΌνλ‘ κ΅¬λΆλ κ²½μ° λͺ¨λ ν¬ν¨)
final_keywords = []
for line in lines:
if line.startswith("-"):
# μ΄μ λΆλΆ μ κ±°
keywords_part = line.strip("- ").split("(μ΄μ :")[0].strip()
# μΌνλ‘ κ΅¬λΆλ ν€μλ λͺ¨λ μΆκ°
for kw in keywords_part.split(","):
kw = kw.strip()
if kw:
final_keywords.append(kw)
# μ²λ¦¬λμ§ μμ μμ 첫 λ²μ§Έ ν€μλλ μΆκ° (LLMμ΄ μ²λ¦¬νμ§ μμ ν€μλ)
if len(pairs) > max_pairs:
logger.info(f"μΆκ° ν€μλ μ²λ¦¬: λ¨μ {len(pairs) - max_pairs}κ° μμ 첫 λ²μ§Έ ν€μλ μΆκ°")
for pair in list(pairs)[max_pairs:]:
# κ° μμ 첫 λ²μ§Έ ν€μλλ§ μ¬μ©
final_keywords.append(pair[0])
# μ νλ ν€μλκ° μμΌλ©΄ κΈ°μ‘΄ ν€μλ λͺ¨λ λ°ν
if not final_keywords:
logger.warning("κ²½κ³ : μ νλ ν€μλκ° μμ΄ λͺ¨λ ν€μλλ₯Ό μ μ§ν©λλ€.")
final_keywords = list(all_keywords)
# μμ κ°μ μμ
corrected_keywords = []
# λ¨μμ μ«μ κ΄λ ¨ μ κ·μ ν¨ν΄
unit_pattern = re.compile(r'(?i)(kg|g|mm|cm|ml|l|리ν°|κ°|ν©|λ°μ€|μΈνΈ|2l|l2)')
number_pattern = re.compile(r'\d+')
for kw in final_keywords:
# 곡백μΌλ‘ λΆλ¦¬
if ' ' in kw:
parts = kw.split()
first_part = parts[0]
# 첫 λΆλΆμ΄ λ¨μλ μ«μλ₯Ό ν¬ν¨νλμ§ νμΈ
if (unit_pattern.search(first_part) or number_pattern.search(first_part)) and len(parts) > 1:
# μμ λ°κΎΈκΈ°: λ¨μ/μ«μ λΆλΆμ λ€λ‘ μ΄λ
corrected_kw = " ".join(parts[1:] + [first_part])
logger.info(f"ν€μλ μμ κ°μ μμ : '{kw}' -> '{corrected_kw}'")
corrected_keywords.append(corrected_kw)
else:
corrected_keywords.append(kw)
else:
corrected_keywords.append(kw)
# νΉλ³ μ²λ¦¬: "L μ€μ§μ΄", "2L μ€μ§μ΄" κ°μ κ²½μ°λ₯Ό λͺ
μμ μΌλ‘ νμΈνκ³ μμ
specific_fixes = []
for kw in corrected_keywords:
# νΉμ ν¨ν΄ 체ν¬
l_pattern = re.compile(r'^([0-9]*L) (.+)$', re.IGNORECASE)
match = l_pattern.match(kw)
if match:
# L λ¨μλ₯Ό λ€λ‘ μ΄λ
l_part = match.group(1)
main_part = match.group(2)
fixed_kw = f"{main_part} {l_part}"
logger.info(f"νΉμ ν¨ν΄ μμ : '{kw}' -> '{fixed_kw}'")
specific_fixes.append(fixed_kw)
else:
specific_fixes.append(kw)
# μ κ±°λ ν€μλ λͺ©λ‘ νμΈ
selected_set = set(specific_fixes)
removed_keywords = all_keywords - selected_set
# μ κ±°λ ν€μλ μΆλ ₯
logger.info("\n=== LLMμ μν΄ μ κ±°λ ν€μλ λͺ©λ‘ ===")
for kw in removed_keywords:
logger.info(f" - {kw}")
logger.info(f"μ΄ {len(all_keywords)}κ° μ€ {len(removed_keywords)}κ° μ κ±°λ¨ ({len(selected_set)}κ° μ μ§)\n")
return specific_fixes
except Exception as e:
logger.error(f"Gemini μ€λ₯: {e}")
logger.error("μ€λ₯ λ°μμΌλ‘ μΈν΄ λͺ¨λ ν€μλλ₯Ό μ μ§ν©λλ€.")
logger.error(f"μ€λ₯ μ ν: {type(e).__name__}")
import traceback
traceback.print_exc()
# μμ νκ² μ²λ¦¬: λͺ¨λ ν€μλλ₯Ό μ μ§
logger.info(f"μμ λͺ¨λ: {len(all_keywords)}κ° ν€μλ λͺ¨λ μ μ§")
return list(all_keywords)
def get_search_volume_range(total_volume):
"""μ΄ κ²μλμ κΈ°λ°μΌλ‘ κ²μλ ꡬκ°μ λ°ν"""
if total_volume == 0:
return "100λ―Έλ§"
elif total_volume <= 100:
return "100λ―Έλ§"
elif total_volume <= 1000:
return "1000λ―Έλ§"
elif total_volume <= 2000:
return "2000λ―Έλ§"
elif total_volume <= 5000:
return "5000λ―Έλ§"
elif total_volume <= 10000:
return "10000λ―Έλ§"
else:
return "10000μ΄μ" |