Spaces:
Running
Running
| """ | |
| 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() | |