실무형 패키지 구조와 핵심 코드 설계

1. 전체 패키지 구조

com.lemuel.ai

├── config
│ ├── AiConfig.java
│ └── VectorStoreConfig.java

├── rag
│ ├── controller
│ │ └── RagController.java
│ │
│ ├── service
│ │ ├── RagService.java
│ │ ├── QueryAnalyzer.java
│ │ ├── RetrievalOrchestrator.java
│ │ └── AnswerGenerator.java
│ │
│ ├── domain
│ │ ├── QueryIntent.java
│ │ └── RagContext.java
│ │
│ ├── embedding
│ │ └── EmbeddingService.java
│ │
│ ├── retrieval
│ │ └── VectorSearchService.java
│ │
│ ├── prompt
│ │ └── PromptBuilder.java
│ │
│ └── guard
│ └── AnswerGuard.java
 

👉 핵심 포인트

  • “LLM 호출 클래스” 하나로 끝내지 않음
  • 의도 분석 / 검색 / 프롬프트 / 가드레일 분리

2. 핵심 흐름 (Service 레벨)

 
@Service
@RequiredArgsConstructor
public class RagService {

private final QueryAnalyzer queryAnalyzer;
private final RetrievalOrchestrator retrievalOrchestrator;
private final AnswerGenerator answerGenerator;
private final AnswerGuard answerGuard;

public String ask(String question) {

// 1. 의도 분석
QueryIntent intent = queryAnalyzer.analyze(question);

// 2. 컨텍스트 생성
RagContext context = retrievalOrchestrator.retrieve(question, intent);

// 3. 답변 생성
String answer = answerGenerator.generate(question, context);

// 4. 가드레일 적용
return answerGuard.validate(answer, context);
}
}
 

👉 이 구조 하나로
**“아키텍처 설계 가능한 개발자”**로 보입니다


3. QueryIntent (질의 분류)

 
public enum QueryIntent {
ORDER,
REFUND,
PRODUCT,
DELIVERY,
FAQ,
UNKNOWN
}
 

4. QueryAnalyzer (의도 분석)

 
@Component
public class QueryAnalyzer {

public QueryIntent analyze(String query) {

if (query.contains("환불")) return QueryIntent.REFUND;
if (query.contains("주문")) return QueryIntent.ORDER;
if (query.contains("배송")) return QueryIntent.DELIVERY;

return QueryIntent.UNKNOWN;
}
}
 

👉 실제 실무에서는

  • LLM 기반 분류 or
  • 규칙 + ML 혼합 구조로 확장 가능

5. RetrievalOrchestrator (핵심 로직)

 
@Service
@RequiredArgsConstructor
public class RetrievalOrchestrator {

private final VectorSearchService vectorSearchService;
private final OrderService orderService;

public RagContext retrieve(String query, QueryIntent intent) {

List<String> documents = new ArrayList<>();

switch (intent) {
case REFUND:
documents.addAll(orderService.findRefundData(query));
documents.addAll(vectorSearchService.search(query));
break;

case PRODUCT:
documents.addAll(vectorSearchService.search(query));
break;

default:
documents.addAll(vectorSearchService.search(query));
}

return new RagContext(documents);
}
}
 

👉 핵심 포인트

  • “무조건 벡터검색” X
  • 정형 + 비정형 데이터 혼합

6. VectorSearchService

 
@Service
@RequiredArgsConstructor
public class VectorSearchService {

private final VectorStore vectorStore;

public List<String> search(String query) {

return vectorStore.similaritySearch(query)
.stream()
.map(Document::getContent)
.toList();
}
}
 

👉 pgvector / Qdrant / OpenSearch 다 여기로 추상화


7. PromptBuilder

 
@Component
public class PromptBuilder {

public String build(String question, List<String> docs) {

return """
당신은 전문 상담 AI입니다.
아래 문서를 기반으로만 답변하세요.

질문:
%s

문서:
%s

규칙:
- 모르면 모른다고 말할 것
- 추측하지 말 것
""".formatted(question, String.join("\n", docs));
}
}
 

8. AnswerGenerator (Spring AI 핵심)

 
@Service
@RequiredArgsConstructor
public class AnswerGenerator {

private final ChatClient chatClient;
private final PromptBuilder promptBuilder;

public String generate(String question, RagContext context) {

String prompt = promptBuilder.build(question, context.getDocuments());

return chatClient.prompt()
.user(prompt)
.call()
.content();
}
}
 

9. AnswerGuard (가드레일)

 
@Component
public class AnswerGuard {

public String validate(String answer, RagContext context) {

if (answer.contains("추측")) {
return "정확한 정보를 찾을 수 없습니다.";
}

return answer;
}
}
 

👉 실제 확장:

  • 금지어 필터
  • PII 마스킹
  • hallucination 검증

10. Controller

 
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/rag")
public class RagController {

private final RagService ragService;

@PostMapping("/ask")
public String ask(@RequestBody String question) {
return ragService.ask(question);
}
}
 

11. 실무 확장 포인트 

1️⃣ 캐싱

@Cacheable("rag-response")

public String ask(String question)

 

2️⃣ 비동기 처리

@Async
public CompletableFuture<List<String>> searchAsync(...)
 

3️⃣ Observability

  • /actuator/prometheus
  • RAG latency metrics
  • vector search hit rate

4️⃣ Batch Embedding

@Scheduled(cron = "0 0 * * * *")
public void reindex() {
embeddingService.rebuild();
}
 

5️⃣ Agent 확장

Tool: getOrderStatus()
Tool: calculateRefund()
Tool: fetchDeliveryInfo()
 

👉 단순 QA → Action 가능한 AI

 

LIST

+ Recent posts