Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.UUID;
Expand Down Expand Up @@ -118,22 +119,61 @@ static String createDailyPlanPrompt(UUID scheduleId, LocalDate startDate, LocalD
throw new RuntimeException("JSON 직렬화에 실패했습니다.", e);
}

return String.format("""
너는 여행 일정 계획 전문가 AI다. 너의 임무는 주어진 장소 목록을 핵심 규칙에 따라 각 여행일에 논리적으로 배정하고, 지정된 JSON 형식으로 반환하는 것이다.
long travelDays = ChronoUnit.DAYS.between(startDate, endDate) + 1;
if (travelDays <= 0) {
travelDays = 1;
}

int totalItemCount = items.size();
long baseTotalCount = totalItemCount / travelDays;
long remainderTotal = totalItemCount % travelDays;
long[] totalItemsPerDay = new long[(int) travelDays];
for (int i = 0; i < travelDays; i++) {
totalItemsPerDay[i] = baseTotalCount + (i < remainderTotal ? 1 : 0);
}

long accommodationsCount = items.stream().filter(item -> "ACCOMMODATION".equals(item.category())).count();
long restaurantsCount = items.stream().filter(item -> "RESTAURANT".equals(item.category())).count();
long avgRestaurantsPerDay = (restaurantsCount > 0 && travelDays > 0) ? (long) Math.ceil((double) restaurantsCount / travelDays) : 2;

StringBuilder distributionInstruction = new StringBuilder();
for (int i = 0; i < travelDays; i++) {
long fixedItemsThisDay = 0;
if (travelDays == 1) {
fixedItemsThisDay += Math.min(1, accommodationsCount);
} else {
if (i == 0 || i == travelDays - 1) {
fixedItemsThisDay += (accommodationsCount > 0) ? 1 : 0;
} else {
fixedItemsThisDay += (accommodationsCount > 1) ? 2 : 0;
}
}
fixedItemsThisDay += avgRestaurantsPerDay;

### **[1] 최종 목표**
모든 규칙을 준수하여, 각 장소를 방문할 날짜(`dayNumber`)만 결정하여 그룹화한다. (방문 순서는 이 단계에서 결정하지 않는다.)
long otherItemsToAdd = totalItemsPerDay[i] - fixedItemsThisDay;
otherItemsToAdd = Math.max(0, otherItemsToAdd);

### **[2] 핵심 작업 규칙**
[A] 숙소 배정 규칙 (최우선 순위!):
* `category`가 `ACCOMMODATION`인 장소를 식별한다.
* 1일차: 첫 번째 숙소를 1일차 그룹에 포함시킨다. 이 숙소는 그날의 마지막 장소가 될 것이다.
* 중간일 (2일차 ~ 마지막 전날): 이전 날의 숙소를 해당일 그룹의 첫 장소로, 다음 순서의 숙소를 해당일 그룹의 마지막 장소로 포함시킨다.
* 마지막 날: 이전 날의 숙소를 마지막 날 그룹의 첫 장소로 포함시킨다.
distributionInstruction.append(String.format("* %d일차: '기타 장소' %d개 추가 (최종 목표: 총 %d개)%n", i + 1, otherItemsToAdd, totalItemsPerDay[i]));
}

[B] 카테고리별 일정 계획 규칙:
* 식당(`RESTAURANT`): 각 날짜별로 점심, 저녁에 방문하도록 하루 2개씩 배정하는 것을 기본으로 한다.
* 기타 장소(`TOURIST_SPOT`, `LEISURE`, `HEALING`): 숙소와 식당 배정 후, 남은 장소들을 전체 여행 기간에 걸쳐 균등하게 분배한다.
return String.format("""
너는 여행 일정 계획 전문가 AI다. 너의 임무는 주어진 장소 목록을 핵심 규칙에 따라 각 여행일에 논리적으로 배정하여, **일자별 총 장소 개수 목표**를 정확히 맞추는 것이다.

### **[1] 핵심 목표**
각 날짜의 `items` 배열에 포함될 장소의 **총개수**가 아래 **[일자별 최종 목표]** 표에 명시된 '최종 목표' 숫자와 정확히 일치하도록 만들어야 한다.

### **[2] 작업 규칙**
장소들은 숙소, 식당, 기타 장소 세 종류로 나뉜다.
1. **숙소(`ACCOMMODATION`)와 식당(`RESTAURANT`)을 규칙에 따라 먼저 마음속으로 배정한다.**
* 숙소 규칙: 1일차와 마지막 날은 1개, 중간 날은 2개(전날 숙소, 당일 숙소)가 기본이다.
* 식당 규칙: 하루 2개 배정을 목표로 한다.
2. **그 다음, 아래 [일자별 최종 목표] 표를 확인한다.**
3. 표에 적힌 **'기타 장소 추가 개수'만큼** `TOURIST_SPOT`, `LEISURE`, `HEALING` 카테고리에서 장소를 골라 추가한다.
4. 이렇게 조합하여 최종적으로 그날의 **'최종 목표' 총개수를 정확히 맞춘다.** 지리적 근접성을 고려하여 장소를 선택하면 가장 좋다.

### **[일자별 최종 목표]**
**아래 지시에 따라 '기타 장소'를 추가하여, 일자별 '최종 목표' 총개수를 반드시 맞춰야 한다.**
%s

### **[3] 출력 데이터 형식 (매우 중요!)**
너의 응답은 반드시 아래 JSON 구조를 따라야 한다. 다른 설명 없이, 순수한 JSON 객체 하나만 출력해야 한다.
Expand All @@ -159,6 +199,7 @@ static String createDailyPlanPrompt(UUID scheduleId, LocalDate startDate, LocalD

이제 위의 규칙에 따라 일자별 계획 JSON을 생성해줘.
""",
distributionInstruction.toString(),
startDate, endDate, scheduleId, itemsJson);
}
}
Expand Down
Loading