"""
logger.info("Gemini API 호출 시작...")
# Gemini API 호출 - 온도를 높여서 더 다양한 결과 생성
response = client.models.generate_content(
model="gemini-2.0-flash",
contents=prompt,
config=GenerateContentConfig(
tools=config_tools, # 검색 엔진에 따라 도구 설정
response_modalities=["TEXT"],
temperature=0.95, # 온도를 높여서 더 다양한 결과
max_output_tokens=2000,
top_p=0.9, # 더 다양한 토큰 선택
top_k=40 # 후보 토큰 수 증가
)
)
logger.info("Gemini API 응답 수신 완료")
# 응답에서 텍스트 추출
result_text = ""
for part in response.candidates[0].content.parts:
if hasattr(part, 'text'):
result_text += part.text
# 결과 정제 - 번호, 설명, 기호 제거 및 중복 제거 강화
lines = result_text.strip().split('\n')
clean_keywords = []
seen_keywords = set() # 중복 방지를 위한 집합
for line in lines:
# 빈 줄 건너뛰기
if not line.strip():
continue
# 정제 작업
clean_line = line.strip()
# 번호 제거 (1., 2., 3. 등)
import re
clean_line = re.sub(r'^\d+\.?\s*', '', clean_line)
# 불릿 포인트 제거 (-, *, •, ✅, ❌ 등)
clean_line = re.sub(r'^[-*•✅❌]\s*', '', clean_line)
# 괄호 안 설명 제거
clean_line = re.sub(r'\([^)]*\)', '', clean_line)
# 추가 설명 제거 (: 이후 내용)
if ':' in clean_line:
clean_line = clean_line.split(':')[0]
# 공백 정리
clean_line = clean_line.strip()
# 유효한 키워드만 추가 (2글자 이상, 50글자 이하)
if clean_line and 2 <= len(clean_line) <= 50:
# 브랜드명이나 제조업체명 필터링
brand_keywords = ['삼성', '엘지', 'LG', '애플', '아이폰', '갤럭시', '나이키', '아디다스', '스타벅스']
if not any(brand in clean_line for brand in brand_keywords):
# 중복 검사 - 대소문자 구분 없이 체크
clean_lower = clean_line.lower()
if clean_lower not in seen_keywords:
seen_keywords.add(clean_lower)
clean_keywords.append(clean_line)
# 50개로 제한하되, 부족하면 추가 생성 요청
if len(clean_keywords) < 50:
logger.info(f"키워드 부족 ({len(clean_keywords)}개), 추가 생성 필요")
# 부족한 만큼 추가 생성을 위한 보조 프롬프트
additional_prompt = f"""
기존에 생성된 키워드: {', '.join(clean_keywords)}
위 키워드들과 절대 중복되지 않는 완전히 새로운 {category} 관련 쇼핑키워드를 {50 - len(clean_keywords)}개 더 생성하세요.
반드시 지켜야 할 규칙:
- 기존 키워드와 절대 중복되지 않음
- 소재, 형태, 기능을 다양하게 조합
- 브랜드명 절대 금지
- 순수 키워드만 출력 (번호, 설명, 기호 금지)
예시 출력:
스테인리스 쟁반
고무 매트
유리 용기
"""
# 추가 생성 요청
additional_response = client.models.generate_content(
model="gemini-2.0-flash",
contents=additional_prompt,
config=GenerateContentConfig(
response_modalities=["TEXT"],
temperature=0.98, # 더 높은 온도로 다양성 보장
max_output_tokens=1000,
top_p=0.95,
top_k=50
)
)
# 추가 키워드 처리
additional_text = ""
for part in additional_response.candidates[0].content.parts:
if hasattr(part, 'text'):
additional_text += part.text
additional_lines = additional_text.strip().split('\n')
for line in additional_lines:
if not line.strip():
continue
clean_line = line.strip()
clean_line = re.sub(r'^\d+\.?\s*', '', clean_line)
clean_line = re.sub(r'^[-*•✅❌]\s*', '', clean_line)
clean_line = re.sub(r'\([^)]*\)', '', clean_line)
if ':' in clean_line:
clean_line = clean_line.split(':')[0]
clean_line = clean_line.strip()
if clean_line and 2 <= len(clean_line) <= 50:
brand_keywords = ['삼성', '엘지', 'LG', '애플', '아이폰', '갤럭시', '나이키', '아디다스', '스타벅스']
if not any(brand in clean_line for brand in brand_keywords):
clean_lower = clean_line.lower()
if clean_lower not in seen_keywords and len(clean_keywords) < 50:
seen_keywords.add(clean_lower)
clean_keywords.append(clean_line)
# 50개로 제한
clean_keywords = clean_keywords[:50]
# 최종 셔플로 순서도 무작위화
random.shuffle(clean_keywords)
# 최종 결과 문자열 생성
final_result = '\n'.join(clean_keywords)
logger.info(f"최종 정제된 키워드 개수: {len(clean_keywords)}개")
logger.info(f"중복 제거된 키워드 수: {len(seen_keywords)}개")
logger.info("=== 다양성 강화 쇼핑키워드 생성 완료 ===")
# 그라운딩 메타데이터 로그
if hasattr(response.candidates[0], 'grounding_metadata'):
logger.info("Google 검색 그라운딩 메타데이터 확인됨")
if hasattr(response.candidates[0].grounding_metadata, 'web_search_queries'):
queries = response.candidates[0].grounding_metadata.web_search_queries
logger.info(f"실행된 검색 쿼리: {queries}")
return final_result
except Exception as e:
error_msg = f"오류 발생: {str(e)}"
logger.error(error_msg)
logger.error("GEMINI_API_KEY 환경변수가 올바르게 설정되었는지 확인해주세요.")
return f"{error_msg}\n\nGEMINI_API_KEY 환경변수가 올바르게 설정되었는지 확인해주세요."
def generate_with_logs(category, additional_request, launch_timing, seasonality, sales_target, sales_channel, competition_level, search_engine):
"""키워드 생성과 로그를 함께 반환하는 함수"""
logger.info("=== 다양성 강화 쇼핑키워드 생성 시작 ===")
# 키워드 생성
result = generate_sourcing_keywords(category, additional_request, launch_timing, seasonality, sales_target, sales_channel, competition_level, search_engine)
# 최근 로그 가져오기
logs = get_recent_logs()
return result, logs
# Gradio 인터페이스 구성
def create_interface():
with gr.Blocks(
title="🎯 다양성 강화 쇼핑키워드 시스템",
theme=gr.themes.Soft(),
css="""
.gradio-container {
max-width: 1200px !important;
}
.title-header {
text-align: center;
background: linear-gradient(45deg, #FF6B6B, #4ECDC4, #45B7D1);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 2.5em;
font-weight: bold;
margin-bottom: 20px;
}
.subtitle {
text-align: center;
color: #666;
font-size: 1.2em;
margin-bottom: 30px;
}
"""
) as demo:
# 헤더
gr.HTML("""
🔄 매번 완전히 다른 결과! 중복 없는 쇼핑키워드 전문 발굴 프로그램
""")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### 📊 다양성 강화 설정")
# 검색 엔진 선택 추가
search_engine = gr.Dropdown(
choices=[
"모든 검색 엔진 통합 분석 (추천)",
"Google 검색 그라운딩만",
"네이버 검색 API만",
"DuckDuckGo 검색만",
"검색 없이 AI만 사용"
],
label="🔍 검색 엔진 선택",
value="모든 검색 엔진 통합 분석 (추천)"
)
# 1. 쇼핑 카테고리 선택
category = gr.Dropdown(
choices=["랜덤적용", "패션잡화", "생활/건강", "출산/육아", "스포츠/레저", "디지털/가전", "가구/인테리어", "패션의류", "화장품/미용"],
label="🛍️ 쇼핑 카테고리",
value="생활/건강"
)
# 2. 추가 요청사항
additional_request = gr.Textbox(
label="📝 추가 요청사항 (다양성 중심)",
placeholder="예: 다양한 소재, 새로운 형태, 독특한 기능 등",
lines=2
)
# 3. 출시 타이밍
launch_timing = gr.Radio(
choices=["랜덤적용", "즉시소싱", "기획형"],
label="⏰ 출시 타이밍",
value="즉시소싱"
)
# 4. 계절성
seasonality = gr.Radio(
choices=["랜덤적용", "봄", "여름", "가을", "겨울", "비계절"],
label="🌱 계절성",
value="비계절"
)
with gr.Column(scale=1):
gr.Markdown("### 💰 목표 설정")
# 5. 매출 목표
sales_target = gr.Radio(
choices=["랜덤적용", "100만원 이하", "100-500만원", "500-1천만원", "1천-5천만원", "5천만원 이상"],
label="💵 매출 목표",
value="100-500만원"
)
# 6. 판매 채널
sales_channel = gr.Radio(
choices=["랜덤적용", "오픈마켓", "SNS마케팅", "광고집행", "오프라인"],
label="📱 판매 채널",
value="오픈마켓"
)
# 7. 경쟁 강도
competition_level = gr.Radio(
choices=[
"랜덤적용",
"초보",
"중수",
"고수"
],
label="⚔️ 경쟁 강도",
value="초보"
)
# 실행 버튼
generate_btn = gr.Button(
"🚀 다양성 강화 쇼핑키워드 발굴 시작 (매번 다른 결과)",
variant="primary",
size="lg"
)
# 결과 출력
with gr.Row():
with gr.Column(scale=2):
gr.Markdown("### 📋 다양성 강화 쇼핑키워드 (50개)")
output = gr.Textbox(
label="중복 없는 쇼핑키워드 결과 (매번 완전히 다름)",
lines=30,
max_lines=50,
placeholder="여기에 매번 다른 50개의 쇼핑키워드가 출력됩니다...",
show_copy_button=True
)
with gr.Column(scale=1):
gr.Markdown("### 📊 실행 로그")
log_output = gr.Textbox(
label="시스템 로그",
lines=30,
max_lines=50,
placeholder="시스템 실행 로그가 여기에 표시됩니다...",
show_copy_button=True
)
# 이벤트 연결
generate_btn.click(
fn=generate_with_logs,
inputs=[
category,
additional_request,
launch_timing,
seasonality,
sales_target,
sales_channel,
competition_level,
search_engine
],
outputs=[output, log_output],
show_progress=True
)
# 사용법 안내
with gr.Accordion("📖 다양성 강화 사용법 안내", open=False):
gr.Markdown("""
### 🎯 다양성 강화 쇼핑키워드 시스템 사용법
#### 🚀 주요 개선 사항
- **완전한 중복 방지**: 매번 실행할 때마다 완전히 다른 키워드 생성
- **랜덤 시드 시스템**: 현재 시각을 기반으로 한 랜덤 시드로 예측 불가능
- **다양한 조합 보장**: 소재×형태×기능의 3차원 조합으로 무한 다양성
- **중복 검사 강화**: 대소문자 구분 없는 엄격한 중복 제거
- **온도 조절**: AI 생성 파라미터 최적화로 창의성 극대화
#### 🔄 다양성 보장 메커니즘
1. **시드 기반 랜덤화**: 마이크로초 단위 시간 기반 랜덤 시드
2. **3차원 조합 시스템**:
- 소재: 실리콘, 스테인리스, 세라믹, 대나무 등 20종
- 형태: 접이식, 원형, 슬림, 휴대용 등 20종
- 기능: 방수, 항균, 마그네틱, 보온 등 20종
3. **중복 방지 알고리즘**: 생성 중 실시간 중복 검사
4. **추가 생성 시스템**: 부족시 자동으로 추가 키워드 생성
#### 🎲 랜덤 적용의 진화
- **키워드별 독립 적용**: 각 키워드마다 다른 조건 조합
- **예측 불가능성**: 같은 설정이라도 매번 다른 결과
- **조합 폭발**: 수천 가지 가능한 조합으로 무한 다양성
#### 📈 생성 품import gradio as gr
import os
import logging
import sys
import random
import requests
import json
from datetime import datetime
from google import genai
from google.genai.types import Tool, GenerateContentConfig, GoogleSearch
# 로깅 설정
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(sys.stdout),
logging.FileHandler('sourcing_app.log', encoding='utf-8')
]
)
logger = logging.getLogger(__name__)
# 키워드 다양성을 위한 시드 풀 확장
DIVERSE_SEED_POOLS = {
"패션잡화": [
"액세서리", "장신구", "가방", "지갑", "모자", "스카프", "벨트", "선글라스", "헤어액세서리", "시계줄",
"키링", "브로치", "목걸이", "팔찌", "반지", "귀걸이", "핸드폰케이스", "파우치", "클러치", "토트백"
],
"생활/건강": [
"주방용품", "욕실용품", "청소용품", "수납용품", "건강용품", "의료용품", "마사지용품", "운동용품",
"다이어트용품", "화장지", "세제", "샴푸", "치약", "비누", "수건", "베개", "이불", "쿠션", "매트"
],
"출산/육아": [
"유아용품", "육아용품", "출산용품", "임산부용품", "신생아용품", "이유식용품", "기저귀", "젖병",
"유모차", "카시트", "아기옷", "장난감", "교육용품", "책", "그림책", "퍼즐", "블록", "인형", "놀이매트"
],
"스포츠/레저": [
"운동용품", "헬스용품", "요가용품", "수영용품", "등산용품", "캠핑용품", "낚시용품", "골프용품",
"축구용품", "농구용품", "배드민턴용품", "탁구용품", "테니스용품", "자전거용품", "스케이트보드용품"
],
"디지털/가전": [
"스마트폰액세서리", "컴퓨터용품", "태블릿용품", "이어폰", "스피커", "충전기", "케이블", "마우스패드",
"키보드", "마우스", "웹캠", "마이크", "헤드셋", "게임패드", "USB", "메모리카드", "파워뱅크"
],
"가구/인테리어": [
"수납가구", "침실가구", "거실가구", "주방가구", "욕실가구", "사무용가구", "인테리어소품", "조명",
"커튼", "블라인드", "카펫", "러그", "액자", "거울", "시계", "화분", "꽃병", "캔들", "방향제"
],
"패션의류": [
"티셔츠", "셔츠", "블라우스", "원피스", "스커트", "바지", "청바지", "레깅스", "자켓", "코트",
"점퍼", "가디건", "니트", "후드", "조끼", "속옷", "잠옷", "양말", "스타킹", "운동복"
],
"화장품/미용": [
"스킨케어", "메이크업", "클렌징", "마스크팩", "선크림", "로션", "에센스", "크림", "립밤",
"립스틱", "아이섀도", "마스카라", "파운데이션", "컨실러", "블러셔", "하이라이터", "네일", "향수"
]
}
# 소재별 키워드 풀
MATERIAL_KEYWORDS = [
"실리콘", "스테인리스", "세라믹", "유리", "나무", "대나무", "면", "린넨", "폴리에스터", "나일론",
"고무", "플라스틱", "종이", "가죽", "인조가죽", "메탈", "알루미늄", "철", "구리", "황동"
]
# 형태별 키워드 풀
SHAPE_KEYWORDS = [
"접이식", "휴대용", "미니", "대형", "원형", "사각", "타원", "직사각", "삼각", "육각",
"슬림", "두꺼운", "얇은", "긴", "짧은", "넓은", "좁은", "깊은", "얕은", "곡선"
]
# 기능별 키워드 풀
FUNCTION_KEYWORDS = [
"방수", "미끄럼방지", "항균", "냄새제거", "보온", "보냉", "속건", "흡수", "차단", "보호",
"마그네틱", "자석", "끈적", "투명", "불투명", "발광", "반사", "신축", "탄력", "고정"
]
# Gemini API 클라이언트 초기화
def initialize_gemini():
logger.info("Gemini API 클라이언트 초기화 시작")
api_key = os.getenv("GEMINI_API_KEY")
if not api_key:
logger.error("GEMINI_API_KEY 환경변수가 설정되지 않았습니다.")
raise ValueError("GEMINI_API_KEY 환경변수가 설정되지 않았습니다.")
client = genai.Client(api_key=api_key)
logger.info("Gemini API 클라이언트 초기화 완료")
return client
def get_recent_logs():
"""최근 로그를 가져오는 함수"""
try:
with open('sourcing_app.log', 'r', encoding='utf-8') as f:
lines = f.readlines()
# 최근 50줄만 반환
return ''.join(lines[-50:])
except FileNotFoundError:
return "로그 파일을 찾을 수 없습니다."
except Exception as e:
return f"로그 읽기 오류: {str(e)}"
def generate_diverse_keyword_combinations(category, count=60):
"""다양한 키워드 조합을 생성하는 함수"""
logger.info(f"다양한 키워드 조합 생성 시작: {category}, {count}개")
combinations = []
category_pool = DIVERSE_SEED_POOLS.get(category, DIVERSE_SEED_POOLS["생활/건강"])
# 1. 단일 키워드 (20%)
single_keywords = random.sample(category_pool, min(12, len(category_pool)))
combinations.extend(single_keywords)
# 2. 소재 + 카테고리 (30%)
for _ in range(18):
material = random.choice(MATERIAL_KEYWORDS)
item = random.choice(category_pool)
combinations.append(f"{material} {item}")
# 3. 형태 + 카테고리 (30%)
for _ in range(18):
shape = random.choice(SHAPE_KEYWORDS)
item = random.choice(category_pool)
combinations.append(f"{shape} {item}")
# 4. 기능 + 카테고리 (20%)
for _ in range(12):
function = random.choice(FUNCTION_KEYWORDS)
item = random.choice(category_pool)
combinations.append(f"{function} {item}")
# 중복 제거 및 셔플
combinations = list(set(combinations))
random.shuffle(combinations)
logger.info(f"생성된 조합 수: {len(combinations)}개")
return combinations[:count]
def search_all_engines(query):
"""모든 검색 엔진을 사용하여 데이터를 취합하는 함수"""
logger.info(f"모든 검색 엔진으로 검색 시작: {query}")
all_results = {
"google": "",
"naver": "",
"duckduckgo": ""
}
# 1. 네이버 검색 API
try:
naver_client_id = os.getenv("NAVER_CLIENT_ID")
naver_client_secret = os.getenv("NAVER_CLIENT_SECRET")
if naver_client_id and naver_client_secret:
url = "https://openapi.naver.com/v1/search/shop.json"
headers = {
"X-Naver-Client-Id": naver_client_id,
"X-Naver-Client-Secret": naver_client_secret
}
params = {"query": query, "display": 10}
response = requests.get(url, headers=headers, params=params, timeout=10)
if response.status_code == 200:
data = response.json()
naver_data = []
for item in data.get('items', [])[:5]:
naver_data.append(f"상품: {item.get('title', '').replace('', '').replace('', '')}")
naver_data.append(f"가격: {item.get('lprice', '')}원")
naver_data.append(f"카테고리: {item.get('category1', '')}")
all_results["naver"] = "\n".join(naver_data)
logger.info("네이버 검색 완료")
else:
all_results["naver"] = "네이버 API 검색 실패"
else:
all_results["naver"] = "네이버 API 키가 설정되지 않음"
except Exception as e:
all_results["naver"] = f"네이버 검색 오류: {str(e)}"
# 2. DuckDuckGo 검색
try:
url = "https://api.duckduckgo.com/"
params = {
"q": query,
"format": "json",
"no_html": "1",
"skip_disambig": "1"
}
response = requests.get(url, params=params, timeout=10)
if response.status_code == 200:
data = response.json()
ddg_data = []
# Abstract 정보
if data.get('Abstract'):
ddg_data.append(f"요약: {data['Abstract']}")
# Related Topics
for topic in data.get('RelatedTopics', [])[:3]:
if isinstance(topic, dict) and topic.get('Text'):
ddg_data.append(f"관련정보: {topic['Text']}")
all_results["duckduckgo"] = "\n".join(ddg_data) if ddg_data else "DuckDuckGo에서 관련 정보 없음"
logger.info("DuckDuckGo 검색 완료")
else:
all_results["duckduckgo"] = "DuckDuckGo 검색 실패"
except Exception as e:
all_results["duckduckgo"] = f"DuckDuckGo 검색 오류: {str(e)}"
# 3. Google 검색은 Gemini에서 자동 처리됨
all_results["google"] = "Google 검색 그라운딩 자동 실행"
logger.info("모든 검색 엔진 데이터 수집 완료")
return all_results
def comprehensive_market_analysis(category, seasonality, sales_target):
"""다양성 강화된 시장 분석"""
logger.info("다양성 강화 시장 분석 시작")
# 랜덤 시드를 현재 시간으로 설정하여 매번 다른 결과 보장
random.seed(datetime.now().microsecond)
# 다양한 검색 각도로 쿼리 생성
search_angles = [
"틈새상품", "신상품", "인기상품", "저가상품", "고급상품", "할인상품",
"간편상품", "실용상품", "트렌드상품", "숨은상품", "베스트상품", "추천상품"
]
search_queries = []
# 카테고리별 다양한 검색어 생성
category_pool = DIVERSE_SEED_POOLS.get(category, DIVERSE_SEED_POOLS["생활/건강"])
for angle in search_angles:
for item in random.sample(category_pool, 3): # 각 카테고리에서 3개씩만 선택
search_queries.append(f"{item} {angle}")
# 소재별 검색어 추가
for material in random.sample(MATERIAL_KEYWORDS, 5):
search_queries.append(f"{material} {category} 상품")
# 형태별 검색어 추가
for shape in random.sample(SHAPE_KEYWORDS, 5):
search_queries.append(f"{shape} {category} 아이템")
# 검색어 셔플하여 예측 불가능하게 만들기
random.shuffle(search_queries)
search_queries = search_queries[:15] # 15개로 제한
comprehensive_data = {}
for i, query in enumerate(search_queries):
logger.info(f"다양성 검색 {i+1}/15: {query}")
comprehensive_data[f"query_{i+1}"] = search_all_engines(query)
# API 과부하 방지를 위한 딜레이
import time
time.sleep(0.5)
# 다양성 강화 데이터 요약
summary = "=== 다양성 강화 시장 분석 결과 ===\n\n"
# 무작위로 결과를 섞어서 패턴 방지
result_keys = list(comprehensive_data.keys())
random.shuffle(result_keys)
summary += "🔍 다양한 시장 검색 결과:\n"
for key in result_keys[:10]:
results = comprehensive_data.get(key, {})
if results.get("naver"):
summary += f"• {results['naver'][:60]}...\n"
summary += "\n"
logger.info("다양성 강화 분석 완료")
return summary
def search_with_api(query, search_engine="Google 검색 그라운딩만"):
"""개별 검색 엔진으로 검색하는 함수 (단일 엔진 선택시 사용)"""
logger.info(f"검색 엔진: {search_engine}, 쿼리: {query}")
search_results = ""
try:
if search_engine == "네이버 검색 API만":
# 네이버 검색 API 사용
naver_client_id = os.getenv("NAVER_CLIENT_ID")
naver_client_secret = os.getenv("NAVER_CLIENT_SECRET")
if naver_client_id and naver_client_secret:
url = "https://openapi.naver.com/v1/search/shop.json"
headers = {
"X-Naver-Client-Id": naver_client_id,
"X-Naver-Client-Secret": naver_client_secret
}
params = {"query": query, "display": 10}
response = requests.get(url, headers=headers, params=params)
if response.status_code == 200:
data = response.json()
for item in data.get('items', [])[:5]:
search_results += f"상품명: {item.get('title', '')}\n"
search_results += f"가격: {item.get('lprice', '')}원\n"
search_results += f"카테고리: {item.get('category1', '')}\n\n"
else:
search_results = "네이버 API 검색 실패"
else:
search_results = "네이버 API 키가 설정되지 않음"
elif search_engine == "DuckDuckGo 검색만":
# DuckDuckGo 검색 (무료, API 키 불필요)
try:
url = "https://api.duckduckgo.com/"
params = {
"q": query,
"format": "json",
"no_html": "1",
"skip_disambig": "1"
}
response = requests.get(url, params=params, timeout=10)
if response.status_code == 200:
data = response.json()
# Abstract 정보
if data.get('Abstract'):
search_results += f"요약: {data['Abstract']}\n\n"
# Related Topics
for topic in data.get('RelatedTopics', [])[:5]:
if isinstance(topic, dict) and topic.get('Text'):
search_results += f"관련 정보: {topic['Text']}\n"
if not search_results:
search_results = "DuckDuckGo에서 관련 정보를 찾지 못함"
else:
search_results = "DuckDuckGo 검색 실패"
except Exception as e:
search_results = f"DuckDuckGo 검색 오류: {str(e)}"
elif search_engine == "검색 없이 AI만 사용":
search_results = "검색 없이 AI 지식만 사용하여 키워드 생성"
else:
# Google 검색 그라운딩 (기본)
search_results = "Google 검색 그라운딩 사용"
except Exception as e:
logger.error(f"검색 오류: {str(e)}")
search_results = f"검색 오류: {str(e)}"
logger.info(f"검색 결과 길이: {len(search_results)} 문자")
return search_results
def apply_random_selection_for_keywords(category, launch_timing, seasonality, sales_target, sales_channel, competition_level):
"""각 키워드마다 랜덤하게 조건을 적용하기 위한 설정 문자열 생성"""
# 각 항목별 선택지 정의
categories = ["패션잡화", "생활/건강", "출산/육아", "스포츠/레저", "디지털/가전", "가구/인테리어", "패션의류", "화장품/미용"]
launch_timings = ["즉시소싱", "기획형"]
seasonalities = ["봄", "여름", "가을", "겨울", "비계절"]
sales_targets = ["100만원 이하", "100-500만원", "500-1천만원", "1천-5천만원", "5천만원 이상"]
sales_channels = ["오픈마켓", "SNS마케팅", "광고집행", "오프라인"]
competition_levels = ["초보", "중수", "고수"]
# 랜덤적용 설정 정보 생성
random_settings = {
'category_random': category == "랜덤적용",
'launch_timing_random': launch_timing == "랜덤적용",
'seasonality_random': seasonality == "랜덤적용",
'sales_target_random': sales_target == "랜덤적용",
'sales_channel_random': sales_channel == "랜덤적용",
'competition_level_random': competition_level == "랜덤적용",
'categories': categories,
'launch_timings': launch_timings,
'seasonalities': seasonalities,
'sales_targets': sales_targets,
'sales_channels': sales_channels,
'competition_levels': competition_levels
}
# 고정값들
fixed_values = {
'category': category if category != "랜덤적용" else None,
'launch_timing': launch_timing if launch_timing != "랜덤적용" else None,
'seasonality': seasonality if seasonality != "랜덤적용" else None,
'sales_target': sales_target if sales_target != "랜덤적용" else None,
'sales_channel': sales_channel if sales_channel != "랜덤적용" else None,
'competition_level': competition_level if competition_level != "랜덤적용" else None
}
logger.info("=== 키워드별 랜덤 설정 ===")
logger.info(f"카테고리 랜덤: {random_settings['category_random']}")
logger.info(f"출시타이밍 랜덤: {random_settings['launch_timing_random']}")
logger.info(f"계절성 랜덤: {random_settings['seasonality_random']}")
logger.info(f"매출목표 랜덤: {random_settings['sales_target_random']}")
logger.info(f"판매채널 랜덤: {random_settings['sales_channel_random']}")
logger.info(f"경쟁강도 랜덤: {random_settings['competition_level_random']}")
return random_settings, fixed_values
def generate_sourcing_keywords(category, additional_request, launch_timing, seasonality, sales_target, sales_channel, competition_level, search_engine="Google 검색 그라운딩만"):
"""다양성 강화된 쇼핑 키워드 50개를 생성하는 함수"""
logger.info("=== 다양성 강화 쇼핑키워드 생성 시작 ===")
logger.info(f"입력 조건 - 검색엔진: {search_engine}")
logger.info(f"입력 조건 - 카테고리: {category}")
logger.info(f"입력 조건 - 추가요청: {additional_request}")
logger.info(f"입력 조건 - 출시타이밍: {launch_timing}")
logger.info(f"입력 조건 - 계절성: {seasonality}")
logger.info(f"입력 조건 - 매출목표: {sales_target}")
logger.info(f"입력 조건 - 판매채널: {sales_channel}")
logger.info(f"입력 조건 - 경쟁강도: {competition_level}")
try:
logger.info("Gemini 클라이언트 초기화 중...")
client = initialize_gemini()
# 매번 다른 시드로 랜덤성 보장
current_time = datetime.now()
random_seed = current_time.microsecond + current_time.second * 1000
random.seed(random_seed)
logger.info(f"랜덤 시드 설정: {random_seed}")
# 프롬프트 구성
logger.info("다양성 강화 프롬프트 구성 중...")
# 랜덤 설정 처리
random_settings, fixed_values = apply_random_selection_for_keywords(
category, launch_timing, seasonality, sales_target, sales_channel, competition_level
)
# 다양한 키워드 조합 미리 생성
diverse_combinations = generate_diverse_keyword_combinations(category, 60)
logger.info(f"다양한 조합 생성 완료: {len(diverse_combinations)}개")
# 검색 엔진별 처리
search_info = ""
config_tools = []
if search_engine == "모든 검색 엔진 통합 분석 (추천)":
logger.info("🔍 다양성 강화 통합 분석 시작...")
# Google 검색 그라운딩 도구 설정
google_search_tool = Tool(google_search=GoogleSearch())
config_tools = [google_search_tool]
# 다양성 강화 종합 시장 분석 실행
comprehensive_analysis = comprehensive_market_analysis(category, seasonality, sales_target)
search_info = f"""
🔍 === 다양성 강화 통합 분석 결과 ===
📈 Google 검색 그라운딩: 실시간 다양한 쇼핑키워드 트렌드 분석 (자동 실행)
🛒 네이버 쇼핑 API: 한국 쇼핑몰 다양한 키워드 데이터 분석
🌐 DuckDuckGo 검색: 글로벌 다양한 쇼핑키워드 정보 분석
{comprehensive_analysis}
💡 위 모든 데이터를 종합하여 매번 다른 조합의 쇼핑키워드를 생성합니다.
🎲 랜덤 시드: {random_seed} (매번 다른 결과 보장)
"""
elif search_engine == "Google 검색 그라운딩만":
logger.info("Google 검색 도구 설정 중...")
google_search_tool = Tool(google_search=GoogleSearch())
config_tools = [google_search_tool]
search_info = f"Google 검색 그라운딩을 통한 다양한 실시간 쇼핑키워드 분석 (시드: {random_seed})"
elif search_engine in ["네이버 검색 API만", "DuckDuckGo 검색만"]:
logger.info(f"{search_engine} 사용하여 다양한 쇼핑키워드 조사 중...")
# 다양성 강화를 위한 검색 실행
search_queries = []
# 랜덤하게 다양한 검색어 생성
base_items = random.sample(diverse_combinations, 8)
for item in base_items:
search_queries.append(f"{item} 쇼핑키워드")
search_results = ""
for query in search_queries:
result = search_with_api(query, search_engine)
search_results += f"[검색어: {query}]\n{result}\n\n"
search_info = f"{search_engine} 다양한 쇼핑키워드 검색 결과 (시드: {random_seed}):\n{search_results}"
else: # 검색 없이 AI만 사용
logger.info("검색 없이 AI 지식만 사용")
search_info = f"AI 내장 지식을 기반으로 다양한 쇼핑키워드 생성 (시드: {random_seed})"
# 다양성을 강화한 프롬프트 - 매번 다른 조합 요청
diverse_sample = random.sample(diverse_combinations, 20)
prompt = f"""
🎯 다양성 강화 쇼핑키워드 발굴 시스템 v5.0
⚡ 중요: 절대 중복되지 않는 다양한 키워드만 생성하세요!
🔬 역할 정의
당신은 매번 완전히 다른 조합의 쇼핑키워드를 생성하는 전문가입니다.
🎯 목표
주어진 조건에 맞는 실제 쇼핑키워드 50개를 발굴하되, 절대 중복되지 않고 매번 다른 조합으로 구성하십시오.
📋 입력된 조건:
카테고리: {category}
추가 요청사항: {additional_request}
출시타이밍: {launch_timing}
계절성: {seasonality}
매출목표: {sales_target}
판매채널: {sales_channel}
경쟁강도: {competition_level}
검색엔진: {search_engine}
🔍 쇼핑키워드 분석 정보:
{search_info}
🎲 다양성 보장 참고 조합 예시 (이것과 다르게 생성하세요):
{', '.join(diverse_sample[:10])}
⚠️ 키워드별 랜덤 적용 규칙:
각 키워드마다 다음과 같이 적용하세요:
{"- 카테고리: 매 키워드마다 " + str(random_settings['categories']) + " 중에서 랜덤 선택" if random_settings['category_random'] else f"- 카테고리: {fixed_values['category']} 고정"}
{"- 출시타이밍: 매 키워드마다 " + str(random_settings['launch_timings']) + " 중에서 랜덤 선택" if random_settings['launch_timing_random'] else f"- 출시타이밍: {fixed_values['launch_timing']} 고정"}
{"- 계절성: 매 키워드마다 " + str(random_settings['seasonalities']) + " 중에서 랜덤 선택" if random_settings['seasonality_random'] else f"- 계절성: {fixed_values['seasonality']} 고정"}
{"- 매출목표: 매 키워드마다 " + str(random_settings['sales_targets']) + " 중에서 랜덤 선택" if random_settings['sales_target_random'] else f"- 매출목표: {fixed_values['sales_target']} 고정"}
{"- 판매채널: 매 키워드마다 " + str(random_settings['sales_channels']) + " 중에서 랜덤 선택" if random_settings['sales_channel_random'] else f"- 판매채널: {fixed_values['sales_channel']} 고정"}
{"- 경쟁강도: 매 키워드마다 " + str(random_settings['competition_levels']) + " 중에서 랜덤 선택" if random_settings['competition_level_random'] else f"- 경쟁강도: {fixed_values['competition_level']} 고정"}
⚙️ 다양성 강화 워크플로우
1단계: 완전히 새로운 조합 생성
- 이전 결과와 절대 중복되지 않는 키워드 조합
- 소재({', '.join(MATERIAL_KEYWORDS[:5])}) + 상품명 조합
- 형태({', '.join(SHAPE_KEYWORDS[:5])}) + 상품명 조합
- 기능({', '.join(FUNCTION_KEYWORDS[:5])}) + 상품명 조합
2단계: 중복 방지 필터링
- 동일한 키워드 조합 완전 배제
- 유사한 의미의 키워드 조합 배제
- 매번 새로운 각도로 접근
3단계: 50개 다양한 키워드 선별
- 브랜드명 절대 금지
- 복잡한 기술 용어 금지
- 최대 2개 단어 조합만 허용
⚠️ 다양성 강화 키워드 구성 규칙 (매우 중요):
🚫 절대 금지 사항:
- 동일하거나 유사한 키워드 반복
- 브랜드명 (삼성, LG, 나이키 등)
- 복잡한 기술 용어
- 3개 이상 복합어
✅ 반드시 다양하게 포함해야 할 형태:
1. 소재별 키워드 (예: 대나무 도마, 구리 컵)
2. 형태별 키워드 (예: 원형 접시, 슬림 케이스)
3. 기능별 키워드 (예: 방수 파우치, 항균 수건)
4. 카테고리별 키워드 (예: 수납함, 조리도구)
🎯 다양성 보장 전략:
- 절대 같은 소재를 2번 이상 사용하지 마세요
- 절대 같은 형태를 2번 이상 사용하지 마세요
- 절대 같은 기능을 2번 이상 사용하지 마세요
- 매 키워드마다 완전히 다른 조합으로 생성하세요
올바른 다양한 키워드 예시:
✅ 대나무 도마 (소재+상품)
✅ 원형 접시 (형태+상품)
✅ 방수 파우치 (기능+상품)
✅ 세라믹 머그컵 (소재+상품)
✅ 접이식 선반 (형태+상품)
✅ 항균 수건 (기능+상품)
잘못된 반복 키워드 예시:
❌ 대나무 도마, 대나무 젓가락 (소재 반복)
❌ 원형 접시, 원형 쟁반 (형태 반복)
❌ 방수 파우치, 방수 케이스 (기능 반복)
📋 출력 형식:
오직 완전히 다른 쇼핑키워드만 한 줄씩 50개 출력
- 번호 금지
- 설명 금지
- 기호나 특수문자 금지
- 괄호 안 설명 금지
- 순수 키워드만 출력
- 절대 중복 금지
예시 출력 형태 (매번 완전히 다르게):
유리 화분
접이식 의자
항균 도마
알루미늄 텀블러
슬림 파일함
⚡ 지금 바로 절대 중복되지 않는 완전히 새로운 쇼핑키워드 50개를 각각 다른 랜덤 조건을 적용하여 출력하세요.
매번 실행할 때마다 완전히 다른 결과가 나와야 합니다!