""" Step 1: GPT-4.1 mini로 메뉴별 Description 자동 생성 실행: python scripts/01_generate_descriptions.py python scripts/01_generate_descriptions.py --input data/raw/real_menus.json python scripts/01_generate_descriptions.py --input data/raw/real_menus.json --async 결과: data/generated/menu_descriptions.jsonl 증분 생성 지원: - 이미 생성된 menu_id는 자동으로 스킵 (API 비용/시간 절약) - 신규 메뉴만 GPT 호출 후 기존 jsonl에 append - 재실행해도 기존 데이터 변경 없음 --async 플래그: - 비동기 병렬 처리 (동시 10개 요청) - 901개 기준 약 5분 (순차 대비 4배 빠름) - Azure rate limit 초과 시 --concurrency 값을 낮추세요 """ import argparse import json import jsonlines import sys import time from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent)) from config import RAW_DIR, GENERATED_DIR from core.llm_client import LLMClient def load_existing_ids(output_path: Path) -> set: """기존 jsonl에서 이미 생성된 menu_id 집합 반환. 파일 없으면 빈 집합.""" if not output_path.exists(): return set() existing_ids = set() with jsonlines.open(output_path, mode="r") as reader: for item in reader: mid = item.get("menu_id") if mid: existing_ids.add(mid) return existing_ids def main(): parser = argparse.ArgumentParser(description="GPT로 메뉴 description 자동 생성") parser.add_argument( "--input", default=str(RAW_DIR / "sample_menus.json"), help="입력 JSON 파일 경로 (기본값: data/raw/sample_menus.json)" ) parser.add_argument( "--async", dest="use_async", action="store_true", help="비동기 병렬 처리 사용 (기본: 순차)" ) parser.add_argument( "--concurrency", type=int, default=10, help="비동기 모드 동시 요청 수 (기본: 10, rate limit 초과 시 낮추세요)" ) args = parser.parse_args() raw_path = Path(args.input) if not raw_path.is_absolute(): raw_path = Path(__file__).parent.parent / raw_path if not raw_path.exists(): print(f"[오류] 입력 파일이 없습니다: {raw_path}") sys.exit(1) menus = json.loads(raw_path.read_text(encoding="utf-8")) GENERATED_DIR.mkdir(parents=True, exist_ok=True) output_path = GENERATED_DIR / "menu_descriptions.jsonl" # ── 증분 처리: 이미 생성된 메뉴 스킵 ──────────────────────────────────── existing_ids = load_existing_ids(output_path) new_menus = [m for m in menus if m["menu_id"] not in existing_ids] print(f"전체 메뉴: {len(menus)}개") print(f"기존 생성 완료: {len(existing_ids)}개 (스킵)") print(f"신규 생성 대상: {len(new_menus)}개\n") if not new_menus: print("[완료] 모든 메뉴가 이미 생성되어 있습니다. 재실행 불필요.") return # ── 신규 메뉴만 GPT 호출 ───────────────────────────────────────────────── client = LLMClient() start = time.time() if args.use_async: print(f"[비동기 모드] 동시 {args.concurrency}개 요청으로 생성 중...") new_descriptions = client.generate_batch_async( new_menus, pass_keywords=True, concurrency=args.concurrency, ) else: print("[순차 모드] 1건씩 순차 생성 중...") new_descriptions = client.generate_batch(new_menus, pass_keywords=True) elapsed = time.time() - start print(f"\n소요 시간: {elapsed:.1f}초 ({elapsed/60:.1f}분)") # ── 기존 jsonl에 append (mode="a") ─────────────────────────────────────── with jsonlines.open(output_path, mode="a") as writer: for desc in new_descriptions: writer.write(desc) total_in_file = len(existing_ids) + len(new_descriptions) print(f"\n[완료] {output_path}") print(f" 신규 추가: {len(new_descriptions)}개") print(f" 파일 내 총 항목: {total_in_file}개") if __name__ == "__main__": main()