7
7
import org .khtml .hexagonal .domain .building .dto .BuildingUpdate ;
8
8
import org .khtml .hexagonal .domain .building .dto .ImageRequest ;
9
9
import org .khtml .hexagonal .domain .building .dto .MaterialInfo ;
10
+ import org .khtml .hexagonal .domain .building .dto .MaterialResult ;
10
11
import org .springframework .beans .factory .annotation .Value ;
11
12
import org .springframework .http .*;
13
+ import org .springframework .stereotype .Component ;
12
14
import org .springframework .stereotype .Service ;
13
15
import org .springframework .transaction .annotation .Transactional ;
14
16
import org .springframework .web .client .RestTemplate ;
18
20
import java .util .List ;
19
21
import java .util .Map ;
20
22
21
- @ Transactional (readOnly = true )
22
23
@ RequiredArgsConstructor
23
- @ Service
24
- public class GptService {
24
+ @ Component
25
+ public class GptManager {
25
26
26
27
@ Value ("${openai.api.url}" )
27
28
private String openAiApiUrl ;
@@ -30,41 +31,52 @@ public class GptService {
30
31
private String openAiApiKey ;
31
32
32
33
public BuildingUpdate analyzeBuilding (List <String > urls ) throws JsonProcessingException {
33
- BuildingUpdate buildingUpdate ;
34
- ImageRequest imageRequest = new ImageRequest ( );
35
- List < ImageRequest . Content > contentList = new ArrayList <>();
34
+ ImageRequest imageRequest = getImageRequest ( urls ) ;
35
+ return analyzeHouseImages ( imageRequest );
36
+ }
36
37
37
- imageRequest .setRole ("user" );
38
+ public MaterialResult analyzeMaterial (List <String > urls ) throws JsonProcessingException {
39
+ ImageRequest imageRequest = getImageRequest (urls );
40
+ Map <String , Object > result = analyzeMaterialImages (imageRequest );
38
41
39
- for (String url : urls ) {
40
- ImageRequest .Content content = new ImageRequest .Content ();
41
- ImageRequest .ImageUrl imageUrl = new ImageRequest .ImageUrl ();
42
+ String material = null ;
43
+ String usage = null ;
42
44
43
- imageUrl .setUrl (url );
44
- content .setType ("image_url" );
45
- content .setImage_url (imageUrl );
46
- contentList .add (content );
45
+ List <Map <String , Object >> choices = (List <Map <String , Object >>) result .get ("choices" );
46
+ if (choices != null && !choices .isEmpty ()) {
47
+ Map <String , Object > firstChoice = choices .get (0 );
48
+ Map <String , Object > message = (Map <String , Object >) firstChoice .get ("message" );
49
+ if (message != null ) {
50
+ String content = (String ) message .get ("content" );
51
+
52
+ // content 값을 JSON으로 파싱
53
+ ObjectMapper objectMapper = new ObjectMapper ();
54
+ Map <String , Object > jsonData = objectMapper .readValue (content , new TypeReference <Map <String , Object >>() {
55
+ });
47
56
57
+ // 필요한 데이터 추출
58
+ material = (String ) jsonData .get ("material" );
59
+ usage = (String ) jsonData .get ("usage" );
60
+ }
48
61
}
49
62
50
- imageRequest .setContent (contentList );
51
- return analyzeHouseImages (imageRequest );
63
+ return new MaterialResult (material , usage );
52
64
}
53
65
54
66
public BuildingUpdate analyzeHouseImages (ImageRequest imageRequest ) throws JsonProcessingException {
55
67
56
68
// 시스템 메시지
57
69
String systemMessage = """
58
-
70
+
59
71
너는 오래된 건물의 상태를 평가하는 챗봇 AI다. 제공된 사진을 분석하여 건물의 상태를 진단하고, 결과를 한글로 작성된 JSON 형식으로 출력해야 한다.
60
72
이 JSON은 아래 예시와 같이 각 필드가 특정 영어 변수에 매핑되도록 구성되어야 한다.
61
-
73
+
62
74
건물 구조 분석 (structureReason):
63
-
75
+
64
76
구조: 건물의 구조가 전통적인지 현대적인지 판단한다.
65
77
이유: 판단의 이유를 설명한다.
66
78
건축 요소 평가 (roof, walls, windowsAndDoors):
67
-
79
+
68
80
지붕 형태 (roof):
69
81
재료: 지붕에 사용된 재료를 확인한다.
70
82
상태: 지붕의 상태를 평가한다.
@@ -75,27 +87,27 @@ public BuildingUpdate analyzeHouseImages(ImageRequest imageRequest) throws JsonP
75
87
재료: 창문과 문에 사용된 재료를 확인한다.
76
88
상태: 창문과 문의 상태를 평가한다.
77
89
건물 상태 평가 (overallCondition):
78
-
90
+
79
91
평가: 건물의 전반적인 상태를 구체적으로 평가한다.
80
92
이유: 해당 평가의 이유를 제시한다.
81
93
상세 점수화 (detailedScores):
82
-
94
+
83
95
균열 여부 (cracks): 균열의 존재 여부와 심각성을 100점 만점으로 점수화한다.
84
96
누수 여부 (leaks): 누수의 존재 여부와 심각성을 100점 만점으로 점수화한다.
85
97
부식 여부 (corrosion): 부식의 정도를 100점 만점으로 점수화한다.
86
98
노후화 정도 (aging): 건물의 노후화를 100점 만점으로 점수화한다.
87
99
총점수 (totalScore): 위의 점수를 합산하여 반올림한 총점을 계산한다.
88
100
보수 필요성 판단 (repairNeeds):
89
-
101
+
90
102
조명: 사진 속 조명이 형광등이라면 LED로 교체가 필요한지, 이미 LED라면 교체가 불필요한지 판단한다.
91
103
창호 보강, 도배, 장판 교체: 창호 보강, 도배, 장판 교체의 필요 여부를 판단한다.
92
-
104
+
93
105
판단하기 어려운 부분, 예를 들어 실내 사진인데 지붕 재료, 지붕 상태 등을 판단해야 하는 경우에는 해당 부분을 ""으로 처리한다.
94
106
repairlist의 경우 반드시 ,로 구분한다. repairlist가 없는 경우에는 ""으로 처리한다.
95
-
96
-
107
+
108
+
97
109
예시 JSON은 다음과 같다.
98
-
110
+
99
111
{
100
112
"structureReason": "구조 평가 이유",
101
113
"roofMaterial": "지붕 재료",
@@ -113,10 +125,10 @@ public BuildingUpdate analyzeHouseImages(ImageRequest imageRequest) throws JsonP
113
125
"totalScore": 20,
114
126
"repairList": "LED 교체, 창호 보강, 도배, 장판 교체"
115
127
}
116
-
117
-
118
-
119
-
128
+
129
+
130
+
131
+
120
132
""" ;
121
133
122
134
// OpenAI API에 보낼 요청 데이터 작성
@@ -182,7 +194,8 @@ public BuildingUpdate analyzeHouseImages(ImageRequest imageRequest) throws JsonP
182
194
183
195
// JSON 데이터를 Map으로 파싱
184
196
ObjectMapper objectMapper = new ObjectMapper ();
185
- Map <String , Object > jsonData = objectMapper .readValue (jsonContent , new TypeReference <Map <String , Object >>() {});
197
+ Map <String , Object > jsonData = objectMapper .readValue (jsonContent , new TypeReference <Map <String , Object >>() {
198
+ });
186
199
187
200
// BuildingUpdate DTO로 데이터 매핑
188
201
BuildingUpdate buildingUpdate = new BuildingUpdate ();
@@ -211,27 +224,27 @@ public BuildingUpdate analyzeHouseImages(ImageRequest imageRequest) throws JsonP
211
224
return buildingUpdate ;
212
225
}
213
226
214
- public ResponseEntity < Map <String , Object > > analyzeMaterialImages (ImageRequest imageRequest ) throws JsonProcessingException {
227
+ public Map <String , Object > analyzeMaterialImages (ImageRequest imageRequest ) throws JsonProcessingException {
215
228
216
229
// 시스템 메시지
217
230
String systemMessage = """
218
- 너는 사진을 받으면 해당 사진에 나오는 부자재의 종류들과 그 사용법들을 반환하는 챗봇 AI다.
219
-
220
- 제공된 사진을 분석하여 부자재의 종류와 사용법을 양식에 맞게 작성된 JSON 형식으로 출력해야 한다.
221
-
222
- 반드시 양식에 맞게 작성해야 하며, 다른 방식으로의 응답은 절대 허용하지 않는다.
223
-
224
- mateial과 usage는 여러개가 올 수 있지만 리스트 형식으로는 '절대' 출력하면 안된다.
225
-
226
- mateiral과 usage가 여러 가지 이상으로 판단될 시, 반드시 ,로만 구분하여 한 개의 string으로 출력해야 한다.
227
-
228
- 예시 JSON은 다음과 같다.
229
-
230
- {
231
- "material" : "나무 판자, 쇠 파이프"
232
- "usage" : "벽 만들기, 천장 고치기"
233
- }
234
- """ ;
231
+ 너는 사진을 받으면 해당 사진에 나오는 부자재의 종류들과 그 사용법들을 반환하는 챗봇 AI다.
232
+
233
+ 제공된 사진을 분석하여 부자재의 종류와 사용법을 양식에 맞게 작성된 JSON 형식으로 출력해야 한다.
234
+
235
+ 반드시 양식에 맞게 작성해야 하며, 다른 방식으로의 응답은 절대 허용하지 않는다.
236
+
237
+ mateial과 usage는 여러개가 올 수 있지만 리스트 형식으로는 '절대' 출력하면 안된다.
238
+
239
+ mateiral과 usage가 여러 가지 이상으로 판단될 시, 반드시 ,로만 구분하여 한 개의 string으로 출력해야 한다.
240
+
241
+ 예시 JSON은 다음과 같다.
242
+
243
+ {
244
+ "material" : "나무 판자, 쇠 파이프",
245
+ "usage" : "벽 만들기, 천장 고치기"
246
+ }
247
+ """ ;
235
248
236
249
// OpenAI API에 보낼 요청 데이터 작성
237
250
Map <String , Object > requestBody = new HashMap <>();
@@ -292,17 +305,38 @@ public ResponseEntity<Map<String, Object>> analyzeMaterialImages(ImageRequest im
292
305
String content = (String ) ((Map <String , Object >) ((Map <String , Object >) ((List <Object >) responseBody .get ("choices" )).get (0 )).get ("message" )).get ("content" );
293
306
294
307
// "```json"과 "```"을 제거하여 실제 JSON 데이터만 추출
295
- String jsonContent = content .replaceAll ("```json\\ n |```" , "" ).trim ();
308
+ String jsonContent = content .replaceAll ("(?s) ```json\\ s* |```" , "" ).trim ();
296
309
297
310
// JSON 데이터를 Map으로 파싱
298
311
ObjectMapper objectMapper = new ObjectMapper ();
299
- Map <String , Object > jsonData = objectMapper .readValue (jsonContent , new TypeReference <Map <String , Object >>() {});
312
+ Map <String , Object > jsonData = objectMapper .readValue (jsonContent , new TypeReference <Map <String , Object >>() {
313
+ });
300
314
// 필요한 데이터 사용
301
315
MaterialInfo materialInfo = new MaterialInfo ((String ) jsonData .get ("material" ), (String ) jsonData .get ("usage" ));
302
316
System .out .println (materialInfo );
303
317
304
318
// 리턴값 반환
305
- return ResponseEntity .ok (responseBody );
319
+ return responseBody ;
320
+ }
321
+
322
+ private static ImageRequest getImageRequest (List <String > urls ) {
323
+ ImageRequest imageRequest = new ImageRequest ();
324
+ List <ImageRequest .Content > contentList = new ArrayList <>();
325
+
326
+ imageRequest .setRole ("user" );
327
+
328
+ for (String url : urls ) {
329
+ ImageRequest .Content content = new ImageRequest .Content ();
330
+ ImageRequest .ImageUrl imageUrl = new ImageRequest .ImageUrl ();
331
+
332
+ imageUrl .setUrl (url );
333
+ content .setType ("image_url" );
334
+ content .setImage_url (imageUrl );
335
+ contentList .add (content );
336
+ }
337
+
338
+ imageRequest .setContent (contentList );
339
+ return imageRequest ;
306
340
}
307
341
308
342
}
0 commit comments