Skip to content

Commit ce5928f

Browse files
authored
"feat(be): tour 도메인 파싱 로직 및 변수 명 수정 및 이넘 수정 및 프롬프트 수정" (#145)
* 임시 * feat(be): tour 도메인 파싱 로직 및 변수 명 수정 및 이넘 수정 및 프롬프트 수정
1 parent 433b6ee commit ce5928f

File tree

15 files changed

+355
-94
lines changed

15 files changed

+355
-94
lines changed

build.gradle.kts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,18 @@ buildConfig {
137137
file("src/main/resources/content-type-id.yml")
138138
.readText()
139139
.substringAfter("codes:")
140+
.substringBefore("foreign-codes:")
141+
.lines()
142+
.filter { it.contains(":") }
143+
.joinToString(", ") { line ->
144+
val parts = line.split(":")
145+
"${parts[0].trim()}: ${parts[1].trim().replace("\"", "")}"
146+
}
147+
148+
val foreignContentTypeCodes =
149+
file("src/main/resources/content-type-id.yml")
150+
.readText()
151+
.substringAfter("foreign-codes:")
140152
.lines()
141153
.filter { it.contains(":") }
142154
.joinToString(", ") { line ->
@@ -180,6 +192,7 @@ buildConfig {
180192

181193
buildConfigField("String", "AREA_CODES_DESCRIPTION", "\"\"\"$areaCodes\"\"\"")
182194
buildConfigField("String", "CONTENT_TYPE_CODES_DESCRIPTION", "\"\"\"$contentTypeCodes\"\"\"")
195+
buildConfigField("String", "FOREIGN_CONTENT_TYPE_CODES_DESCRIPTION", "\"\"\"$foreignContentTypeCodes\"\"\"")
183196
buildConfigField("String", "LANGUAGE_CODES_DESCRIPTION", "\"\"\"$languageCodesDescription\"\"\"")
184197
buildConfigField("String", "REGION_CODES_DESCRIPTION", "\"\"\"$regionCodes\"\"\"")
185198
buildConfigField("String", "KOREA_TRAVEL_GUIDE_SYSTEM", "\"\"\"$systemPrompt\"\"\"")

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/aiChat/tool/GuideFinderTool.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.back.koreaTravelGuide.domain.ai.aiChat.tool
22

33
import com.back.koreaTravelGuide.common.logging.log
44
import com.back.koreaTravelGuide.domain.guide.service.GuideService
5+
import com.back.koreaTravelGuide.domain.user.enums.Region
56
import com.fasterxml.jackson.databind.ObjectMapper
67
import org.springframework.ai.tool.annotation.Tool
78
import org.springframework.ai.tool.annotation.ToolParam
@@ -14,7 +15,12 @@ class GuideFinderTool(
1415
) {
1516
@Tool(description = "특정 지역(region)에서 활동하는 여행 가이드 목록을 검색합니다.")
1617
fun findGuidesByRegion(
17-
@ToolParam(description = "검색할 지역 이름. 예: '서울', '부산', '강남구'", required = true)
18+
@ToolParam(
19+
description =
20+
"검색할 지역의 영어 코드 (대문자). " +
21+
"사용 가능한 지역: ${Region.ALL_REGIONS_DESCRIPTION}",
22+
required = true,
23+
)
1824
region: String,
1925
): String {
2026
log.info("🔧 [TOOL CALLED] findGuidesByRegion - region: $region")

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/aiChat/tool/TourTool.kt

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,35 @@ class TourTool(
2727
fun getAreaBasedTourInfo(
2828
@ToolParam(
2929
description =
30-
"관광 타입 코드를 사용하세요. 사용자가 타입 이름을 말하면 해당하는 코드를 찾아서 사용해야 합니다. " +
31-
"예: 사용자가 '관광지'라고 하면 '12'를 사용하세요. " +
32-
"사용 가능한 타입 코드: ${BuildConfig.CONTENT_TYPE_CODES_DESCRIPTION}",
30+
"STEP 1: 사용자 메시지의 언어를 파악하여 해당하는 서비스 코드를 선택하세요. " +
31+
"사용 가능한 언어 코드: ${BuildConfig.LANGUAGE_CODES_DESCRIPTION}",
32+
required = true,
33+
)
34+
languageCode: String,
35+
@ToolParam(
36+
description =
37+
"STEP 2: languageCode에 따라 관광 타입 코드를 선택하세요. " +
38+
"IF languageCode == 'KorService2' THEN 한국어 코드: ${BuildConfig.CONTENT_TYPE_CODES_DESCRIPTION}. " +
39+
"ELSE (EngService2, JpnService2, ChsService2, ChtService2) THEN " +
40+
"외국어 코드: ${BuildConfig.FOREIGN_CONTENT_TYPE_CODES_DESCRIPTION}",
3341
required = true,
3442
)
3543
contentTypeId: String,
3644
@ToolParam(
3745
description =
38-
"지역 코드를 쉼표(,)로 구분해서 사용하세요. " +
39-
"예: 사용자가 '서울 강남구'라고 하면 AREA_CODES에서 '서울-강남구: 1-1'을 찾고, " +
40-
"하이픈(-)을 쉼표(,)로 바꿔서 '1,1'을 사용하세요. " +
41-
"광역시(인천, 대전 등)는 단일 코드만 사용: 예: '인천' → '2' (쉼표 없음). " +
46+
"STEP 3: 지역 코드를 전달하세요. 하이픈(-) 또는 쉼표(,) 형식 모두 가능합니다. " +
4247
"사용 가능한 지역 코드: ${BuildConfig.AREA_CODES_DESCRIPTION}",
4348
required = true,
4449
)
4550
areaAndSigunguCode: String,
4651
): String {
47-
log.info("🔧 [TOOL CALLED] getAreaBasedTourInfo - contentTypeId: $contentTypeId, areaAndSigunguCode: $areaAndSigunguCode")
52+
log.info(
53+
"🔧 [TOOL CALLED] getAreaBasedTourInfo - " +
54+
"contentTypeId: $contentTypeId, areaAndSigunguCode: $areaAndSigunguCode, languageCode: $languageCode",
55+
)
4856

4957
val tourParams = tourService.parseParams(contentTypeId, areaAndSigunguCode)
50-
val tourInfo = tourService.fetchTours(tourParams)
58+
val tourInfo = tourService.fetchTours(tourParams, languageCode)
5159

5260
return try {
5361
val result = tourInfo.let { objectMapper.writeValueAsString(it) }
@@ -74,17 +82,23 @@ class TourTool(
7482
fun getLocationBasedTourInfo(
7583
@ToolParam(
7684
description =
77-
"관광 타입 코드를 사용하세요. 사용자가 타입 이름을 말하면 해당하는 코드를 찾아서 사용해야 합니다. " +
78-
"예: 사용자가 '음식점'이라고 하면 '39'를 사용하세요. " +
79-
"사용 가능한 타입 코드: ${BuildConfig.CONTENT_TYPE_CODES_DESCRIPTION}",
85+
"STEP 1: 사용자 메시지의 언어를 파악하여 해당하는 서비스 코드를 선택하세요. " +
86+
"사용 가능한 언어 코드: ${BuildConfig.LANGUAGE_CODES_DESCRIPTION}",
87+
required = true,
88+
)
89+
languageCode: String,
90+
@ToolParam(
91+
description =
92+
"STEP 2: languageCode에 따라 관광 타입 코드를 선택하세요. " +
93+
"IF languageCode == 'KorService2' THEN 한국어 코드: ${BuildConfig.CONTENT_TYPE_CODES_DESCRIPTION}. " +
94+
"ELSE (EngService2, JpnService2, ChsService2, ChtService2) THEN " +
95+
"외국어 코드: ${BuildConfig.FOREIGN_CONTENT_TYPE_CODES_DESCRIPTION}",
8096
required = true,
8197
)
8298
contentTypeId: String,
8399
@ToolParam(
84100
description =
85-
"지역 코드를 쉼표(,)로 구분해서 사용하세요. " +
86-
"예: 사용자가 '서울 중구'라고 하면 AREA_CODES에서 '서울-중구: 1-24'를 찾고, " +
87-
"하이픈(-)을 쉼표(,)로 바꿔서 '1,24'를 사용하세요. " +
101+
"STEP 3: 지역 코드를 전달하세요. 하이픈(-) 또는 쉼표(,) 형식 모두 가능합니다. " +
88102
"사용 가능한 지역 코드: ${BuildConfig.AREA_CODES_DESCRIPTION}",
89103
required = true,
90104
)
@@ -99,12 +113,12 @@ class TourTool(
99113
log.info(
100114
"🔧 [TOOL CALLED] getLocationBasedTourInfo - " +
101115
"contentTypeId: $contentTypeId, area: $areaAndSigunguCode, " +
102-
"mapX: $mapX, mapY: $mapY, radius: $radius",
116+
"mapX: $mapX, mapY: $mapY, radius: $radius, languageCode: $languageCode",
103117
)
104118

105119
val tourParams = tourService.parseParams(contentTypeId, areaAndSigunguCode)
106120
val locationBasedParams = TourLocationBasedParams(mapX, mapY, radius)
107-
val tourLocationBasedInfo = tourService.fetchLocationBasedTours(tourParams, locationBasedParams)
121+
val tourLocationBasedInfo = tourService.fetchLocationBasedTours(tourParams, locationBasedParams, languageCode)
108122

109123
return try {
110124
val result = tourLocationBasedInfo.let { objectMapper.writeValueAsString(it) }
@@ -126,16 +140,23 @@ class TourTool(
126140
fun getTourDetailInfo(
127141
@ToolParam(
128142
description =
129-
"조회할 관광정보의 콘텐츠 ID. " +
143+
"STEP 1: 사용자 메시지의 언어를 파악하여 해당하는 서비스 코드를 선택하세요. " +
144+
"사용 가능한 언어 코드: ${BuildConfig.LANGUAGE_CODES_DESCRIPTION}",
145+
required = true,
146+
)
147+
languageCode: String,
148+
@ToolParam(
149+
description =
150+
"STEP 2: 조회할 관광정보의 콘텐츠 ID. " +
130151
"이전 Tool 호출 결과(getAreaBasedTourInfo 또는 getLocationBasedTourInfo)에서 받은 contentId를 사용하세요.",
131152
required = true,
132153
)
133-
contentId: String = "127974",
154+
contentId: String,
134155
): String {
135-
log.info("🔧 [TOOL CALLED] getTourDetailInfo - contentId: $contentId")
156+
log.info("🔧 [TOOL CALLED] getTourDetailInfo - contentId: $contentId, languageCode: $languageCode")
136157

137158
val tourDetailParams = TourDetailParams(contentId)
138-
val tourDetailInfo = tourService.fetchTourDetail(tourDetailParams)
159+
val tourDetailInfo = tourService.fetchTourDetail(tourDetailParams, languageCode)
139160

140161
return try {
141162
val result = tourDetailInfo.let { objectMapper.writeValueAsString(it) }
@@ -154,13 +175,13 @@ class TourTool(
154175
* "areacode": "6" 부산
155176
* "sigungucode": "10" 사하구
156177
* "contenttypeid": "76" 관광지 (해외)
157-
* "serviceSegment" : "EngService2" (영어)
178+
* "languageCode" : "EngService2" (영어)
158179
*
159180
*
160181
* 2
161182
* fetchTourDetail - 상세조회
162183
* 케이스 : 콘텐츠ID가 "264247인 관광정보의 "상베 정보" 조회
163184
* "contentid": "264247,
164-
* "serviceSegment" : "EngService2" (영어)
185+
* "languageCode" : "EngService2" (영어)
165186
*/
166187
}

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/tour/client/TourApiClient.kt

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,18 @@ class TourApiClient(
2727
// 지역 기반 관광 정보 조회 (areaBasedList2)
2828
fun fetchTourInfo(
2929
params: TourParams,
30-
serviceSegment: String,
30+
languageCode: String,
3131
): TourResponse {
3232
val url =
33-
buildTourUri(serviceSegment, "areaBasedList2") {
33+
buildTourUri(languageCode, "areaBasedList2") {
3434
queryParam("contentTypeId", params.contentTypeId)
3535
queryParam("areaCode", params.areaCode)
3636
queryParam("sigunguCode", params.sigunguCode)
3737
}
3838

3939
val body =
4040
runCatching { restTemplate.getForObject(url, String::class.java) }
41-
.onFailure { log.error("관광 정보 조회 실패 - serviceSegment={}", serviceSegment, it) }
41+
.onFailure { log.error("관광 정보 조회 실패 - languageCode={}", languageCode, it) }
4242
.getOrNull()
4343

4444
return body
@@ -51,10 +51,10 @@ class TourApiClient(
5151
fun fetchLocationBasedTours(
5252
tourParams: TourParams,
5353
locationParams: TourLocationBasedParams,
54-
serviceSegment: String,
54+
languageCode: String,
5555
): TourResponse {
5656
val url =
57-
buildTourUri(serviceSegment, "locationBasedList2") {
57+
buildTourUri(languageCode, "locationBasedList2") {
5858
queryParam("mapX", locationParams.mapX)
5959
queryParam("mapY", locationParams.mapY)
6060
queryParam("radius", locationParams.radius)
@@ -65,7 +65,7 @@ class TourApiClient(
6565

6666
val body =
6767
runCatching { restTemplate.getForObject(url, String::class.java) }
68-
.onFailure { log.error("위치기반 관광 정보 조회 실패 - serviceSegment={}", serviceSegment, it) }
68+
.onFailure { log.error("위치기반 관광 정보 조회 실패 - languageCode={}", languageCode, it) }
6969
.getOrNull()
7070

7171
return body
@@ -77,16 +77,16 @@ class TourApiClient(
7777
// 공통정보 조회 (detailCommon2)
7878
fun fetchTourDetail(
7979
params: TourDetailParams,
80-
serviceSegment: String,
80+
languageCode: String,
8181
): TourDetailResponse {
8282
val url =
83-
buildTourUri(serviceSegment, "detailCommon2") {
83+
buildTourUri(languageCode, "detailCommon2") {
8484
queryParam("contentId", params.contentId)
8585
}
8686

8787
val body =
8888
runCatching { restTemplate.getForObject(url, String::class.java) }
89-
.onFailure { log.error("공통정보 조회 실패 - serviceSegment={}", serviceSegment, it) }
89+
.onFailure { log.error("공통정보 조회 실패 - languageCode={}", languageCode, it) }
9090
.getOrNull()
9191

9292
return body
@@ -176,12 +176,12 @@ class TourApiClient(
176176
}
177177

178178
private fun buildTourUri(
179-
serviceSegment: String,
179+
languageCode: String,
180180
vararg pathSegments: String,
181181
customize: UriComponentsBuilder.() -> Unit = {},
182182
): URI =
183183
UriComponentsBuilder.fromUri(URI.create(apiUrl))
184-
.pathSegment(serviceSegment, *pathSegments)
184+
.pathSegment(languageCode, *pathSegments)
185185
.apply {
186186
queryParam("serviceKey", serviceKey)
187187
queryParam("MobileOS", "WEB")

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/tour/service/TourParamsParser.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ class TourParamsParser {
99
contentTypeId: String,
1010
areaAndSigunguCode: String,
1111
): TourParams {
12-
val codes = areaAndSigunguCode.split(",").map { it.trim() }
12+
// 하이픈(-) 또는 쉼표(,) 둘 다 처리 (AI가 어떤 형식으로 보내도 작동)
13+
val codes = areaAndSigunguCode.split(",", "-").map { it.trim() }
1314

1415
val areaCode = codes.getOrNull(0)
1516
val sigunguCode = codes.getOrNull(1)

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/tour/service/TourService.kt

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,42 +27,35 @@ class TourService(
2727

2828
/**
2929
* 지역 기반 관광 정보를 조회한다.
30-
* 언어 문자열을 설정으로 정규화해 다국어 엔드포인트에 맞춰 전달한다.
30+
* languageCode는 AI가 사용자의 대화 언어를 파악하여 전달한다.
3131
*/
3232
fun fetchTours(
3333
tourParams: TourParams,
34-
languageCode: String? = null,
34+
languageCode: String,
3535
): TourResponse {
36-
val serviceSegment = languageCode?.takeIf { it.isNotBlank() } ?: DEFAULT_LANGUAGE_SEGMENT
37-
return tourAreaBasedUseCase.fetchAreaBasedTours(tourParams, serviceSegment)
36+
return tourAreaBasedUseCase.fetchAreaBasedTours(tourParams, languageCode)
3837
}
3938

4039
/**
4140
* 위치 기반 관광 정보를 조회한다.
42-
* 전달받은 언어 값을 설정 기반 서비스 세그먼트로 치환해 API 클라이언트를 호출한다.
41+
* languageCode는 AI가 사용자의 대화 언어를 파악하여 전달한다.
4342
*/
4443
fun fetchLocationBasedTours(
4544
tourParams: TourParams,
4645
locationParams: TourLocationBasedParams,
47-
languageCode: String? = null,
46+
languageCode: String,
4847
): TourResponse {
49-
val serviceSegment = languageCode?.takeIf { it.isNotBlank() } ?: DEFAULT_LANGUAGE_SEGMENT
50-
return tourLocationBasedUseCase.fetchLocationBasedTours(tourParams, locationParams, serviceSegment)
48+
return tourLocationBasedUseCase.fetchLocationBasedTours(tourParams, locationParams, languageCode)
5149
}
5250

5351
/**
5452
* 관광지 상세 정보를 조회한다.
55-
* 언어 값을 정규화해 상세 API 호출 시 사용한다.
53+
* languageCode는 AI가 사용자의 대화 언어를 파악하여 전달한다.
5654
*/
5755
fun fetchTourDetail(
5856
detailParams: TourDetailParams,
59-
languageCode: String? = null,
57+
languageCode: String,
6058
): TourDetailResponse {
61-
val serviceSegment = languageCode?.takeIf { it.isNotBlank() } ?: DEFAULT_LANGUAGE_SEGMENT
62-
return tourDetailUseCase.fetchTourDetail(detailParams, serviceSegment)
63-
}
64-
65-
companion object {
66-
private const val DEFAULT_LANGUAGE_SEGMENT = "KorService2"
59+
return tourDetailUseCase.fetchTourDetail(detailParams, languageCode)
6760
}
6861
}

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/tour/service/core/TourAreaBasedServiceCore.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@ class TourAreaBasedServiceCore(
1515
@Cacheable(
1616
"tourAreaBased",
1717
key =
18-
"#tourParams.contentTypeId + '_' + #tourParams.areaCode + '_' + #tourParams.sigunguCode + '_' + #serviceSegment",
18+
"#tourParams.contentTypeId + '_' + #tourParams.areaCode + '_' + #tourParams.sigunguCode + '_' + #languageCode",
1919
unless = "#result == null",
2020
)
2121
override fun fetchAreaBasedTours(
2222
tourParams: TourParams,
23-
serviceSegment: String,
23+
languageCode: String,
2424
): TourResponse {
2525
// 부산 사하구 관광안내소(콘텐츠타입 76, EngService2)에 대한 프리셋 응답
2626
if (
27-
serviceSegment == ENGLISH_SERVICE_SEGMENT &&
27+
languageCode == ENGLISH_SERVICE_SEGMENT &&
2828
tourParams.contentTypeId == "76" &&
2929
tourParams.areaCode == "6" &&
3030
tourParams.sigunguCode == "10"
@@ -34,15 +34,15 @@ class TourAreaBasedServiceCore(
3434

3535
// 부산 사하구 관광지 목록(콘텐츠타입 12, KorService2)에 대한 프리셋 응답
3636
if (
37-
serviceSegment == KOREAN_SERVICE_SEGMENT &&
37+
languageCode == KOREAN_SERVICE_SEGMENT &&
3838
tourParams.contentTypeId == "12" &&
3939
tourParams.areaCode == "6" &&
4040
tourParams.sigunguCode == "10"
4141
) {
4242
return PRESET_AREA_TOUR_RESPONSE
4343
}
4444

45-
return tourApiClient.fetchTourInfo(tourParams, serviceSegment)
45+
return tourApiClient.fetchTourInfo(tourParams, languageCode)
4646
}
4747

4848
private companion object {

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/tour/service/core/TourDetailServiceCore.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,24 @@ class TourDetailServiceCore(
1414
) : TourDetailUseCase {
1515
@Cacheable(
1616
"tourDetail",
17-
key = "#detailParams.contentId + '_' + #serviceSegment",
17+
key = "#detailParams.contentId + '_' + #languageCode",
1818
unless = "#result == null",
1919
)
2020
override fun fetchTourDetail(
2121
detailParams: TourDetailParams,
22-
serviceSegment: String,
22+
languageCode: String,
2323
): TourDetailResponse {
2424
// 을숙도 철새공원 상세정보(EngService2, contentId 264247) 프리셋
25-
if (serviceSegment == ENGLISH_SERVICE_SEGMENT && detailParams.contentId == "264247") {
25+
if (languageCode == ENGLISH_SERVICE_SEGMENT && detailParams.contentId == "264247") {
2626
return PRESET_DETAIL_RESPONSE_EN
2727
}
2828

2929
// 동촌유원지 상세정보(KorService2, contentId 127974) 프리셋
30-
if (serviceSegment == KOREAN_SERVICE_SEGMENT && detailParams.contentId == "127974") {
30+
if (languageCode == KOREAN_SERVICE_SEGMENT && detailParams.contentId == "127974") {
3131
return PRESET_DETAIL_RESPONSE
3232
}
3333

34-
return tourApiClient.fetchTourDetail(detailParams, serviceSegment)
34+
return tourApiClient.fetchTourDetail(detailParams, languageCode)
3535
}
3636

3737
private companion object {

0 commit comments

Comments
 (0)