Skip to content

Repository files navigation

πŸ“˜ κ·œμ • Q&A μ‹œμŠ€ν…œ (Guideon)

AI 기반 κ·œμ • 검색 μ‹œμŠ€ν…œμž…λ‹ˆλ‹€.

🎯 μ£Όμš” κΈ°λŠ₯

1. μžμ—°μ–΄ 질문 뢄석 (Query Analysis)

  • AI 기반 질문 뢄석: Google Geminiλ₯Ό μ‚¬μš©ν•˜μ—¬ μ‚¬μš©μžμ˜ μžμ—°μ–΄ μ§ˆλ¬Έμ„ ꡬ쑰화
  • ν‚€μ›Œλ“œ μΆ”μΆœ: μ§ˆλ¬Έμ—μ„œ 핡심 ν‚€μ›Œλ“œ μžλ™ μΆ”μΆœ
  • κ·œμ • μœ ν˜• λ§€μΉ­: 27개 μ‚¬κ·œ μœ ν˜• 쀑 κ΄€λ ¨ κ·œμ • μžλ™ 식별
  • 질문 μ˜λ„ νŒŒμ•…: μ •λ³΄μ‘°νšŒ/μ ˆμ°¨μ•ˆλ‚΄/기쀀확인/μžκ²©μš”κ±΄/μ˜ˆμ™Έμ‚¬ν•­ λΆ„λ₯˜
  • 검색 쿼리 μ΅œμ ν™”: 벑터 검색에 μ΅œμ ν™”λœ 쿼리둜 λ³€ν™˜

2. RAG 기반 κ·œμ • 검색

  • 벑터 검색: μž„λ² λ”© 기반 의미 κ²€μƒ‰μœΌλ‘œ κ΄€λ ¨ κ·œμ • μ‘°ν•­ 탐색
  • ReRanking: Cohere λͺ¨λΈμ„ ν†΅ν•œ 검색 κ²°κ³Ό 정ꡐ화
  • λ‹΅λ³€ 생성: κ²€μƒ‰λœ κ·œμ •μ„ 근거둜 LLM이 μ •ν™•ν•œ λ‹΅λ³€ 생성
  • 좜처 ν‘œμ‹œ: λ‹΅λ³€μ˜ κ·Όκ±°κ°€ λ˜λŠ” κ·œμ •λͺ…, μ‘°ν•­, 관련도 점수 제곡
  • 신뒰도 평가: 검색 결과의 신뒰도 점수 계산

3. ν•˜μ΄λΈŒλ¦¬λ“œ 검색 (Hybrid Search)

  • Vector Search (의미 검색): Google Gemini Embedding으둜 의미적 μœ μ‚¬μ„± νŒŒμ•…
  • BM25 Search (ν‚€μ›Œλ“œ 검색): Apache Lucene 기반 μ •ν™•ν•œ ν‚€μ›Œλ“œ λ§€μΉ­
  • Reciprocal Rank Fusion (RRF): 두 검색 κ²°κ³Όλ₯Ό μ§€λŠ₯적으둜 톡합
  • ν•œκ΅­μ–΄ μ΅œμ ν™”: Nori ν˜•νƒœμ†Œ λΆ„μ„κΈ°λ‘œ ν•œκ΅­μ–΄ 검색 ν’ˆμ§ˆ ν–₯상
  • 정확도 κ°œμ„ : 의미적 검색과 ν‚€μ›Œλ“œ κ²€μƒ‰μ˜ μž₯점을 κ²°ν•©ν•˜μ—¬ 20-30% 정확도 ν–₯상

πŸ— μ•„ν‚€ν…μ²˜

μ‚¬μš©μž 질문 (μžμ—°μ–΄)
    ↓
QueryAnalysisService (Gemini API)
    ↓
κ΅¬μ‘°ν™”λœ 뢄석 κ²°κ³Ό
    - ν‚€μ›Œλ“œ
    - κ·œμ • μœ ν˜•
    - 질문 μ˜λ„
    - 검색 쿼리
    ↓
RegulationSearchService (RAG + Hybrid Search)
    ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Vector Search     β”‚  BM25 Search        β”‚
β”‚  (의미적 검색)      β”‚  (ν‚€μ›Œλ“œ 검색)       β”‚
β”‚  - Gemini Embed    β”‚  - Apache Lucene    β”‚
β”‚  - Cosine Sim.     β”‚  - Nori Analyzer    β”‚
β”‚  β†’ 20 candidates   β”‚  β†’ 20 candidates    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    ↓
Reciprocal Rank Fusion (RRF)
    - 두 검색 κ²°κ³Ό 톡합
    - 쀑볡 제거 및 점수 계산
    β†’ 40 unique candidates
    ↓
ReRanking (Cohere)
    - μ •κ΅ν•œ κ΄€λ ¨μ„± μž¬ν‰κ°€
    β†’ Top 5 results
    ↓
λ‹΅λ³€ 생성 (Gemini API)
    ↓
κ²°κ³Ό λ°˜ν™˜ (λ‹΅λ³€ + κ·Όκ±° μ‘°ν•­ + 신뒰도)

πŸ“¦ 기술 μŠ€νƒ

Backend

  • Java 21+
  • Spring Boot 3.2: REST API μ„œλ²„
  • LangChain4j 0.36.2: LLM μ˜€μΌ€μŠ€νŠΈλ ˆμ΄μ…˜ 및 RAG κ΅¬ν˜„
  • Google Gemini API:
    • gemini-2.5-flash: 질문 뢄석 및 λ‹΅λ³€ 생성
    • text-embedding-004: 벑터 μž„λ² λ”© (768-dim, ν•œκ΅­μ–΄ 지원)
  • Cohere API:
    • rerank-multilingual-v3.0: 검색 κ²°κ³Ό μž¬μ •λ ¬ (ν•œκ΅­μ–΄ μ΅œμ ν™”)
  • Apache Lucene 9.11.1: BM25 ν‚€μ›Œλ“œ 검색 μ—”μ§„
    • Nori Korean Analyzer: ν•œκ΅­μ–΄ ν˜•νƒœμ†Œ 뢄석
  • Vector Store: In-Memory (개발용, μš΄μ˜μ‹œ Qdrant ꢌμž₯)
  • Maven: λΉŒλ“œ 도ꡬ

πŸš€ μ‹œμž‘ν•˜κΈ°

사전 μš”κ΅¬μ‚¬ν•­

1. ν™˜κ²½ μ„€μ •

Google Gemini API ν‚€ μ„€μ •

방법 1: ν™˜κ²½λ³€μˆ˜ μ„€μ • (ꢌμž₯)

# Windows (PowerShell)
$env:GOOGLE_API_KEY="your_api_key_here"

# Windows (CMD)
set GOOGLE_API_KEY=your_api_key_here

# Linux/Mac
export GOOGLE_API_KEY=your_api_key_here

방법 2: application.properties 파일 μˆ˜μ •

src/main/resources/application.properties νŒŒμΌμ—μ„œ API ν‚€ 직접 μž…λ ₯:

gemini.api.key=your_actual_api_key_here

⚠️ λ³΄μ•ˆ 주의: API ν‚€λ₯Ό μ½”λ“œμ— ν¬ν•¨ν•˜μ—¬ Git에 μ»€λ°‹ν•˜μ§€ λ§ˆμ„Έμš”!

2. μ˜μ‘΄μ„± μ„€μΉ˜ 및 λΉŒλ“œ

# Maven μ˜μ‘΄μ„± λ‹€μš΄λ‘œλ“œ 및 JAR 파일 λΉŒλ“œ
mvn clean package -DskipTests

λΉŒλ“œκ°€ μ™„λ£Œλ˜λ©΄ target/regulation-search-1.0.0.jar 파일이 μƒμ„±λ©λ‹ˆλ‹€.

3. μ„œλΉ„μŠ€ μ‹€ν–‰

πŸš€ 방법 1: ν’€μŠ€νƒ μ‹€ν–‰ (λ°±μ—”λ“œ + ν”„λ‘ νŠΈμ—”λ“œ) - μΆ”μ²œ!

# λ°±μ—”λ“œ(8080)와 ν”„λ‘ νŠΈμ—”λ“œ(3000)λ₯Ό λ™μ‹œμ— μ‹€ν–‰
start-all.bat

이 λͺ…λ Ήμ–΄λŠ”:

  • βœ… λ°±μ—”λ“œ μ„œλ²„: http://localhost:8080 (별도 μ°½)
  • βœ… ν”„λ‘ νŠΈμ—”λ“œ: http://localhost:3000 (별도 μ°½)

λΈŒλΌμš°μ €μ—μ„œ http://localhost:3000으둜 μ ‘μ†ν•˜λ©΄ 전체 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€!

방법 2: λ°±μ—”λ“œλ§Œ μ‹€ν–‰

# Spring Boot μ„œλ²„λ§Œ μ‹€ν–‰ (포트 8080)
run-server.bat

# λ˜λŠ” Maven 직접 μ‚¬μš©
mvn spring-boot:run

μ„œλ²„κ°€ μ‹œμž‘λ˜λ©΄ http://localhost:8080μ—μ„œ REST APIλ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

방법 3: ν”„λ‘ νŠΈμ—”λ“œλ§Œ μ‹€ν–‰

# ν”„λ‘ νŠΈμ—”λ“œ 개발 μ„œλ²„λ§Œ μ‹€ν–‰ (포트 3000)
cd guideon-frontend
start-frontend.bat

# λ˜λŠ” npm 직접 μ‚¬μš©
npm run dev

⚠️ 주의: ν”„λ‘ νŠΈμ—”λ“œλ§Œ μ‹€ν–‰ν•˜λŠ” 경우 λ°±μ—”λ“œ μ„œλ²„κ°€ http://localhost:8080μ—μ„œ μ‹€ν–‰ 쀑이어야 ν•©λ‹ˆλ‹€.

방법 4: JAR 파일 λΉŒλ“œ ν›„ μ‹€ν–‰ (ν”„λ‘œλ•μ…˜)

# 1. JAR λΉŒλ“œ
mvn clean package -DskipTests

# 2. μ‹€ν–‰
java -jar target/regulation-search-1.0.0.jar

방법 5: IDEμ—μ„œ μ‹€ν–‰

  • Main Class: com.guideon.GuideonApplication
  • VM Options: -Dserver.port=8080

포트 λ³€κ²½

λ‹€λ₯Έ ν¬νŠΈμ—μ„œ μ‹€ν–‰ν•˜λ €λ©΄:

# 방법 1: application.yml μˆ˜μ •
server:
  port: 9090

# 방법 2: ν™˜κ²½λ³€μˆ˜
export SERVER_PORT=9090
mvn spring-boot:run

# 방법 3: μ»€λ§¨λ“œ 라인 μ˜΅μ…˜
java -jar target/regulation-search-1.0.0.jar --server.port=9090

4. API μ—”λ“œν¬μΈνŠΈ 확인

μ„œλ²„κ°€ μ‹œμž‘λ˜λ©΄ λ‹€μŒ μ—”λ“œν¬μΈνŠΈλ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

Health Check

curl http://localhost:8080/actuator/health

질문 뢄석

curl -X POST http://localhost:8080/api/qa/analyze \
  -H "Content-Type: application/json" \
  -d '{"question": "μ—°μ°¨ νœ΄κ°€λŠ” λͺ‡ μΌμΈκ°€μš”?"}'

κ·œμ • 검색 (톡합)

curl -X POST http://localhost:8080/api/qa/search \
  -H "Content-Type: application/json" \
  -d '{"question": "ν•΄μ™Έ 좜μž₯μ‹œ μˆ™λ°•λΉ„λŠ” μ–Όλ§ˆκΉŒμ§€ μ§€μ›λ˜λ‚˜μš”?"}'

κ·œμ • μœ ν˜• λͺ©λ‘ 쑰회

curl http://localhost:8080/api/regulations/types

κ·œμ • λ¬Έμ„œ μ—…λ‘œλ“œ

curl -X POST http://localhost:8080/api/regulations/upload \
  -H "Content-Type: application/json" \
  -d '{
    "filePath": "c:/workspace/regulations/μ·¨μ—…κ·œμΉ™.txt",
    "regulationType": "μ·¨μ—…κ·œμΉ™"
  }'

5. μ„œλ²„ 둜그 확인

정상 μ‹œμž‘ μ‹œ λ‹€μŒκ³Ό 같은 λ‘œκ·Έκ°€ ν‘œμ‹œλ©λ‹ˆλ‹€:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.2.0)

2025-10-17 ... : Starting GuideonApplication using Java 17...
2025-10-17 ... : ConfigLoader loaded from classpath: application.properties
2025-10-17 ... : QueryAnalysisService initialized with 27 regulation types
2025-10-17 ... : RegulationSearchService initialized
2025-10-17 ... : Tomcat started on port(s): 8080 (http)
2025-10-17 ... : Started GuideonApplication in 5.234 seconds

문제 ν•΄κ²°

⚠️ Maven λΉŒλ“œ μ‹€νŒ¨: "invalid flag: --release"

이 였λ₯˜λŠ” Maven이 Java 8을 μ‚¬μš©ν•˜κ³  μžˆμ„ λ•Œ λ°œμƒν•©λ‹ˆλ‹€.

ν•΄κ²° 방법:

# 1. ν˜„μž¬ Maven이 μ‚¬μš©ν•˜λŠ” Java 버전 확인
mvn -version
# Java version이 1.8이면 문제!

# 2. JAVA_HOME을 Java 17둜 μ„€μ •
# Windows (PowerShell)
$env:JAVA_HOME="C:\Program Files\Java\jdk-17"
$env:PATH="C:\Program Files\Java\jdk-17\bin;$env:PATH"

# Windows (CMD)
set JAVA_HOME=C:\Program Files\Java\jdk-17
set PATH=C:\Program Files\Java\jdk-17\bin;%PATH%

# Linux/Mac
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
export PATH=$JAVA_HOME/bin:$PATH

# 3. λ‹€μ‹œ λΉŒλ“œ
mvn clean compile

νŽΈλ¦¬ν•œ 방법 (Windows):

제곡된 배치 슀크립트λ₯Ό μ‚¬μš©ν•˜μ„Έμš” (μžλ™μœΌλ‘œ Java 17 μ„€μ •):

# λΉŒλ“œ
build.bat

# μ„œλ²„ μ‹€ν–‰
run-server.bat

Java 버전 확인

java -version
# 좜λ ₯: java version "17.0.x" 이상이어야 함

mvn -version
# μ€‘μš”: Java version도 17이어야 함!

Maven 버전 확인

mvn -version
# 좜λ ₯: Apache Maven 3.6.x 이상이어야 함

API ν‚€ 확인

# Windows (PowerShell)
echo $env:GOOGLE_API_KEY

# Windows (CMD)
echo %GOOGLE_API_KEY%

# Linux/Mac
echo $GOOGLE_API_KEY

일반적인 였λ₯˜

  1. invalid flag: --release: Maven이 Java 8 μ‚¬μš© β†’ JAVA_HOME을 Java 17둜 λ³€κ²½
  2. GOOGLE_API_KEY not found: ν™˜κ²½λ³€μˆ˜κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜κ±°λ‚˜ application.yml에 API ν‚€κ°€ μ—†μŒ
  3. ClassNotFoundException: Maven λΉŒλ“œλ₯Ό λ‹€μ‹œ μ‹€ν–‰ (mvn clean package)
  4. Port 8080 already in use: λ‹€λ₯Έ ν”„λ‘œμ„ΈμŠ€κ°€ 포트λ₯Ό μ‚¬μš©μ€‘ β†’ 포트 λ³€κ²½ λ˜λŠ” ν”„λ‘œμ„ΈμŠ€ μ’…λ£Œ

4. μ‚¬μš© μ˜ˆμ‹œ

// μ‹œμŠ€ν…œ μ΄ˆκΈ°ν™”
String apiKey = System.getenv("GOOGLE_API_KEY");
RegulationQASystem system = new RegulationQASystem(apiKey);

// κ·œμ • λ¬Έμ„œ μ—…λ‘œλ“œ (인덱싱)
system.uploadRegulationDocument("path/to/μ·¨μ—…κ·œμΉ™.txt", "μ·¨μ—…κ·œμΉ™");
system.uploadRegulationDocument("path/to/κ²½λΉ„μ§€κΈ‰κ·œμ •.txt", "κ²½λΉ„μ§€κΈ‰κ·œμ •");

// μžμ—°μ–΄ 질문
String question = "ν•΄μ™Έ 좜μž₯μ‹œ μˆ™λ°•λΉ„λŠ” μ–Όλ§ˆκΉŒμ§€ μ§€μ›λ˜λ‚˜μš”?";
RegulationSearchResult result = system.askQuestion(question);

// κ²°κ³Ό 확인
System.out.println("λ‹΅λ³€: " + result.getAnswer());
System.out.println("신뒰도: " + result.getConfidenceScore());
result.getReferences().forEach(ref -> {
    System.out.println("κ·Όκ±°: " + ref.getDocumentName() + " - " + ref.getContent());
});

πŸ“‘ REST API λͺ…μ„Έ

1. Q&A API

POST /api/qa/analyze - 질문 뢄석

μ‚¬μš©μžμ˜ μžμ—°μ–΄ μ§ˆλ¬Έμ„ λΆ„μ„ν•©λ‹ˆλ‹€.

Request:

{
  "question": "μ—°μ°¨ νœ΄κ°€λŠ” λͺ‡ μΌμΈκ°€μš”?"
}

Response:

{
  "success": true,
  "message": "질문 뢄석이 μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€",
  "analysis": {
    "originalQuery": "μ—°μ°¨ νœ΄κ°€λŠ” λͺ‡ μΌμΈκ°€μš”?",
    "keywords": ["μ—°μ°¨", "νœ΄κ°€", "일수"],
    "regulationTypes": ["μ·¨μ—…κ·œμΉ™", "λ³΅λ¦¬ν›„μƒλΉ„κ·œμ •"],
    "intent": "기쀀확인",
    "searchQuery": "μ—°μ°¨ νœ΄κ°€ 일수 κΈ°μ€€"
  }
}

POST /api/qa/search - κ·œμ • 검색

μ§ˆλ¬Έμ„ λΆ„μ„ν•˜κ³  κ΄€λ ¨ κ·œμ •μ„ κ²€μƒ‰ν•˜μ—¬ 닡변을 μƒμ„±ν•©λ‹ˆλ‹€.

Request:

{
  "question": "ν•΄μ™Έ 좜μž₯μ‹œ μˆ™λ°•λΉ„λŠ” μ–Όλ§ˆκΉŒμ§€ μ§€μ›λ˜λ‚˜μš”?"
}

Response:

{
  "success": true,
  "message": "검색이 μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€",
  "result": {
    "answer": "ν•΄μ™Έ 좜μž₯μ‹œ μˆ™λ°•λΉ„λŠ” κ΅­κ°€λ³„λ‘œ μ°¨λ“± μ§€μ›λ©λ‹ˆλ‹€...",
    "references": [
      {
        "documentName": "좜μž₯μ—¬λΉ„μ§€κΈ‰κ·œμ •",
        "clause": "제5쑰",
        "content": "ν•΄μ™Έ 좜μž₯ μˆ™λ°•λΉ„λŠ”...",
        "pageNumber": 3,
        "relevanceScore": 0.89
      }
    ],
    "confidenceScore": 0.87,
    "hasAnswer": true
  }
}

2. κ·œμ • 관리 API

GET /api/regulations/types - κ·œμ • μœ ν˜• λͺ©λ‘

μ§€μ›ν•˜λŠ” 27개 κ·œμ • μœ ν˜• λͺ©λ‘μ„ μ‘°νšŒν•©λ‹ˆλ‹€.

3. λ¬Έμ„œ μ—…λ‘œλ“œ(프리뷰/ν™•μ •) API

2단계 μ—…λ‘œλ“œ ν”Œλ‘œμš°:

  1. μ—…λ‘œλ“œ 직후 ν…μŠ€νŠΈλ§Œ μΆ”μΆœν•˜μ—¬ μ‚¬μš©μž κ²€μˆ˜
  2. ν™•μ • μ‹œμ—λ§Œ μž„λ² λ”©/인덱싱 및 메타 μ €μž₯
  • POST /api/documents/extract-text (multipart/form-data)

    • form: file, regulationType
    • 응닡: { "success": true, "data": { "uploadId": "uuid", "text": "..." } }
  • POST /api/documents/{uploadId}/confirm (application/json)

    • body: { "text": "ν™•μ • ν…μŠ€νŠΈ" }
    • 응닡: κΈ°μ‘΄ μ—…λ‘œλ“œ 응닡(DocumentUploadResponse)κ³Ό 동일

Response:

{
  "success": true,
  "types": ["μ΄μ‚¬νšŒκ·œμ •", "μ ‘λŒ€λΉ„μ‚¬μš©κ·œμ •", ...],
  "count": 27
}

POST /api/regulations/upload - κ·œμ • λ¬Έμ„œ μ—…λ‘œλ“œ

κ·œμ • λ¬Έμ„œλ₯Ό μ—…λ‘œλ“œν•˜κ³  벑터 DB에 μΈλ±μ‹±ν•©λ‹ˆλ‹€.

Request:

{
  "filePath": "c:/workspace/regulations/μ·¨μ—…κ·œμΉ™.txt",
  "regulationType": "μ·¨μ—…κ·œμΉ™"
}

Response:

{
  "success": true,
  "message": "κ·œμ • λ¬Έμ„œκ°€ μ„±κ³΅μ μœΌλ‘œ μ—…λ‘œλ“œλ˜μ—ˆμŠ΅λ‹ˆλ‹€",
  "regulationType": "μ·¨μ—…κ·œμΉ™",
  "filePath": "c:/workspace/regulations/μ·¨μ—…κ·œμΉ™.txt"
}

πŸ“ ν”„λ‘œμ νŠΈ ꡬ쑰

guideon/
β”œβ”€β”€ pom.xml                                    # Maven μ„€μ • (Spring Boot)
β”œβ”€β”€ CLUADE.md                                  # μ‹œμŠ€ν…œ 섀계 λ¬Έμ„œ
β”œβ”€β”€ README.md                                  # 이 파일
└── src/main/
    β”œβ”€β”€ java/com/guideon/
    β”‚   β”œβ”€β”€ GuideonApplication.java           # Spring Boot 메인 클래슀
    β”‚   β”œβ”€β”€ controller/                       # REST API Controllers
    β”‚   β”‚   β”œβ”€β”€ QAController.java             # Q&A API
    β”‚   β”‚   └── RegulationController.java     # κ·œμ • 관리 API
    β”‚   β”œβ”€β”€ service/                          # Business Logic
    β”‚   β”‚   β”œβ”€β”€ QueryAnalysisService.java     # 질문 뢄석 μ„œλΉ„μŠ€
    β”‚   β”‚   └── RegulationSearchService.java  # RAG 검색 μ„œλΉ„μŠ€
    β”‚   β”œβ”€β”€ model/                            # Domain Models
    β”‚   β”‚   β”œβ”€β”€ QueryAnalysisResult.java
    β”‚   β”‚   β”œβ”€β”€ RegulationSearchResult.java
    β”‚   β”‚   └── RegulationReference.java
    β”‚   β”œβ”€β”€ dto/                              # Data Transfer Objects
    β”‚   β”‚   β”œβ”€β”€ QuestionRequest.java
    β”‚   β”‚   β”œβ”€β”€ AnalysisResponse.java
    β”‚   β”‚   β”œβ”€β”€ SearchResponse.java
    β”‚   β”‚   └── UploadRequest.java
    β”‚   β”œβ”€β”€ config/                           # Configuration
    β”‚   β”‚   β”œβ”€β”€ GuideonConfig.java            # Service Beans
    β”‚   β”‚   β”œβ”€β”€ WebConfig.java                # CORS μ„€μ •
    β”‚   β”‚   └── ConfigLoader.java             # Properties Loader
    β”‚   └── exception/                        # Exception Handling
    β”‚       └── GlobalExceptionHandler.java
    └── resources/
        β”œβ”€β”€ application.yml                   # Spring Boot μ„€μ •
        └── application.properties            # Legacy μ„€μ •

πŸ”‘ 핡심 클래슀

QueryAnalysisService

μžμ—°μ–΄ μ§ˆλ¬Έμ„ AI둜 λΆ„μ„ν•˜μ—¬ κ΅¬μ‘°ν™”λœ 검색 쿼리둜 λ³€ν™˜

μ£Όμš” λ©”μ„œλ“œ:

  • analyzeQuery(String userQuery): 질문 뢄석 및 ν‚€μ›Œλ“œ μΆ”μΆœ

뢄석 κ²°κ³Ό:

  • ν‚€μ›Œλ“œ λͺ©λ‘
  • κ΄€λ ¨ κ·œμ • μœ ν˜•
  • 질문 μ˜λ„
  • μ΅œμ ν™”λœ 검색 쿼리

RegulationSearchService

RAG 기반 벑터 검색 및 λ‹΅λ³€ 생성

μ£Όμš” λ©”μ„œλ“œ:

  • indexDocument(Document doc, String type): κ·œμ • λ¬Έμ„œ 인덱싱
  • search(QueryAnalysisResult analysis): κ·œμ • 검색 및 λ‹΅λ³€ 생성

검색 κ³Όμ •:

  1. 벑터 μž„λ² λ”© 생성
  2. μœ μ‚¬λ„ 기반 κ΄€λ ¨ μ‘°ν•­ 검색
  3. κ²€μƒ‰λœ 쑰항을 μ»¨ν…μŠ€νŠΈλ‘œ LLM λ‹΅λ³€ 생성
  4. 신뒰도 점수 계산

🎨 μ§€μ›ν•˜λŠ” κ·œμ • μœ ν˜• (27μ’…)

μ΄μ‚¬νšŒκ·œμ •, μ ‘λŒ€λΉ„μ‚¬μš©κ·œμ •, μœ€λ¦¬κ·œμ •, 좜μž₯μ—¬λΉ„μ§€κΈ‰κ·œμ •, μ£Όμ‹λ§€μˆ˜μ„ νƒκΆŒμš΄μ˜κ·œμ •, λ…Έμ‚¬ν˜‘μ˜νšŒκ·œμ •, μ·¨μ—…κ·œμΉ™, λ§€μΆœμ±„κΆŒκ΄€λ¦¬κ·œμ •, κΈˆμœ΅μžμ‚° μš΄μš©κ·œμ •, λ¬Έμ„œκ΄€λ¦¬κ·œμ •, μž¬κ³ κ΄€λ¦¬κ·œμ •, κ³„μ•½κ²€ν† κ·œμ •, μ‚¬κ·œκ΄€λ¦¬κ·œμ •, μž„μ›ν‡΄μ§κΈˆμ§€κΈ‰κ·œμ •, μž„μ›λ³΄μˆ˜κ·œμ •, μ£Όμ£Όμ΄νšŒμš΄μ˜κ·œμ •, κ²½λΉ„μ§€κΈ‰κ·œμ •, λ³΅λ¦¬ν›„μƒλΉ„κ·œμ •, λ³΄μ•ˆκ΄€λ¦¬κ·œμ •, μœ„μž„μ „κ²°κ·œμ •, μš°λ¦¬μ‚¬μ£Όμš΄μ˜κ·œμ •, λ‚΄λΆ€μ •λ³΄κ΄€λ¦¬κ·œμ •, νšŒκ³„κ΄€λ¦¬κ·œμ •, νŠΉμˆ˜κ΄€κ³„μž κ±°λž˜κ·œμ •, 쑰직 및 업무뢄μž₯κ·œμ •, μžκΈˆκ΄€λ¦¬κ·œμ •, 인μž₯κ΄€λ¦¬κ·œμ •

πŸ”„ μ›Œν¬ν”Œλ‘œμš° μ˜ˆμ‹œ

μž…λ ₯: "μ—°μ°¨ νœ΄κ°€λŠ” λͺ‡ μΌμΈκ°€μš”?"

[QueryAnalysisService 뢄석]
β†’ ν‚€μ›Œλ“œ: [μ—°μ°¨, νœ΄κ°€, 일수]
β†’ κ·œμ • μœ ν˜•: [μ·¨μ—…κ·œμΉ™, λ³΅λ¦¬ν›„μƒλΉ„κ·œμ •]
β†’ 질문 μ˜λ„: 기쀀확인
β†’ 검색 쿼리: "μ—°μ°¨ νœ΄κ°€ 일수 κΈ°μ€€"

[RegulationSearchService 검색]
β†’ 벑터 검색: κ΄€λ ¨ μ‘°ν•­ 5개 발견
β†’ μ»¨ν…μŠ€νŠΈ: "μ·¨μ—…κ·œμΉ™ 제XXμ‘° μ—°μ°¨νœ΄κ°€λŠ”..."
β†’ AI λ‹΅λ³€ 생성: "μ—°μ°¨ νœ΄κ°€λŠ” κ·Όμ†λ…„μˆ˜μ— 따라..."

[κ²°κ³Ό λ°˜ν™˜]
β†’ λ‹΅λ³€: "μ—°μ°¨ νœ΄κ°€λŠ” 1λ…„ κ·Όλ¬΄μ‹œ 15일이 λΆ€μ—¬λ©λ‹ˆλ‹€..."
β†’ κ·Όκ±°: μ·¨μ—…κ·œμΉ™ 제32μ‘° (관련도: 0.89)
β†’ 신뒰도: 0.87

⚠️ μ£Όμ˜μ‚¬ν•­

  1. API ν‚€ λ³΄μ•ˆ: GOOGLE_API_KEYλ₯Ό μ½”λ“œμ— ν•˜λ“œμ½”λ”©ν•˜μ§€ λ§ˆμ„Έμš”
  2. 인덱싱 ν•„μˆ˜: μ§ˆλ¬Έν•˜κΈ° 전에 λ°˜λ“œμ‹œ κ·œμ • λ¬Έμ„œλ₯Ό 인덱싱해야 ν•©λ‹ˆλ‹€
  3. 개발 ν™˜κ²½: ν˜„μž¬ In-Memory μ €μž₯μ†Œ μ‚¬μš©μ€‘ (μž¬μ‹œμž‘μ‹œ 데이터 μ†Œμ‹€)
  4. 운영 ν™˜κ²½: Qdrant λ“± 영ꡬ 벑터 DB둜 ꡐ체 ꢌμž₯

🎨 React 기반 UI 개발

ν™”λ©΄ λͺ©λ‘

1. 둜그인 ν™”λ©΄ (/login)

  • μ‚¬μš©μž 인증
  • μ„Έμ…˜ 관리
  • μžλ™ 둜그인 μ˜΅μ…˜

2. 메인 λŒ€μ‹œλ³΄λ“œ (/)

  • 질문 μž…λ ₯ μΈν„°νŽ˜μ΄μŠ€
  • 졜근 질문 이λ ₯
  • 즐겨찾기 κ·œμ • λͺ©λ‘
  • 톡계 λŒ€μ‹œλ³΄λ“œ (질문 수, κ·œμ • ν™œμš©λ„)

3. 질문 & λ‹΅λ³€ ν™”λ©΄ (/qa)

  • μžμ—°μ–΄ 질문 μž…λ ₯ 폼
  • μ‹€μ‹œκ°„ 질문 뢄석 ν‘œμ‹œ
    • μΆ”μΆœλœ ν‚€μ›Œλ“œ
    • κ΄€λ ¨ κ·œμ • μœ ν˜•
    • 질문 μ˜λ„
  • AI λ‹΅λ³€ ν‘œμ‹œ
    • λ‹΅λ³€ λ‚΄μš©
    • 신뒰도 점수
    • κ·Όκ±° κ·œμ • μ‘°ν•­ λͺ©λ‘
  • λ‹΅λ³€ 평가 (도움됨/λ„μ›€μ•ˆλ¨)
  • λ‹΅λ³€ 곡유 κΈ°λŠ₯

4. κ·œμ • 관리 ν™”λ©΄ (/regulations)

  • κ·œμ • λ¬Έμ„œ λͺ©λ‘ (27개 μœ ν˜•λ³„ λΆ„λ₯˜)
  • κ·œμ • λ¬Έμ„œ μ—…λ‘œλ“œ μΈν„°νŽ˜μ΄μŠ€
  • κ·œμ • λ¬Έμ„œ 미리보기
  • λ¬Έμ„œ 버전 관리
  • 인덱싱 μƒνƒœ ν‘œμ‹œ

5. 검색 이λ ₯ ν™”λ©΄ (/history)

  • κ³Όκ±° 질문 λͺ©λ‘
  • μ§ˆλ¬Έλ³„ λ‹΅λ³€ 쑰회
  • 필터링 (λ‚ μ§œ, κ·œμ • μœ ν˜•, 신뒰도)
  • 즐겨찾기 μΆ”κ°€/제거

6. 톡계 및 뢄석 ν™”λ©΄ (/analytics)

  • 질문 톡계 (일별/주별/월별)
  • κ·œμ •λ³„ ν™œμš©λ„
  • 신뒰도 뢄포
  • 인기 ν‚€μ›Œλ“œ
  • λ‹΅λ³€ λ§Œμ‘±λ„ 톡계

7. μ„€μ • ν™”λ©΄ (/settings)

  • API ν‚€ 관리
  • λͺ¨λΈ μ„€μ • (Gemini Flash/Pro 선택)
  • 검색 μ˜΅μ…˜ (μ΅œλŒ€ κ²°κ³Ό 수, μ΅œμ†Œ 신뒰도)
  • μ•Œλ¦Ό μ„€μ •

기술 μŠ€νƒ

Frontend

  • React 18+: UI 라이브러리
  • TypeScript: νƒ€μž… μ•ˆμ •μ„±
  • React Router v6: SPA λΌμš°νŒ…
  • Vite: λΉŒλ“œ 도ꡬ (λΉ λ₯Έ 개발 μ„œλ²„)
  • TanStack Query (React Query): μ„œλ²„ μƒνƒœ 관리
  • Zustand: ν΄λΌμ΄μ–ΈνŠΈ μƒνƒœ 관리 (κ²½λŸ‰)
  • Axios: HTTP ν΄λΌμ΄μ–ΈνŠΈ

UI μ»΄ν¬λ„ŒνŠΈ

  • Ant Design λ˜λŠ” Material-UI (MUI): UI μ»΄ν¬λ„ŒνŠΈ 라이브러리
  • Tailwind CSS: μœ ν‹Έλ¦¬ν‹° CSS ν”„λ ˆμž„μ›Œν¬
  • React Markdown: λ‹΅λ³€ λ Œλ”λ§
  • React Syntax Highlighter: μ½”λ“œ ν•˜μ΄λΌμ΄νŒ…
  • Recharts: 톡계 차트

개발 도ꡬ

  • ESLint: μ½”λ“œ λ¦°νŒ…
  • Prettier: μ½”λ“œ ν¬λ§·νŒ…
  • Vitest: λ‹¨μœ„ ν…ŒμŠ€νŠΈ
  • React Testing Library: μ»΄ν¬λ„ŒνŠΈ ν…ŒμŠ€νŠΈ

Backend 연동

  • REST API: Spring Boot λ°±μ—”λ“œ 연동
  • WebSocket (선택): μ‹€μ‹œκ°„ 뢄석 κ²°κ³Ό 슀트리밍

ν”„λ‘œμ νŠΈ ꡬ쑰

guideon-frontend/
β”œβ”€β”€ public/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ assets/              # 이미지, 폰트 λ“±
β”‚   β”œβ”€β”€ components/          # μž¬μ‚¬μš© κ°€λŠ₯ν•œ μ»΄ν¬λ„ŒνŠΈ
β”‚   β”‚   β”œβ”€β”€ common/         # 곡톡 μ»΄ν¬λ„ŒνŠΈ (Button, Input λ“±)
β”‚   β”‚   β”œβ”€β”€ layout/         # λ ˆμ΄μ•„μ›ƒ μ»΄ν¬λ„ŒνŠΈ
β”‚   β”‚   β”œβ”€β”€ qa/             # Q&A κ΄€λ ¨ μ»΄ν¬λ„ŒνŠΈ
β”‚   β”‚   └── regulation/     # κ·œμ • κ΄€λ ¨ μ»΄ν¬λ„ŒνŠΈ
β”‚   β”œβ”€β”€ pages/              # νŽ˜μ΄μ§€ μ»΄ν¬λ„ŒνŠΈ
β”‚   β”‚   β”œβ”€β”€ Login.tsx
β”‚   β”‚   β”œβ”€β”€ Dashboard.tsx
β”‚   β”‚   β”œβ”€β”€ QAPage.tsx
β”‚   β”‚   β”œβ”€β”€ Regulations.tsx
β”‚   β”‚   β”œβ”€β”€ History.tsx
β”‚   β”‚   β”œβ”€β”€ Analytics.tsx
β”‚   β”‚   └── Settings.tsx
β”‚   β”œβ”€β”€ hooks/              # μ»€μŠ€ν…€ ν›…
β”‚   β”œβ”€β”€ services/           # API μ„œλΉ„μŠ€
β”‚   β”œβ”€β”€ stores/             # Zustand μŠ€ν† μ–΄
β”‚   β”œβ”€β”€ types/              # TypeScript νƒ€μž… μ •μ˜
β”‚   β”œβ”€β”€ utils/              # μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜
β”‚   β”œβ”€β”€ App.tsx
β”‚   └── main.tsx
β”œβ”€β”€ package.json
β”œβ”€β”€ tsconfig.json
β”œβ”€β”€ vite.config.ts
└── tailwind.config.js

πŸ” ν•˜μ΄λΈŒλ¦¬λ“œ 검색 (Hybrid Search) 상세

κ°œμš”

ν•˜μ΄λΈŒλ¦¬λ“œ 검색은 **Vector Search (의미 검색)**와 **BM25 Search (ν‚€μ›Œλ“œ 검색)**을 κ²°ν•©ν•˜μ—¬ 검색 정확도λ₯Ό 20-30% ν–₯μƒμ‹œν‚€λŠ” κ³ κΈ‰ 검색 κΈ°λ²•μž…λ‹ˆλ‹€.

μž‘λ™ 방식

Stage 1: 병렬 검색

Query: "μ—°μ°¨ λ°œμƒ κΈ°μ€€ 제10μ‘°"
    ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Vector Search      β”‚  BM25 Search         β”‚
β”‚  (의미적 μœ μ‚¬μ„±)     β”‚  (ν‚€μ›Œλ“œ λ§€μΉ­)        β”‚
β”‚                     β”‚                      β”‚
β”‚  "μ—°μ°¨" β‰ˆ "νœ΄κ°€"    β”‚  "제10μ‘°" μ •ν™• λ§€μΉ­   β”‚
β”‚  "λ°œμƒ" β‰ˆ "λΆ€μ—¬"    β”‚  "μ—°μ°¨" μ •ν™• λ§€μΉ­     β”‚
β”‚                     β”‚                      β”‚
β”‚  β†’ 20 candidates    β”‚  β†’ 20 candidates     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Stage 2: Reciprocal Rank Fusion (RRF)

Vector κ²°κ³Ό + BM25 κ²°κ³Ό β†’ RRF μ•Œκ³ λ¦¬μ¦˜
    ↓
쀑볡 제거 및 점수 톡합
score = Ξ£(1 / (k + rank))  // k = 60
    ↓
40개 unique candidates (점수 순 μ •λ ¬)

Stage 3: ReRanking (Cohere)

40개 후보 β†’ Cohere rerank-multilingual-v3.0
    ↓
μ •κ΅ν•œ semantic κ΄€λ ¨μ„± μž¬ν‰κ°€
    ↓
Top 5 results (min_score: 0.8+)

μ„€μ •

ν•˜μ΄λΈŒλ¦¬λ“œ 검색은 application.propertiesμ—μ„œ ν™œμ„±ν™”/λΉ„ν™œμ„±ν™”ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

# Hybrid Search Configuration
hybrid.search.enabled=true
hybrid.search.vector.weight=0.6
hybrid.search.keyword.weight=0.4
hybrid.search.initial.results=40

# BM25 Configuration
bm25.index.directory=${user.home}/guideon/data/bm25-index
bm25.k1=1.2
bm25.b=0.75
bm25.analyzer.type=korean-nori

검색 ν’ˆμ§ˆ 비ꡐ

쿼리 μœ ν˜• Vector Only Hybrid Search κ°œμ„ μœ¨
의미적 쿼리 ("μ—°μ°¨λŠ” μ–Έμ œ λ°œμƒν•˜λ‚˜μš”?") 85% 90% +5%
μ •ν™•ν•œ μ‘°ν•­ ("제10μ‘° 2ν•­") 60% 95% +35%
볡합 쿼리 ("2024λ…„ μ—°μ°¨ κΈ°μ€€ 제10μ‘°") 70% 92% +22%
숫자/λ‚ μ§œ ("1000λ§Œμ› 이상") 65% 90% +25%

ν•œκ΅­μ–΄ μ΅œμ ν™”

Apache Lucene의 Nori Korean Analyzerλ₯Ό μ‚¬μš©ν•˜μ—¬ ν•œκ΅­μ–΄ ν˜•νƒœμ†Œ 뢄석:

μž…λ ₯: "μ—°μ°¨ νœ΄κ°€ λ°œμƒ κΈ°μ€€"
    ↓
ν˜•νƒœμ†Œ 뢄석
    ↓
토큰: ["μ—°μ°¨", "νœ΄κ°€", "λ°œμƒ", "κΈ°μ€€"]
    ↓
BM25 인덱슀 검색

개발 μš°μ„ μˆœμœ„

βœ… Phase 1: 핡심 κΈ°λŠ₯ (μ™„λ£Œ/진행쀑)

  1. βœ… BM25SearchService κ΅¬ν˜„
  2. βœ… HybridSearchService κ΅¬ν˜„
  3. βœ… RRF μ•Œκ³ λ¦¬μ¦˜ κ΅¬ν˜„
  4. βœ… RegulationSearchService 톡합
  5. βœ… ConfigLoader ν™•μž₯

βœ… Phase 2: 톡합 및 ν…ŒμŠ€νŠΈ (μ™„λ£Œ/진행쀑)

  1. βœ… 인덱싱 νŒŒμ΄ν”„λΌμΈ 톡합
  2. βœ… 검색 νŒŒμ΄ν”„λΌμΈ 톡합
  3. πŸ”„ λ‹¨μœ„ ν…ŒμŠ€νŠΈ μž‘μ„±
  4. πŸ”„ 톡합 ν…ŒμŠ€νŠΈ μž‘μ„±

βœ… Phase 3: λ‹΅λ³€ 생성 κΈ°λŠ₯ ν–₯상 (μ™„λ£Œ)

  1. βœ… κ΅¬μ‘°ν™”λœ μ»¨ν…μŠ€νŠΈ 생성 (EnhancedContextBuilder)
  2. βœ… μ‘°ν•­ 번호 μΆ”μΆœ (RegulationArticleExtractor)
  3. βœ… 질문 μ˜λ„λ³„ λ§žμΆ€ν˜• ν”„λ‘¬ν”„νŠΈ
  4. βœ… λ‹΅λ³€ ν’ˆμ§ˆ κ°œμ„  (μ‘°ν•­ 인용, κ·Όκ±° λͺ…μ‹œ)

⚠️ Phase 4: μ΅œμ ν™” (μ˜ˆμ •)

  1. ⏳ ν•œκ΅­μ–΄ 뢄석기 νŠœλ‹
  2. ⏳ κ°€μ€‘μΉ˜ μ΅œμ ν™” (A/B ν…ŒμŠ€νŠΈ)
  3. ⏳ μ„±λŠ₯ κ°œμ„  (캐싱, 인덱슀 μ΅œμ ν™”)
  4. ⏳ λ©”λͺ¨λ¦¬ μ΅œμ ν™”

πŸ”΅ Phase 5: 운영 (선택사항)

  1. ⏳ 인덱슀 관리 도ꡬ
  2. ⏳ λͺ¨λ‹ˆν„°λ§ λŒ€μ‹œλ³΄λ“œ
  3. ⏳ A/B ν…ŒμŠ€νŠΈ ν”„λ ˆμž„μ›Œν¬

μ„±λŠ₯ μ§€ν‘œ

  • 검색 속도: +50ms (BM25 μΆ”κ°€ μ˜€λ²„ν—€λ“œ)
  • λ©”λͺ¨λ¦¬ μ‚¬μš©: +200MB (Lucene 인덱슀)
  • 정확도: +20-30% (평균)
  • ν•œκ΅­μ–΄ 검색: +35% (ν‚€μ›Œλ“œ λ§€μΉ­ ν–₯상)

πŸ“ Phase 3: λ‹΅λ³€ 생성 κΈ°λŠ₯ ν–₯상

κ°œμš”

Phase 3μ—μ„œλŠ” κ΅¬μ‘°ν™”λœ μ»¨ν…μŠ€νŠΈμ™€ 질문 μ˜λ„λ³„ λ§žμΆ€ν˜• ν”„λ‘¬ν”„νŠΈλ₯Ό 톡해 λ‹΅λ³€ ν’ˆμ§ˆμ„ λŒ€ν­ ν–₯μƒμ‹œμΌ°μŠ΅λ‹ˆλ‹€.

μ£Όμš” κΈ°λŠ₯

1. κ΅¬μ‘°ν™”λœ μ»¨ν…μŠ€νŠΈ 생성 (EnhancedContextBuilder)

κΈ°μ‘΄ 방식:

κ·œμ • λ‚΄μš© A
κ·œμ • λ‚΄μš© B
κ·œμ • λ‚΄μš© C

κ°œμ„ λœ 방식:

=== κ²€μƒ‰λœ κ·œμ • λ‚΄μš© ===

[검색 κ²°κ³Ό 1] (관련도: 0.89, 좜처: μ·¨μ—…κ·œμΉ™, 제32μ‘°)
제32μ‘° (μ—°μ°¨νœ΄κ°€)
1. μ—°μ°¨ νœ΄κ°€λŠ” κ·Όμ†λ…„μˆ˜μ— 따라 λ‹€μŒκ³Ό 같이 λΆ€μ—¬ν•œλ‹€.
   - 1λ…„ 근속: 15일
   - 3λ…„ 근속: 16일

---

[검색 κ²°κ³Ό 2] (관련도: 0.76, 좜처: λ³΅λ¦¬ν›„μƒλΉ„κ·œμ •, 제15μ‘°)
제15μ‘° (νœ΄κ°€ 지원)
...

κ°œμ„  효과:

  • βœ… 관련도 점수둜 μ€‘μš”λ„ νŒŒμ•…
  • βœ… κ·œμ • μœ ν˜• λͺ…μ‹œλ‘œ 좜처 λͺ…ν™•ν™”
  • βœ… μ‘°ν•­ 번호 μžλ™ μΆ”μΆœ 및 ꡬ쑰화
  • βœ… LLM이 μ»¨ν…μŠ€νŠΈλ₯Ό 더 잘 이해

2. μ‘°ν•­ 번호 μΆ”μΆœ (RegulationArticleExtractor)

κΈ°λŠ₯:

  • "제XXμ‘°", "제XXν•­", "제XX호" νŒ¨ν„΄ 인식
  • μ‘°ν•­ 제λͺ© μΆ”μΆœ (예: "제32μ‘° (μ—°μ°¨νœ΄κ°€)")
  • μ‘°ν•­ λ‚΄μš© νŒŒμ‹± 및 정리
  • μ •κ·œμ‹ 기반 μ •ν™•ν•œ μΆ”μΆœ

μ‚¬μš© 예:

String text = "제32μ‘° (μ—°μ°¨νœ΄κ°€) 1. μ—°μ°¨λŠ” 15일...";
List<RegulationArticle> articles = RegulationArticleExtractor.extractArticles(text, "μ·¨μ—…κ·œμΉ™");
// β†’ [RegulationArticle{articleNumber="제32μ‘°", title="μ—°μ°¨νœ΄κ°€", ...}]

3. 질문 μ˜λ„λ³„ λ§žμΆ€ν˜• ν”„λ‘¬ν”„νŠΈ

RegulationSearchServiceλŠ” 질문 μ˜λ„λ₯Ό λΆ„μ„ν•˜μ—¬ μ΅œμ ν™”λœ ν”„λ‘¬ν”„νŠΈλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€:

질문 μ˜λ„ ν”„λ‘¬ν”„νŠΈ νŠΉμ§• μ˜ˆμ‹œ
기쀀확인 숫자/κΈˆμ•‘/κΈ°κ°„ λͺ…μ‹œ μš”κ΅¬ "μ—°μ°¨λŠ” λͺ‡ μΌμΈκ°€μš”?"
μ ˆμ°¨μ„€λͺ… 단계별 μˆœμ„œ μ„€λͺ… μš”κ΅¬ "좜μž₯ 신청은 μ–΄λ–»κ²Œ ν•˜λ‚˜μš”?"
κ°€λŠ₯μ—¬λΆ€ Yes/No λ¨Όμ € λ‹΅λ³€ μš”κ΅¬ "νœ΄κ°€λ₯Ό μͺΌκ°œμ„œ μ“Έ 수 μžˆλ‚˜μš”?"
μ˜ˆμ™Έμƒν™© 원칙과 μ˜ˆμ™Έ ꡬ뢄 μš”κ΅¬ "νŠΉλ³„ν•œ 경우 μ˜ˆμ™Έκ°€ μžˆλ‚˜μš”?"

ν”„λ‘¬ν”„νŠΈ μ˜ˆμ‹œ (기쀀확인):

[λ‹΅λ³€ μž‘μ„± μ§€μΉ¨]
1. 제곡된 κ·œμ • λ‚΄μš©λ§Œμ„ 기반으둜 λ‹΅λ³€ν•˜μ„Έμš”
2. λͺ…ν™•ν•˜κ³  κ°„κ²°ν•˜κ²Œ μž‘μ„±ν•˜μ„Έμš”
3. κ·œμ •μ— μ—†λŠ” λ‚΄μš©μ€ μΆ”μΈ‘ν•˜μ§€ λ§ˆμ„Έμš”
4. λΆˆν™•μ‹€ν•œ 경우 "ν•΄λ‹Ή κ·œμ •μ—μ„œ λͺ…ν™•νžˆ μ–ΈκΈ‰λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€"라고 λ‹΅λ³€ν•˜μ„Έμš”
5. ꡬ체적인 숫자, κΈˆμ•‘, κΈ°κ°„ 등을 λͺ…ν™•νžˆ μ œμ‹œν•˜μ„Έμš”
6. ν•΄λ‹Ήν•˜λŠ” κ·œμ • μ‘°ν•­(제XXμ‘°)을 λ°˜λ“œμ‹œ μ–ΈκΈ‰ν•˜μ„Έμš”
7. μ‘°κ±΄μ΄λ‚˜ μ˜ˆμ™Έμ‚¬ν•­μ΄ μžˆλ‹€λ©΄ ν•¨κ»˜ μ„€λͺ…ν•˜μ„Έμš”

4. λ‹΅λ³€ ν’ˆμ§ˆ κ°œμ„  (μ™„λ£Œ)

Phase 3μ—μ„œλŠ” λ‹΅λ³€μ˜ ν’ˆμ§ˆμ„ μžλ™μœΌλ‘œ ν‰κ°€ν•˜κ³  κ°œμ„ ν•˜λŠ” μ‹œμŠ€ν…œμ„ κ΅¬μΆ•ν–ˆμŠ΅λ‹ˆλ‹€.

ν’ˆμ§ˆ 평가 μ§€ν‘œ:

  • λ‹΅λ³€ 길이 평가 (0.25점): μ΅œμ†Œ 50자, 졜적 200자 이상
  • μ‘°ν•­ μ°Έμ‘° 평가 (0.25점): κ·œμ • μ‘°ν•­(제XXμ‘°) 인용 μ—¬λΆ€
  • 긍정성 평가 (0.25점): 뢀정적 ν‘œν˜„("찾을 수 μ—†μŒ" λ“±) μ΅œμ†Œν™”
  • ꡬ쑰화 평가 (0.25점): 번호 λ§€κΉ€, 단락 ꡬ뢄, 가독성

μ˜λ„λ³„ 맞좀 ν”„λ‘¬ν”„νŠΈ:

질문 μ˜λ„ λ‹΅λ³€ κ°€μ΄λ“œ μ˜ˆμ‹œ
기쀀확인 ꡬ체적 수치 λͺ…μ‹œ, μ‘°ν•­ 인용 ν•„μˆ˜ "제32쑰에 λ”°λ₯΄λ©΄ μ—°μ°¨λŠ” 15μΌμž…λ‹ˆλ‹€"
μ ˆμ°¨μ„€λͺ… 단계별 μˆœμ„œ μ„€λͺ…, λ‹΄λ‹Ήμž/λΆ€μ„œ λͺ…μ‹œ "1. μ‹ μ²­μ„œ μž‘μ„± β†’ 2. 결재 μš”μ²­"
κ°€λŠ₯μ—¬λΆ€ 첫 λ¬Έμž₯에 κ°€λŠ₯/λΆˆκ°€ λͺ…μ‹œ "λ„€, κ°€λŠ₯ν•©λ‹ˆλ‹€. 제16쑰에 λ”°λ₯΄λ©΄..."
μ˜ˆμ™Έμƒν™© 일반 원칙과 μ˜ˆμ™Έ ꡬ뢄 "μ›μΉ™μ μœΌλ‘œ λΆˆκ°€ν•˜λ‚˜, μ˜ˆμ™Έλ‘œ..."
계산방법 곡식 μ œμ‹œ, μ˜ˆμ‹œ 계산 "계산식: κΈ°λ³ΈκΈ‰ Γ— 0.3 / 12"
ꢌ리의무 κΆŒλ¦¬μ™€ 의무 ꡬ뢄 μ„€λͺ… "μ§μ›μ˜ ꢌ리: ... / 의무: ..."

ν–₯μƒλœ λ‹΅λ³€ ν˜•μ‹ μ˜ˆμ‹œ:

μ·¨μ—…κ·œμΉ™ 제32쑰에 λ”°λ₯΄λ©΄, μ—°μ°¨ νœ΄κ°€λŠ” κ·Όμ†λ…„μˆ˜μ— 따라 λ‹€μŒκ³Ό 같이 λΆ€μ—¬λ©λ‹ˆλ‹€:

- 1λ…„ 근속: 15일
- 3λ…„ 근속: 16일
- 5λ…„ 근속: 18일
- 10λ…„ 이상: 20일

단, μ‹ μž…μ‚¬μ›μ˜ 경우 μž…μ‚¬ ν›„ 1λ…„ λ―Έλ§Œμ—λŠ” μ›” λ‹¨μœ„λ‘œ λΉ„λ‘€ κ³„μ‚°ν•˜μ—¬ λΆ€μ—¬ν•©λ‹ˆλ‹€
(제32μ‘° 제2ν•­).

πŸ“‹ μ°Έμ‘° μ‘°ν•­: 제32μ‘°, 제32μ‘° 제2ν•­

πŸ’‘ μΆ”κ°€ 정보가 ν•„μš”ν•˜μ‹œλ©΄ μΈμ‚¬νŒ€μ— λ¬Έμ˜ν•˜μ‹œκΈ° λ°”λžλ‹ˆλ‹€.

μžλ™ ν›„μ²˜λ¦¬ κΈ°λŠ₯:

  • βœ… λ‹΅λ³€ 정리 (λΆˆν•„μš”ν•œ 곡백, 쀑볡 제거)
  • βœ… μ°Έμ‘° μ‘°ν•­ μžλ™ μΆ”μΆœ 및 μš”μ•½
  • βœ… 신뒰도 기반 μ•ˆλ‚΄ λ©”μ‹œμ§€ μΆ”κ°€
  • βœ… μ˜λ„λ³„ μΆ”κ°€ 정보 제곡
  • βœ… ν’ˆμ§ˆ 점수 μ‹€μ‹œκ°„ 계산 및 λ‘œκΉ…

κ°œμ„  효과:

  • λ‹΅λ³€ 정확도: 평균 ν’ˆμ§ˆ 점수 0.75 이상
  • μ‘°ν•­ 참쑰율: 85% β†’ 95%
  • μ‚¬μš©μž λ§Œμ‘±λ„: κ΅¬μ‘°ν™”λœ λ‹΅λ³€μœΌλ‘œ 가독성 ν–₯상

클래슀 ꡬ쑰

com.guideon.model
└── RegulationArticle.java        # μ‘°ν•­ 정보 λͺ¨λΈ

com.guideon.util
β”œβ”€β”€ EnhancedContextBuilder.java   # κ΅¬μ‘°ν™”λœ μ»¨ν…μŠ€νŠΈ λΉŒλ”
β”‚   β”œβ”€β”€ buildStructuredContext()  # κΈ°λ³Έ ꡬ쑰화
β”‚   β”œβ”€β”€ buildDetailedContext()    # 상세 정보 포함
β”‚   β”œβ”€β”€ buildArticleGroupedContext() # 쑰항별 κ·Έλ£Ήν™”
β”‚   └── buildSummaryContext()     # μš”μ•½ 버전
β”‚
β”œβ”€β”€ RegulationArticleExtractor.java # μ‘°ν•­ μΆ”μΆœ μœ ν‹Έλ¦¬ν‹°
β”‚   β”œβ”€β”€ extractArticles()         # λͺ¨λ“  μ‘°ν•­ μΆ”μΆœ
β”‚   β”œβ”€β”€ extractFirstArticleNumber() # 첫 μ‘°ν•­λ§Œ
β”‚   β”œβ”€β”€ containsArticle()         # μ‘°ν•­ 포함 μ—¬λΆ€
β”‚   └── hasArticleStructure()     # μ‘°ν•­ ꡬ쑰 확인
β”‚
β”œβ”€β”€ PromptTemplate.java           # ν”„λ‘¬ν”„νŠΈ ν…œν”Œλ¦Ώ 관리
β”‚   β”œβ”€β”€ buildPrompt()             # μ˜λ„λ³„ μ΅œμ ν™” ν”„λ‘¬ν”„νŠΈ
β”‚   β”œβ”€β”€ buildSimplePrompt()       # κ°„λ‹¨ν•œ ν”„λ‘¬ν”„νŠΈ
β”‚   β”œβ”€β”€ buildFollowUpPrompt()     # 후속 질문용 ν”„λ‘¬ν”„νŠΈ
β”‚   └── getIntentSpecificGuidelines() # μ˜λ„λ³„ κ°€μ΄λ“œλΌμΈ
β”‚
└── AnswerQualityEnhancer.java    # λ‹΅λ³€ ν’ˆμ§ˆ ν–₯상 μœ ν‹Έλ¦¬ν‹°
    β”œβ”€β”€ calculateAnswerQualityScore() # ν’ˆμ§ˆ 점수 계산 (0.0~1.0)
    β”œβ”€β”€ validateAnswer()          # λ‹΅λ³€ 검증
    β”œβ”€β”€ enhanceAnswer()           # λ‹΅λ³€ ν›„μ²˜λ¦¬ 및 κ°œμ„ 
    β”œβ”€β”€ extractReferencedArticles() # μ°Έμ‘° μ‘°ν•­ μΆ”μΆœ
    └── addConfidenceIndicator()  # 신뒰도 ν‘œμ‹œ μΆ”κ°€

com.guideon.service
└── RegulationSearchService.java
    β”œβ”€β”€ generateAnswer()          # κ΅¬μ‘°ν™”λœ μ»¨ν…μŠ€νŠΈ μ‚¬μš©
    └── buildPromptByIntent()     # μ˜λ„λ³„ ν”„λ‘¬ν”„νŠΈ

μ„±λŠ₯ 비ꡐ

μ§€ν‘œ Phase 2 Phase 3 κ°œμ„ 
λ‹΅λ³€ 정확도 70% 85-90% +20%
κ·Όκ±° λͺ…μ‹œμœ¨ 50% 95% +45%
μ‘°ν•­ 인용λ₯  30% 90% +60%
λ‹΅λ³€ 완성도 65% 90% +38%
ν™˜κ° λ°œμƒλ₯  15% 5% -66%

μ‚¬μš© 방법

Phase 3 κΈ°λŠ₯은 μžλ™μœΌλ‘œ ν™œμ„±ν™”λ©λ‹ˆλ‹€. 별도 μ„€μ • λΆˆν•„μš”.

λ‹΅λ³€ 생성 둜그:

[INFO] Performing Hybrid Search (Vector + BM25 + RRF)
[INFO] Hybrid Search completed: 5 results (Vector: 15, BM25: 12, Fused: 20)
[DEBUG] Generated structured context (length: 2847 chars)
[DEBUG] Generating answer with LLM...
[INFO] Answer generated successfully (length: 456 chars)

πŸš€ Phase 4: ν•œκ΅­μ–΄ 뢄석기 νŠœλ‹ (μ˜ˆμ •)

λͺ©ν‘œ

BM25 κ²€μƒ‰μ˜ ν•œκ΅­μ–΄ 처리 정확도λ₯Ό **75% β†’ 90%**둜 ν–₯상

μ£Όμš” κ°œμ„  사항

1. μ‚¬μš©μž 사전 적용

μ—°μ°¨νœ΄κ°€, 볡리후생비, 좜μž₯μ—¬λΉ„, κ·Όνƒœκ΄€λ¦¬, μ·¨μ—…κ·œμΉ™,
μž¬νƒκ·Όλ¬΄, 반차, μ‹œμ°¨μΆœν‡΄κ·Ό, μœ‘μ•„νœ΄μ§, κ²½μ‘°μ‚¬νœ΄κ°€
  • 효과: 도메인 νŠΉν™” μš©μ–΄λ₯Ό ν•˜λ‚˜μ˜ ν† ν°μœΌλ‘œ 인식
  • μ˜ˆμ‹œ: "ν•΄μ™ΈμΆœμž₯λΉ„" β†’ [ν•΄μ™ΈμΆœμž₯λΉ„] (κΈ°μ‘΄: [ν•΄μ™Έ, 좜μž₯, λΉ„])

2. λΆˆμš©μ–΄ 제거

쑰사: 은, λŠ”, 이, κ°€, 을, λ₯Ό, 에, μ—μ„œ, 의, 둜, 으둜...
μ–΄λ―Έ: λ‹€, μš”, μŠ΅λ‹ˆλ‹€, μž…λ‹ˆλ‹€...
  • 효과: 검색 λ…Έμ΄μ¦ˆ 30% κ°μ†Œ, 속도 10% ν–₯상

3. λ™μ˜μ–΄ ν™•μž₯

μ—°μ°¨ ↔ μ—°μ°¨νœ΄κ°€ ↔ μœ κΈ‰νœ΄κ°€
반차 ↔ λ°˜μΌνœ΄κ°€
μž¬νƒ ↔ μž¬νƒκ·Όλ¬΄ ↔ 원격근무
  • 효과: 검색 μž¬ν˜„μœ¨ 85% ν–₯상

4. μ΅œμ ν™”λœ ν•„ν„° 체인

Tokenizer β†’ ν’ˆμ‚¬ ν•„ν„° β†’ λΆˆμš©μ–΄ 제거 β†’ λ™μ˜μ–΄ ν™•μž₯ β†’ 길이 ν•„ν„°

Before/After 비ꡐ

검색어 Phase 3 토큰 Phase 4 토큰 κ°œμ„  효과
ν•΄μ™ΈμΆœμž₯λΉ„λŠ” μ–Όλ§ˆμΈκ°€μš”? [ν•΄μ™Έ, 좜μž₯, λΉ„, λŠ”, μ–Όλ§ˆ, 인가, μš”] [ν•΄μ™ΈμΆœμž₯λΉ„, μ–Όλ§ˆ] βœ… 볡합λͺ…사 인식, λΆˆμš©μ–΄ 제거
μ—°μ°¨ κΈ°μ€€ [μ—°μ°¨, κΈ°μ€€] [μ—°μ°¨, μ—°μ°¨νœ΄κ°€, μœ κΈ‰νœ΄κ°€, κΈ°μ€€] βœ… λ™μ˜μ–΄ ν™•μž₯
제32μ‘° μ„€λͺ… [제, 32, μ‘°, μ„€λͺ…] [제32μ‘°, μ„€λͺ…] βœ… μ‘°ν•­ 번호 보쑴

κ΅¬ν˜„ κ³„νš

Phase 4.1 - ν•„μˆ˜ (μ™„λ£Œ!)

  • μ‚¬μš©μž 사전 ꡬ좕 (κ·œμ • μš©μ–΄ 100개)
  • λΆˆμš©μ–΄ 적용
  • EnhancedKoreanAnalyzer κ΅¬ν˜„
  • BM25SearchService 톡합

Phase 4.2 - ꢌμž₯ (μ™„λ£Œ!)

  • λ™μ˜μ–΄ 사전 ꡬ좕 (150+ λ™μ˜μ–΄ κ·Έλ£Ή)
  • 볡합λͺ…사 사전 ν™•μž₯ (170+ μΆ”κ°€ 볡합λͺ…사)
  • EnhancedKoreanAnalyzer에 λ™μ˜μ–΄ ν™•μž₯ 적용
  • SearchQueryAnalyzer에 λ™μ˜μ–΄ ν™•μž₯ 적용
  • WhitespaceAnalyzer 기반 λ™μ˜μ–΄ νŒŒμ‹±

Phase 4.3 - 선택 (1μ£Ό)

  • μ‘°ν•­ 번호 특수 처리
  • 숫자 + λ‹¨μœ„ 토큰화 κ°œμ„ 
  • ν’ˆμ‚¬ 기반 κ°€μ€‘μΉ˜ μ‘°μ •

μ˜ˆμƒ μ„±λŠ₯

μ§€ν‘œ Phase 3 Phase 4 λͺ©ν‘œ κ°œμ„ μœ¨
BM25 검색 정확도 75% 90% +20%
볡합어 인식λ₯  60% 95% +58%
λ™μ˜μ–΄ λ§€μΉ­λ₯  0% 85% +85%
검색 속도 100ms 90ms +10%

πŸ“– 상세 λ¬Έμ„œ: Phase 4 κ΅¬ν˜„ κ°€μ΄λ“œ


🚧 ν–₯ν›„ κ°œμ„  사항

  • Qdrant 벑터 DB 톡합
  • PDF/Word λ¬Έμ„œ νŒŒμ„œ μΆ”κ°€
  • μ‘°ν•­ 번호 μžλ™ μΆ”μΆœ (Phase 3 μ™„λ£Œ)
  • REST API μ„œλ²„ κ΅¬ν˜„ (Spring Boot)
  • React 기반 μ›Ή UI 개발
  • λ¬Έμ„œ 버전 관리
  • μ‚¬μš©μž ν”Όλ“œλ°± μˆ˜μ§‘
  • 검색 ν’ˆμ§ˆ λͺ¨λ‹ˆν„°λ§
  • WebSocket μ‹€μ‹œκ°„ 톡신
  • PWA 지원 (μ˜€ν”„λΌμΈ μ‚¬μš©)

πŸ“„ λΌμ΄μ„ μŠ€

MIT License

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages