o2o-site-AEO/solution/backend/scripts/generate_lodging_catchphrases.py
김성경 a093921b62 feat(backend,site): NOL 수집 및 숙박 안내 구조화와 지역 콘텐츠 연동
수집한 객실·이용 정보가 발행 화면에 연결되지 않던 경로를 보완하고, 숙소 소개와 지역 맛집 표시를 개선한다.

- NOL 브라우저 수집 어댑터와 수집·반영 스크립트 추가
- 크롤링 fact 즉시 노출 및 직접 입력·정정값 보호
- 이용안내 항목별 구조화와 기존 표 연결, 원문 UI 비표시
- 군산 한일옥 고정 등록과 지역 맛집 탐색·보강 경로 추가
- 숙소 소개 요약, 히어로 문구, 지역 콘텐츠·목업 표시 개선

검증: 작업 트리 기준 site 타입·린트·빌드 및 안내 렌더링 테스트 통과, PC·모바일 화면 확인. 스테이징 diff 공백 검사 통과. 사용자 요청에 따라 현재 스테이징된 55개 파일만 포함하며 미스테이징 문서·테스트 등은 제외.
2026-09-14 19:40:04 +09:00

43 lines
2.1 KiB
Python

"""숙박 공통 감성 문구를 한 번 생성한다. 방문 시에는 저장된 문구만 순환한다."""
import asyncio
import json
import sys
from pathlib import Path
import httpx
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from services.llm.gemini import DEFAULT_MODEL, call, extract_text
PROMPT = """여러 숙박업체 홈페이지 첫 화면에서 돌아가며 보여 줄 한국어 감성 문구 10개를 작성하세요.
호텔, 모텔, 펜션, 게스트하우스 어디에나 쓸 수 있어야 합니다.
휴식, 여행, 머무는 시간, 일상에서의 잠깐의 쉼을 담아 담백하고 따뜻하게 쓰세요.
각 문구는 공백 포함 15~30자. 과장, 진부한 최상급, 상호명, 지명, 계절, 날씨,
바다/숲/정원/독채 등 시설이나 경관, 청결/조식/가격 등 확인이 필요한 사실과 서비스 약속은 금지합니다.
서로 다른 표현으로 정확히 10개, 문자열 JSON 배열만 반환하세요."""
async def main():
async with httpx.AsyncClient(timeout=90) as client:
response = await call(client, DEFAULT_MODEL, {
"contents": [{"role": "user", "parts": [{"text": PROMPT}]}],
"generationConfig": {
"responseMimeType": "application/json",
"responseSchema": {"type": "ARRAY", "items": {"type": "STRING"}, "minItems": 10, "maxItems": 10},
"temperature": 0.8,
},
}, max_retries=0)
lines = json.loads(extract_text(response))
if not isinstance(lines, list) or len(lines) != 10 or not all(isinstance(s, str) and 15 <= len(s.strip()) <= 30 for s in lines):
raise ValueError("문구 개수·길이 검증 실패")
lines = [s.strip() for s in lines]
if len(set(lines)) != 10:
raise ValueError("중복 문구")
target = Path(__file__).resolve().parents[2] / "site/src/lib/lodging-catchphrases.json"
target.write_text(json.dumps(lines, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
print(json.dumps({"model": DEFAULT_MODEL, "items": lines}, ensure_ascii=False))
if __name__ == "__main__":
asyncio.run(main())