o2o-triple-pick/backend/app/regenerate_ai.py
hbyang d8db7ed0f4 perf(ai): 자동 생성은 비어있는 예측만 채우도록(기존 보존)
generate_ai_predictions(only_missing=True 기본): 이미 있는 (경기×모델)
예측은 보존하고 누락분만 생성 — 매일/기동/새 경기 시 기존 데이터 불변,
API 비용↓, 실패했던 모델만 자동 보충. 수동 재생성 스크립트는 강제 전체(only_missing=False).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 16:17:12 +09:00

59 lines
1.8 KiB
Python

"""전체 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())