Improve modal fit and Gemini search refinement
build-push / docker (push) Successful in 4m8s

This commit is contained in:
AI Assistant
2026-03-17 12:30:50 +09:00
parent 0b68feff80
commit 556d4d6c1b
8 changed files with 353 additions and 77 deletions
+26 -36
View File
@@ -11,7 +11,7 @@ import (
const GeminiFallbackReason = "Gemini Vision 응답이 부족해 키워드 기준으로 보강된 결과입니다."
const FallbackPreviewReason = "Fallback due to missing provider preview."
const PendingVisualReason = "Ranked candidate pending stronger visual evidence."
const SupplementalFallbackReason = "추가 탐색 후에도 충분한 확신 후보가 부족해 시각 자산이 있는 후보를 제한적으로 보강했습니다."
type GeminiBatchStats struct {
CandidateCap int `json:"candidateCap"`
@@ -258,6 +258,7 @@ func BuildFallbackRecommendations(ranked []SearchResult, limit int, reason strin
Source: item.Source,
Reason: reason,
Recommended: false,
Assessment: "unclear",
}))
}
return fallback
@@ -370,18 +371,22 @@ func NeedsSupplementalExploration(items []AIRecommendation) bool {
recommendedCount := 0
negativeCount := 0
unclearCount := 0
for _, item := range items {
if item.Recommended {
if item.Recommended && item.Assessment == "positive" {
recommendedCount++
}
if looksNegativeReason(item.Reason) {
if IsExcludedAssessment(item.Assessment) || looksNegativeReason(item.Reason) {
negativeCount++
}
if item.Assessment == "unclear" {
unclearCount++
}
}
if recommendedCount >= 3 {
if recommendedCount >= 4 {
return false
}
return negativeCount >= max(2, len(items)/2)
return negativeCount >= max(2, len(items)/3) || unclearCount >= max(2, len(items)/2)
}
func looksNegativeReason(reason string) bool {
@@ -403,11 +408,9 @@ func looksNegativeReason(reason string) bool {
func MergeRecommendations(recommended []AIRecommendation, ranked []SearchResult, limit int) []AIRecommendation {
merged := make([]AIRecommendation, 0, min(limit, len(ranked)))
seen := map[string]bool{}
fillerCount := 0
maxFiller := min(4, limit)
for _, item := range recommended {
if !item.Recommended {
if !item.Recommended || item.Assessment != "positive" {
continue
}
if item.Link == "" || seen[item.Link] {
@@ -421,7 +424,10 @@ func MergeRecommendations(recommended []AIRecommendation, ranked []SearchResult,
if item.Recommended || item.Link == "" || seen[item.Link] || len(merged) >= limit {
continue
}
if looksNegativeReason(item.Reason) || strings.Contains(item.Reason, GeminiFallbackReason) {
if IsExcludedAssessment(item.Assessment) || looksNegativeReason(item.Reason) || strings.Contains(item.Reason, GeminiFallbackReason) {
continue
}
if item.Assessment == "unclear" {
continue
}
if strings.TrimSpace(item.PreviewVideoURL) == "" && !hasUsableThumbnail(item.ThumbnailURL) {
@@ -430,32 +436,6 @@ func MergeRecommendations(recommended []AIRecommendation, ranked []SearchResult,
seen[item.Link] = true
merged = append(merged, DecorateRecommendationMedia(item))
}
if len(merged) < min(16, limit) {
for _, item := range ranked {
if len(merged) >= min(16, limit) || item.Link == "" || seen[item.Link] {
continue
}
if fillerCount >= maxFiller {
break
}
if strings.TrimSpace(item.PreviewVideoURL) == "" && !hasUsableThumbnail(item.ThumbnailURL) {
continue
}
seen[item.Link] = true
merged = append(merged, DecorateRecommendationMedia(AIRecommendation{
Title: item.Title,
Link: item.Link,
Snippet: item.Snippet,
ThumbnailURL: item.ThumbnailURL,
PreviewVideoURL: item.PreviewVideoURL,
Source: item.Source,
Reason: PendingVisualReason,
Recommended: false,
}))
fillerCount++
}
}
return merged
}
@@ -489,14 +469,24 @@ func BackfillRecommendations(existing []AIRecommendation, ranked []SearchResult,
ThumbnailURL: item.ThumbnailURL,
PreviewVideoURL: item.PreviewVideoURL,
Source: item.Source,
Reason: firstNonEmpty(strings.TrimSpace(reason), FallbackPreviewReason),
Reason: firstNonEmpty(strings.TrimSpace(reason), SupplementalFallbackReason),
Recommended: false,
Assessment: "unclear",
}))
fillerCount++
}
return merged
}
func IsExcludedAssessment(assessment string) bool {
switch strings.ToLower(strings.TrimSpace(assessment)) {
case "irrelevant", "inappropriate":
return true
default:
return false
}
}
func max(a, b int) int {
if a > b {
return a