""" 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개를 각각 다른 랜덤 조건을 적용하여 출력하세요. 매번 실행할 때마다 완전히 다른 결과가 나와야 합니다!