Explorar el Código

视频增加表现--code-review

luojunhui hace 5 días
padre
commit
8d75063cb5

+ 10 - 24
core/src/main/java/com/tzld/videoVector/service/rank/RankServiceImpl.java

@@ -91,7 +91,7 @@ public class RankServiceImpl implements RankService {
                                              Modality modality, RankingParams params) {
         Double rov = signals.getRov();
 
-        // 按维度独立 boost:优先取 boostsByCode[configCode],回退 deconstructBoost
+        // boost 仅作用于相关性分(解构维度权重),ROV 是视频粒度不加 boost
         Double codeBoost = params.getBoostsByCode() != null
                 ? params.getBoostsByCode().getOrDefault(signals.getProvenance() != null
                     ? signals.getProvenance().getConfigCode() : null, params.getDeconstructBoost())
@@ -99,17 +99,20 @@ public class RankServiceImpl implements RankService {
         if (codeBoost == null) codeBoost = params.getDeconstructBoost();
 
         boolean hasRov = rov != null && Double.isFinite(rov);
-        double boost = (modality == Modality.VIDEO && hasRov) ? codeBoost : 1.0;
 
         if (!hasRov) {
-            double composite = boost * simNorm;
-            return ScoreBreakdown.of(composite, simNorm, 0, boost, lowerBound, passesThreshold);
+            if (modality == Modality.VIDEO) {
+                double composite = codeBoost * params.getAlpha() * simNorm;
+                return ScoreBreakdown.of(composite, simNorm, 0, codeBoost, lowerBound, passesThreshold);
+            }
+            double composite = simNorm;
+            return ScoreBreakdown.of(composite, simNorm, 0, 1, lowerBound, passesThreshold);
         }
 
         double rovDenom = params.getRovClipHigh() - params.getRovClipLow();
         double rovNorm = rovDenom > 0 ? clip01((rov - params.getRovClipLow()) / rovDenom) : 0;
-        double composite = boost * (params.getAlpha() * simNorm + (1 - params.getAlpha()) * rovNorm);
-        return ScoreBreakdown.of(composite, simNorm, rovNorm, boost, lowerBound, passesThreshold);
+        double composite = params.getAlpha() * codeBoost * simNorm + (1 - params.getAlpha()) * rovNorm;
+        return ScoreBreakdown.of(composite, simNorm, rovNorm, codeBoost, lowerBound, passesThreshold);
     }
 
     private double effectiveSimThreshold(String configCode, RankingParams params) {
@@ -151,27 +154,10 @@ public class RankServiceImpl implements RankService {
 
         double maxSim = sims.stream().max(Double::compare).orElse(0.0);
 
-        // 自适应 ROV 分位(从候选集估计 P5/P95,样本需足够大才有统计意义)
         double rovLow = baseParams.getRovClipLow();
         double rovHigh = baseParams.getRovClipHigh();
-        if (rovs.size() >= 30) {
-            rovs.sort(Double::compare);
-            int p5Idx = Math.max(0, (int) (rovs.size() * 0.05));
-            int p95Idx = Math.min(rovs.size() - 1, (int) (rovs.size() * 0.95));
-            rovLow = rovs.get(p5Idx);
-            rovHigh = rovs.get(p95Idx);
-            // 防止上下界过于接近
-            if (rovHigh - rovLow < 0.001) {
-                rovHigh = rovLow + 0.01;
-            }
-        }
 
-        // 自适应 sim 下界:max(绝对兜底, 相对阈值 max_sim - delta)
-        double absFloor = baseParams.getSimThreshold();
-        double relFloor = maxSim - 0.15;
-        double simFloor = Math.max(absFloor, relFloor);
-        // 安全钳:不高于 0.9,不低于 0.4
-        simFloor = Math.max(0.4, Math.min(0.9, simFloor));
+        double simFloor = baseParams.getSimThreshold();
 
         // 构建自适应参数
         RankingParams adaptiveParams = new RankingParams();

+ 55 - 1
core/src/main/java/com/tzld/videoVector/service/recall/impl/VectorRecallTestServiceImpl.java

@@ -449,8 +449,11 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
 
         // 视频/文章按 sim 排序截断
         List<VideoMatchResult> topVideo = dedupedVideo.stream()
+                .sorted(Comparator.comparingDouble((VideoMatchResult m) ->
+                        m.getScore() != null ? -m.getScore() : 0.0))
                 .limit(enrichK).collect(Collectors.toList());
         List<ArticleMatch> topArticle = dedupedArticle.stream()
+                .sorted(Comparator.comparingDouble((ArticleMatch m) -> -m.getScore()))
                 .limit(enrichK).collect(Collectors.toList());
 
         // enrich
@@ -807,6 +810,9 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
                 ? Collections.emptyMap()
                 : videoApiService.getVideoDetail(videoIds);
 
+        // 从 Redis 加载视频详情(含 ROV 等指标数据)用于精排打分
+        Map<Long, Map<String, Object>> redisDetails = loadRedisVideoDetails(matches);
+
         List<VideoMatchEnrichedVO> items = new ArrayList<>(matches.size());
         for (VideoMatchResult m : matches) {
             if (m == null || m.getVideoId() == null) continue;
@@ -824,7 +830,18 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
                 vo.setCover(vd.getCover());
             }
 
-            vo.setVideoDetail(m.getVideoDetail());
+            // 优先使用 Redis 视频详情(含 ROV),回退 match 自带详情
+            Map<String, Object> detail = redisDetails.get(m.getVideoId());
+            if (detail != null) {
+                // 如果 match 自带 deconstruct 子对象,补充到 Redis 详情中(不覆盖 ROV 等字段)
+                Map<String, Object> matchDetail = m.getVideoDetail();
+                if (matchDetail != null && matchDetail.containsKey("deconstruct")) {
+                    detail.put("deconstruct", matchDetail.get("deconstruct"));
+                }
+                vo.setVideoDetail(detail);
+            } else {
+                vo.setVideoDetail(m.getVideoDetail());
+            }
             applyCompatibilityFields(vo);
             applySignals(vo, requestConfigCode, "ann");
             items.add(vo);
@@ -832,6 +849,43 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
         return items;
     }
 
+    /**
+     * 从 Redis 批量加载视频详情(key: video:detail:{metricsDays}d:{videoId})
+     * 用于提取 ROV 等指标数据做精排打分。
+     */
+    private Map<Long, Map<String, Object>> loadRedisVideoDetails(List<VideoMatchResult> matches) {
+        Map<Long, Map<String, Object>> result = new HashMap<>();
+        if (CollectionUtils.isEmpty(matches)) return result;
+
+        List<Long> orderedIds = new ArrayList<>();
+        List<String> keys = new ArrayList<>();
+        for (VideoMatchResult m : matches) {
+            if (m != null && m.getVideoId() != null) {
+                orderedIds.add(m.getVideoId());
+                keys.add(VectorConstants.VIDEO_DETAIL_DAYS_KEY_PREFIX + metricsDays + "d:" + m.getVideoId());
+            }
+        }
+        if (keys.isEmpty()) return result;
+
+        try {
+            List<String> values = redisUtils.mGet(keys);
+            if (values == null) return result;
+            for (int i = 0; i < orderedIds.size() && i < values.size(); i++) {
+                String detailJson = values.get(i);
+                if (StringUtils.hasText(detailJson)) {
+                    try {
+                        result.put(orderedIds.get(i), JSONObject.parseObject(detailJson, Map.class));
+                    } catch (Exception e) {
+                        log.error("解析视频Redis详情失败 videoId={}: {}", orderedIds.get(i), e.getMessage());
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.error("批量加载视频Redis详情失败: {}", e.getMessage());
+        }
+        return result;
+    }
+
     /**
      * 素材召回结果 enrich
      * 数据源:material_deconstruct_result.result(dataContent JSON)+ source_type