From 9e68720c705ab71eaaf864aa9373b783318b4c9d Mon Sep 17 00:00:00 2001 From: wooh Date: Tue, 8 Sep 2026 14:36:31 +0900 Subject: [PATCH] =?UTF-8?q?[Fix]=20=EB=8F=99=EC=A0=81=20Few-shot=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=EB=AA=A8=EB=93=9C=20=EB=A1=9C=EA=B9=85=20?= =?UTF-8?q?=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FewShotSelectionMode enum 추가 - 임베딩 선택, 로컬 fallback, 정적 fallback 경로 구분 - 동적 Few-shot 선택 완료 로그에 selectionMode 추가 - 선택 캐시에 최초 선택 모드를 함께 저장 - 캐시 적중 시 기존 선택 모드가 유지되도록 개선 - 빈 검색 결과와 검색 예외의 정적 fallback 로그 통일 - Cohere 임베딩 선택 경로 테스트 보강 --- .../service/ai/AnalysisPromptBuilder.java | 7 ++++-- .../fewshot/DefaultFewShotSearchService.java | 24 +++++++++++++++---- .../ai/fewshot/FewShotSelectionMode.java | 7 ++++++ .../DefaultFewShotSearchServiceTest.java | 1 + 4 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/FewShotSelectionMode.java diff --git a/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/AnalysisPromptBuilder.java b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/AnalysisPromptBuilder.java index ce3e740a..78fe8056 100644 --- a/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/AnalysisPromptBuilder.java +++ b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/AnalysisPromptBuilder.java @@ -8,6 +8,7 @@ import com.jobdri.jobdri_api.domain.analysis.service.ai.fewshot.FewShotProperties; import com.jobdri.jobdri_api.domain.analysis.service.ai.fewshot.FewShotSearchQuery; import com.jobdri.jobdri_api.domain.analysis.service.ai.fewshot.FewShotSearchService; +import com.jobdri.jobdri_api.domain.analysis.service.ai.fewshot.FewShotSelectionMode; import com.jobdri.jobdri_api.domain.analysis.service.ai.fewshot.SelectedFewShotCase; import com.jobdri.jobdri_api.domain.corpus.service.CorpusRetrievalService.RetrievalContext; import com.jobdri.jobdri_api.domain.corpus.service.CorpusRetrievalService.RetrievedJobPostingReference; @@ -615,7 +616,8 @@ private String resolveFewShotPromptBlock(AnalysisPromptInput promptInput) { ); if (selectedFewShots.isEmpty()) { log.warn( - "dynamic few-shot selection returned empty result. fallback=fixed, caseId={}, datasetVersion={}", + "dynamic few-shot selection returned empty result. selectionMode={}, caseId={}, datasetVersion={}", + FewShotSelectionMode.STATIC_FALLBACK, promptInput.caseId(), fewShotProperties.getDatasetVersion() ); @@ -632,7 +634,8 @@ private String resolveFewShotPromptBlock(AnalysisPromptInput promptInput) { return fewShotPromptProvider.buildPromptBlock(selectedFewShots); } catch (Exception e) { log.warn( - "dynamic few-shot selection failed. fallback=fixed, caseId={}, datasetVersion={}, reason={}, message={}", + "dynamic few-shot selection failed. selectionMode={}, caseId={}, datasetVersion={}, reason={}, message={}", + FewShotSelectionMode.STATIC_FALLBACK, promptInput.caseId(), fewShotProperties.getDatasetVersion(), e.getClass().getSimpleName(), diff --git a/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/DefaultFewShotSearchService.java b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/DefaultFewShotSearchService.java index b5daefb5..b303a702 100644 --- a/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/DefaultFewShotSearchService.java +++ b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/DefaultFewShotSearchService.java @@ -61,21 +61,33 @@ public List searchRelevantFewShots(FewShotSearchQuery query String cacheKey = selectionCacheKey(query, requestedTopK, datasetFingerprint); SelectionCacheEntry cached = readSelectionCache(cacheKey); if (cached != null) { - log.debug("few-shot selection cache hit. selectedCount={}, datasetVersion={}", cached.selectedCases().size(), properties.getDatasetVersion()); + log.debug( + "few-shot selection cache hit. selectionMode={}, selectedCount={}, datasetVersion={}", + cached.selectionMode(), + cached.selectedCases().size(), + properties.getDatasetVersion() + ); return cached.selectedCases(); } long startedAt = System.nanoTime(); List candidates = localPrefilter(activeCases, query); List selected = selectWithCohere(query, candidates, requestedTopK); + FewShotSelectionMode selectionMode = selected.isEmpty() + ? FewShotSelectionMode.STATIC_FALLBACK + : FewShotSelectionMode.EMBEDDING; if (selected.isEmpty() && properties.isFallbackEnabled()) { selected = selectLocally(query, candidates, requestedTopK, "local-fallback"); + if (!selected.isEmpty()) { + selectionMode = FewShotSelectionMode.LOCAL_FALLBACK; + } } if (properties.isCacheEnabled()) { - selectionCache.put(cacheKey, new SelectionCacheEntry(selected, expiresAt())); + selectionCache.put(cacheKey, new SelectionCacheEntry(selected, selectionMode, expiresAt())); } log.info( - "dynamic few-shot selection completed. enabled=true, totalCandidates={}, filteredCandidates={}, selectedIds={}, sources={}, scores={}, latencyMs={}", + "dynamic few-shot selection completed. enabled=true, selectionMode={}, totalCandidates={}, filteredCandidates={}, selectedIds={}, sources={}, scores={}, latencyMs={}", + selectionMode, activeCases.size(), candidates.size(), selected.stream().map(item -> item.fewShotCase().id()).toList(), @@ -447,7 +459,11 @@ private record PendingDocumentEmbedding( ) { } - private record SelectionCacheEntry(List selectedCases, Instant expiresAt) { + private record SelectionCacheEntry( + List selectedCases, + FewShotSelectionMode selectionMode, + Instant expiresAt + ) { private SelectionCacheEntry { selectedCases = selectedCases == null ? List.of() : List.copyOf(selectedCases); } diff --git a/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/FewShotSelectionMode.java b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/FewShotSelectionMode.java new file mode 100644 index 00000000..64c90450 --- /dev/null +++ b/src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/FewShotSelectionMode.java @@ -0,0 +1,7 @@ +package com.jobdri.jobdri_api.domain.analysis.service.ai.fewshot; + +public enum FewShotSelectionMode { + EMBEDDING, + LOCAL_FALLBACK, + STATIC_FALLBACK +} diff --git a/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/DefaultFewShotSearchServiceTest.java b/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/DefaultFewShotSearchServiceTest.java index d6972f45..56759859 100644 --- a/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/DefaultFewShotSearchServiceTest.java +++ b/src/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/fewshot/DefaultFewShotSearchServiceTest.java @@ -61,6 +61,7 @@ void excludesSelfReferenceCandidate() { assertThat(result).extracting(item -> item.fewShotCase().id()) .containsExactly("EV-02"); + assertThat(result.getFirst().selectionMethod()).isEqualTo("cohere-embedding"); } @Test