"""전체 AI 예측 재생성 — 수동 트리거. 워커의 generate_ai_predictions() 를 즉시 1회 실행한다(매일 00:05 자동 생성과 동일 로직). 미종료(결과 미입력) 경기 전부에 대해 GPT/Claude/Gemini 를 실 API 호출하여 ai_predictions 를 덮어쓴다(source='llm'). 키 없는/실패한 모델은 건너뛴다. DB 접속은 backend/.env(DB_* 또는 DATABASE_URL)를 따른다. 실행: # Docker (운영 DB로 1회 실행) docker compose run --rm worker python -m app.regenerate_ai # 로컬 cd backend && python -m app.regenerate_ai """ from __future__ import annotations import asyncio import logging from sqlalchemy import func, select from .database import SessionLocal, init_db from .models import AIPrediction, Match from .worker import generate_ai_predictions logging.basicConfig(level=logging.INFO) log = logging.getLogger("triplepick.regen") async def main() -> None: await init_db() # 테이블 보장(idempotent) async with SessionLocal() as db: targets = ( await db.execute( select(func.count()) .select_from(Match) .where(Match.result_outcome.is_(None)) ) ).scalar_one() log.info("재생성 대상(미종료) 경기: %d", targets) # 수동 트리거 = 전부 강제 재생성(덮어쓰기). 프롬프트 수정 후 갱신 등에 사용. await generate_ai_predictions(only_missing=False) async with SessionLocal() as db: rows = ( await db.execute( select(AIPrediction.model, func.count()) .where(AIPrediction.source == "llm") .group_by(AIPrediction.model) ) ).all() log.info("재생성 완료. 모델별 LLM 예측 수: %s", {m: c for m, c in rows}) if __name__ == "__main__": asyncio.run(main())