Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
2735626
chore: env 파일 import
kjyyjk May 8, 2026
f75ad57
feat: 유저 챗 엔드포인트 및 ai 호출 구현
kjyyjk May 8, 2026
f6fa3c4
feat: llm에게 필요한 데이터 제공
kjyyjk May 9, 2026
d250287
refactor: builder에서 프롬프트 설정
kjyyjk May 9, 2026
4ecdb13
chore: advisors-vector-store 의존성 추가
kjyyjk May 9, 2026
4ac5a9a
refactor: 벡터 db 및 어드바이저 활용
kjyyjk May 9, 2026
46b20a7
chore: markdown-document-reader 의존성 추가
kjyyjk May 9, 2026
62b7aad
docs: resources/data에 layer_n 복사
kjyyjk May 9, 2026
28b3413
feat: md 파일 벡터 저장소에 저장
kjyyjk May 9, 2026
8759009
feat: 응답 content type json으로 변경
kjyyjk May 10, 2026
8ec9bac
refactor: RequestBody dto로 매핑
kjyyjk May 10, 2026
d2901a6
refactor: 테스트 incorrect 이유 글자수 2배 증가
kjyyjk May 10, 2026
64ee947
refactor: topk 2배 증가
kjyyjk May 10, 2026
aea6e10
feat: 디버깅용 엔드포인트 추가
kjyyjk May 11, 2026
e3fe990
docs: deprecated 문서 vector db에서 제거
kjyyjk May 11, 2026
071ec9b
refactor: advisor 커스텀 템플릿 설정
kjyyjk May 13, 2026
6eb21ee
refactor: 프롬프트 내 할루시네이션 방지 내용 추가
kjyyjk May 13, 2026
838f408
feat: 챗봇 응답에 token 정보 포함
kjyyjk May 15, 2026
162cb3c
feat: 디버깅 엔드포인트 응답에 token 정보 포함
kjyyjk May 15, 2026
ebaab45
refactor: controller 패키지 분리
kjyyjk May 15, 2026
ef3815c
refactor: service 분리
kjyyjk May 15, 2026
f8f89e7
feat: 대화 맥락 유지를 위한 chatmemory 추가
kjyyjk May 16, 2026
deb627d
feat: 챗봇 세션 시작/종료 엔드포인트 추가
kjyyjk May 16, 2026
2c6e041
feat: 챗봇 세션 시작/종료 엔드포인트 변경
kjyyjk May 16, 2026
9383c9a
refactor: config 설정 분리
kjyyjk May 16, 2026
3700df4
feat: 챗봇 ui 구현
kjyyjk May 16, 2026
53caf47
feat: 챗봇 ui 개선
kjyyjk May 16, 2026
71121c4
refactor: vip 연락수단 제거
kjyyjk May 16, 2026
5106f1d
refactor: 챗로그 청크 제거
kjyyjk May 17, 2026
ea159ca
chore: ai rag 의존성 추가
kjyyjk May 17, 2026
918edd4
refactor: qa 어드바이저 미사용 및 쿼리 확장 추가
kjyyjk May 18, 2026
d35b780
refactor: 미사용 코드 제거(디버깅 엔드포인트)
kjyyjk May 18, 2026
0d96e85
feat: 리랭크 구현
kjyyjk May 18, 2026
9f1ec40
refactor: 리랭크 수정 및 디버깅
kjyyjk May 18, 2026
f287a6d
refactor: 검색되 청크에 해당하는 문서 전체를 컨텍스트에 포함
kjyyjk May 19, 2026
24f09c9
refactor: 메모리 어드바이저 임시 제거
kjyyjk May 19, 2026
5f0b94c
refactor: 미사용 코드 제거
kjyyjk May 20, 2026
c5bb941
refactor: 주석 추가
kjyyjk May 20, 2026
165f724
docs: 벽 리포트 작성
kjyyjk May 20, 2026
d7f6d68
refactor: vector store 임베딩 runner 분리
kjyyjk May 28, 2026
83f9f41
docs: 미션 readme와 챗봇 readme 분리
kjyyjk May 28, 2026
0f3cc39
style: 초록 회사명 있어보이게 변경
kjyyjk May 28, 2026
d20992d
docs: readme 실행 섹션 수정
kjyyjk May 28, 2026
da03e45
refactor: 임베딩 벡터 영속화 및 임베딩 로직 runner로 분리
kjyyjk May 29, 2026
e01ede4
feat: vector 파일 gitignore
kjyyjk May 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@ __pycache__/

# Matrix metadata
**/.omc/

data/vector-store.json
92 changes: 92 additions & 0 deletions BASE-README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Stage 1: 고객지원 챗봇 만들기

Spring AI로 FAQ 챗봇을 직접 만드는 실습입니다.

커리큘럼 소개는 [SYLLABUS.md](SYLLABUS.md)를, 진행 방법은 [GUIDE.md](GUIDE.md)를 참고하세요.

---

## 필요한 것

| 항목 | 비고 |
|------|------|
| Java 17+ | `java -version`으로 확인 |
| OpenAI API 키 | [platform.openai.com](https://platform.openai.com)에서 발급 |
| IDE | IntelliJ IDEA 권장 (VS Code + Java Extension Pack도 가능) |

- Spring Boot로 REST API를 만들어 본 경험이 있으면 됩니다
- AI/ML 사전 지식은 없어도 됩니다
- 끝까지 해도 API 비용은 **$1-5** 안쪽입니다 (GPT-4.1-nano)

---

## 빠른 시작

```bash
# 프로젝트 루트에서
cp .env.example .env # OpenAI API 키 입력
./gradlew bootRun
```

서버가 `http://localhost:8080`에서 시작됩니다.

그 다음 [mission/MISSION.md](mission/MISSION.md)를 여세요. 거기서부터 시작입니다.

---

## 평가

`data/test_questions.json`에 100개의 테스트 질문이 있습니다 (easy 30 / medium 44 / hard 26).

서버를 띄운 상태에서 평가 스크립트를 실행할 수 있습니다:

```bash
cd data

# Python 환경 준비
python -m venv .venv
.venv/bin/pip install openai qdrant-client python-dotenv

# 평가 실행 (judge 모델 gpt-4o-mini 사용, 100문항 기준 약 $0.5~1 추가 비용)
.venv/bin/python evaluate_rag.py
```

---

## 프로젝트 구조

```
spring-ai-bootcamp-basic/
├── mission/
│ ├── MISSION.md # 미션 설명 (여기서 시작)
│ └── wall-report.md # 벽 리포트 (마지막에 작성)
├── hints/
│ ├── HINT_01.md ~ HINT_06.md # 막혔을 때 열어보세요
├── data/
│ ├── layer1_faq/ # 공식 FAQ 문서
│ ├── layer2_policies/ # 사내 정책 문서
│ ├── layer3_chatlogs/ # 고객 상담 로그
│ └── test_questions.json # 평가용 질문 100개
├── src/
│ └── main/java/com/cholog/bootcamp/
│ └── Application.java # 여기서부터 만드세요
├── SYLLABUS.md # 커리큘럼 소개 ← 과정 전체 그림
├── GUIDE.md # 진행 가이드 ← 미션 중 참고
└── build.gradle
```

---

## 자주 묻는 질문

**Q: API 키 없이 시작할 수 있나요?**

코드 작성과 컴파일은 가능하지만, 실제 실행에는 OpenAI API 키가 필요합니다.

**Q: FAQ 데이터가 영어인데 질문은 한국어로 해도 되나요?**

네. GPT-4.1-nano는 교차 언어 이해가 가능합니다.

**Q: 어떤 도구를 써도 되나요?**

네. 구현 방법에 제약은 없습니다.
97 changes: 13 additions & 84 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,92 +1,21 @@
# Stage 1: 고객지원 챗봇 만들기
# 프로젝트 개요

Spring AI로 FAQ 챗봇을 직접 만드는 실습입니다.
초록코퍼레이션 고객 응대 챗봇입니다.

커리큘럼 소개는 [SYLLABUS.md](SYLLABUS.md)를, 진행 방법은 [GUIDE.md](GUIDE.md)를 참고하세요.
# 애플리케이션 실행

---
## 실행 옵션

## 필요한 것

| 항목 | 비고 |
|------|------|
| Java 17+ | `java -version`으로 확인 |
| OpenAI API 키 | [platform.openai.com](https://platform.openai.com)에서 발급 |
| IDE | IntelliJ IDEA 권장 (VS Code + Java Extension Pack도 가능) |

- Spring Boot로 REST API를 만들어 본 경험이 있으면 됩니다
- AI/ML 사전 지식은 없어도 됩니다
- 끝까지 해도 API 비용은 **$1-5** 안쪽입니다 (GPT-4.1-nano)

---

## 빠른 시작

```bash
# 프로젝트 루트에서
cp .env.example .env # OpenAI API 키 입력
./gradlew bootRun
```

서버가 `http://localhost:8080`에서 시작됩니다.

그 다음 [mission/MISSION.md](mission/MISSION.md)를 여세요. 거기서부터 시작입니다.

---

## 평가

`data/test_questions.json`에 100개의 테스트 질문이 있습니다 (easy 30 / medium 44 / hard 26).

서버를 띄운 상태에서 평가 스크립트를 실행할 수 있습니다:
애플리케이션 실행 시 VectorStore 임베딩을 수행하려면 인자로 --mode=embedding 옵션을 주어야합니다.
임베딩 수행하지 않을 경우 옵션을 제외합니다.

## 실행 명령어
```bash
cd data

# Python 환경 준비
python -m venv .venv
.venv/bin/pip install openai qdrant-client python-dotenv
# bootrun 실행 시
./gradlew bootrun
./gradlew bootrun --args='--mode=embedding'

# 평가 실행 (judge 모델 gpt-4o-mini 사용, 100문항 기준 약 $0.5~1 추가 비용)
.venv/bin/python evaluate_rag.py
# jar 실행 시
java -jar <jar 파일>
java -jar <jar 파일> --mode=embedding
```

---

## 프로젝트 구조

```
spring-ai-bootcamp-basic/
├── mission/
│ ├── MISSION.md # 미션 설명 (여기서 시작)
│ └── wall-report.md # 벽 리포트 (마지막에 작성)
├── hints/
│ ├── HINT_01.md ~ HINT_06.md # 막혔을 때 열어보세요
├── data/
│ ├── layer1_faq/ # 공식 FAQ 문서
│ ├── layer2_policies/ # 사내 정책 문서
│ ├── layer3_chatlogs/ # 고객 상담 로그
│ └── test_questions.json # 평가용 질문 100개
├── src/
│ └── main/java/com/cholog/bootcamp/
│ └── Application.java # 여기서부터 만드세요
├── SYLLABUS.md # 커리큘럼 소개 ← 과정 전체 그림
├── GUIDE.md # 진행 가이드 ← 미션 중 참고
└── build.gradle
```

---

## 자주 묻는 질문

**Q: API 키 없이 시작할 수 있나요?**

코드 작성과 컴파일은 가능하지만, 실제 실행에는 OpenAI API 키가 필요합니다.

**Q: FAQ 데이터가 영어인데 질문은 한국어로 해도 되나요?**

네. GPT-4.1-nano는 교차 언어 이해가 가능합니다.

**Q: 어떤 도구를 써도 되나요?**

네. 구현 방법에 제약은 없습니다.
2 changes: 1 addition & 1 deletion SYLLABUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Easy는 그럭저럭. Medium부터 급락합니다.
- 챗봇 구현·실행: **$1~5** 안쪽 (GPT-4.1-nano 기준)
- 평가 스크립트까지 돌리면: **+$0.5~1** (judge 모델 `gpt-4o-mini`)

API 키 발급과 환경 설정은 [README.md](README.md#필요한-것)를 참고하세요.
API 키 발급과 환경 설정은 [BASE-README.md](BASE-README.md#필요한-것)를 참고하세요.

---

Expand Down
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ dependencyManagement {

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
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.springframework.ai:spring-ai-rag'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
Expand Down
2 changes: 1 addition & 1 deletion data/evaluate.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def main():
if args.verbose:
print(f"[{qid}] {marker} ({tier}) {question_ko[:40]}...")
if score == 0:
print(f" 이유: {judgment.get('reason', '')[:80]}")
print(f" 이유: {judgment.get('reason', '')[:160]}")

# 진행률 (10개마다)
if not args.verbose and (i + 1) % 10 == 0:
Expand Down
41 changes: 28 additions & 13 deletions mission/wall-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,51 @@

> 구현하면서 잘 안 됐던 것, 예상과 달랐던 것을 적어주세요.

-

- 초반 조회 결과에 질문 관련 내용이 없길래 topK 값을 2배~4배까지 늘려 해당 질문에 대한 답변을 잘할 수 있도록 개선했습니다.
하지만 그 외 다른 질문에 대한 정확도는 확연히 떨어지는 것을 발견했습니다. 컨텍스트에 많은 내용이 들어가 오히려 혼란을 준 것 같습니다.
현재 구현 기준 기본값인 topK=4가 가장 최적으로 보여 롤백했습니다.
- 네이버페이에 대해 물어봤는데 카카오페이에 대한 답변을 하는 경우가 있었습니다.
알고보니 ChatMemory 의해 이전 질문이 컨텍스트에 포함되며 이전 질문을 현재 질문이라고 착각해서 생긴 문제였습니다.
ChatMemory를 제거하고 대화 내역이 보존되지 않게 했지만 추후 해결해야할 문제입니다.

## 2. 해결하지 못한 것

> 시도했지만 결국 해결 못한 문제가 있다면 적어주세요.

-

- 컨텍스트에 질문에 대한 정답이 포함되어 있는데 틀린 답변을 내놓습니다. 관련 케이스를 살펴보니 정답 내용이 첫번째에 오지 않을 경우 주로 발생했습니다.
해결하기 위해 리랭크 개념을 학습하고 llm에게 청크 재정렬을 맡겨봤는데 이 또한 제가 의도한대로 재정렬되지 않았습니다.
- 컨텍스트에 상반된 내용이 있을 때에 따라 다른 대답을 하는 문제를 해결하지 못했습니다. 예를 들어 `VIP는 냉장 배송도 무료배송인가요?`를 물었을 때
`vip는 모두 무료다`와 `냉장 배송은 등급 상관없이 4000원 고정이다`라는 내용이 섞여 `vip는 냉장 배송도 무료다` 라는 잘못된 답변을 내놓습니다.
프롬프트에 내용이 상반될 경우 더 상세한 것, 세부적인 것을 기준으로 하라고 명시했지만 상세하고 세부적인 것의 기준 조차 애매해 잘동작하지 않습니다.
- 명확하지 않은 단어를 잘 이해하지 못하는 경우가 많습니다. 예를 들어 `비구독 상품`이라고 하면 비구독 사용자가 상품을 구매하는 경우라고 잘못 이해해 이해해
틀린 답변을 내뱉습니다.

## 3. 정확도 측정 결과

> 테스트 질문 100개로 측정한 정확도를 기록해주세요.

| 난이도 | 정확도 | 비고 |
|--------|--------|------|
| easy | | |
| medium | | |
| hard | | |
> 테스트 질문 150개로 측정한 정확도를 기록해주세요.

| 난이도 | 정확도 | 비고 |
|--------|-------|----|
| easy | 14/30 | |
| medium | 27/94 | |
| hard | 6/26 | |

## 4. 왜 그런 결과가 나왔는지

> 정확도가 낮은 난이도의 질문을 몇 개 살펴보고, 왜 틀렸는지 분석해주세요.

-


## 5. 개선하고 싶은 것

> 시간이 더 있었다면 시도해보고 싶은 개선점을 적어주세요.

-
- 답변의 말투를 개선하고 싶습니다. 현재는 같은 내용을 반복하거나, 사용자가 응?할만한 말투를 구사합니다.
또한 내부 컨텍스트 문서의 존재를 답변에 포함하기도 합니다.
- 대화 맥락을 보존해 더욱 고객 응대에 능한 챗봇을 만들고 싶습니다.
- 인메모리 외 인프라를 구축해 정말 실무의 챗봇과 유사하게 만들어보고 싶습니다.
- 평가 방식을 개선해 모델의 성능을 제대로 측정하고 반복 개선해나가보고 싶습니다. 현재 평가는 이걸 왜 틀렸다고 하는거지?싶은 부분이 많습니다.
챗봇의 목적마다 평가 방식이 다르니 제가 만들고자 하는 챗봇을 명확히 정의하고 그에 맞는 평가 방식을 새로 만들어보고 싶습니다.
- 챗 로그, 이력을 관리하고 활용해보고 싶습니다. 주어진 데이터셋의 챗 로그는 컨텍스트에 넣지 않고 가장 많이 묻는 질문과 같은 운영 통계 목적으로 활용했습니다.
다른 방향으로 활용할 수 있다면 참고해 챗 로그까지 활용해 데이터 축적에 따른 성능 향상을 이루고 싶습니다.
- 구버전 문서도 마찬가지로 필요 없다고 느껴 제거했는데 필요하다면 포함해 응답 수준을 높여보고 싶습니다.
48 changes: 48 additions & 0 deletions src/main/java/com/cholog/bootcamp/config/MarkdownReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.cholog.bootcamp.config;

import java.util.ArrayList;
import java.util.List;

import org.springframework.ai.document.Document;
import org.springframework.ai.reader.markdown.MarkdownDocumentReader;
import org.springframework.ai.reader.markdown.config.MarkdownDocumentReaderConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class MarkdownReader {

private final Resource[] resources;

public MarkdownReader(@Value("classpath:data/**/*.md") Resource[] resources) {
this.resources = resources;
}

public List<Document> loadAll() {
List<Document> allDocuments = new ArrayList<>();
for (Resource resource : resources) {
MarkdownDocumentReaderConfig config = MarkdownDocumentReaderConfig.builder()
.withHorizontalRuleCreateDocument(true)
.withIncludeCodeBlock(false)
.withIncludeBlockquote(false)
.withAdditionalMetadata("filename", resource.getFilename())
.build();

MarkdownDocumentReader reader = new MarkdownDocumentReader(resource, config);
allDocuments.addAll(reader.get());
}

allDocuments.forEach(doc -> log.info(
"filename={}, title={}, text={}",
doc.getMetadata().get("filename"),
doc.getMetadata().get("title"),
doc.getText()
));

return allDocuments;
}
}
Loading