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