From 34a7b8bd1670277b2ecbdac79b817b8889e94be2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Sat, 9 May 2026 13:00:32 +0900 Subject: [PATCH 01/45] =?UTF-8?q?build:=20Lombok=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index 941e596..8283162 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.ai:spring-ai-starter-model-openai' + implementation 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' } From ef83a9c57511dd44825f9289e2d06503a1b59096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Sat, 9 May 2026 13:05:15 +0900 Subject: [PATCH 02/45] =?UTF-8?q?feat:=20POST=20/api/chat=20=EC=B4=88?= =?UTF-8?q?=EA=B8=B0=20=EC=8A=A4=EC=BC=88=EB=A0=88=ED=86=A4=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cholog/bootcamp/ChatController.java | 23 +++++++++++++++++++ .../java/com/cholog/bootcamp/ChatRequest.java | 7 ++++++ .../com/cholog/bootcamp/ChatResponse.java | 14 +++++++++++ .../java/com/cholog/bootcamp/ChatService.java | 11 +++++++++ 4 files changed, 55 insertions(+) create mode 100644 src/main/java/com/cholog/bootcamp/ChatController.java create mode 100644 src/main/java/com/cholog/bootcamp/ChatRequest.java create mode 100644 src/main/java/com/cholog/bootcamp/ChatResponse.java create mode 100644 src/main/java/com/cholog/bootcamp/ChatService.java diff --git a/src/main/java/com/cholog/bootcamp/ChatController.java b/src/main/java/com/cholog/bootcamp/ChatController.java new file mode 100644 index 0000000..94db05c --- /dev/null +++ b/src/main/java/com/cholog/bootcamp/ChatController.java @@ -0,0 +1,23 @@ +package com.cholog.bootcamp; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/chat") +public class ChatController { + + private final ChatService chatService; + + @PostMapping + public ResponseEntity askQuestion(@RequestBody ChatRequest request) { + ChatResponse response = chatService.askQuestion(request); + return ResponseEntity.ok(response); + } +} diff --git a/src/main/java/com/cholog/bootcamp/ChatRequest.java b/src/main/java/com/cholog/bootcamp/ChatRequest.java new file mode 100644 index 0000000..c8e0185 --- /dev/null +++ b/src/main/java/com/cholog/bootcamp/ChatRequest.java @@ -0,0 +1,7 @@ +package com.cholog.bootcamp; + +public record ChatRequest( + String question +) { + +} diff --git a/src/main/java/com/cholog/bootcamp/ChatResponse.java b/src/main/java/com/cholog/bootcamp/ChatResponse.java new file mode 100644 index 0000000..8499321 --- /dev/null +++ b/src/main/java/com/cholog/bootcamp/ChatResponse.java @@ -0,0 +1,14 @@ +package com.cholog.bootcamp; + +public record ChatResponse( + String answer, + InnerTokenUsageResponse tokenUsage +) { + public record InnerTokenUsageResponse( + Integer promptTokens, + Integer completionTokens, + Integer totalTokens + ) { + + } +} diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java new file mode 100644 index 0000000..eb6beba --- /dev/null +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -0,0 +1,11 @@ +package com.cholog.bootcamp; + +import org.springframework.stereotype.Service; + +@Service +public class ChatService { + + public ChatResponse askQuestion(ChatRequest request) { + return null; + } +} From 31a476e2d746a4bfe4335de64e0f78a72e859a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Sat, 9 May 2026 13:44:30 +0900 Subject: [PATCH 03/45] =?UTF-8?q?fix:=20=EC=9A=94=EC=B2=AD/=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20DTO=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EB=B0=8D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/cholog/bootcamp/ChatController.java | 4 ++-- src/main/java/com/cholog/bootcamp/ChatService.java | 2 +- .../bootcamp/{ChatRequest.java => QuestionAskRequest.java} | 2 +- .../bootcamp/{ChatResponse.java => QuestionAskResponse.java} | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename src/main/java/com/cholog/bootcamp/{ChatRequest.java => QuestionAskRequest.java} (62%) rename src/main/java/com/cholog/bootcamp/{ChatResponse.java => QuestionAskResponse.java} (87%) diff --git a/src/main/java/com/cholog/bootcamp/ChatController.java b/src/main/java/com/cholog/bootcamp/ChatController.java index 94db05c..ec1b775 100644 --- a/src/main/java/com/cholog/bootcamp/ChatController.java +++ b/src/main/java/com/cholog/bootcamp/ChatController.java @@ -16,8 +16,8 @@ public class ChatController { private final ChatService chatService; @PostMapping - public ResponseEntity askQuestion(@RequestBody ChatRequest request) { - ChatResponse response = chatService.askQuestion(request); + public ResponseEntity askQuestion(@RequestBody QuestionAskRequest request) { + QuestionAskResponse response = chatService.askQuestion(request); return ResponseEntity.ok(response); } } diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index eb6beba..a7431a3 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -5,7 +5,7 @@ @Service public class ChatService { - public ChatResponse askQuestion(ChatRequest request) { + public QuestionAskResponse askQuestion(QuestionAskRequest request) { return null; } } diff --git a/src/main/java/com/cholog/bootcamp/ChatRequest.java b/src/main/java/com/cholog/bootcamp/QuestionAskRequest.java similarity index 62% rename from src/main/java/com/cholog/bootcamp/ChatRequest.java rename to src/main/java/com/cholog/bootcamp/QuestionAskRequest.java index c8e0185..80118d8 100644 --- a/src/main/java/com/cholog/bootcamp/ChatRequest.java +++ b/src/main/java/com/cholog/bootcamp/QuestionAskRequest.java @@ -1,6 +1,6 @@ package com.cholog.bootcamp; -public record ChatRequest( +public record QuestionAskRequest( String question ) { diff --git a/src/main/java/com/cholog/bootcamp/ChatResponse.java b/src/main/java/com/cholog/bootcamp/QuestionAskResponse.java similarity index 87% rename from src/main/java/com/cholog/bootcamp/ChatResponse.java rename to src/main/java/com/cholog/bootcamp/QuestionAskResponse.java index 8499321..cfcfd76 100644 --- a/src/main/java/com/cholog/bootcamp/ChatResponse.java +++ b/src/main/java/com/cholog/bootcamp/QuestionAskResponse.java @@ -1,6 +1,6 @@ package com.cholog.bootcamp; -public record ChatResponse( +public record QuestionAskResponse( String answer, InnerTokenUsageResponse tokenUsage ) { From 4518ab0433c4da236ae30212286b41fbec0e8b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Sat, 9 May 2026 13:55:33 +0900 Subject: [PATCH 04/45] =?UTF-8?q?feat:=20OpenAI=20API=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/cholog/bootcamp/ChatService.java | 22 ++++++++++++++++++- .../cholog/bootcamp/QuestionAskResponse.java | 13 +++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index a7431a3..57b988c 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -1,11 +1,31 @@ package com.cholog.bootcamp; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.chat.metadata.Usage; +import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.chat.model.ChatResponse; import org.springframework.stereotype.Service; @Service public class ChatService { + private final ChatClient chatClient; + + public ChatService(ChatModel chatModel) { + this.chatClient = ChatClient.builder(chatModel).build(); + } + public QuestionAskResponse askQuestion(QuestionAskRequest request) { - return null; + ChatResponse chatResponse = chatClient.prompt(request.question()) + .call() + .chatResponse(); + Usage usage = chatResponse.getMetadata().getUsage(); + + return QuestionAskResponse.from( + chatResponse.getResults().get(0).getOutput().getText(), + usage.getPromptTokens(), + usage.getCompletionTokens(), + usage.getTotalTokens() + ); } } diff --git a/src/main/java/com/cholog/bootcamp/QuestionAskResponse.java b/src/main/java/com/cholog/bootcamp/QuestionAskResponse.java index cfcfd76..391e6ad 100644 --- a/src/main/java/com/cholog/bootcamp/QuestionAskResponse.java +++ b/src/main/java/com/cholog/bootcamp/QuestionAskResponse.java @@ -9,6 +9,19 @@ public record InnerTokenUsageResponse( Integer completionTokens, Integer totalTokens ) { + public static InnerTokenUsageResponse from( + Integer promptTokens, Integer completionTokens, Integer totalTokens + ) { + return new InnerTokenUsageResponse(promptTokens, completionTokens, totalTokens); + } + } + public static QuestionAskResponse from( + String answer, Integer promptTokens, Integer completionTokens, Integer totalTokens + ) { + return new QuestionAskResponse( + answer, + InnerTokenUsageResponse.from(promptTokens, completionTokens, totalTokens) + ); } } From 71f066d9983e96cb503ef0f9413e7e7c18156519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Mon, 11 May 2026 14:03:47 +0900 Subject: [PATCH 05/45] =?UTF-8?q?feat:=20FAQ=20=EC=BB=A8=ED=85=8D=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=9C=EA=B3=B5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/cholog/bootcamp/ChatService.java | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index 57b988c..09dc7ce 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -1,5 +1,10 @@ package com.cholog.bootcamp; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.metadata.Usage; import org.springframework.ai.chat.model.ChatModel; @@ -16,7 +21,17 @@ public ChatService(ChatModel chatModel) { } public QuestionAskResponse askQuestion(QuestionAskRequest request) { - ChatResponse chatResponse = chatClient.prompt(request.question()) + String faq = getFAQ(); + + ChatResponse chatResponse = chatClient.prompt() + .system(""" + 당신은 초록 코퍼레이션에서 고객지원 챗봇을 담당하는 역할입니다. + 제공된 문서를 참고하여 고객에게 답변을 해주세요. + 모든 응답은 한국어로 해야하며, 초록 코퍼레이션과 무관한 내용은 다루지 마세요. + + %s + """.formatted(faq)) + .user(request.question()) .call() .chatResponse(); Usage usage = chatResponse.getMetadata().getUsage(); @@ -28,4 +43,25 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { usage.getTotalTokens() ); } + + private String getFAQ() { + StringBuilder sb = new StringBuilder(); + + File file = new File("data/layer1_faq"); + File[] files = file.listFiles(); + + for (File fs : files) { + sb.append(file.getName()).append('\n'); + try (BufferedReader br = new BufferedReader(new FileReader(fs))) { + String line; + while ((line = br.readLine()) != null) { + sb.append(line).append('\n'); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + return sb.toString(); + } } From 336c798cba2d07af80d4e4e429bd82f7dfb472a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Mon, 11 May 2026 14:38:02 +0900 Subject: [PATCH 06/45] =?UTF-8?q?feat:=20=EC=A0=95=EC=B1=85=20=EC=BB=A8?= =?UTF-8?q?=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=A0=9C=EA=B3=B5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/cholog/bootcamp/ChatService.java | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index 09dc7ce..4fbf1f8 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -22,6 +22,9 @@ public ChatService(ChatModel chatModel) { public QuestionAskResponse askQuestion(QuestionAskRequest request) { String faq = getFAQ(); + String currentPolicy = getCurrentPolicy(); + String deprecatedPolicy = getDeprecatedPolicy(); + String internalPolicy = getInternalPolicy(); ChatResponse chatResponse = chatClient.prompt() .system(""" @@ -29,8 +32,18 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { 제공된 문서를 참고하여 고객에게 답변을 해주세요. 모든 응답은 한국어로 해야하며, 초록 코퍼레이션과 무관한 내용은 다루지 마세요. + FAQ는 다음과 같습니다. %s - """.formatted(faq)) + + 현재 정책은 다음과 같습니다. + %s + + 폐지된 정책은 다음과 같습니다. + %s + + 회사 내부 정책은 다음과 같습니다. + %s + """.formatted(faq, currentPolicy, deprecatedPolicy, internalPolicy)) .user(request.question()) .call() .chatResponse(); @@ -48,6 +61,31 @@ private String getFAQ() { StringBuilder sb = new StringBuilder(); File file = new File("data/layer1_faq"); + return readFileToString(file, sb); + } + + private String getCurrentPolicy() { + StringBuilder sb = new StringBuilder(); + + File file = new File("data/layer2_policies/current"); + return readFileToString(file, sb); + } + + private String getDeprecatedPolicy() { + StringBuilder sb = new StringBuilder(); + + File file = new File("data/layer2_policies/deprecated"); + return readFileToString(file, sb); + } + + private String getInternalPolicy() { + StringBuilder sb = new StringBuilder(); + + File file = new File("data/layer2_policies/internal"); + return readFileToString(file, sb); + } + + private String readFileToString(File file, StringBuilder sb) { File[] files = file.listFiles(); for (File fs : files) { From 5019f9dc15526d1dd4702f78ce46cc4f18e65b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Tue, 12 May 2026 12:59:09 +0900 Subject: [PATCH 07/45] =?UTF-8?q?chore:=20LLM=20=EC=9A=94=EC=B2=AD/?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20=EB=A1=9C=EA=B9=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/cholog/bootcamp/ChatService.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index 4fbf1f8..9a91b2e 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -11,6 +11,9 @@ import org.springframework.ai.chat.model.ChatResponse; import org.springframework.stereotype.Service; +import lombok.extern.slf4j.Slf4j; + +@Slf4j @Service public class ChatService { @@ -49,6 +52,11 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { .chatResponse(); Usage usage = chatResponse.getMetadata().getUsage(); + log.info(""" + request : {} + response : {} + """, request.question(), chatResponse.getResults().get(0).getOutput().getText()); + return QuestionAskResponse.from( chatResponse.getResults().get(0).getOutput().getText(), usage.getPromptTokens(), From f53777e69c48c8423f49d0e8ce2ac17dac86732e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Tue, 12 May 2026 13:48:04 +0900 Subject: [PATCH 08/45] =?UTF-8?q?fix:=20=ED=8F=90=EC=A7=80=20=EC=A0=95?= =?UTF-8?q?=EC=B1=85=20=EC=BB=A8=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=A0=9C?= =?UTF-8?q?=EA=B3=B5=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/cholog/bootcamp/ChatService.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index 9a91b2e..1d6045e 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -41,12 +41,9 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { 현재 정책은 다음과 같습니다. %s - 폐지된 정책은 다음과 같습니다. - %s - 회사 내부 정책은 다음과 같습니다. %s - """.formatted(faq, currentPolicy, deprecatedPolicy, internalPolicy)) + """.formatted(faq, currentPolicy, internalPolicy)) .user(request.question()) .call() .chatResponse(); From afd182a9d6daf711e679dd0cba7090f2cd0718b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Tue, 12 May 2026 13:48:25 +0900 Subject: [PATCH 09/45] =?UTF-8?q?fix:=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20?= =?UTF-8?q?=ED=94=84=EB=A1=AC=ED=94=84=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/cholog/bootcamp/ChatService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index 1d6045e..993ed08 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -32,8 +32,8 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { ChatResponse chatResponse = chatClient.prompt() .system(""" 당신은 초록 코퍼레이션에서 고객지원 챗봇을 담당하는 역할입니다. - 제공된 문서를 참고하여 고객에게 답변을 해주세요. - 모든 응답은 한국어로 해야하며, 초록 코퍼레이션과 무관한 내용은 다루지 마세요. + 질문으로 들어오는 내용과 관련된 문서를 참고하여 최대한 자세하고 구체적으로 답변해주세요. + 요청이 한국어로 들어오면 영어로 생각하고 한국어로 응답해주시고, 초록 코퍼레이션과 무관한 내용은 다루지 마세요. FAQ는 다음과 같습니다. %s From 70732453299e3b793d8f02993db386aace0a103c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Tue, 12 May 2026 13:56:29 +0900 Subject: [PATCH 10/45] =?UTF-8?q?chore:=20=ED=86=A0=ED=81=B0=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EB=9F=89=20=EB=A1=9C=EA=B9=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/cholog/bootcamp/ChatService.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index 993ed08..3dc1193 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -52,7 +52,14 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { log.info(""" request : {} response : {} - """, request.question(), chatResponse.getResults().get(0).getOutput().getText()); + promptTokens : {} completionTokens : {}, totalTokens : {} + """, + request.question(), + chatResponse.getResults().get(0).getOutput().getText(), + usage.getPromptTokens(), + usage.getCompletionTokens(), + usage.getTotalTokens() + ); return QuestionAskResponse.from( chatResponse.getResults().get(0).getOutput().getText(), From 28a05aa7b41b89cf6272008ca5c632bd4a1b7062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Tue, 12 May 2026 14:15:24 +0900 Subject: [PATCH 11/45] =?UTF-8?q?fix:=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20?= =?UTF-8?q?=ED=94=84=EB=A1=AC=ED=94=84=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/cholog/bootcamp/ChatService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index 3dc1193..6373179 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -32,7 +32,7 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { ChatResponse chatResponse = chatClient.prompt() .system(""" 당신은 초록 코퍼레이션에서 고객지원 챗봇을 담당하는 역할입니다. - 질문으로 들어오는 내용과 관련된 문서를 참고하여 최대한 자세하고 구체적으로 답변해주세요. + 질문으로 들어오는 내용과 관련된 문서를 참고하고, 제공된 정보만을 사용하여 최대한 자세하고 구체적으로 답변해주세요. 요청이 한국어로 들어오면 영어로 생각하고 한국어로 응답해주시고, 초록 코퍼레이션과 무관한 내용은 다루지 마세요. FAQ는 다음과 같습니다. From 289c43ee947efb0de231fe02578bea101b3f8649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Thu, 14 May 2026 06:29:05 +0900 Subject: [PATCH 12/45] =?UTF-8?q?fix:=20=EC=9E=98=EB=AA=BB=EB=90=9C=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=9D=B4=EB=A6=84=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/cholog/bootcamp/ChatService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index 6373179..1a5ab55 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -101,7 +101,7 @@ private String readFileToString(File file, StringBuilder sb) { File[] files = file.listFiles(); for (File fs : files) { - sb.append(file.getName()).append('\n'); + sb.append(fs.getName()).append('\n'); try (BufferedReader br = new BufferedReader(new FileReader(fs))) { String line; while ((line = br.readLine()) != null) { From 69d8b5ac78bef0a710f649a8f7b0e55042919c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Thu, 14 May 2026 06:29:29 +0900 Subject: [PATCH 13/45] =?UTF-8?q?fix:=20AI=20=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=EB=A9=94=EC=86=8C=EB=93=9C=20=ED=98=B8=EC=B6=9C=EB=B6=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/cholog/bootcamp/ChatService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index 1a5ab55..5b18e46 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -62,7 +62,7 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { ); return QuestionAskResponse.from( - chatResponse.getResults().get(0).getOutput().getText(), + chatResponse.getResult().getOutput().getText(), usage.getPromptTokens(), usage.getCompletionTokens(), usage.getTotalTokens() From f06ef968b5f4f091cd142ee460554aef35565136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Thu, 14 May 2026 14:19:11 +0900 Subject: [PATCH 14/45] =?UTF-8?q?build:=20VectorStore=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 8283162..6886bb9 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,7 @@ dependencyManagement { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.ai:spring-ai-starter-model-openai' + implementation 'org.springframework.ai:spring-ai-advisors-vector-store' implementation 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' From 27e5167d9e534f7c877b897b9dba9befdf6af5bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Thu, 14 May 2026 15:42:22 +0900 Subject: [PATCH 15/45] =?UTF-8?q?build:=20MarkdownReader=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 6886bb9..400ffb3 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.ai:spring-ai-starter-model-openai' implementation 'org.springframework.ai:spring-ai-advisors-vector-store' + implementation 'org.springframework.ai:spring-ai-markdown-document-reader' implementation 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' From 0b397203fbdb6a89ab3ca6a134d4452d5ce1abe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Sat, 16 May 2026 14:15:48 +0900 Subject: [PATCH 16/45] =?UTF-8?q?fix:=20=ED=8F=89=EA=B0=80=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A6=BD=ED=8A=B8=EC=97=90=EC=84=9C=20=EC=98=81?= =?UTF-8?q?=EC=96=B4=EB=A1=9C=20=EC=A7=88=EB=AC=B8=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/evaluate.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data/evaluate.py b/data/evaluate.py index ed941cd..948ee79 100644 --- a/data/evaluate.py +++ b/data/evaluate.py @@ -138,6 +138,7 @@ def main(): for i, q in enumerate(questions): qid = q.get("id", f"Q{i+1}") question_ko = q["question_ko"] + question_en = q["question_en"] expected = q["expected_answer"] tier = q.get("tier", "unknown") @@ -146,7 +147,8 @@ def main(): tier_results[tier]["total"] += 1 # 서버에 질문 - response = ask_server(question_ko) +# response = ask_server(question_ko) + response = ask_server(question_en) if response is None: results["error"] += 1 if args.verbose: From f0d94dedfab0ef709502798b31f8f3d3002858a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Sat, 16 May 2026 14:59:24 +0900 Subject: [PATCH 17/45] =?UTF-8?q?feat:=20VectorStore=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cholog/bootcamp/VectorStoreConfig.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/java/com/cholog/bootcamp/VectorStoreConfig.java diff --git a/src/main/java/com/cholog/bootcamp/VectorStoreConfig.java b/src/main/java/com/cholog/bootcamp/VectorStoreConfig.java new file mode 100644 index 0000000..c494610 --- /dev/null +++ b/src/main/java/com/cholog/bootcamp/VectorStoreConfig.java @@ -0,0 +1,16 @@ +package com.cholog.bootcamp; + +import org.springframework.ai.embedding.EmbeddingModel; +import org.springframework.ai.vectorstore.SimpleVectorStore; +import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class VectorStoreConfig { + + @Bean + public VectorStore vectorStore(EmbeddingModel embeddingModel) { + return SimpleVectorStore.builder(embeddingModel).build(); + } +} From 478262e94d9cee54983d68ed7968939b94686963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Sat, 16 May 2026 14:59:33 +0900 Subject: [PATCH 18/45] =?UTF-8?q?feat:=20ChatClient=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cholog/bootcamp/ChatClientConfig.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/main/java/com/cholog/bootcamp/ChatClientConfig.java diff --git a/src/main/java/com/cholog/bootcamp/ChatClientConfig.java b/src/main/java/com/cholog/bootcamp/ChatClientConfig.java new file mode 100644 index 0000000..32c9232 --- /dev/null +++ b/src/main/java/com/cholog/bootcamp/ChatClientConfig.java @@ -0,0 +1,15 @@ +package com.cholog.bootcamp; + +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.chat.model.ChatModel; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ChatClientConfig { + + @Bean + public ChatClient chatClient(ChatModel chatModel) { + return ChatClient.builder(chatModel).build(); + } +} From 3602fb0513ef435846d0b7fa4b35ae66d2c31b2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Sat, 16 May 2026 17:22:47 +0900 Subject: [PATCH 19/45] =?UTF-8?q?fix:=20=ED=8C=8C=EC=9D=BC=20=EC=9D=BD?= =?UTF-8?q?=EA=B8=B0=20=EB=A1=9C=EC=A7=81=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/cholog/bootcamp/ChatService.java | 62 +------------------ 1 file changed, 1 insertion(+), 61 deletions(-) diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index 5b18e46..5c350ea 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -24,26 +24,12 @@ public ChatService(ChatModel chatModel) { } public QuestionAskResponse askQuestion(QuestionAskRequest request) { - String faq = getFAQ(); - String currentPolicy = getCurrentPolicy(); - String deprecatedPolicy = getDeprecatedPolicy(); - String internalPolicy = getInternalPolicy(); - ChatResponse chatResponse = chatClient.prompt() .system(""" 당신은 초록 코퍼레이션에서 고객지원 챗봇을 담당하는 역할입니다. 질문으로 들어오는 내용과 관련된 문서를 참고하고, 제공된 정보만을 사용하여 최대한 자세하고 구체적으로 답변해주세요. 요청이 한국어로 들어오면 영어로 생각하고 한국어로 응답해주시고, 초록 코퍼레이션과 무관한 내용은 다루지 마세요. - - FAQ는 다음과 같습니다. - %s - - 현재 정책은 다음과 같습니다. - %s - - 회사 내부 정책은 다음과 같습니다. - %s - """.formatted(faq, currentPolicy, internalPolicy)) + """) .user(request.question()) .call() .chatResponse(); @@ -68,50 +54,4 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { usage.getTotalTokens() ); } - - private String getFAQ() { - StringBuilder sb = new StringBuilder(); - - File file = new File("data/layer1_faq"); - return readFileToString(file, sb); - } - - private String getCurrentPolicy() { - StringBuilder sb = new StringBuilder(); - - File file = new File("data/layer2_policies/current"); - return readFileToString(file, sb); - } - - private String getDeprecatedPolicy() { - StringBuilder sb = new StringBuilder(); - - File file = new File("data/layer2_policies/deprecated"); - return readFileToString(file, sb); - } - - private String getInternalPolicy() { - StringBuilder sb = new StringBuilder(); - - File file = new File("data/layer2_policies/internal"); - return readFileToString(file, sb); - } - - private String readFileToString(File file, StringBuilder sb) { - File[] files = file.listFiles(); - - for (File fs : files) { - sb.append(fs.getName()).append('\n'); - try (BufferedReader br = new BufferedReader(new FileReader(fs))) { - String line; - while ((line = br.readLine()) != null) { - sb.append(line).append('\n'); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - return sb.toString(); - } } From 88b096750d6ab8d76211084248818a5c5a67014e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Sat, 16 May 2026 17:42:40 +0900 Subject: [PATCH 20/45] =?UTF-8?q?feat:=20FAQ=20=EB=A6=AC=EB=8D=94=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/cholog/bootcamp/FaqReader.java | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/main/java/com/cholog/bootcamp/FaqReader.java diff --git a/src/main/java/com/cholog/bootcamp/FaqReader.java b/src/main/java/com/cholog/bootcamp/FaqReader.java new file mode 100644 index 0000000..2636657 --- /dev/null +++ b/src/main/java/com/cholog/bootcamp/FaqReader.java @@ -0,0 +1,91 @@ +package com.cholog.bootcamp; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.springframework.ai.document.Document; +import org.springframework.stereotype.Component; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +public class FaqReader { + + private static final Path FAQ_DIRECTORY = Path.of("data/layer1_faq"); + + public List read() { + try (Stream paths = Files.list(FAQ_DIRECTORY)) { + return paths + .flatMap(path -> readFile(path).stream()) + .toList(); + } catch (IOException e) { + throw new IllegalArgumentException(""); + } + } + + private List readFile(Path path) { + try { + return parse(path, Files.readAllLines(path, StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new IllegalArgumentException(""); + } + } + + private List parse(Path path, List lines) { + List documents = new ArrayList<>(); + String category = null; + String question = null; + StringBuilder answer = new StringBuilder(); + + for (String line : lines) { + if (line.startsWith("# ")) { + category = line.substring(2).trim(); + continue; + } + + if (line.startsWith("## ")) { + continue; + } + + if (line.startsWith("### ")) { + addDocument(documents, path, category, question, answer); + question = line.substring(4).trim(); + answer.setLength(0); + continue; + } + + if (question != null) { + answer.append(line).append('\n'); + } + } + + addDocument(documents, path, category, question, answer); + + return documents; + } + + private void addDocument( + List documents, Path path, String category, String question, StringBuilder answer + ) { + if (question == null) { + return; + } + + documents.add(new Document(""" + Question: %s + Answer: %s + """.formatted(question, answer), Map.of( + "layer", "layer1_faq", + "source", path.getFileName().toString(), + "category", category, + "question", question + ))); + } +} From 6f5011f7081561a9016820f11811f6ed19c303d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Sat, 16 May 2026 17:43:23 +0900 Subject: [PATCH 21/45] =?UTF-8?q?fix:=20VectorStore=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=BB=A8=ED=85=8D=EC=8A=A4=ED=8A=B8=EB=A5=BC=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/cholog/bootcamp/ChatService.java | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index 5c350ea..e152ada 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -1,36 +1,49 @@ package com.cholog.bootcamp; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.metadata.Usage; -import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.document.Document; +import org.springframework.ai.vectorstore.SearchRequest; +import org.springframework.ai.vectorstore.VectorStore; import org.springframework.stereotype.Service; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j @Service +@RequiredArgsConstructor public class ChatService { private final ChatClient chatClient; - - public ChatService(ChatModel chatModel) { - this.chatClient = ChatClient.builder(chatModel).build(); - } + private final VectorStore vectorStore; public QuestionAskResponse askQuestion(QuestionAskRequest request) { + SearchRequest searchRequest = SearchRequest.builder() + .query(request.question()) + .build(); + List documents = vectorStore.similaritySearch(searchRequest); + String context = documents.stream() + .map(Document::getText) + .collect(Collectors.joining("\n\n")); + ChatResponse chatResponse = chatClient.prompt() .system(""" 당신은 초록 코퍼레이션에서 고객지원 챗봇을 담당하는 역할입니다. 질문으로 들어오는 내용과 관련된 문서를 참고하고, 제공된 정보만을 사용하여 최대한 자세하고 구체적으로 답변해주세요. 요청이 한국어로 들어오면 영어로 생각하고 한국어로 응답해주시고, 초록 코퍼레이션과 무관한 내용은 다루지 마세요. """) - .user(request.question()) + .user(""" + 참고 자료 + %s + + 질문 + %s + """.formatted(context, request.question())) .call() .chatResponse(); Usage usage = chatResponse.getMetadata().getUsage(); From 36fc12cada0b5b3c5afce86031dbecccbc0d41c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Sat, 16 May 2026 17:52:31 +0900 Subject: [PATCH 22/45] =?UTF-8?q?feat:=20Policy=20=EB=A6=AC=EB=8D=94=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cholog/bootcamp/CurrentPolicyReader.java | 128 ++++++++++++++++++ .../cholog/bootcamp/InternalPolicyReader.java | 128 ++++++++++++++++++ 2 files changed, 256 insertions(+) create mode 100644 src/main/java/com/cholog/bootcamp/CurrentPolicyReader.java create mode 100644 src/main/java/com/cholog/bootcamp/InternalPolicyReader.java diff --git a/src/main/java/com/cholog/bootcamp/CurrentPolicyReader.java b/src/main/java/com/cholog/bootcamp/CurrentPolicyReader.java new file mode 100644 index 0000000..d73e2bb --- /dev/null +++ b/src/main/java/com/cholog/bootcamp/CurrentPolicyReader.java @@ -0,0 +1,128 @@ +package com.cholog.bootcamp; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.springframework.ai.document.Document; +import org.springframework.stereotype.Component; + +@Component +public class CurrentPolicyReader { + + private static final Path DIRECTORY = Path.of("data/layer2_policies/current"); + + public List read() { + try (Stream paths = Files.list(DIRECTORY)) { + return paths + .flatMap(path -> readFile(path).stream()) + .toList(); + } catch (IOException e) { + throw new IllegalArgumentException(""); + } + } + + private List readFile(Path path) { + try { + return parse(path, Files.readAllLines(path, StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new IllegalArgumentException(""); + } + } + + private List parse(Path path, List lines) { + List documents = new ArrayList<>(); + Map metadata = new HashMap<>(); + String title = path.getFileName().toString(); + String section = null; + StringBuilder sectionBody = new StringBuilder(); + boolean frontMatter = false; + boolean frontMatterDone = false; + + for (String line : lines) { + if (!frontMatterDone && line.equals("---")) { + if (!frontMatter) { + frontMatter = true; + } else { + frontMatterDone = true; + } + continue; + } + + if (frontMatter && !frontMatterDone) { + parseMetadata(line, metadata); + continue; + } + + if (line.startsWith("# ")) { + title = line.substring(2).trim(); + continue; + } + + if (line.startsWith("## ")) { + addDocument(documents, path, metadata, title, section, sectionBody); + section = line.substring(3).trim(); + sectionBody.setLength(0); + continue; + } + + if (section != null) { + sectionBody.append(line).append('\n'); + } + } + + addDocument(documents, path, metadata, title, section, sectionBody); + + return documents; + } + + private void parseMetadata(String line, Map metadata) { + int separator = line.indexOf(':'); + if (separator < 0) { + return; + } + + String key = line.substring(0, separator).trim(); + String value = line.substring(separator + 1).trim(); + if (!key.isBlank() && !value.isBlank()) { + metadata.put(key, value); + } + } + + private void addDocument( + List documents, + Path path, + Map fileMetadata, + String title, + String section, + StringBuilder sectionBody + ) { + if (section == null) { + return; + } + + String body = sectionBody.toString().trim(); + if (body.isBlank()) { + return; + } + + Map metadata = new HashMap<>(fileMetadata); + metadata.put("layer", "layer2_policies"); + metadata.put("policy_scope", "current"); + metadata.put("source", path.getFileName().toString()); + metadata.put("title", metadata.getOrDefault("title", title)); + metadata.put("section", section); + + documents.add(new Document(""" + Policy: %s + Section: %s + %s + """.formatted(metadata.get("title"), section, body).trim(), metadata)); + } +} diff --git a/src/main/java/com/cholog/bootcamp/InternalPolicyReader.java b/src/main/java/com/cholog/bootcamp/InternalPolicyReader.java new file mode 100644 index 0000000..2e970c2 --- /dev/null +++ b/src/main/java/com/cholog/bootcamp/InternalPolicyReader.java @@ -0,0 +1,128 @@ +package com.cholog.bootcamp; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.springframework.ai.document.Document; +import org.springframework.stereotype.Component; + +@Component +public class InternalPolicyReader { + + private static final Path DIRECTORY = Path.of("data/layer2_policies/internal"); + + public List read() { + try (Stream paths = Files.list(DIRECTORY)) { + return paths + .flatMap(path -> readFile(path).stream()) + .toList(); + } catch (IOException e) { + throw new IllegalArgumentException(""); + } + } + + private List readFile(Path path) { + try { + return parse(path, Files.readAllLines(path, StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new IllegalArgumentException(""); + } + } + + private List parse(Path path, List lines) { + List documents = new ArrayList<>(); + Map metadata = new HashMap<>(); + String title = path.getFileName().toString(); + String section = null; + StringBuilder sectionBody = new StringBuilder(); + boolean frontMatter = false; + boolean frontMatterDone = false; + + for (String line : lines) { + if (!frontMatterDone && line.equals("---")) { + if (!frontMatter) { + frontMatter = true; + } else { + frontMatterDone = true; + } + continue; + } + + if (frontMatter && !frontMatterDone) { + parseMetadata(line, metadata); + continue; + } + + if (line.startsWith("# ")) { + title = line.substring(2).trim(); + continue; + } + + if (line.startsWith("## ")) { + addDocument(documents, path, metadata, title, section, sectionBody); + section = line.substring(3).trim(); + sectionBody.setLength(0); + continue; + } + + if (section != null) { + sectionBody.append(line).append('\n'); + } + } + + addDocument(documents, path, metadata, title, section, sectionBody); + + return documents; + } + + private void parseMetadata(String line, Map metadata) { + int separator = line.indexOf(':'); + if (separator < 0) { + return; + } + + String key = line.substring(0, separator).trim(); + String value = line.substring(separator + 1).trim(); + if (!key.isBlank() && !value.isBlank()) { + metadata.put(key, value); + } + } + + private void addDocument( + List documents, + Path path, + Map fileMetadata, + String title, + String section, + StringBuilder sectionBody + ) { + if (section == null) { + return; + } + + String body = sectionBody.toString().trim(); + if (body.isBlank()) { + return; + } + + Map metadata = new HashMap<>(fileMetadata); + metadata.put("layer", "layer2_policies"); + metadata.put("policy_scope", "internal"); + metadata.put("source", path.getFileName().toString()); + metadata.put("title", metadata.getOrDefault("title", title)); + metadata.put("section", section); + + documents.add(new Document(""" + Policy: %s + Section: %s + %s + """.formatted(metadata.get("title"), section, body).trim(), metadata)); + } +} From fcaa43123727356a312311254bd5022477016eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Sat, 16 May 2026 17:59:23 +0900 Subject: [PATCH 23/45] =?UTF-8?q?feat:=20VectorStore=EC=97=90=20Document?= =?UTF-8?q?=20=EC=A0=81=EC=9E=AC=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cholog/bootcamp/VectorStoreConfig.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/main/java/com/cholog/bootcamp/VectorStoreConfig.java b/src/main/java/com/cholog/bootcamp/VectorStoreConfig.java index c494610..7fa3f42 100644 --- a/src/main/java/com/cholog/bootcamp/VectorStoreConfig.java +++ b/src/main/java/com/cholog/bootcamp/VectorStoreConfig.java @@ -1,11 +1,19 @@ package com.cholog.bootcamp; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.vectorstore.SimpleVectorStore; import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.boot.ApplicationRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import lombok.extern.slf4j.Slf4j; + +@Slf4j @Configuration public class VectorStoreConfig { @@ -13,4 +21,22 @@ public class VectorStoreConfig { public VectorStore vectorStore(EmbeddingModel embeddingModel) { return SimpleVectorStore.builder(embeddingModel).build(); } + + @Bean + public ApplicationRunner vectorStoreInitializer( + FaqReader faqReader, + CurrentPolicyReader currentPolicyReader, + InternalPolicyReader internalPolicyReader, + VectorStore vectorStore + ) { + return args -> { + List documents = new ArrayList<>(); + documents.addAll(faqReader.read()); + documents.addAll(currentPolicyReader.read()); + documents.addAll(internalPolicyReader.read()); + + vectorStore.add(documents); + log.info("Loaded {} documents into VectorStore", documents.size()); + }; + } } From 4373bf08fffb62168a34f82cb78e662d0cbc2041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Sat, 16 May 2026 17:59:33 +0900 Subject: [PATCH 24/45] =?UTF-8?q?fix:=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20?= =?UTF-8?q?=ED=94=84=EB=A1=AC=ED=94=84=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/cholog/bootcamp/ChatService.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index e152ada..b235431 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -33,9 +33,15 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { ChatResponse chatResponse = chatClient.prompt() .system(""" - 당신은 초록 코퍼레이션에서 고객지원 챗봇을 담당하는 역할입니다. - 질문으로 들어오는 내용과 관련된 문서를 참고하고, 제공된 정보만을 사용하여 최대한 자세하고 구체적으로 답변해주세요. - 요청이 한국어로 들어오면 영어로 생각하고 한국어로 응답해주시고, 초록 코퍼레이션과 무관한 내용은 다루지 마세요. + 당신은 초록 코퍼레이션에서 고객지원을 담당하고 있습니다. + 제공된 참고자료를 바탕으로 질문에 대한 답변을 진행해주세요. + + 답변 간 유의 사항은 다음과 같습니다. + - 초록 코퍼레이션과 무관한 내용은 답변하지 마세요. + - 참고자료에 없는 내용을 추측해서 답변하지 마세요. + - 단답식 대답은 지양하고, 제공된 참고자료에서 추가로 제공할 수 있는 정보가 최대한 많이 전달해주세요. + - Bad Answer : VIP가 될려면 100,000원을 사용해야 합니다. + - Good Answer : VIP가 될려면 100,000원을 사용해야 합니다. VIP가 되면 무료 배송의 혜택을 얻을 수 있습니다. """) .user(""" 참고 자료 @@ -49,10 +55,10 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { Usage usage = chatResponse.getMetadata().getUsage(); log.info(""" - request : {} - response : {} - promptTokens : {} completionTokens : {}, totalTokens : {} - """, + request : {} + response : {} + promptTokens : {} completionTokens : {}, totalTokens : {} + """, request.question(), chatResponse.getResults().get(0).getOutput().getText(), usage.getPromptTokens(), From 0db51d61a43474a5b90222da47b82c8c3121a919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Sat, 16 May 2026 18:17:03 +0900 Subject: [PATCH 25/45] =?UTF-8?q?feat:=20=EA=B0=81=20Layer=EB=B3=84?= =?UTF-8?q?=EB=A1=9C=20Document=EB=A5=BC=20=EC=A1=B0=ED=9A=8C=ED=95=A0=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/cholog/bootcamp/ChatService.java | 57 ++++++++++++++++--- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index b235431..eed7c30 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -19,17 +19,15 @@ @RequiredArgsConstructor public class ChatService { + private static final int FAQ_TOP_K = 4; + private static final int CURRENT_POLICY_TOP_K = 3; + private static final int INTERNAL_POLICY_TOP_K = 2; + private final ChatClient chatClient; private final VectorStore vectorStore; public QuestionAskResponse askQuestion(QuestionAskRequest request) { - SearchRequest searchRequest = SearchRequest.builder() - .query(request.question()) - .build(); - List documents = vectorStore.similaritySearch(searchRequest); - String context = documents.stream() - .map(Document::getText) - .collect(Collectors.joining("\n\n")); + String context = getContext(request.question()); ChatResponse chatResponse = chatClient.prompt() .system(""" @@ -73,4 +71,49 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { usage.getTotalTokens() ); } + + private String getContext(String question) { + List faqDocuments = search(question, FAQ_TOP_K, "layer == 'layer1_faq'"); + List currentPolicyDocuments = search( + question, + CURRENT_POLICY_TOP_K, + "layer == 'layer2_policies' && policy_scope == 'current'" + ); + List internalPolicyDocuments = search( + question, + INTERNAL_POLICY_TOP_K, + "layer == 'layer2_policies' && policy_scope == 'internal'" + ); + + return """ + FAQ + %s + + Current Policies + %s + + Internal Policies + %s + """.formatted( + toContext(faqDocuments), + toContext(currentPolicyDocuments), + toContext(internalPolicyDocuments) + ); + } + + private List search(String question, int topK, String filterExpression) { + SearchRequest searchRequest = SearchRequest.builder() + .query(question) + .topK(topK) + .filterExpression(filterExpression) + .build(); + + return vectorStore.similaritySearch(searchRequest); + } + + private String toContext(List documents) { + return documents.stream() + .map(Document::getText) + .collect(Collectors.joining("\n\n")); + } } From 00d75345da4116d663d01bfe61860b767dc6dc70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Sat, 16 May 2026 18:37:34 +0900 Subject: [PATCH 26/45] =?UTF-8?q?feat:=20Chatlog=20=EB=A6=AC=EB=8D=94=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cholog/bootcamp/ChatLogReader.java | 109 ++++++++++++++++++ .../java/com/cholog/bootcamp/ChatService.java | 52 ++++++--- .../cholog/bootcamp/VectorStoreConfig.java | 2 + 3 files changed, 149 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/cholog/bootcamp/ChatLogReader.java diff --git a/src/main/java/com/cholog/bootcamp/ChatLogReader.java b/src/main/java/com/cholog/bootcamp/ChatLogReader.java new file mode 100644 index 0000000..09242f9 --- /dev/null +++ b/src/main/java/com/cholog/bootcamp/ChatLogReader.java @@ -0,0 +1,109 @@ +package com.cholog.bootcamp; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.ai.document.Document; +import org.springframework.stereotype.Component; + +@Component +public class ChatLogReader { + + private static final Path DIRECTORY = Path.of("data/layer3_chatlogs"); + + private final ObjectMapper objectMapper = new ObjectMapper(); + + public List read() { + try (Stream paths = Files.list(DIRECTORY)) { + return paths + .flatMap(path -> readFile(path).stream()) + .toList(); + } catch (IOException e) { + throw new IllegalArgumentException(""); + } + } + + private List readFile(Path path) { + try (Stream lines = Files.lines(path, StandardCharsets.UTF_8)) { + return lines + .map(line -> parse(path, line)) + .flatMap(List::stream) + .toList(); + } catch (IOException e) { + throw new IllegalArgumentException(""); + } + } + + private List parse(Path path, String line) { + try { + JsonNode root = objectMapper.readTree(line); + if (!"correct".equals(root.path("agent_accuracy").asText())) { + return List.of(); + } + + return List.of(toDocument(path, root)); + } catch (IOException e) { + throw new IllegalArgumentException(""); + } + } + + private Document toDocument(Path path, JsonNode root) { + Map metadata = new HashMap<>(); + metadata.put("layer", "layer3_chatlogs"); + metadata.put("source", path.getFileName().toString()); + metadata.put("conversation_id", root.path("conversation_id").asText()); + metadata.put("timestamp", root.path("timestamp").asText()); + metadata.put("channel", root.path("channel").asText()); + metadata.put("customer_tier", root.path("customer_tier").asText()); + metadata.put("primary_intent", root.path("primary_intent").asText()); + metadata.put("agent_accuracy", root.path("agent_accuracy").asText()); + metadata.put("tags", tags(root.path("tags"))); + + return new Document(""" + Conversation ID: %s + Primary Intent: %s + Tags: %s + Conversation: + %s + """.formatted( + root.path("conversation_id").asText(), + root.path("primary_intent").asText(), + tags(root.path("tags")), + turns(root.path("turns")) + ).trim(), metadata); + } + + private String turns(JsonNode turns) { + StringBuilder sb = new StringBuilder(); + + for (JsonNode turn : turns) { + sb.append(turn.path("role").asText()) + .append(": ") + .append(turn.path("text").asText()) + .append('\n'); + } + + return sb.toString().trim(); + } + + private String tags(JsonNode tags) { + StringBuilder sb = new StringBuilder(); + + for (JsonNode tag : tags) { + if (!sb.isEmpty()) { + sb.append(", "); + } + sb.append(tag.asText()); + } + + return sb.toString(); + } +} diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index eed7c30..c15554d 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -22,6 +22,7 @@ public class ChatService { private static final int FAQ_TOP_K = 4; private static final int CURRENT_POLICY_TOP_K = 3; private static final int INTERNAL_POLICY_TOP_K = 2; + private static final int CHAT_LOG_TOP_K = 2; private final ChatClient chatClient; private final VectorStore vectorStore; @@ -73,17 +74,7 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { } private String getContext(String question) { - List faqDocuments = search(question, FAQ_TOP_K, "layer == 'layer1_faq'"); - List currentPolicyDocuments = search( - question, - CURRENT_POLICY_TOP_K, - "layer == 'layer2_policies' && policy_scope == 'current'" - ); - List internalPolicyDocuments = search( - question, - INTERNAL_POLICY_TOP_K, - "layer == 'layer2_policies' && policy_scope == 'internal'" - ); + SearchResult searchResult = searchDocuments(question); return """ FAQ @@ -94,13 +85,38 @@ private String getContext(String question) { Internal Policies %s + + Chat Logs + %s """.formatted( - toContext(faqDocuments), - toContext(currentPolicyDocuments), - toContext(internalPolicyDocuments) + toContext(searchResult.faqDocuments()), + toContext(searchResult.currentPolicyDocuments()), + toContext(searchResult.internalPolicyDocuments()), + toContext(searchResult.chatLogDocuments()) ); } + private SearchResult searchDocuments(String question) { + List faqDocuments = search(question, FAQ_TOP_K, "layer == 'layer1_faq'"); + List currentPolicyDocuments = search( + question, + CURRENT_POLICY_TOP_K, + "layer == 'layer2_policies' && policy_scope == 'current'" + ); + List internalPolicyDocuments = search( + question, + INTERNAL_POLICY_TOP_K, + "layer == 'layer2_policies' && policy_scope == 'internal'" + ); + List chatLogDocuments = search( + question, + CHAT_LOG_TOP_K, + "layer == 'layer3_chatlogs' && agent_accuracy == 'correct'" + ); + + return new SearchResult(faqDocuments, currentPolicyDocuments, internalPolicyDocuments, chatLogDocuments); + } + private List search(String question, int topK, String filterExpression) { SearchRequest searchRequest = SearchRequest.builder() .query(question) @@ -116,4 +132,12 @@ private String toContext(List documents) { .map(Document::getText) .collect(Collectors.joining("\n\n")); } + + private record SearchResult( + List faqDocuments, + List currentPolicyDocuments, + List internalPolicyDocuments, + List chatLogDocuments + ) { + } } diff --git a/src/main/java/com/cholog/bootcamp/VectorStoreConfig.java b/src/main/java/com/cholog/bootcamp/VectorStoreConfig.java index 7fa3f42..87992c4 100644 --- a/src/main/java/com/cholog/bootcamp/VectorStoreConfig.java +++ b/src/main/java/com/cholog/bootcamp/VectorStoreConfig.java @@ -27,6 +27,7 @@ public ApplicationRunner vectorStoreInitializer( FaqReader faqReader, CurrentPolicyReader currentPolicyReader, InternalPolicyReader internalPolicyReader, + ChatLogReader chatLogReader, VectorStore vectorStore ) { return args -> { @@ -34,6 +35,7 @@ public ApplicationRunner vectorStoreInitializer( documents.addAll(faqReader.read()); documents.addAll(currentPolicyReader.read()); documents.addAll(internalPolicyReader.read()); + documents.addAll(chatLogReader.read()); vectorStore.add(documents); log.info("Loaded {} documents into VectorStore", documents.size()); From 4b8ab690d9c80d8e3a5d9e8aba6f793b565bb59f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Mon, 18 May 2026 12:41:35 +0900 Subject: [PATCH 27/45] =?UTF-8?q?fix:=20=ED=8F=89=EA=B0=80=20=EA=B8=B0?= =?UTF-8?q?=EC=A4=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/evaluate.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/data/evaluate.py b/data/evaluate.py index 948ee79..7508937 100644 --- a/data/evaluate.py +++ b/data/evaluate.py @@ -80,9 +80,11 @@ def judge_answer(question: str, expected: str, actual: str) -> dict: 실제 답변 (챗봇): {actual} 실제 답변이 기대 답변과 사실적으로 일치하는지 평가하세요. -- 표현이 달라도 핵심 사실이 같으면 정답입니다 -- 핵심 사실이 빠져있거나 틀렸으면 오답입니다 -- 부분적으로만 맞으면 오답으로 처리하세요 +- 기대 답변에 여러 정보가 포함되어 있더라도, 질문에 직접 답하는 핵심 사실을 정확히 전달했다면 정답입니다 +- 실제 답변이 기대 답변보다 짧거나 부가 정보를 생략했더라도, 질문의 의도에 필요한 핵심 답변이 맞으면 정답입니다 +- 질문과 직접 관련된 핵심 사실이 빠졌거나 틀렸으면 오답입니다 +- 실제 답변에 기대 답변과 충돌하는 잘못된 정보가 포함되어 있으면 오답입니다 +- 기대 답변의 모든 문장을 그대로 포함했는지가 아니라, 질문에 대한 답변으로 충분한지를 기준으로 판단하세요 JSON으로만 응답하세요: {{"score": 1, "reason": "..."}} (정답) From a121fba0d38180927f44db6060a7edfa642dc706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Mon, 18 May 2026 12:41:58 +0900 Subject: [PATCH 28/45] =?UTF-8?q?fix:=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20?= =?UTF-8?q?=ED=94=84=EB=A1=AC=ED=94=84=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/cholog/bootcamp/ChatService.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/ChatService.java index c15554d..8d092f3 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/ChatService.java @@ -38,9 +38,6 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { 답변 간 유의 사항은 다음과 같습니다. - 초록 코퍼레이션과 무관한 내용은 답변하지 마세요. - 참고자료에 없는 내용을 추측해서 답변하지 마세요. - - 단답식 대답은 지양하고, 제공된 참고자료에서 추가로 제공할 수 있는 정보가 최대한 많이 전달해주세요. - - Bad Answer : VIP가 될려면 100,000원을 사용해야 합니다. - - Good Answer : VIP가 될려면 100,000원을 사용해야 합니다. VIP가 되면 무료 배송의 혜택을 얻을 수 있습니다. """) .user(""" 참고 자료 From df3badc5b7008edff8e80e919457ed2c1f4f0dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Mon, 18 May 2026 14:13:16 +0900 Subject: [PATCH 29/45] =?UTF-8?q?fix:=20=EC=9E=84=EB=B2=A0=EB=94=A9=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=88=98=EC=A0=95=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20Reader=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cholog/bootcamp/CurrentPolicyReader.java | 60 +++++------------ .../java/com/cholog/bootcamp/FaqReader.java | 40 ++++++------ .../cholog/bootcamp/InternalPolicyReader.java | 64 +++++-------------- 3 files changed, 52 insertions(+), 112 deletions(-) diff --git a/src/main/java/com/cholog/bootcamp/CurrentPolicyReader.java b/src/main/java/com/cholog/bootcamp/CurrentPolicyReader.java index d73e2bb..d6c5ded 100644 --- a/src/main/java/com/cholog/bootcamp/CurrentPolicyReader.java +++ b/src/main/java/com/cholog/bootcamp/CurrentPolicyReader.java @@ -20,9 +20,10 @@ public class CurrentPolicyReader { public List read() { try (Stream paths = Files.list(DIRECTORY)) { - return paths - .flatMap(path -> readFile(path).stream()) - .toList(); + List documents = new ArrayList<>(); + paths.sorted() + .forEach(path -> documents.addAll(readFile(path))); + return documents; } catch (IOException e) { throw new IllegalArgumentException(""); } @@ -38,35 +39,22 @@ private List readFile(Path path) { private List parse(Path path, List lines) { List documents = new ArrayList<>(); - Map metadata = new HashMap<>(); - String title = path.getFileName().toString(); + String category = null; String section = null; StringBuilder sectionBody = new StringBuilder(); - boolean frontMatter = false; - boolean frontMatterDone = false; for (String line : lines) { - if (!frontMatterDone && line.equals("---")) { - if (!frontMatter) { - frontMatter = true; - } else { - frontMatterDone = true; - } - continue; - } - - if (frontMatter && !frontMatterDone) { - parseMetadata(line, metadata); + if (line.startsWith("# ")) { + category = line.substring(2).trim(); continue; } - if (line.startsWith("# ")) { - title = line.substring(2).trim(); + if (category == null) { continue; } if (line.startsWith("## ")) { - addDocument(documents, path, metadata, title, section, sectionBody); + addDocument(documents, path, category, section, sectionBody); section = line.substring(3).trim(); sectionBody.setLength(0); continue; @@ -77,29 +65,15 @@ private List parse(Path path, List lines) { } } - addDocument(documents, path, metadata, title, section, sectionBody); + addDocument(documents, path, category, section, sectionBody); return documents; } - private void parseMetadata(String line, Map metadata) { - int separator = line.indexOf(':'); - if (separator < 0) { - return; - } - - String key = line.substring(0, separator).trim(); - String value = line.substring(separator + 1).trim(); - if (!key.isBlank() && !value.isBlank()) { - metadata.put(key, value); - } - } - private void addDocument( List documents, Path path, - Map fileMetadata, - String title, + String category, String section, StringBuilder sectionBody ) { @@ -112,17 +86,13 @@ private void addDocument( return; } - Map metadata = new HashMap<>(fileMetadata); + Map metadata = new HashMap<>(); metadata.put("layer", "layer2_policies"); metadata.put("policy_scope", "current"); - metadata.put("source", path.getFileName().toString()); - metadata.put("title", metadata.getOrDefault("title", title)); + metadata.put("filepath", path.toString()); + metadata.put("category", category); metadata.put("section", section); - documents.add(new Document(""" - Policy: %s - Section: %s - %s - """.formatted(metadata.get("title"), section, body).trim(), metadata)); + documents.add(new Document(body, metadata)); } } diff --git a/src/main/java/com/cholog/bootcamp/FaqReader.java b/src/main/java/com/cholog/bootcamp/FaqReader.java index 2636657..f3a484c 100644 --- a/src/main/java/com/cholog/bootcamp/FaqReader.java +++ b/src/main/java/com/cholog/bootcamp/FaqReader.java @@ -5,6 +5,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Stream; @@ -22,9 +23,9 @@ public class FaqReader { public List read() { try (Stream paths = Files.list(FAQ_DIRECTORY)) { - return paths - .flatMap(path -> readFile(path).stream()) - .toList(); + List documents = new ArrayList<>(); + paths.forEach(path -> documents.addAll(readFile(path))); + return documents; } catch (IOException e) { throw new IllegalArgumentException(""); } @@ -42,7 +43,7 @@ private List parse(Path path, List lines) { List documents = new ArrayList<>(); String category = null; String question = null; - StringBuilder answer = new StringBuilder(); + StringBuilder body = new StringBuilder(); for (String line : lines) { if (line.startsWith("# ")) { @@ -55,37 +56,40 @@ private List parse(Path path, List lines) { } if (line.startsWith("### ")) { - addDocument(documents, path, category, question, answer); + addDocument(documents, path, category, question, body); question = line.substring(4).trim(); - answer.setLength(0); + body.setLength(0); continue; } if (question != null) { - answer.append(line).append('\n'); + body.append(line).append('\n'); } } - addDocument(documents, path, category, question, answer); + addDocument(documents, path, category, question, body); return documents; } private void addDocument( - List documents, Path path, String category, String question, StringBuilder answer + List documents, Path path, String category, String question, StringBuilder body ) { if (question == null) { return; } - documents.add(new Document(""" - Question: %s - Answer: %s - """.formatted(question, answer), Map.of( - "layer", "layer1_faq", - "source", path.getFileName().toString(), - "category", category, - "question", question - ))); + String text = body.toString().trim(); + if (text.isBlank()) { + return; + } + + Map metadata = new HashMap<>(); + metadata.put("question", question); + metadata.put("layer", "layer1_faq"); + metadata.put("filepath", path.toString()); + metadata.put("category", category); + + documents.add(new Document(text, metadata)); } } diff --git a/src/main/java/com/cholog/bootcamp/InternalPolicyReader.java b/src/main/java/com/cholog/bootcamp/InternalPolicyReader.java index 2e970c2..b377217 100644 --- a/src/main/java/com/cholog/bootcamp/InternalPolicyReader.java +++ b/src/main/java/com/cholog/bootcamp/InternalPolicyReader.java @@ -20,9 +20,10 @@ public class InternalPolicyReader { public List read() { try (Stream paths = Files.list(DIRECTORY)) { - return paths - .flatMap(path -> readFile(path).stream()) - .toList(); + List documents = new ArrayList<>(); + paths.sorted() + .forEach(path -> documents.addAll(readFile(path))); + return documents; } catch (IOException e) { throw new IllegalArgumentException(""); } @@ -38,35 +39,22 @@ private List readFile(Path path) { private List parse(Path path, List lines) { List documents = new ArrayList<>(); - Map metadata = new HashMap<>(); - String title = path.getFileName().toString(); + String category = null; String section = null; StringBuilder sectionBody = new StringBuilder(); - boolean frontMatter = false; - boolean frontMatterDone = false; for (String line : lines) { - if (!frontMatterDone && line.equals("---")) { - if (!frontMatter) { - frontMatter = true; - } else { - frontMatterDone = true; - } - continue; - } - - if (frontMatter && !frontMatterDone) { - parseMetadata(line, metadata); + if (line.startsWith("# ")) { + category = line.substring(2).trim(); continue; } - if (line.startsWith("# ")) { - title = line.substring(2).trim(); + if (category == null) { continue; } if (line.startsWith("## ")) { - addDocument(documents, path, metadata, title, section, sectionBody); + addDocument(documents, path, category, section, sectionBody); section = line.substring(3).trim(); sectionBody.setLength(0); continue; @@ -77,31 +65,13 @@ private List parse(Path path, List lines) { } } - addDocument(documents, path, metadata, title, section, sectionBody); + addDocument(documents, path, category, section, sectionBody); return documents; } - private void parseMetadata(String line, Map metadata) { - int separator = line.indexOf(':'); - if (separator < 0) { - return; - } - - String key = line.substring(0, separator).trim(); - String value = line.substring(separator + 1).trim(); - if (!key.isBlank() && !value.isBlank()) { - metadata.put(key, value); - } - } - private void addDocument( - List documents, - Path path, - Map fileMetadata, - String title, - String section, - StringBuilder sectionBody + List documents, Path path, String category, String section, StringBuilder sectionBody ) { if (section == null) { return; @@ -112,17 +82,13 @@ private void addDocument( return; } - Map metadata = new HashMap<>(fileMetadata); + Map metadata = new HashMap<>(); metadata.put("layer", "layer2_policies"); metadata.put("policy_scope", "internal"); - metadata.put("source", path.getFileName().toString()); - metadata.put("title", metadata.getOrDefault("title", title)); + metadata.put("filepath", path.toString()); + metadata.put("category", category); metadata.put("section", section); - documents.add(new Document(""" - Policy: %s - Section: %s - %s - """.formatted(metadata.get("title"), section, body).trim(), metadata)); + documents.add(new Document(body, metadata)); } } From cb26abd89255e5d2b39d6c4a35b30a873e9a1dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Mon, 18 May 2026 14:13:35 +0900 Subject: [PATCH 30/45] =?UTF-8?q?fix:=20=EC=9E=84=EB=B2=A0=EB=94=A9=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=88=98=EC=A0=95=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20Reader=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cholog/bootcamp/ChatLogReader.java | 62 ++++++------------- 1 file changed, 20 insertions(+), 42 deletions(-) diff --git a/src/main/java/com/cholog/bootcamp/ChatLogReader.java b/src/main/java/com/cholog/bootcamp/ChatLogReader.java index 09242f9..79c09dc 100644 --- a/src/main/java/com/cholog/bootcamp/ChatLogReader.java +++ b/src/main/java/com/cholog/bootcamp/ChatLogReader.java @@ -4,16 +4,18 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Stream; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.ai.document.Document; import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + @Component public class ChatLogReader { @@ -23,9 +25,9 @@ public class ChatLogReader { public List read() { try (Stream paths = Files.list(DIRECTORY)) { - return paths - .flatMap(path -> readFile(path).stream()) - .toList(); + List documents = new ArrayList<>(); + paths.forEach(path -> documents.addAll(readFile(path))); + return documents; } catch (IOException e) { throw new IllegalArgumentException(""); } @@ -33,23 +35,27 @@ public List read() { private List readFile(Path path) { try (Stream lines = Files.lines(path, StandardCharsets.UTF_8)) { - return lines - .map(line -> parse(path, line)) - .flatMap(List::stream) - .toList(); + List documents = new ArrayList<>(); + lines.forEach(line -> { + Document document = parse(path, line); + if (document != null) { + documents.add(document); + } + }); + return documents; } catch (IOException e) { throw new IllegalArgumentException(""); } } - private List parse(Path path, String line) { + private Document parse(Path path, String line) { try { JsonNode root = objectMapper.readTree(line); if (!"correct".equals(root.path("agent_accuracy").asText())) { - return List.of(); + return null; } - return List.of(toDocument(path, root)); + return toDocument(path, root); } catch (IOException e) { throw new IllegalArgumentException(""); } @@ -58,27 +64,12 @@ private List parse(Path path, String line) { private Document toDocument(Path path, JsonNode root) { Map metadata = new HashMap<>(); metadata.put("layer", "layer3_chatlogs"); - metadata.put("source", path.getFileName().toString()); + metadata.put("filepath", path.toString()); metadata.put("conversation_id", root.path("conversation_id").asText()); - metadata.put("timestamp", root.path("timestamp").asText()); - metadata.put("channel", root.path("channel").asText()); - metadata.put("customer_tier", root.path("customer_tier").asText()); metadata.put("primary_intent", root.path("primary_intent").asText()); metadata.put("agent_accuracy", root.path("agent_accuracy").asText()); - metadata.put("tags", tags(root.path("tags"))); - return new Document(""" - Conversation ID: %s - Primary Intent: %s - Tags: %s - Conversation: - %s - """.formatted( - root.path("conversation_id").asText(), - root.path("primary_intent").asText(), - tags(root.path("tags")), - turns(root.path("turns")) - ).trim(), metadata); + return new Document(turns(root.path("turns")), metadata); } private String turns(JsonNode turns) { @@ -93,17 +84,4 @@ private String turns(JsonNode turns) { return sb.toString().trim(); } - - private String tags(JsonNode tags) { - StringBuilder sb = new StringBuilder(); - - for (JsonNode tag : tags) { - if (!sb.isEmpty()) { - sb.append(", "); - } - sb.append(tag.asText()); - } - - return sb.toString(); - } } From 8c9cac24d962fd4551ee225f382a2299a79e23d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Mon, 18 May 2026 14:14:33 +0900 Subject: [PATCH 31/45] =?UTF-8?q?chore:=20=ED=8C=A8=ED=82=A4=EC=A7=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cholog/bootcamp/{ => config}/ChatClientConfig.java | 2 +- .../cholog/bootcamp/{ => config}/VectorStoreConfig.java | 7 ++++++- .../cholog/bootcamp/{ => controller}/ChatController.java | 6 +++++- .../com/cholog/bootcamp/{ => dto}/QuestionAskRequest.java | 2 +- .../com/cholog/bootcamp/{ => dto}/QuestionAskResponse.java | 2 +- .../com/cholog/bootcamp/{ => reader}/ChatLogReader.java | 2 +- .../cholog/bootcamp/{ => reader}/CurrentPolicyReader.java | 2 +- .../java/com/cholog/bootcamp/{ => reader}/FaqReader.java | 2 +- .../cholog/bootcamp/{ => reader}/InternalPolicyReader.java | 2 +- .../com/cholog/bootcamp/{ => service}/ChatService.java | 5 ++++- 10 files changed, 22 insertions(+), 10 deletions(-) rename src/main/java/com/cholog/bootcamp/{ => config}/ChatClientConfig.java (91%) rename src/main/java/com/cholog/bootcamp/{ => config}/VectorStoreConfig.java (85%) rename src/main/java/com/cholog/bootcamp/{ => controller}/ChatController.java (79%) rename src/main/java/com/cholog/bootcamp/{ => dto}/QuestionAskRequest.java (65%) rename src/main/java/com/cholog/bootcamp/{ => dto}/QuestionAskResponse.java (96%) rename src/main/java/com/cholog/bootcamp/{ => reader}/ChatLogReader.java (98%) rename src/main/java/com/cholog/bootcamp/{ => reader}/CurrentPolicyReader.java (98%) rename src/main/java/com/cholog/bootcamp/{ => reader}/FaqReader.java (98%) rename src/main/java/com/cholog/bootcamp/{ => reader}/InternalPolicyReader.java (98%) rename src/main/java/com/cholog/bootcamp/{ => service}/ChatService.java (97%) diff --git a/src/main/java/com/cholog/bootcamp/ChatClientConfig.java b/src/main/java/com/cholog/bootcamp/config/ChatClientConfig.java similarity index 91% rename from src/main/java/com/cholog/bootcamp/ChatClientConfig.java rename to src/main/java/com/cholog/bootcamp/config/ChatClientConfig.java index 32c9232..694e4d1 100644 --- a/src/main/java/com/cholog/bootcamp/ChatClientConfig.java +++ b/src/main/java/com/cholog/bootcamp/config/ChatClientConfig.java @@ -1,4 +1,4 @@ -package com.cholog.bootcamp; +package com.cholog.bootcamp.config; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; diff --git a/src/main/java/com/cholog/bootcamp/VectorStoreConfig.java b/src/main/java/com/cholog/bootcamp/config/VectorStoreConfig.java similarity index 85% rename from src/main/java/com/cholog/bootcamp/VectorStoreConfig.java rename to src/main/java/com/cholog/bootcamp/config/VectorStoreConfig.java index 87992c4..fa9883b 100644 --- a/src/main/java/com/cholog/bootcamp/VectorStoreConfig.java +++ b/src/main/java/com/cholog/bootcamp/config/VectorStoreConfig.java @@ -1,4 +1,4 @@ -package com.cholog.bootcamp; +package com.cholog.bootcamp.config; import java.util.ArrayList; import java.util.List; @@ -11,6 +11,11 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import com.cholog.bootcamp.reader.ChatLogReader; +import com.cholog.bootcamp.reader.CurrentPolicyReader; +import com.cholog.bootcamp.reader.FaqReader; +import com.cholog.bootcamp.reader.InternalPolicyReader; + import lombok.extern.slf4j.Slf4j; @Slf4j diff --git a/src/main/java/com/cholog/bootcamp/ChatController.java b/src/main/java/com/cholog/bootcamp/controller/ChatController.java similarity index 79% rename from src/main/java/com/cholog/bootcamp/ChatController.java rename to src/main/java/com/cholog/bootcamp/controller/ChatController.java index ec1b775..bbebf66 100644 --- a/src/main/java/com/cholog/bootcamp/ChatController.java +++ b/src/main/java/com/cholog/bootcamp/controller/ChatController.java @@ -1,4 +1,4 @@ -package com.cholog.bootcamp; +package com.cholog.bootcamp.controller; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -6,6 +6,10 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.cholog.bootcamp.dto.QuestionAskRequest; +import com.cholog.bootcamp.dto.QuestionAskResponse; +import com.cholog.bootcamp.service.ChatService; + import lombok.RequiredArgsConstructor; @RestController diff --git a/src/main/java/com/cholog/bootcamp/QuestionAskRequest.java b/src/main/java/com/cholog/bootcamp/dto/QuestionAskRequest.java similarity index 65% rename from src/main/java/com/cholog/bootcamp/QuestionAskRequest.java rename to src/main/java/com/cholog/bootcamp/dto/QuestionAskRequest.java index 80118d8..e97bdc3 100644 --- a/src/main/java/com/cholog/bootcamp/QuestionAskRequest.java +++ b/src/main/java/com/cholog/bootcamp/dto/QuestionAskRequest.java @@ -1,4 +1,4 @@ -package com.cholog.bootcamp; +package com.cholog.bootcamp.dto; public record QuestionAskRequest( String question diff --git a/src/main/java/com/cholog/bootcamp/QuestionAskResponse.java b/src/main/java/com/cholog/bootcamp/dto/QuestionAskResponse.java similarity index 96% rename from src/main/java/com/cholog/bootcamp/QuestionAskResponse.java rename to src/main/java/com/cholog/bootcamp/dto/QuestionAskResponse.java index 391e6ad..0525536 100644 --- a/src/main/java/com/cholog/bootcamp/QuestionAskResponse.java +++ b/src/main/java/com/cholog/bootcamp/dto/QuestionAskResponse.java @@ -1,4 +1,4 @@ -package com.cholog.bootcamp; +package com.cholog.bootcamp.dto; public record QuestionAskResponse( String answer, diff --git a/src/main/java/com/cholog/bootcamp/ChatLogReader.java b/src/main/java/com/cholog/bootcamp/reader/ChatLogReader.java similarity index 98% rename from src/main/java/com/cholog/bootcamp/ChatLogReader.java rename to src/main/java/com/cholog/bootcamp/reader/ChatLogReader.java index 79c09dc..91f8ad9 100644 --- a/src/main/java/com/cholog/bootcamp/ChatLogReader.java +++ b/src/main/java/com/cholog/bootcamp/reader/ChatLogReader.java @@ -1,4 +1,4 @@ -package com.cholog.bootcamp; +package com.cholog.bootcamp.reader; import java.io.IOException; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/com/cholog/bootcamp/CurrentPolicyReader.java b/src/main/java/com/cholog/bootcamp/reader/CurrentPolicyReader.java similarity index 98% rename from src/main/java/com/cholog/bootcamp/CurrentPolicyReader.java rename to src/main/java/com/cholog/bootcamp/reader/CurrentPolicyReader.java index d6c5ded..5ab8958 100644 --- a/src/main/java/com/cholog/bootcamp/CurrentPolicyReader.java +++ b/src/main/java/com/cholog/bootcamp/reader/CurrentPolicyReader.java @@ -1,4 +1,4 @@ -package com.cholog.bootcamp; +package com.cholog.bootcamp.reader; import java.io.IOException; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/com/cholog/bootcamp/FaqReader.java b/src/main/java/com/cholog/bootcamp/reader/FaqReader.java similarity index 98% rename from src/main/java/com/cholog/bootcamp/FaqReader.java rename to src/main/java/com/cholog/bootcamp/reader/FaqReader.java index f3a484c..3e75151 100644 --- a/src/main/java/com/cholog/bootcamp/FaqReader.java +++ b/src/main/java/com/cholog/bootcamp/reader/FaqReader.java @@ -1,4 +1,4 @@ -package com.cholog.bootcamp; +package com.cholog.bootcamp.reader; import java.io.IOException; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/com/cholog/bootcamp/InternalPolicyReader.java b/src/main/java/com/cholog/bootcamp/reader/InternalPolicyReader.java similarity index 98% rename from src/main/java/com/cholog/bootcamp/InternalPolicyReader.java rename to src/main/java/com/cholog/bootcamp/reader/InternalPolicyReader.java index b377217..24e0184 100644 --- a/src/main/java/com/cholog/bootcamp/InternalPolicyReader.java +++ b/src/main/java/com/cholog/bootcamp/reader/InternalPolicyReader.java @@ -1,4 +1,4 @@ -package com.cholog.bootcamp; +package com.cholog.bootcamp.reader; import java.io.IOException; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/com/cholog/bootcamp/ChatService.java b/src/main/java/com/cholog/bootcamp/service/ChatService.java similarity index 97% rename from src/main/java/com/cholog/bootcamp/ChatService.java rename to src/main/java/com/cholog/bootcamp/service/ChatService.java index 8d092f3..b654a74 100644 --- a/src/main/java/com/cholog/bootcamp/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/service/ChatService.java @@ -1,4 +1,4 @@ -package com.cholog.bootcamp; +package com.cholog.bootcamp.service; import java.util.List; import java.util.stream.Collectors; @@ -11,6 +11,9 @@ import org.springframework.ai.vectorstore.VectorStore; import org.springframework.stereotype.Service; +import com.cholog.bootcamp.dto.QuestionAskRequest; +import com.cholog.bootcamp.dto.QuestionAskResponse; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; From 417cb4f042338f997070893d1231bd7b0022391e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Mon, 18 May 2026 15:55:11 +0900 Subject: [PATCH 32/45] =?UTF-8?q?fix:=20=EA=B2=80=EC=A6=9D=20=ED=94=84?= =?UTF-8?q?=EB=A1=AC=ED=94=84=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/evaluate.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/data/evaluate.py b/data/evaluate.py index 7508937..e8da7c9 100644 --- a/data/evaluate.py +++ b/data/evaluate.py @@ -80,11 +80,9 @@ def judge_answer(question: str, expected: str, actual: str) -> dict: 실제 답변 (챗봇): {actual} 실제 답변이 기대 답변과 사실적으로 일치하는지 평가하세요. -- 기대 답변에 여러 정보가 포함되어 있더라도, 질문에 직접 답하는 핵심 사실을 정확히 전달했다면 정답입니다 -- 실제 답변이 기대 답변보다 짧거나 부가 정보를 생략했더라도, 질문의 의도에 필요한 핵심 답변이 맞으면 정답입니다 -- 질문과 직접 관련된 핵심 사실이 빠졌거나 틀렸으면 오답입니다 -- 실제 답변에 기대 답변과 충돌하는 잘못된 정보가 포함되어 있으면 오답입니다 -- 기대 답변의 모든 문장을 그대로 포함했는지가 아니라, 질문에 대한 답변으로 충분한지를 기준으로 판단하세요 +- 질문에 필요한 핵심 사실을 정확히 답했다면, 기대 답변보다 짧거나 부가 정보가 없어도 정답입니다. +- 핵심 사실이 빠졌거나 틀렸거나, 기대 답변과 충돌하는 정보가 있으면 오답입니다. +- 문장 일치가 아니라 질문에 대한 충분성을 기준으로 판단하세요. JSON으로만 응답하세요: {{"score": 1, "reason": "..."}} (정답) @@ -149,8 +147,8 @@ def main(): tier_results[tier]["total"] += 1 # 서버에 질문 -# response = ask_server(question_ko) - response = ask_server(question_en) + response = ask_server(question_ko) +# response = ask_server(question_en) if response is None: results["error"] += 1 if args.verbose: From 640c7bd7f2d202d16ab40ec5c316d3328af8e9ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Wed, 20 May 2026 13:48:10 +0900 Subject: [PATCH 33/45] =?UTF-8?q?fix:=20ChatService=20=EB=82=B4=EB=B6=80?= =?UTF-8?q?=20=EB=A0=88=EC=BD=94=EB=93=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cholog/bootcamp/service/ChatService.java | 41 ++++--------------- 1 file changed, 8 insertions(+), 33 deletions(-) diff --git a/src/main/java/com/cholog/bootcamp/service/ChatService.java b/src/main/java/com/cholog/bootcamp/service/ChatService.java index b654a74..4f46fe6 100644 --- a/src/main/java/com/cholog/bootcamp/service/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/service/ChatService.java @@ -3,6 +3,8 @@ import java.util.List; import java.util.stream.Collectors; +import javax.print.Doc; + import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.metadata.Usage; import org.springframework.ai.chat.model.ChatResponse; @@ -74,8 +76,6 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { } private String getContext(String question) { - SearchResult searchResult = searchDocuments(question); - return """ FAQ %s @@ -89,32 +89,15 @@ private String getContext(String question) { Chat Logs %s """.formatted( - toContext(searchResult.faqDocuments()), - toContext(searchResult.currentPolicyDocuments()), - toContext(searchResult.internalPolicyDocuments()), - toContext(searchResult.chatLogDocuments()) + toContext(searchDocuments(question, FAQ_TOP_K, "'layer1_faq'")), + toContext(searchDocuments(question, CURRENT_POLICY_TOP_K, "'layer2_policies'")), + toContext(searchDocuments(question, INTERNAL_POLICY_TOP_K, "'layer2_internal")), + toContext(searchDocuments(question, CHAT_LOG_TOP_K, "'layer3_chatlogs'")) ); } - private SearchResult searchDocuments(String question) { - List faqDocuments = search(question, FAQ_TOP_K, "layer == 'layer1_faq'"); - List currentPolicyDocuments = search( - question, - CURRENT_POLICY_TOP_K, - "layer == 'layer2_policies' && policy_scope == 'current'" - ); - List internalPolicyDocuments = search( - question, - INTERNAL_POLICY_TOP_K, - "layer == 'layer2_policies' && policy_scope == 'internal'" - ); - List chatLogDocuments = search( - question, - CHAT_LOG_TOP_K, - "layer == 'layer3_chatlogs' && agent_accuracy == 'correct'" - ); - - return new SearchResult(faqDocuments, currentPolicyDocuments, internalPolicyDocuments, chatLogDocuments); + private List searchDocuments(String question, int topK, String layer) { + return search(question, topK, "layer == " + layer); } private List search(String question, int topK, String filterExpression) { @@ -132,12 +115,4 @@ private String toContext(List documents) { .map(Document::getText) .collect(Collectors.joining("\n\n")); } - - private record SearchResult( - List faqDocuments, - List currentPolicyDocuments, - List internalPolicyDocuments, - List chatLogDocuments - ) { - } } From 28e468ebd3776a36c5e7a54f5f28b1225b5cdf75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Wed, 20 May 2026 13:48:29 +0900 Subject: [PATCH 34/45] =?UTF-8?q?fix:=20InternalPolicy=20=EB=A9=94?= =?UTF-8?q?=ED=83=80=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/cholog/bootcamp/reader/InternalPolicyReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/cholog/bootcamp/reader/InternalPolicyReader.java b/src/main/java/com/cholog/bootcamp/reader/InternalPolicyReader.java index 24e0184..a642a53 100644 --- a/src/main/java/com/cholog/bootcamp/reader/InternalPolicyReader.java +++ b/src/main/java/com/cholog/bootcamp/reader/InternalPolicyReader.java @@ -83,7 +83,7 @@ private void addDocument( } Map metadata = new HashMap<>(); - metadata.put("layer", "layer2_policies"); + metadata.put("layer", "layer2_internal"); metadata.put("policy_scope", "internal"); metadata.put("filepath", path.toString()); metadata.put("category", category); From ab270bc0ffdff1e05979a8c32e234aa0a8544611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Wed, 20 May 2026 13:49:54 +0900 Subject: [PATCH 35/45] =?UTF-8?q?fix:=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20?= =?UTF-8?q?=ED=94=84=EB=A1=AC=ED=94=84=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/cholog/bootcamp/service/ChatService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/cholog/bootcamp/service/ChatService.java b/src/main/java/com/cholog/bootcamp/service/ChatService.java index 4f46fe6..63a6ece 100644 --- a/src/main/java/com/cholog/bootcamp/service/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/service/ChatService.java @@ -43,6 +43,7 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { 답변 간 유의 사항은 다음과 같습니다. - 초록 코퍼레이션과 무관한 내용은 답변하지 마세요. - 참고자료에 없는 내용을 추측해서 답변하지 마세요. + - Chatlog의 경우 FAQ, Current Policies, Internal Policies의 내용들과 비교했을 때 불일치할 경우 답변간 근거로 사용하지 마세요. """) .user(""" 참고 자료 From 70c53bd3c43b1cdb571185a17b9a926c35c8fb6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Wed, 20 May 2026 13:50:08 +0900 Subject: [PATCH 36/45] =?UTF-8?q?chore:=20=EC=BD=94=EB=93=9C=20=ED=8F=AC?= =?UTF-8?q?=EB=A9=A7=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/cholog/bootcamp/service/ChatService.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/cholog/bootcamp/service/ChatService.java b/src/main/java/com/cholog/bootcamp/service/ChatService.java index 63a6ece..3a56c90 100644 --- a/src/main/java/com/cholog/bootcamp/service/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/service/ChatService.java @@ -3,8 +3,6 @@ import java.util.List; import java.util.stream.Collectors; -import javax.print.Doc; - import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.metadata.Usage; import org.springframework.ai.chat.model.ChatResponse; @@ -80,13 +78,13 @@ private String getContext(String question) { return """ FAQ %s - + Current Policies %s - + Internal Policies %s - + Chat Logs %s """.formatted( From ccfeb1c282600c6844858f32bee2df1be7d090bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Wed, 20 May 2026 13:54:13 +0900 Subject: [PATCH 37/45] =?UTF-8?q?fix:=20InternalPolicy=20filterExpression?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/cholog/bootcamp/service/ChatService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/cholog/bootcamp/service/ChatService.java b/src/main/java/com/cholog/bootcamp/service/ChatService.java index 3a56c90..aa50611 100644 --- a/src/main/java/com/cholog/bootcamp/service/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/service/ChatService.java @@ -90,7 +90,7 @@ private String getContext(String question) { """.formatted( toContext(searchDocuments(question, FAQ_TOP_K, "'layer1_faq'")), toContext(searchDocuments(question, CURRENT_POLICY_TOP_K, "'layer2_policies'")), - toContext(searchDocuments(question, INTERNAL_POLICY_TOP_K, "'layer2_internal")), + toContext(searchDocuments(question, INTERNAL_POLICY_TOP_K, "'layer2_internal'")), toContext(searchDocuments(question, CHAT_LOG_TOP_K, "'layer3_chatlogs'")) ); } From 14f0f852e5ed528501616abea2a83a4aa56ae1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Wed, 20 May 2026 14:25:35 +0900 Subject: [PATCH 38/45] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B9=85=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cholog/bootcamp/service/ChatService.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/main/java/com/cholog/bootcamp/service/ChatService.java b/src/main/java/com/cholog/bootcamp/service/ChatService.java index aa50611..25a5c84 100644 --- a/src/main/java/com/cholog/bootcamp/service/ChatService.java +++ b/src/main/java/com/cholog/bootcamp/service/ChatService.java @@ -15,9 +15,7 @@ import com.cholog.bootcamp.dto.QuestionAskResponse; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -@Slf4j @Service @RequiredArgsConstructor public class ChatService { @@ -54,18 +52,6 @@ public QuestionAskResponse askQuestion(QuestionAskRequest request) { .chatResponse(); Usage usage = chatResponse.getMetadata().getUsage(); - log.info(""" - request : {} - response : {} - promptTokens : {} completionTokens : {}, totalTokens : {} - """, - request.question(), - chatResponse.getResults().get(0).getOutput().getText(), - usage.getPromptTokens(), - usage.getCompletionTokens(), - usage.getTotalTokens() - ); - return QuestionAskResponse.from( chatResponse.getResult().getOutput().getText(), usage.getPromptTokens(), From 29f2289a2d59802a6cb3d0134dd06d005bb3d49f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Thu, 21 May 2026 14:27:23 +0900 Subject: [PATCH 39/45] =?UTF-8?q?docs:=20wall-report=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/eval_result.json | 22 +++++++++++++ mission/wall-report.md | 75 +++++++++++++++++++++++++++++++++++------- 2 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 data/eval_result.json diff --git a/data/eval_result.json b/data/eval_result.json new file mode 100644 index 0000000..37139d7 --- /dev/null +++ b/data/eval_result.json @@ -0,0 +1,22 @@ +{ + "total": 150, + "correct": 112, + "incorrect": 38, + "error": 0, + "accuracy": 0.7466666666666667, + "tier_results": { + "easy": { + "correct": 23, + "total": 30 + }, + "medium": { + "correct": 69, + "total": 94 + }, + "hard": { + "correct": 20, + "total": 26 + } + }, + "elapsed_seconds": 556.8951289653778 +} \ No newline at end of file diff --git a/mission/wall-report.md b/mission/wall-report.md index 4994eca..ca7e9ea 100644 --- a/mission/wall-report.md +++ b/mission/wall-report.md @@ -7,36 +7,87 @@ > 구현하면서 잘 안 됐던 것, 예상과 달랐던 것을 적어주세요. -- - +### 토큰 사용량 개선 +- 원인 + - 매 요청마다 입력 토큰이 약 9,600개가 소모되는 상황이였습니다. + - FAQ, Policy, Chatlog 세 개의 데이터를 모두 컨텍스트로 제공함에 따라 토큰 사용량이 늘어나는 것은 당연함을 알게 됐습니다. +- 고민 + - 세 개의 데이터를 모두 컨텍스트로 제공해도, 답변을 하는 과정에서 불필요한 데이터가 분명 있을 것이라 생각했습니다. + - 그렇다면 답변에 필요한 컨텍스트만 제공해야겠다는 생각이 들었고, 관련해서 Spring AI 공식문서를 찾던 중 ETL 파이프라인에 대해서 알게 됐습니다. +- 해결 + - Spring AI에서 제공해주는 DocumentReader/Transformer 구현체를 그대로 사용하지 않고 각 데이터에 맞는 Reader 클래스를 구현했습니다. + - 각 데이터 레이어가 이미 정형화되어 있어 토큰 분할이 불필요하고, DocumentTransformer로 메타데이터를 뽑을 때마다 임베딩 모델을 호출하기 때문에 불필요한 비용이 발생한다고 판단했기 때문입니다. + - 이후, VectorStore에서 사용자 질문과 연관된 내용을 찾아 AI에게 컨텍스트를 제공함으로써 토큰 사용량이 9,600개에서 592개로 줄어들었습니다. +- 학습 + - 임베딩, 코사인 유사도 개념에 대해서 학습했습니다. + - Spring AI의 ETL 파이프라인에 대해서 학습했습니다. + +### 평가 기준 개선 +- 원인 + - `적립 포인트 1점은 얼마인가요?`라는 질문에 `1포인트는 1원입니다.`라는 답을 했음에도 오답처리가 됨을 확인했습니다. + - 핵심 정보는 전달했으나, `1,000 포인트 이상부터 사용 가능`이라는 부가적인 정보를 전달하지 않아 오답처리가 됐습니다. +- 고민 + - 질문에 직접 대응하는 답변만 정확히 했다면 정답이라고 봐야한다고 생각했습니다. + - 개인적으로 AI에게 질문을 던졌을 때, 답변 이외의 부가정보를 너무 많이 전달하여 어디서부터 읽어야할지 혼란을 겪은 경험이 있었기 때문입니다. + - 다만, 기업의 관점에서는 한 번에 많은 정보를 담은 정보를 전달하여 리소스 절감을 하는데 목적이 있을 수 있다고 생각했습니다. +- 해결 + - 결론적으로는 직접 대응하는 답변을 정확히 했다면 정답 처리가 되도록 검증 스크립트를 수정했습니다. + - 사용자 입장에서도 필요한 정보만 전달하는 것이 좋다고 판단했고, 많은 정보를 전달하는 것이 사용자 경험상으로 불편함을 느낄 수 있을 가능성이 높다고 생각했기 때문입니다. + - 정확도가 `81개 -> 113개`로 증가했지만, 평균 응답 시간이 `2.6초 -> 3.3초`로 증가함을 확인했습니다. + - 프롬프트 개선을 통해 정확도가 `113개 -> 108개`로 감소했지만, 평균 응답 시간이 `3.3초 -> 2.7초`로 감소함을 확인했습니다. +- 학습 + - 평가 기준에 대해서 고민하고 개선하는 경험을 할 수 있었습니다. + +### 할루시네이션 개선 +- 원인 + - `VIP 등급 조건`을 물어보는 질문에 대해서 `800만원`이라고 계속 답하여 오답처리가 됨을 확인했습니다. + - 해당 질문의 컨텍스트로 제공되는 데이터를 확인한 결과 Chatlog에서 상담원이 800만원이라 답변한 로그가 존재했고, FAQ에는 80만원이라고 명시되어 있었습니다. +- 고민 + - Chatlog에서 agent_accuracy가 correct 처리가 되어 있어도, 잘못된 데이터가 있을 수 있으니 필터링을 해야하는 상황이였습니다. + - Chatlog의 데이터는 너무 많아 직접 확인해서 라벨링을 수정하는 과정은 리소스가 많이 든다고 생각했습니다. + - 또한, Chatlog를 임베딩할 때 OpenAI API를 호출하여 Chatlog 데이터가 FAQ와 Policy의 내용과 적합한지를 판단하고 적절하다면 임베딩하는 방법을 생각했습니다. + - 애플리케이션이 실행될 때마다 임베딩을 하기 때문에 비용이 발생하기 때문에 적용하기 어렵다고 생각했습니다. +- 해결 + - `FAQ -> Policy -> Chatlog` 순서로 정렬해서 컨텍스트를 정렬해서 제공하면 우선적으로 FAQ를 읽기 때문에 할루시네이션이 해소될 것이라 생각했습니다. + - 여전히 800만원으로 답변을 했습니다. + - Chatlog에 부정확한 데이터가 존재하기 때문에 이를 제외하고 FAQ와 Policy만 제공하면 할루시네이션을 개선할 수 있을 거 같았습니다. + - 80만원으로 답변을 했지만, 정확도가 `113개 -> 88개`로 감소했습니다. + - Chatlog를 포함하되, FAQ와 Policy의 내용 검증을 통해 일치한 경우에만 참고하도록 프롬프트를 수정했습니다. + - 80만원으로 답변을 했으며, 정확도가 `88개 -> 112개`로 증가했습니다. +- 학습 + - 할루시네이션 개선을 위해 다양한 가설을 세우고 이를 검증하는 경험을 했습니다. + - 라벨링의 중요성을 알게 됐습니다. ## 2. 해결하지 못한 것 > 시도했지만 결국 해결 못한 문제가 있다면 적어주세요. -- - +- 평가 기준과 할루시네이션 개선을 통해 프롬프트를 수정했지만, 간혈적으로 개선된 점이 적용되지 않아 오답처리가 되는 질문들이 있습니다. +- 이를 해결하기 위해 각 데이터 레이어에서 가져오는 데이터의 개수와 유사도를 조절하면서 검증을 진행했으나 개선 결과가 미미했습니다. ## 3. 정확도 측정 결과 > 테스트 질문 100개로 측정한 정확도를 기록해주세요. -| 난이도 | 정확도 | 비고 | -|--------|--------|------| -| easy | | | -| medium | | | -| hard | | | +| 난이도 | 정확도 | 비고 | +|--------|-------|-----| +| easy | 23/30 | 77% | +| medium | 20/26 | 77% | +| hard | 69/94 | 73% | ## 4. 왜 그런 결과가 나왔는지 > 정확도가 낮은 난이도의 질문을 몇 개 살펴보고, 왜 틀렸는지 분석해주세요. -- - +- 핵심 사실을 전달했으나, 부가적인 정보를 전달하지 않아 오답처리가 된 질문들이 몇 개 있었습니다. +- 이는 검증 스크립트에서 호출하는 AI에게 제공하는 프롬프트의 문제라 생각됩니다. +- `저번에 물어본 배송 건 어떻게 됐어요?`와 같은 질문에 대해서 고객의 주문 번호를 요청하지 않고, 직접 확인하라는 답변을 하여 오답처리가 된 질문이 있습니다. +- 현재 구현한 챗봇은 과거의 기록 혹은 이전 질문에 대한 컨텍스트가 없기 때문에 답변이 어렵기 때문에, 이런 유형의 질문이 들어오면 과거의 이력을 조회하기 위한 정보를 요청할 수 있도록 프롬프트를 수정해야할 거 같습니다. ## 5. 개선하고 싶은 것 > 시간이 더 있었다면 시도해보고 싶은 개선점을 적어주세요. -- +- 현재는 프롬프트를 통해 Chatlog에서 노이즈를 우회적으로 제거하고 있지만, 프롬프트 이외의 방법으로 노이즈를 제거할 수 있는 방법이 있다면 적용하여 개선하고 싶습니다. +- 세 개의 난이도에 대해서 평균 80%의 정확도로 답변할 수 있도록 개선하고 싶습니다. From d21404a04254592f7f77841a380c6ff9214afd13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Thu, 21 May 2026 14:47:27 +0900 Subject: [PATCH 40/45] =?UTF-8?q?build:=20=EB=AF=B8=EC=82=AC=EC=9A=A9=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle b/build.gradle index 400ffb3..6886bb9 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.ai:spring-ai-starter-model-openai' implementation 'org.springframework.ai:spring-ai-advisors-vector-store' - implementation 'org.springframework.ai:spring-ai-markdown-document-reader' implementation 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' From 486ebf407a0b4c0170c7967738d485b7e0e20a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Thu, 21 May 2026 14:48:27 +0900 Subject: [PATCH 41/45] =?UTF-8?q?chore:=20eof=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/eval_result.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/eval_result.json b/data/eval_result.json index 37139d7..e4a7a5b 100644 --- a/data/eval_result.json +++ b/data/eval_result.json @@ -19,4 +19,4 @@ } }, "elapsed_seconds": 556.8951289653778 -} \ No newline at end of file +} From 52598a36eb7f82561fce1a97f1100ffed27b460e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Thu, 21 May 2026 14:49:16 +0900 Subject: [PATCH 42/45] Update src/main/java/com/cholog/bootcamp/reader/ChatLogReader.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- src/main/java/com/cholog/bootcamp/reader/ChatLogReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/cholog/bootcamp/reader/ChatLogReader.java b/src/main/java/com/cholog/bootcamp/reader/ChatLogReader.java index 91f8ad9..8d85033 100644 --- a/src/main/java/com/cholog/bootcamp/reader/ChatLogReader.java +++ b/src/main/java/com/cholog/bootcamp/reader/ChatLogReader.java @@ -29,7 +29,7 @@ public List read() { paths.forEach(path -> documents.addAll(readFile(path))); return documents; } catch (IOException e) { - throw new IllegalArgumentException(""); + throw new RuntimeException("채팅 로그 디렉토리를 읽는 중 오류가 발생했습니다.", e); } } From 7801477b1c6ec8bf877aada072078a09bef21503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Thu, 21 May 2026 14:49:24 +0900 Subject: [PATCH 43/45] Update src/main/java/com/cholog/bootcamp/reader/CurrentPolicyReader.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../java/com/cholog/bootcamp/reader/CurrentPolicyReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/cholog/bootcamp/reader/CurrentPolicyReader.java b/src/main/java/com/cholog/bootcamp/reader/CurrentPolicyReader.java index 5ab8958..c363692 100644 --- a/src/main/java/com/cholog/bootcamp/reader/CurrentPolicyReader.java +++ b/src/main/java/com/cholog/bootcamp/reader/CurrentPolicyReader.java @@ -25,7 +25,7 @@ public List read() { .forEach(path -> documents.addAll(readFile(path))); return documents; } catch (IOException e) { - throw new IllegalArgumentException(""); + throw new RuntimeException("정책 디렉토리를 읽는 중 오류가 발생했습니다.", e); } } From 6f39a503ae7287d81eb780fef42963875224646c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Thu, 21 May 2026 14:49:32 +0900 Subject: [PATCH 44/45] Update src/main/java/com/cholog/bootcamp/reader/FaqReader.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- src/main/java/com/cholog/bootcamp/reader/FaqReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/cholog/bootcamp/reader/FaqReader.java b/src/main/java/com/cholog/bootcamp/reader/FaqReader.java index 3e75151..0e0b2c0 100644 --- a/src/main/java/com/cholog/bootcamp/reader/FaqReader.java +++ b/src/main/java/com/cholog/bootcamp/reader/FaqReader.java @@ -27,7 +27,7 @@ public List read() { paths.forEach(path -> documents.addAll(readFile(path))); return documents; } catch (IOException e) { - throw new IllegalArgumentException(""); + throw new RuntimeException("FAQ 디렉토리를 읽는 중 오류가 발생했습니다.", e); } } From c88bb206ced39f713f9323d303f7f1298a8744ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EA=B4=80=EA=B7=9C?= Date: Thu, 21 May 2026 14:49:39 +0900 Subject: [PATCH 45/45] Update src/main/java/com/cholog/bootcamp/reader/InternalPolicyReader.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../java/com/cholog/bootcamp/reader/InternalPolicyReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/cholog/bootcamp/reader/InternalPolicyReader.java b/src/main/java/com/cholog/bootcamp/reader/InternalPolicyReader.java index a642a53..7bf263d 100644 --- a/src/main/java/com/cholog/bootcamp/reader/InternalPolicyReader.java +++ b/src/main/java/com/cholog/bootcamp/reader/InternalPolicyReader.java @@ -25,7 +25,7 @@ public List read() { .forEach(path -> documents.addAll(readFile(path))); return documents; } catch (IOException e) { - throw new IllegalArgumentException(""); + throw new RuntimeException("내부 정책 디렉토리를 읽는 중 오류가 발생했습니다.", e); } }