This commit is contained in:
+51
-18
@@ -3,6 +3,7 @@ package services
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type GeminiBatchStats struct {
|
||||
@@ -80,42 +81,63 @@ func RankSearchResults(query string, results []SearchResult) []SearchResult {
|
||||
}
|
||||
|
||||
func GeminiCandidateLimit(total int) int {
|
||||
switch {
|
||||
case total <= 12:
|
||||
return total
|
||||
case total <= 16:
|
||||
return 12
|
||||
default:
|
||||
return 16
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func EvaluateAllCandidatesWithGemini(service *GeminiService, query string, ranked []SearchResult) ([]AIRecommendation, GeminiBatchStats) {
|
||||
const chunkSize = 8
|
||||
const maxConcurrentBatches = 2
|
||||
limit := GeminiCandidateLimit(len(ranked))
|
||||
stats := GeminiBatchStats{
|
||||
CandidateCap: limit,
|
||||
Requested: min(limit, len(ranked)),
|
||||
}
|
||||
merged := make([]AIRecommendation, 0, len(ranked))
|
||||
seen := map[string]bool{}
|
||||
type batchResult struct {
|
||||
index int
|
||||
recommendations []AIRecommendation
|
||||
err error
|
||||
}
|
||||
batches := make([][]SearchResult, 0, (limit+chunkSize-1)/chunkSize)
|
||||
for start := 0; start < limit; start += chunkSize {
|
||||
end := start + chunkSize
|
||||
if end > limit {
|
||||
end = limit
|
||||
}
|
||||
batch := ranked[start:end]
|
||||
stats.Batches++
|
||||
recommended, err := service.Recommend(query, batch)
|
||||
if err != nil {
|
||||
batches = append(batches, ranked[start:end])
|
||||
}
|
||||
stats.Batches = len(batches)
|
||||
|
||||
results := make([]batchResult, len(batches))
|
||||
var wg sync.WaitGroup
|
||||
sem := make(chan struct{}, maxConcurrentBatches)
|
||||
for idx, batch := range batches {
|
||||
wg.Add(1)
|
||||
go func(batchIndex int, candidates []SearchResult) {
|
||||
defer wg.Done()
|
||||
sem <- struct{}{}
|
||||
defer func() { <-sem }()
|
||||
recommended, err := service.Recommend(query, candidates)
|
||||
results[batchIndex] = batchResult{
|
||||
index: batchIndex,
|
||||
recommendations: recommended,
|
||||
err: err,
|
||||
}
|
||||
}(idx, batch)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
merged := make([]AIRecommendation, 0, len(ranked))
|
||||
seen := map[string]bool{}
|
||||
for _, batch := range results {
|
||||
if batch.err != nil {
|
||||
stats.Failed++
|
||||
if len(stats.Errors) < 5 {
|
||||
stats.Errors = append(stats.Errors, err.Error())
|
||||
stats.Errors = append(stats.Errors, batch.err.Error())
|
||||
}
|
||||
continue
|
||||
}
|
||||
stats.Succeeded++
|
||||
for _, item := range recommended {
|
||||
for _, item := range batch.recommendations {
|
||||
if item.Link == "" || seen[item.Link] {
|
||||
continue
|
||||
}
|
||||
@@ -132,6 +154,9 @@ func MergeRecommendations(recommended []AIRecommendation, ranked []SearchResult,
|
||||
seen := map[string]bool{}
|
||||
|
||||
for _, item := range recommended {
|
||||
if !item.Recommended {
|
||||
continue
|
||||
}
|
||||
if item.Link == "" || seen[item.Link] {
|
||||
continue
|
||||
}
|
||||
@@ -139,6 +164,14 @@ func MergeRecommendations(recommended []AIRecommendation, ranked []SearchResult,
|
||||
merged = append(merged, item)
|
||||
}
|
||||
|
||||
for _, item := range recommended {
|
||||
if item.Recommended || item.Link == "" || seen[item.Link] || len(merged) >= limit {
|
||||
continue
|
||||
}
|
||||
seen[item.Link] = true
|
||||
merged = append(merged, item)
|
||||
}
|
||||
|
||||
for _, item := range ranked {
|
||||
if len(merged) >= limit || item.Link == "" || seen[item.Link] {
|
||||
continue
|
||||
@@ -151,8 +184,8 @@ func MergeRecommendations(recommended []AIRecommendation, ranked []SearchResult,
|
||||
ThumbnailURL: item.ThumbnailURL,
|
||||
PreviewVideoURL: item.PreviewVideoURL,
|
||||
Source: item.Source,
|
||||
Reason: "Keyword-ranked result added without extra Gemini vision tokens.",
|
||||
Recommended: true,
|
||||
Reason: "Gemini Vision 응답이 부족해 키워드 기준으로 보강된 결과입니다.",
|
||||
Recommended: false,
|
||||
})
|
||||
}
|
||||
return merged
|
||||
|
||||
Reference in New Issue
Block a user