Refine result modal and Gemini recovery
build-push / docker (push) Successful in 4m16s

This commit is contained in:
AI Assistant
2026-03-16 12:22:13 +09:00
parent 975bd99610
commit 82cead950e
5 changed files with 195 additions and 68 deletions
+92 -7
View File
@@ -12,13 +12,14 @@ import (
const GeminiFallbackReason = "Gemini Vision 응답이 부족해 키워드 기준으로 보강된 결과입니다."
type GeminiBatchStats struct {
CandidateCap int `json:"candidateCap"`
Requested int `json:"requested"`
Batches int `json:"batches"`
Succeeded int `json:"succeeded"`
Failed int `json:"failed"`
RecommendedCount int `json:"recommendedCount"`
Errors []string `json:"errors,omitempty"`
CandidateCap int `json:"candidateCap"`
Requested int `json:"requested"`
Batches int `json:"batches"`
Succeeded int `json:"succeeded"`
Failed int `json:"failed"`
SequentialRetried int `json:"sequentialRetried"`
RecommendedCount int `json:"recommendedCount"`
Errors []string `json:"errors,omitempty"`
}
func RankSearchResults(query string, results []SearchResult) []SearchResult {
@@ -142,6 +143,27 @@ func EvaluateAllCandidatesWithGemini(service *GeminiService, query string, ranke
seen := map[string]bool{}
for _, batch := range results {
if batch.err != nil {
recovered, recoveredErrs := recoverGeminiBatchSequentially(service, query, ranked, batch.index*chunkSize)
if len(recovered) > 0 {
stats.SequentialRetried++
stats.Succeeded++
for _, item := range recovered {
if item.Link == "" || seen[item.Link] {
continue
}
seen[item.Link] = true
merged = append(merged, item)
}
if len(recoveredErrs) > 0 {
stats.Failed++
for _, recoveredErr := range recoveredErrs {
if len(stats.Errors) < 5 {
stats.Errors = append(stats.Errors, recoveredErr)
}
}
}
continue
}
stats.Failed++
if len(stats.Errors) < 5 {
stats.Errors = append(stats.Errors, batch.err.Error())
@@ -210,6 +232,62 @@ func RandomizeTopRecommendations(items []AIRecommendation, window int) []AIRecom
return shuffled
}
func recoverGeminiBatchSequentially(service *GeminiService, query string, ranked []SearchResult, startIndex int) ([]AIRecommendation, []string) {
recovered := make([]AIRecommendation, 0, 8)
errs := make([]string, 0, 4)
endIndex := min(startIndex+8, len(ranked))
for idx := startIndex; idx < endIndex; idx++ {
recs, err := service.Recommend(query, []SearchResult{ranked[idx]})
if err != nil {
if len(errs) < 4 {
errs = append(errs, err.Error())
}
time.Sleep(350 * time.Millisecond)
continue
}
recovered = append(recovered, recs...)
time.Sleep(350 * time.Millisecond)
}
return recovered, errs
}
func NeedsSupplementalExploration(items []AIRecommendation) bool {
if len(items) == 0 {
return true
}
recommendedCount := 0
negativeCount := 0
for _, item := range items {
if item.Recommended {
recommendedCount++
}
if looksNegativeReason(item.Reason) {
negativeCount++
}
}
if recommendedCount >= 3 {
return false
}
return negativeCount >= max(2, len(items)/2)
}
func looksNegativeReason(reason string) bool {
lower := strings.ToLower(strings.TrimSpace(reason))
if lower == "" {
return false
}
for _, token := range []string{
"부적합", "관련이 없", "맞지 않", "의도와 맞지", "무관", "연관성 낮", "적절하지 않", "불일치",
"not relevant", "irrelevant", "mismatch", "does not match", "unsuitable",
} {
if strings.Contains(lower, token) {
return true
}
}
return false
}
func MergeRecommendations(recommended []AIRecommendation, ranked []SearchResult, limit int) []AIRecommendation {
merged := make([]AIRecommendation, 0, min(limit, len(ranked)))
seen := map[string]bool{}
@@ -251,3 +329,10 @@ func MergeRecommendations(recommended []AIRecommendation, ranked []SearchResult,
}
return merged
}
func max(a, b int) int {
if a > b {
return a
}
return b
}