Conversation
Feat/#61 slot generator
- 취소/노쇼 예약 내역 조회 AI Tool 추가 - 주변 인기 맛집 추천 AI Tool 추가 (반경 3km, 인기순 정렬) - 예약 가능 시간대 조회 AI Tool 추가 - 챗봇 요청에 위치 좌표(latitude/longitude) 필드 추가 - 쿠폰 목록 API Cache-Control: no-store 추가 - 챗봇 대화 내역 조회 시 세션 없으면 빈 목록 반환으로 수정 - SecurityConfig ASYNC DispatcherType 설정 제거 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- N+1 쿼리 제거: PENDING orderId 조회 IN 쿼리 1회로 교체 (PaymentRepository) - DB 레벨 필터링: findAllByUserAndStatusIn 추가로 메모리 필터 제거 (ReservationRepository) - DB 레벨 매장 조회: findByStoreNameIgnoreCaseAndIsDeletedFalse 추가 (StoreRepository) - ThreadLocal 누수 방지: PendingPaymentHolder.clear() try-finally로 보장 (ChatbotService) - 챗봇 시스템 프롬프트 가드레일 6항목 추가 (역할고정, 범위제한, 프롬프트보호, 타인정보보호, 역할극거부, 개인정보수집금지) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Refactor/#66 chatbot api
- build.gradle: resilience4j-spring-boot3 의존성 추가 - application.yaml: ai-api 서킷브레이커 설정 추가 (10회 슬라이딩 윈도우, 실패율 50% OPEN, 30초 대기, HALF-OPEN 3회 테스트) - ChatbotService: callAi() 내부에 CircuitBreaker.executeSupplier() 프로그래매틱 적용 - ErrorCode: CHAT_AI_CIRCUIT_OPEN(503) 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: AI API 서킷브레이커 적용 (Resilience4j)
There was a problem hiding this comment.
Code Review
This pull request enhances the chatbot system by integrating Resilience4j for circuit breaking, implementing conversation history summarization, and adding several AI-driven tools for store searching, reservation management, and location-based recommendations. It also introduces a ThreadLocal holder for pending payment information and optimizes reservation list retrieval with batch queries. Feedback focuses on improving system stability by applying the circuit breaker to the summarization logic and ensuring timezone consistency in the system prompt. Additionally, it is recommended to include store IDs in AI tool responses to support the link format requested in the prompt and to refactor duplicated orderId generation logic into a shared method.
| try { | ||
| return chatClient.prompt() | ||
| .messages(messages) | ||
| .tools(reservationService, couponService) | ||
| .toolContext(Map.of("userId", userId)) | ||
| String summary = chatClient.prompt() | ||
| .user("다음 대화를 핵심 정보(예약 정보, 사용자 선호 등)만 3문장 이내로 요약해줘:\n" + historyText) | ||
| .call() | ||
| .content(); | ||
|
|
||
| return "\n\n[이전 대화 요약]: " + summary; |
There was a problem hiding this comment.
buildSummarySuffix 메서드에서 AI를 호출할 때 서킷 브레이커가 적용되지 않았습니다. AI 서비스에 장애가 발생하거나 응답이 느려질 경우, 메인 로직인 callAi에 도달하기 전에 이 부분에서 요청이 지연되거나 실패할 수 있습니다. 요약 기능에도 aiCircuitBreaker를 적용하여 시스템의 안정성을 높이는 것이 좋습니다.
try {
return aiCircuitBreaker.executeSupplier(() -> {
String summary = chatClient.prompt()
.user("다음 대화를 핵심 정보(예약 정보, 사용자 선호 등)만 3문장 이내로 요약해줘:\n" + historyText)
.call()
.content();
return "\n\n[이전 대화 요약]: " + summary;
});
} catch (CallNotPermittedException e) {
return "";
} catch (Exception e) {|
|
||
| private String buildSystemPrompt(String summarySuffix) { | ||
| return "너는 'CatchEat(캐치잇)'이라는 레스토랑 예약 플랫폼의 AI 비서야. " | ||
| + "오늘 날짜는 " + java.time.LocalDate.now() + "이야. " |
There was a problem hiding this comment.
| return reservations.stream().map(r -> { | ||
| StoreRemain sr = r.getStoreRemain(); | ||
| return String.format("예약번호 %d: %s %s %s, %d명, 상태: %s", | ||
| r.getId(), | ||
| sr.getStore().getStoreName(), | ||
| sr.getRemainDate(), | ||
| sr.getRemainTime(), | ||
| r.getMember(), | ||
| r.getStatus() == ReservationStatus.PENDING ? "결제 대기" : "확정"); | ||
| }).collect(Collectors.joining("\n")); |
There was a problem hiding this comment.
시스템 프롬프트(ChatbotService)에서는 매장 이름을 링크 형식([매장이름](/stores/{storeId}))으로 작성하도록 지시하고 있지만, getMyReservationsForAi 도구의 응답에는 storeId 정보가 포함되어 있지 않습니다. AI가 정확한 링크를 생성할 수 있도록 응답 문자열에 매장 ID 정보를 포함하는 것이 좋습니다. getCanceledReservationsForAi 메서드도 동일한 개선이 필요해 보입니다.
return reservations.stream().map(r -> {
StoreRemain sr = r.getStoreRemain();
return String.format("예약번호 %d: [%s](/stores/%d) %s %s, %d명, 상태: %s",
r.getId(),
sr.getStore().getStoreName(),
sr.getStore().getId(),
sr.getRemainDate(),
sr.getRemainTime(),
r.getMember(),
r.getStatus() == ReservationStatus.PENDING ? "결제 대기" : "확정");
}).collect(Collectors.joining("\n"));| storeRemain.getRemainTime().toString() | ||
| )); | ||
| // Payment 레코드 생성 (결제창 호출을 위해 orderId 필요) | ||
| String orderId = "CATCH-" + saved.getId() + "-" + System.currentTimeMillis(); |
📢 기능 설명
필요시 실행결과 스크린샷 첨부
연결된 issue
연결된 issue를 자동을 닫기 위해 아래 {이슈넘버}를 입력해주세요.
close #{이슈넘버}
✅ 체크리스트