여러 줄 주석이 설명보다 경위(예전·실측·지적)를 적고 있어 읽는 사람이 결론을 찾기 어려웠다. - ts·tsx·js·mjs·css·py 478개: 여러 줄 주석은 첫 문장 한 줄로, 과거형·날짜 문장은 삭제 - 주석 위치는 TypeScript 파서·파이썬 tokenize/ast 로 찾는다 — 문자열 안의 # · /* 는 건드리지 않는다 - eslint·ts·noqa·type: ignore 같은 지시 주석은 그대로 둔다 파이썬 275개 정리 전후 AST 동일, TS 298개 주석 뺀 토큰 동일(빈 JSX 주석 10곳만 차이). site·frontend·admin tsc, site vitest 105 passed Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
107 lines
5.3 KiB
Python
107 lines
5.3 KiB
Python
"""여행 일정 프롬프트 — "1박 2일·2박 3일을 각각 5개 컨셉 × 2개씩, 총 10개로"."""
|
||
|
||
# 화면 탭이 되는 값이다(`ItineraryItem.duration`).
|
||
DURATIONS: tuple[str, str] = ("1박 2일", "2박 3일")
|
||
|
||
MAX_TOKENS = 16000
|
||
|
||
SYSTEM_PROMPT = (
|
||
"너는 지역 여행 코스 플래너다. 검색으로 확인한 실제 장소만 쓰고, "
|
||
"확인하지 못한 값은 필드를 통째로 뺀다. JSON 하나만 출력한다."
|
||
)
|
||
|
||
_CONCEPTS = """1. 역사·근대건축 — 박물관·옛 건물·유적 위주
|
||
2. 자연 풍경 — 바다·산·호수·공원 중 그 지역에 실제로 있는 것 위주
|
||
3. 미식 — 시장·맛집·지역 음식 위주
|
||
4. 아이와 함께 — 체험·동물·놀이·넓은 공원 위주
|
||
5. 야외활동 — 걷기·자전거·물놀이·전망처럼 몸으로 즐기는 것 위주"""
|
||
|
||
# 하루하루의 시작·종료 시각.
|
||
DAY_SCHEDULE: dict[str, tuple[dict[str, object], ...]] = {
|
||
"1박 2일": (
|
||
{"label": "첫째 날", "start": "15:00", "end": "19:00", "returns": True},
|
||
{"label": "둘째 날", "start": "09:00", "end": "14:00", "returns": False},
|
||
),
|
||
"2박 3일": (
|
||
{"label": "첫째 날", "start": "15:00", "end": "19:00", "returns": True},
|
||
{"label": "둘째 날", "start": "09:00", "end": "19:00", "returns": True},
|
||
{"label": "셋째 날", "start": "09:00", "end": "14:00", "returns": False},
|
||
),
|
||
}
|
||
|
||
|
||
def _schedule_text(duration: str) -> str:
|
||
"""DAY_SCHEDULE 을 프롬프트에 박을 문장으로."""
|
||
lines = []
|
||
for slot in DAY_SCHEDULE[duration]:
|
||
ending = "업소 복귀" if slot["returns"] else "종료(체크아웃, 복귀 없음)"
|
||
lines.append(f"· {slot['label']}: {slot['start']} 시작 ~ {slot['end']} {ending}")
|
||
return "\n".join(lines)
|
||
|
||
|
||
# verified 를 요구하지 않는다.
|
||
_SCHEMA = """{ "kind":"itinerary", "version":1, "title":"추천 일정", "items":[
|
||
{ "name":"코스 이름(컨셉이 드러나게)",
|
||
"duration":"%(duration)s",
|
||
"audience":"누구에게 맞는 일정인가",
|
||
"why":"왜 이 일정인가 (두 문장 이내)",
|
||
"days":[
|
||
{ "label":"첫째 날", "startTime":"15:00", "stops":[
|
||
{ "name":"장소", "minutes":90, "moveMinutes":12,
|
||
"note":"한 줄 설명", "searchQuery":"지도 검색어",
|
||
"latitude":35.9908197, "longitude":126.7121231 } ] } ],
|
||
"source":{ "name":"출처 이름", "url":"https://..." } } ] }"""
|
||
|
||
_TASK = """[업소] %(place)s
|
||
[지역] %(region)s
|
||
|
||
[해야 할 일]
|
||
[업소]에 숙박하는 손님을 위한 %(duration)s 여행 일정을 **아래 5개 컨셉마다 2개씩, 총 10개** 만든다.
|
||
|
||
[컨셉]
|
||
%(concepts)s
|
||
|
||
· 같은 컨셉 안의 두 코스도 서로 다른 일정이어야 한다 — 정거장이 겹치는 것은 괜찮지만
|
||
(규칙 7 참고), 같은 장소를 그대로 두 번 우려내지 않는다. 컨셉에 맞는 장소가 둘째 코스를
|
||
못 채울 만큼 모자라면 그 컨셉은 1개만 낸다 — 억지로 채우지 않는다.
|
||
|
||
· 모든 일정의 duration 은 "%(duration)s" 이다.
|
||
· 하루하루의 시작·종료 시각은 고정이다. days 는 이 순서·시각대로 나눈다.
|
||
%(schedule)s
|
||
· 마지막 정거장은 그 날 종료 시각 전에 끝나야 한다(머무는 시간까지 포함해서) — 업소로
|
||
돌아오거나 체크아웃하러 이동하는 시간으로 30분 정도는 남겨 둔다.
|
||
· 정거장은 하루에 3~5곳. 여섯 곳부터는 아무도 그대로 못 돈다.
|
||
· minutes 는 거기서 머무는 시간, moveMinutes 는 앞 칸에서 오는 데 걸리는 시간이다.
|
||
· 첫 정거장의 moveMinutes 는 **업소에서 나서는 시간**이다.
|
||
· [업소] 자신은 정거장에 넣지 않는다 — 일정은 업소에서 출발하는 것이다.
|
||
|
||
[스키마]
|
||
%(schema)s
|
||
|
||
[규칙]
|
||
1. JSON 하나만 출력한다. 인사말·설명·코드펜스를 붙이지 않는다.
|
||
2. 확인되지 않은 값은 필드를 통째로 뺀다. 빈 문자열로 채우거나 지어내지 않는다.
|
||
3. 검색으로 실제 존재가 확인된 장소만 쓴다. 폐업·휴업한 곳은 넣지 않는다.
|
||
4. 순위를 매기지 않는다. rank 같은 칸은 없다.
|
||
5. 링크를 만들지 않는다. searchQuery 에 지도 검색어만 적는다.
|
||
6. 모든 정거장에 latitude·longitude 를 적는다.
|
||
7. ★ 코스 둘이 **같은 정거장 집합**이면 안 된다. 순서만 바꿔 놓은 것은 같은 코스다.
|
||
각 코스에는 다른 코스들에 없는 정거장이 **최소 두 곳** 들어가야 한다.
|
||
컨셉에 맞는 장소가 모자라면 그 코스의 정거장을 줄인다 — 다른 코스의 장소를 빌려오지 않는다.
|
||
8. ★ 하루 종료 시각을 넘기는 정거장은 아예 적지 않는다 — 넘긴 값은 서버가 뒤에서부터
|
||
잘라내므로, 넘길 걸 알면서 적을 이유가 없다.
|
||
9. source.url 은 실제로 열리는 공식·기관·언론 페이지여야 한다."""
|
||
|
||
|
||
def build_prompt(place_name: str, region_label: str, duration: str) -> str:
|
||
"""업소 하나 × 기간 하나의 프롬프트."""
|
||
if duration not in DURATIONS:
|
||
raise ValueError(f"모르는 기간: {duration}")
|
||
ctx = {"place": place_name, "region": region_label, "duration": duration}
|
||
return _TASK % {
|
||
**ctx,
|
||
"concepts": _CONCEPTS,
|
||
"schema": _SCHEMA % ctx,
|
||
"schedule": _schedule_text(duration),
|
||
}
|