#359 扩展1层回流str+&召回

Merged
jiachanghui merged 2 commits from algorithm/feature/1st_return into algorithm/master 1 week ago
12 changed files with 393 additions and 133 deletions
  1. 34 7
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/FeatureService.java
  2. 9 2
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/RecommendService.java
  3. 1 0
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/RankParam.java
  4. 6 1
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/bo/UserShareReturnProfile.java
  5. 31 40
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/strategy/RankStrategy4RegionMergeModelV567.java
  6. 30 5
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/tansform/FeatureV6.java
  7. 1 0
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/RecallParam.java
  8. 4 0
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/RecallService.java
  9. 112 0
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/strategy/AbstractRedisRecallStrategy.java
  10. 53 0
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/strategy/Return1Cate2RosRecallStrategy.java
  11. 53 0
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/strategy/Return1Cate2StrRecallStrategy.java
  12. 59 78
      recommend-server-service/src/main/resources/feeds_score_config_fm_xgb_20250729.conf

+ 34 - 7
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/FeatureService.java

@@ -7,6 +7,7 @@ import com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto;
 import com.tzld.piaoquan.recommend.server.model.MachineInfo;
 import com.tzld.piaoquan.recommend.server.remote.FeatureV2RemoteService;
 import com.tzld.piaoquan.recommend.server.service.rank.RankParam;
+import com.tzld.piaoquan.recommend.server.service.rank.bo.UserSRBO;
 import com.tzld.piaoquan.recommend.server.service.rank.bo.UserShareReturnProfile;
 import com.tzld.piaoquan.recommend.server.util.FeatureUtils;
 import com.tzld.piaoquan.recommend.server.util.JSONUtils;
@@ -295,17 +296,43 @@ public class FeatureService {
         return null;
     }
 
-    public Map<String, String> getUserInfo(String table, String id) {
+    public Map<String, Map<String, String>> getBehaviorVideos(String headVid, UserShareReturnProfile userProfile) {
+        Map<String, Map<String, String>> behaviorVideos = new HashMap<>();
         try {
-            Feature feature = getFeatureByProto(Collections.singletonList(genWithMid(table, id)));
-            Map<String, Map<String, String>> userFeature = feature.getUserFeature();
-            if (null != userFeature) {
-                return userFeature.get(table);
+            Set<String> vidSet = new HashSet<>();
+            if (null != headVid && !headVid.isEmpty()) {
+                vidSet.add(headVid);
+            }
+            if (null != userProfile) {
+                for (List<UserSRBO> list : Arrays.asList(userProfile.getM_s_s(), userProfile.getM_r_s(), userProfile.getL_s_s(), userProfile.getL_r_s(), userProfile.getL_r1_s())) {
+                    if (null != list) {
+                        for (UserSRBO u : list) {
+                            if (null != u) {
+                                vidSet.add(u.getId() + "");
+                            }
+                        }
+                    }
+                }
+            }
+            if (!vidSet.isEmpty()) {
+                Map<String, Map<String, Map<String, String>>> videoMap = getVideoBaseInfo("", new ArrayList<>(vidSet));
+                if (null != videoMap && !videoMap.isEmpty()) {
+                    for (Map.Entry<String, Map<String, Map<String, String>>> entry : videoMap.entrySet()) {
+                        String vid = entry.getKey();
+                        Map<String, Map<String, String>> map = entry.getValue();
+                        if (null != map && map.containsKey("alg_vid_feature_basic_info")) {
+                            Map<String, String> info = map.get("alg_vid_feature_basic_info");
+                            if (null != info && !info.isEmpty()) {
+                                behaviorVideos.put(vid, info);
+                            }
+                        }
+                    }
+                }
             }
         } catch (Exception e) {
-            log.error("get user info error! value=[{}]", id, e);
+            log.error("get user behavior video info error! value=[{}]", headVid, e);
         }
-        return null;
+        return behaviorVideos;
     }
 
     public Map<String, String> getHeadVideoInfo(String headVid) {

+ 9 - 2
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/RecommendService.java

@@ -1,6 +1,5 @@
 package com.tzld.piaoquan.recommend.server.service;
 
-import com.alibaba.fastjson.JSONObject;
 import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
 import com.google.common.base.Stopwatch;
 import com.google.common.base.Strings;
@@ -407,7 +406,13 @@ public class RecommendService {
         Stopwatch stopwatch = Stopwatch.createStarted();
         String vid = String.valueOf(param.getVideoId());
         UserShareReturnProfile userProfile = featureService.getUserProfile(param.getUid(), param.getMid());
-        Map<String, String> headVideoInfo = featureService.getHeadVideoInfo(vid);
+        Map<String, Map<String, String>> behaviorVideos = featureService.getBehaviorVideos(vid, userProfile);
+        Map<String, String> headVideoInfo;
+        if (null != behaviorVideos && behaviorVideos.containsKey(vid)) {
+            headVideoInfo = behaviorVideos.get(vid);
+        } else {
+            headVideoInfo = new HashMap<>();
+        }
         // long infoTime = stopwatch.elapsed(TimeUnit.MILLISECONDS);
         // timerLogMapTL.get().put("infoTime", infoTime);
         stopwatch.reset().start();
@@ -416,6 +421,7 @@ public class RecommendService {
         recallParam.setUserProfile(userProfile);
         recallParam.setHeadInfo(headVideoInfo);
         recallParam.setUserRTShareList(param.getUserRTShareList());
+        recallParam.setBehaviorVideos(behaviorVideos);
         RecallResult recallResult = recallService.recall(recallParam);
 
         long recallTime = stopwatch.elapsed(TimeUnit.MILLISECONDS);
@@ -427,6 +433,7 @@ public class RecommendService {
         rankParam.setUserProfile(userProfile);
         rankParam.setHeadInfo(headVideoInfo);
         rankParam.setUserRTShareList(param.getUserRTShareList());
+        rankParam.setBehaviorVideos(behaviorVideos);
         RankResult rankResult = rankRouter.rank(rankParam);
 
         long rankTime = stopwatch.elapsed(TimeUnit.MILLISECONDS);

+ 1 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/RankParam.java

@@ -47,4 +47,5 @@ public class RankParam {
     private Long requestVideoId;
     private String currentPageSource;
     private Map<String, String> creativeInfoFeature;
+    private Map<String, Map<String, String>> behaviorVideos;
 }

+ 6 - 1
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/bo/UserShareReturnProfile.java

@@ -18,7 +18,8 @@ public class UserShareReturnProfile {
     private List<UserSRBO> m_s_s;    // max_share_seq(最大分享序列)
     private List<UserSRBO> m_r_s;   // max_return_seq(最大回流序列)
     private List<UserSRBO> l_s_s;    // last_share_seq(最近分享序列)
-    private List<UserSRBO> l_r_s;   // last_return_seq(最近回流序列)
+    private List<UserSRBO> l_r_s;   // last_return_seq(最近n层回流序列)
+    private List<UserSRBO> l_r1_s;   // last_return_seq(最近1层回流序列)
     private Map<String, VideoAttrSRBO> c1_s;   // cate1_seq(merge_first_level_cate序列-回流率)
     private Map<String, VideoAttrSRBO> c2_s;   // cate2_seq(merge_second_level_cate序列-回流率)
     private Map<String, VideoAttrSRBO> l1_s;   // label1_seq(festive_label1序列-回流率)
@@ -40,6 +41,10 @@ public class UserShareReturnProfile {
         this.l_r_s = JSON.parseArray(data, UserSRBO.class);
     }
 
+    public void setL_r1_s(String data) {
+        this.l_r1_s = JSON.parseArray(data, UserSRBO.class);
+    }
+
     public void setC1_s(String data) {
         this.c1_s = parseVideoAttrSR(data);
     }

+ 31 - 40
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/strategy/RankStrategy4RegionMergeModelV567.java

@@ -8,7 +8,6 @@ import com.tzld.piaoquan.recommend.server.model.MachineInfo;
 import com.tzld.piaoquan.recommend.server.model.Video;
 import com.tzld.piaoquan.recommend.server.service.FeatureService;
 import com.tzld.piaoquan.recommend.server.service.rank.RankParam;
-import com.tzld.piaoquan.recommend.server.service.rank.bo.UserSRBO;
 import com.tzld.piaoquan.recommend.server.service.rank.bo.UserShareReturnProfile;
 import com.tzld.piaoquan.recommend.server.service.rank.extractor.ExtractVideoMergeCate;
 import com.tzld.piaoquan.recommend.server.service.rank.tansform.FeatureV6;
@@ -45,6 +44,7 @@ public class RankStrategy4RegionMergeModelV567 extends RankStrategy4RegionMergeM
 
         long currentMs = System.currentTimeMillis();
         Set<Long> setVideo = new HashSet<>();
+        setVideo.add(param.getHeadVid());
         List<Video> rovRecallRank = new ArrayList<>();
         // -------------------5路特殊旧召回------------------
         RecallUtils.extractOldSpecialRecall(param, setVideo, rovRecallRank);
@@ -68,6 +68,16 @@ public class RankStrategy4RegionMergeModelV567 extends RankStrategy4RegionMergeM
         RecallUtils.extractRecall(mergeWeight.getOrDefault("headCate2Rov", 5.0).intValue(), param, HeadCate2RovRecallStrategy.PUSH_FROM, setVideo, rovRecallRank);
         //-------------------city rovn------------------
         RecallUtils.extractRecall(mergeWeight.getOrDefault("cityRov", 5.0).intValue(), param, CityRovnRecallStrategy.PUSH_FROM, setVideo, rovRecallRank);
+        //-------------------priori province rovn------------------
+        RecallUtils.extractRecall(mergeWeight.getOrDefault("prioriProvinceRov", 3.0).intValue(), param, PrioriProvinceRovnRecallStrategy.PUSH_FROM, setVideo, rovRecallRank);
+        //-------------------priori province str------------------
+        RecallUtils.extractRecall(mergeWeight.getOrDefault("prioriProvinceStr", 1.0).intValue(), param, PrioriProvinceStrRecallStrategy.PUSH_FROM, setVideo, rovRecallRank);
+        //-------------------priori province ros------------------
+        RecallUtils.extractRecall(mergeWeight.getOrDefault("prioriProvinceRos", 1.0).intValue(), param, PrioriProvinceRosRecallStrategy.PUSH_FROM, setVideo, rovRecallRank);
+        //-------------------return1 cate2 ros------------------
+        RecallUtils.extractRecall(mergeWeight.getOrDefault("return1Cate2Ros", 5.0).intValue(), param, Return1Cate2RosRecallStrategy.PUSH_FORM, setVideo, rovRecallRank);
+        //-------------------return1 cate2 str------------------
+        RecallUtils.extractRecall(mergeWeight.getOrDefault("return1Cate2Str", 5.0).intValue(), param, Return1Cate2StrRecallStrategy.PUSH_FORM, setVideo, rovRecallRank);
 
         //-------------------排-------------------
         //-------------------序-------------------
@@ -77,24 +87,24 @@ public class RankStrategy4RegionMergeModelV567 extends RankStrategy4RegionMergeM
 
         // 1. 批量获取特征  省份参数要对齐  headvid  要传递过来!
         // k1:视频、k2:表、k3:特征、v:特征值
+        Map<String, String> headVideoInfo = param.getHeadInfo();
         List<String> vids = CommonCollectionUtils.toListDistinct(rovRecallRank, v -> String.valueOf(v.getVideoId()));
-        String headVid = String.valueOf(param.getHeadVid());
-        Map<String, Map<String, Map<String, String>>> videoBaseInfoMap = featureService.getVideoBaseInfo(headVid, vids);
-        FeatureService.Feature feature = featureService.getFeatureV3(param, videoBaseInfoMap, vids);
+        Map<String, Map<String, Map<String, String>>> videoBaseInfoMap = featureService.getVideoBaseInfo("", vids);
+        FeatureService.Feature feature = featureService.getFeatureV4(param, headVideoInfo, videoBaseInfoMap, vids);
         Map<String, Map<String, String>> featureOriginUser = feature.getUserFeature();
         Map<String, Map<String, Map<String, String>>> featureOriginVideo = feature.getVideoFeature();
-        Map<String, String> headVideoInfo = videoBaseInfoMap.getOrDefault(headVid, new HashMap<>()).getOrDefault("alg_vid_feature_basic_info", new HashMap<>());
 
         // 2. 用户信息预处理
         Map<String, Map<String, String[]>> newC7Map = FeatureV6.parseUCFScore(featureOriginUser.getOrDefault("alg_mid_feature_sharecf", new HashMap<>()));
         Map<String, Map<String, String[]>> newC8Map = FeatureV6.parseUCFScore(featureOriginUser.getOrDefault("alg_mid_feature_returncf", new HashMap<>()));
         UserShareReturnProfile userProfile = parseUserProfile(featureOriginUser);
-        Map<String, Map<String, String>> userBehaviorVideoMap = getUserBehaviorVideoMap(userProfile);
+        Map<String, Map<String, String>> userBehaviorVideoMap = param.getBehaviorVideos();
+        Map<String, String> creativeInfo = param.getCreativeInfoFeature();
 
         // 3. 特征处理
         List<RankItem> rankItems = CommonCollectionUtils.toList(rovRecallRank, RankItem::new);
-        Map<String, Float> userFeatureMap = getUserFeature(currentMs, param, headVideoInfo, userProfile, featureOriginUser);
-        batchGetVideoFeature(currentMs, userProfile, headVideoInfo, videoBaseInfoMap,
+        Map<String, Float> userFeatureMap = getUserFeature(currentMs, param, creativeInfo, headVideoInfo, userProfile, featureOriginUser);
+        batchGetVideoFeature(currentMs, userProfile, creativeInfo, headVideoInfo, videoBaseInfoMap,
                 newC7Map, newC8Map, featureOriginUser, userBehaviorVideoMap, featureOriginVideo, rankItems);
 
         // 4. 排序模型计算
@@ -118,6 +128,7 @@ public class RankStrategy4RegionMergeModelV567 extends RankStrategy4RegionMergeM
             cate2Coefficient.putAll(simCateScore);
         }
         Double cate2CoefficientDenominator = mergeWeight.getOrDefault("cate2CoefficientDenominator", 1d);
+        Map<String, String> regionMap = getUserRegion(param);
 
         List<Video> result = new ArrayList<>();
         for (RankItem item : items) {
@@ -166,6 +177,12 @@ public class RankStrategy4RegionMergeModelV567 extends RankStrategy4RegionMergeM
             if (null != rtFeatureDumpsMap && !rtFeatureDumpsMap.isEmpty()) {
                 video.getMetaFeatureMap().put("rt", rtFeatureDumpsMap);
             }
+            if (MapUtils.isNotEmpty(param.getCreativeInfoFeature())) {
+                video.getMetaFeatureMap().put("creativeInfo", param.getCreativeInfoFeature());
+            }
+            if (MapUtils.isNotEmpty(regionMap)) {
+                video.getMetaFeatureMap().put("region", regionMap);
+            }
             result.add(video);
         }
         ExtractVideoMergeCate.addOtherParam(result, videoBaseInfoMap);
@@ -190,42 +207,13 @@ public class RankStrategy4RegionMergeModelV567 extends RankStrategy4RegionMergeM
         return null;
     }
 
-    private Map<String, Map<String, String>> getUserBehaviorVideoMap(UserShareReturnProfile userProfile) {
-        Set<String> vidSet = new HashSet<>();
-        if (null != userProfile) {
-            for (List<UserSRBO> list : Arrays.asList(userProfile.getM_s_s(), userProfile.getM_r_s(), userProfile.getL_s_s(), userProfile.getL_r_s())) {
-                if (null != list) {
-                    for (UserSRBO u : list) {
-                        if (null != u) {
-                            vidSet.add(u.getId() + "");
-                        }
-                    }
-                }
-            }
-        }
-
-        Map<String, Map<String, String>> historyVideoMap = new HashMap<>();
-        if (!vidSet.isEmpty()) {
-            Map<String, Map<String, Map<String, String>>> videoMap = featureService.getVideoBaseInfo("", new ArrayList<>(vidSet));
-            if (null != videoMap && !videoMap.isEmpty()) {
-                for (Map.Entry<String, Map<String, Map<String, String>>> entry : videoMap.entrySet()) {
-                    String vid = entry.getKey();
-                    Map<String, Map<String, String>> map = entry.getValue();
-                    if (null != map && map.containsKey("alg_vid_feature_basic_info")) {
-                        historyVideoMap.put(vid, map.get("alg_vid_feature_basic_info"));
-                    }
-                }
-            }
-        }
-        return historyVideoMap;
-    }
-
-    private Map<String, Float> getUserFeature(long currentMs, RankParam param, Map<String, String> headInfo, UserShareReturnProfile userProfile, Map<String, Map<String, String>> userOriginInfo) {
+    private Map<String, Float> getUserFeature(long currentMs, RankParam param, Map<String, String> creativeInfo, Map<String, String> headInfo, UserShareReturnProfile userProfile, Map<String, Map<String, String>> userOriginInfo) {
         Map<String, Double> featMap = new HashMap<>();
         // context feature
         String appType = String.valueOf(param.getAppType());
         String hotSceneType = String.valueOf(param.getHotSceneType());
         FeatureV6.getContextFeature(currentMs, appType, hotSceneType, featMap);
+        FeatureV6.getCreativeBaseFeature("e1", creativeInfo, featMap);
 
         // head video feature
         FeatureV6.getVideoBaseFeature("h", currentMs, headInfo, featMap);
@@ -240,6 +228,7 @@ public class RankStrategy4RegionMergeModelV567 extends RankStrategy4RegionMergeM
 
     private Map<String, Float> getVideoFeature(long currentMs, String vid,
                                                UserShareReturnProfile userProfile,
+                                               Map<String, String> creativeInfo,
                                                Map<String, String> headInfo, Map<String, String> rankInfo,
                                                Map<String, Map<String, String[]>> c7Map,
                                                Map<String, Map<String, String[]>> c8Map,
@@ -259,6 +248,7 @@ public class RankStrategy4RegionMergeModelV567 extends RankStrategy4RegionMergeM
 
         // head&rank cross feature
         FeatureV6.getHeadRankVideoCrossFeature(headInfo, rankInfo, featMap);
+        FeatureV6.getCreativeCrossFeature("e1", creativeInfo, rankInfo, featMap);
 
         // user profile & rank cross
         FeatureV6.getProfileVideoCrossFeature(currentMs, userProfile, rankInfo, historyVideoMap, featMap);
@@ -268,6 +258,7 @@ public class RankStrategy4RegionMergeModelV567 extends RankStrategy4RegionMergeM
 
     private void batchGetVideoFeature(long currentMs,
                                       UserShareReturnProfile userProfile,
+                                      Map<String, String> creativeInfo,
                                       Map<String, String> headInfo,
                                       Map<String, Map<String, Map<String, String>>> videoBaseInfoMap,
                                       Map<String, Map<String, String[]>> c7Map,
@@ -282,7 +273,7 @@ public class RankStrategy4RegionMergeModelV567 extends RankStrategy4RegionMergeM
                 String vid = item.getVideoId() + "";
                 Map<String, String> rankInfo = videoBaseInfoMap.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_basic_info", new HashMap<>());
                 Future<Integer> future = ThreadPoolFactory.defaultPool().submit(() -> {
-                    item.featureMap = getVideoFeature(currentMs, vid, userProfile, headInfo, rankInfo, c7Map, c8Map, userOriginInfo, historyVideoMap, videoOriginInfo);
+                    item.featureMap = getVideoFeature(currentMs, vid, userProfile, creativeInfo, headInfo, rankInfo, c7Map, c8Map, userOriginInfo, historyVideoMap, videoOriginInfo);
                     item.norFeatureMap = item.featureMap;
                     return 1;
                 });

+ 30 - 5
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/tansform/FeatureV6.java

@@ -42,6 +42,12 @@ public class FeatureV6 {
     private static final List<String> userAttrList = Arrays.asList("province", "city", "model", "brand", "system", "user_channel", "user_level");
     private static final Set<String> appSet = new HashSet<>(Arrays.asList("0", "2", "4"));
     private static final Set<String> hotSceneSet = new HashSet<>(Arrays.asList("1008", "1007", "1058", "1074", "1010"));
+    private static final Map<String, String> histotyVideoAttrMAP = new HashMap<>();
+
+    static {
+        histotyVideoAttrMAP.put("merge_first_level_cate", "cate1");
+        histotyVideoAttrMAP.put("merge_second_level_cate", "cate2");
+    }
 
     public static void getContextFeature(long currentMs, String appType, String hotSceneType, Map<String, Double> featureMap) {
         Calendar calendar = Calendar.getInstance();
@@ -255,10 +261,11 @@ public class FeatureV6 {
         if (null == profile) {
             return;
         }
-        getRSCrossFeature("c9_mss", currentMs, seqMaxN, profile.getM_s_s(), rankVideo, hVideoMap, featMap);
-        getRSCrossFeature("c9_mrs", currentMs, seqMaxN, profile.getM_r_s(), rankVideo, hVideoMap, featMap);
-        getRSCrossFeature("c9_lss", currentMs, seqLastN, profile.getL_s_s(), rankVideo, hVideoMap, featMap);
-        getRSCrossFeature("c9_lrs", currentMs, seqLastN, profile.getL_r_s(), rankVideo, hVideoMap, featMap);
+        getRSCrossFeature(false, "c9_mss", currentMs, seqMaxN, profile.getM_s_s(), rankVideo, hVideoMap, featMap);
+        getRSCrossFeature(false, "c9_mrs", currentMs, seqMaxN, profile.getM_r_s(), rankVideo, hVideoMap, featMap);
+        getRSCrossFeature(true, "c9_lss", currentMs, seqLastN, profile.getL_s_s(), rankVideo, hVideoMap, featMap);
+        getRSCrossFeature(false, "c9_lrs", currentMs, seqLastN, profile.getL_r_s(), rankVideo, hVideoMap, featMap);
+        getRSCrossFeature(true, "c9_lr1s", currentMs, seqLastN, profile.getL_r1_s(), rankVideo, hVideoMap, featMap);
 
         if (null == rankVideo || rankVideo.isEmpty()) {
             return;
@@ -269,7 +276,7 @@ public class FeatureV6 {
         getVideoAttrSRCrossFeature("c9_l2s", rankVideo.getOrDefault("festive_label2", ""), profile.getL2_s(), featMap);
     }
 
-    private static void getRSCrossFeature(String prefix, long currentMs, int maxN, List<UserSRBO> list, Map<String, String> rankVideo, Map<String, Map<String, String>> hVideoMap, Map<String, Double> featMap) {
+    private static void getRSCrossFeature(boolean flag, String prefix, long currentMs, int maxN, List<UserSRBO> list, Map<String, String> rankVideo, Map<String, Map<String, String>> hVideoMap, Map<String, Double> featMap) {
         if (null != list && !list.isEmpty()) {
             for (int i = 0; i < list.size() && i < maxN; i++) {
                 UserSRBO u = list.get(i);
@@ -300,6 +307,9 @@ public class FeatureV6 {
                         if (null != hVideoMap && hVideoMap.containsKey(vid)) {
                             Map<String, String> hVideo = hVideoMap.get(vid);
                             getTwoVideoCrossFeature(baseKey, hVideoSimAttrs, hVideo, rankVideo, featMap);
+                            if (flag) {
+                                getHistoryVideoCateFeature(baseKey, hVideo, featMap);
+                            }
                         }
                     }
                 }
@@ -550,4 +560,19 @@ public class FeatureV6 {
         }
         return map.isEmpty() ? 0 : Double.parseDouble(map.getOrDefault(name, "0.0"));
     }
+
+    private static void getHistoryVideoCateFeature(String prefix, Map<String, String> videoInfo, Map<String, Double> featMap) {
+        if (null == videoInfo || videoInfo.isEmpty()) {
+            return;
+        }
+        for (Map.Entry<String, String> entry : histotyVideoAttrMAP.entrySet()) {
+            String attr = entry.getKey();
+            String attrVal = videoInfo.getOrDefault(attr, "");
+            attrVal = attrVal.trim();
+            if (!attrVal.isEmpty() && !attrVal.equals("unknown")) {
+                String key = String.format("%s@%s@%s", prefix, entry.getValue(), attrVal);
+                featMap.put(key, 1.0);
+            }
+        }
+    }
 }

+ 1 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/RecallParam.java

@@ -47,4 +47,5 @@ public class RecallParam {
     private Long requestVideoId;
     private String currentPageSource;
     private Map<String, String> creativeInfoFeature;
+    private Map<String, Map<String, String>> behaviorVideos;
 }

+ 4 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/RecallService.java

@@ -129,6 +129,10 @@ public class RecallService implements ApplicationContextAware {
             strategies.add(strategyMap.get(ChannelLayerRovnRecallStrategy.class.getSimpleName()));
             strategies.add(strategyMap.get(ChannelLayerHeadRovnRecallStrategy.class.getSimpleName()));
         }
+        if (CollectionUtils.isNotEmpty(abExpCodes) && abExpCodes.contains("567")) {
+            strategies.add(strategyMap.get(Return1Cate2RosRecallStrategy.class.getSimpleName()));
+            strategies.add(strategyMap.get(Return1Cate2StrRecallStrategy.class.getSimpleName()));
+        }
 
         // 命中用户黑名单不走流量池
         if (!param.isRiskUser()) {

+ 112 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/strategy/AbstractRedisRecallStrategy.java

@@ -0,0 +1,112 @@
+package com.tzld.piaoquan.recommend.server.service.recall.strategy;
+
+import com.tzld.piaoquan.recommend.server.model.Video;
+import com.tzld.piaoquan.recommend.server.service.filter.FilterParam;
+import com.tzld.piaoquan.recommend.server.service.filter.FilterResult;
+import com.tzld.piaoquan.recommend.server.service.filter.FilterService;
+import com.tzld.piaoquan.recommend.server.service.recall.FilterParamFactory;
+import com.tzld.piaoquan.recommend.server.service.recall.RecallParam;
+import com.tzld.piaoquan.recommend.server.service.recall.RecallStrategy;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.tuple.MutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Slf4j
+public abstract class AbstractRedisRecallStrategy implements RecallStrategy {
+    @Autowired
+    private FilterService filterService;
+    @Autowired
+    @Qualifier("redisTemplate")
+    public RedisTemplate<String, String> redisTemplate;
+
+    @Override
+    public List<Video> recall(RecallParam param) {
+        List<Video> videosResult = new ArrayList<>();
+        try {
+            List<String> keys = getRedisKeys(param);
+            if (null != keys && !keys.isEmpty()) {
+                List<String> values = redisTemplate.opsForValue().multiGet(keys);
+                if (null != values && !values.isEmpty()) {
+                    Pair<List<Long>, Map<Long, Double>> pair = parseVidAndScore(param.getVideoId(), values);
+                    fillVideoResult(param, pair, videosResult);
+                }
+            }
+        } catch (Exception e) {
+            log.error("recall is wrong in {}, error={}", pushFrom(), e);
+        }
+        return videosResult;
+    }
+
+    private Pair<List<Long>, Map<Long, Double>> parseVidAndScore(Long headVid, List<String> values) {
+        int topN = getTopN();
+        Map<Long, Double> scoresMap = new HashMap<>();
+        if (null != values && !values.isEmpty()) {
+            for (String value : values) {
+                if (null != value && !value.isEmpty()) {
+                    String[] cells = value.split("\t");
+                    if (2 == cells.length) {
+                        List<Long> ids = Arrays.stream(cells[0].split(",")).map(Long::valueOf).collect(Collectors.toList());
+                        List<Double> scores = Arrays.stream(cells[1].split(",")).map(Double::valueOf).collect(Collectors.toList());
+                        if (!ids.isEmpty() && ids.size() == scores.size()) {
+                            for (int i = 0; i < ids.size(); ++i) {
+                                long vid = ids.get(i);
+                                double score = scores.get(i);
+                                if (vid == headVid) {
+                                    continue;
+                                }
+                                double oldScore = scoresMap.getOrDefault(vid, 0.0);
+                                scoresMap.put(vid, Math.max(oldScore, score));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        List<Long> idsList = new ArrayList<>();
+        if (!scoresMap.isEmpty()) {
+            List<Pair<Long, Double>> list = new ArrayList<>();
+            for (Map.Entry<Long, Double> entry : scoresMap.entrySet()) {
+                list.add(MutablePair.of(entry.getKey(), entry.getValue()));
+            }
+            list.sort(Comparator.comparingDouble(o -> -o.getRight()));
+            for (Pair<Long, Double> pair : list) {
+                idsList.add(pair.getLeft());
+                if (idsList.size() >= topN) {
+                    break;
+                }
+            }
+        }
+        return new MutablePair<>(idsList, scoresMap);
+    }
+
+    private void fillVideoResult(RecallParam param, Pair<List<Long>, Map<Long, Double>> pair, List<Video> videosResult) {
+        if (null != pair) {
+            List<Long> ids = pair.getLeft();
+            Map<Long, Double> scoresMap = pair.getRight();
+            if (null != ids && null != scoresMap && !ids.isEmpty()) {
+                FilterParam filterParam = FilterParamFactory.create(param, ids);
+                FilterResult filterResult = filterService.filter(filterParam);
+                if (null != filterResult && CollectionUtils.isNotEmpty(filterResult.getVideoIds())) {
+                    filterResult.getVideoIds().forEach(vid -> {
+                        Video video = new Video();
+                        video.setVideoId(vid);
+                        video.setRovScore(scoresMap.getOrDefault(vid, 0D));
+                        video.setPushFrom(pushFrom());
+                        videosResult.add(video);
+                    });
+                }
+            }
+        }
+    }
+
+    abstract List<String> getRedisKeys(RecallParam param);
+
+    abstract int getTopN();
+}

+ 53 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/strategy/Return1Cate2RosRecallStrategy.java

@@ -0,0 +1,53 @@
+package com.tzld.piaoquan.recommend.server.service.recall.strategy;
+
+import com.tzld.piaoquan.recommend.server.service.rank.bo.UserSRBO;
+import com.tzld.piaoquan.recommend.server.service.recall.RecallParam;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class Return1Cate2RosRecallStrategy extends AbstractRedisRecallStrategy {
+    private static final int keysN = 2;
+    private static final int topN = 80;
+    public static final String PUSH_FORM = "return_1_cate2_ros";
+    private static final String redisKeyPrefix = "return_1_cate2_recall";
+
+    @Override
+    public String pushFrom() {
+        return PUSH_FORM;
+    }
+
+    @Override
+    protected List<String> getRedisKeys(RecallParam param) {
+        List<String> keys = new ArrayList<>();
+        if (null != param && null != param.getBehaviorVideos() && null != param.getUserProfile()) {
+            Map<String, Map<String, String>> behaviorVideos = param.getBehaviorVideos();
+            List<UserSRBO> l_r1_s = param.getUserProfile().getL_r1_s();
+            if (null != l_r1_s) {
+                for (UserSRBO u : l_r1_s) {
+                    if (null != u) {
+                        String vid = String.valueOf(u.getId());
+                        if (behaviorVideos.containsKey(vid)) {
+                            String cate2 = behaviorVideos.get(vid).getOrDefault("merge_second_level_cate", "");
+                            if (null != cate2 && !cate2.isEmpty()) {
+                                keys.add(String.format("%s:%s:%s", redisKeyPrefix, cate2, "ros"));
+                                if (keys.size() >= keysN) {
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return keys;
+    }
+
+    @Override
+    protected int getTopN() {
+        return topN;
+    }
+}

+ 53 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/strategy/Return1Cate2StrRecallStrategy.java

@@ -0,0 +1,53 @@
+package com.tzld.piaoquan.recommend.server.service.recall.strategy;
+
+import com.tzld.piaoquan.recommend.server.service.rank.bo.UserSRBO;
+import com.tzld.piaoquan.recommend.server.service.recall.RecallParam;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class Return1Cate2StrRecallStrategy extends AbstractRedisRecallStrategy {
+    private static final int keysN = 2;
+    private static final int topN = 80;
+    public static final String PUSH_FORM = "return_1_cate2_str";
+    private static final String redisKeyPrefix = "return_1_cate2_recall";
+
+    @Override
+    public String pushFrom() {
+        return PUSH_FORM;
+    }
+
+    @Override
+    protected List<String> getRedisKeys(RecallParam param) {
+        List<String> keys = new ArrayList<>();
+        if (null != param && null != param.getBehaviorVideos() && null != param.getUserProfile()) {
+            Map<String, Map<String, String>> behaviorVideos = param.getBehaviorVideos();
+            List<UserSRBO> l_r1_s = param.getUserProfile().getL_r1_s();
+            if (null != l_r1_s) {
+                for (UserSRBO u : l_r1_s) {
+                    if (null != u) {
+                        String vid = String.valueOf(u.getId());
+                        if (behaviorVideos.containsKey(vid)) {
+                            String cate2 = behaviorVideos.get(vid).getOrDefault("merge_second_level_cate", "");
+                            if (null != cate2 && !cate2.isEmpty()) {
+                                keys.add(String.format("%s:%s:%s", redisKeyPrefix, cate2, "str"));
+                                if (keys.size() >= keysN) {
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return keys;
+    }
+
+    @Override
+    protected int getTopN() {
+        return topN;
+    }
+}

+ 59 - 78
recommend-server-service/src/main/resources/feeds_score_config_fm_xgb_20250729.conf

@@ -7,10 +7,11 @@ scorer-config = {
   nor-score-config = {
     scorer-name = "com.tzld.piaoquan.recommend.server.service.score.NorXGBRegressionScorer"
     scorer-priority = 97
-    model-path = "zhangbo/model_xgb_for_recsys_v1_ros.tar.gz"
+    model-path = "zhangbo/model_xgb_for_recsys_v7_nor.tar.gz"
     param = {
-      localDir = "xgboost/recsys_v1_ros"
+      localDir = "xgboost/recsys_v7_nor"
       features = [
+      "b0_12h@return_1_uv",
       "b0_12h@ros1_#",
       "b0_12h@ros_#",
       "b0_12h@ros_minus1_#",
@@ -20,6 +21,7 @@ scorer-config = {
       "b0_12h@ros_one",
       "b0_12h@rovn1_#",
       "b0_12h@rovn_#",
+      "b0_1h@return_1_uv",
       "b0_1h@ros1_#",
       "b0_1h@ros_#",
       "b0_1h@ros_minus1_#",
@@ -29,6 +31,7 @@ scorer-config = {
       "b0_1h@ros_one",
       "b0_1h@rovn1_#",
       "b0_1h@rovn_#",
+      "b0_3h@return_1_uv",
       "b0_3h@ros1_#",
       "b0_3h@ros_#",
       "b0_3h@ros_minus1_#",
@@ -38,6 +41,7 @@ scorer-config = {
       "b0_3h@ros_one",
       "b0_3h@rovn1_#",
       "b0_3h@rovn_#",
+      "b0_6h@return_1_uv",
       "b0_6h@ros1_#",
       "b0_6h@ros_#",
       "b0_6h@ros_minus1_#",
@@ -69,80 +73,79 @@ scorer-config = {
       "b11_12h@str",
       "b11_12h@str_plus",
       "b13_1h@exp",
+      "b13_1h@is_share",
       "b13_1h@ros_#",
       "b13_1h@ros_minus_#",
       "b13_1h@ros_n_#",
       "b13_1h@ros_one",
       "b13_1h@rovn_#",
+      "b13_1h@share_cnt",
       "b13_1h@str",
       "b13_1h@str_plus",
       "b13_24h@exp",
+      "b13_24h@is_share",
       "b13_24h@ros_#",
       "b13_24h@ros_minus_#",
       "b13_24h@ros_n_#",
       "b13_24h@ros_one",
       "b13_24h@rovn_#",
+      "b13_24h@share_cnt",
       "b13_24h@str",
       "b13_24h@str_plus",
       "b13_3h@exp",
+      "b13_3h@is_share",
       "b13_3h@ros_#",
       "b13_3h@ros_minus_#",
       "b13_3h@ros_n_#",
       "b13_3h@ros_one",
       "b13_3h@rovn_#",
+      "b13_3h@share_cnt",
       "b13_3h@str",
       "b13_3h@str_plus",
       "b13_72h@exp",
+      "b13_72h@is_share",
       "b13_72h@ros_#",
       "b13_72h@ros_minus_#",
       "b13_72h@ros_n_#",
       "b13_72h@ros_one",
       "b13_72h@rovn_#",
+      "b13_72h@share_cnt",
       "b13_72h@str",
       "b13_72h@str_plus",
-      "b1_1h@exp",
       "b1_1h@ros_#",
       "b1_1h@ros_minus_#",
       "b1_1h@ros_n_#",
       "b1_1h@ros_one",
       "b1_1h@rovn_#",
-      "b1_24h@exp",
       "b1_24h@ros_#",
       "b1_24h@ros_minus_#",
       "b1_24h@ros_n_#",
       "b1_24h@ros_one",
       "b1_24h@rovn_#",
-      "b1_3h@exp",
       "b1_3h@ros_#",
       "b1_3h@ros_minus_#",
       "b1_3h@ros_n_#",
       "b1_3h@ros_one",
       "b1_3h@rovn_#",
-      "b1_72h@exp",
       "b1_72h@ros_#",
       "b1_72h@ros_minus_#",
       "b1_72h@ros_n_#",
       "b1_72h@ros_one",
       "b1_72h@rovn_#",
-      "b2_24h@exp",
+      "b2_24h@return_n_uv",
       "b2_24h@ros_#",
       "b2_24h@ros_minus_#",
       "b2_24h@ros_n_#",
       "b2_24h@ros_one",
       "b2_24h@rovn_#",
-      "b2_3h@exp",
+      "b2_3h@return_n_uv",
       "b2_3h@ros_#",
       "b2_3h@ros_minus_#",
       "b2_3h@ros_n_#",
       "b2_3h@ros_one",
       "b2_3h@rovn_#",
-      "b2_6h@exp",
-      "b2_6h@ros_#",
-      "b2_6h@ros_minus_#",
-      "b2_6h@ros_n_#",
-      "b2_6h@ros_one",
-      "b2_6h@rovn_#",
       "b3_24h@is_share",
+      "b3_24h@return_n_uv",
       "b3_24h@ros_#",
       "b3_24h@ros_minus_#",
       "b3_24h@ros_n_#",
@@ -150,22 +153,15 @@ scorer-config = {
       "b3_24h@rovn_#",
       "b3_24h@str",
       "b3_24h@str_plus",
-      "b3_3h@is_share",
-      "b3_3h@ros_#",
-      "b3_3h@ros_minus_#",
-      "b3_3h@ros_n_#",
-      "b3_3h@ros_one",
-      "b3_3h@rovn_#",
-      "b3_3h@str",
-      "b3_3h@str_plus",
-      "b3_6h@is_share",
-      "b3_6h@ros_#",
-      "b3_6h@ros_minus_#",
-      "b3_6h@ros_n_#",
-      "b3_6h@ros_one",
-      "b3_6h@rovn_#",
-      "b3_6h@str",
-      "b3_6h@str_plus",
+      "b3_72h@is_share",
+      "b3_72h@return_n_uv",
+      "b3_72h@ros_#",
+      "b3_72h@ros_minus_#",
+      "b3_72h@ros_n_#",
+      "b3_72h@ros_one",
+      "b3_72h@rovn_#",
+      "b3_72h@str",
+      "b3_72h@str_plus",
       "b4_12h@is_share",
       "b4_12h@return_n_uv",
       "b4_12h@ros_#",
@@ -173,13 +169,6 @@ scorer-config = {
       "b4_12h@ros_n_#",
       "b4_12h@ros_one",
       "b4_12h@rovn_#",
-      "b4_1h@is_share",
-      "b4_1h@return_n_uv",
-      "b4_1h@ros_#",
-      "b4_1h@ros_minus_#",
-      "b4_1h@ros_n_#",
-      "b4_1h@ros_one",
-      "b4_1h@rovn_#",
       "b4_3h@is_share",
       "b4_3h@return_n_uv",
       "b4_3h@ros_#",
@@ -187,13 +176,6 @@ scorer-config = {
       "b4_3h@ros_n_#",
       "b4_3h@ros_one",
       "b4_3h@rovn_#",
-      "b4_6h@is_share",
-      "b4_6h@return_n_uv",
-      "b4_6h@ros_#",
-      "b4_6h@ros_minus_#",
-      "b4_6h@ros_n_#",
-      "b4_6h@ros_one",
-      "b4_6h@rovn_#",
       "b5_12h@exp",
       "b5_12h@is_share",
       "b5_12h@return_n_uv",
@@ -218,11 +200,13 @@ scorer-config = {
       "b5_1h@str_plus",
       "b5_24h@exp",
       "b5_24h@is_share",
+      "b5_24h@return_n_uv",
       "b5_24h@ros_#",
       "b5_24h@ros_minus_#",
       "b5_24h@ros_n_#",
       "b5_24h@ros_one",
       "b5_24h@rovn_#",
+      "b5_24h@share_cnt",
       "b5_24h@str",
       "b5_24h@str_plus",
       "b5_3h@exp",
@@ -249,11 +233,13 @@ scorer-config = {
       "b5_6h@str_plus",
       "b5_72h@exp",
       "b5_72h@is_share",
+      "b5_72h@return_n_uv",
       "b5_72h@ros_#",
       "b5_72h@ros_minus_#",
       "b5_72h@ros_n_#",
       "b5_72h@ros_one",
       "b5_72h@rovn_#",
+      "b5_72h@share_cnt",
       "b5_72h@str",
       "b5_72h@str_plus",
       "b6_1h@is_share",
@@ -266,6 +252,7 @@ scorer-config = {
       "b6_1h@str",
       "b6_1h@str_plus",
       "b6_24h@is_share",
+      "b6_24h@return_n_uv",
       "b6_24h@ros_#",
       "b6_24h@ros_minus_#",
       "b6_24h@ros_n_#",
@@ -273,17 +260,6 @@ scorer-config = {
       "b6_24h@rovn_#",
       "b6_24h@str",
       "b6_24h@str_plus",
-      "b7_12h@exp",
-      "b7_12h@is_share",
-      "b7_12h@return_n_uv",
-      "b7_12h@ros_#",
-      "b7_12h@ros_minus_#",
-      "b7_12h@ros_n_#",
-      "b7_12h@ros_one",
-      "b7_12h@rovn_#",
-      "b7_12h@str",
-      "b7_12h@str_plus",
-      "b7_1h@exp",
       "b7_1h@is_share",
       "b7_1h@return_n_uv",
       "b7_1h@ros_#",
@@ -293,8 +269,8 @@ scorer-config = {
       "b7_1h@rovn_#",
       "b7_1h@str",
       "b7_1h@str_plus",
-      "b7_24h@exp",
       "b7_24h@is_share",
+      "b7_24h@return_n_uv",
       "b7_24h@ros_#",
       "b7_24h@ros_minus_#",
       "b7_24h@ros_n_#",
@@ -302,7 +278,6 @@ scorer-config = {
       "b7_24h@rovn_#",
       "b7_24h@str",
       "b7_24h@str_plus",
-      "b7_3h@exp",
       "b7_3h@is_share",
       "b7_3h@return_n_uv",
       "b7_3h@ros_#",
@@ -312,18 +287,8 @@ scorer-config = {
       "b7_3h@rovn_#",
       "b7_3h@str",
       "b7_3h@str_plus",
-      "b7_6h@exp",
-      "b7_6h@is_share",
-      "b7_6h@return_n_uv",
-      "b7_6h@ros_#",
-      "b7_6h@ros_minus_#",
-      "b7_6h@ros_n_#",
-      "b7_6h@ros_one",
-      "b7_6h@rovn_#",
-      "b7_6h@str",
-      "b7_6h@str_plus",
-      "b7_72h@exp",
       "b7_72h@is_share",
+      "b7_72h@return_n_uv",
       "b7_72h@ros_#",
       "b7_72h@ros_minus_#",
       "b7_72h@ros_n_#",
@@ -331,7 +296,15 @@ scorer-config = {
       "b7_72h@rovn_#",
       "b7_72h@str",
       "b7_72h@str_plus",
+      "b8_1h@is_share",
+      "b8_1h@return_n_uv",
+      "b8_1h@ros_#",
+      "b8_1h@ros_minus_#",
+      "b8_1h@rovn_#",
+      "b8_1h@str",
+      "b8_1h@str_plus",
       "b8_24h@is_share",
+      "b8_24h@return_n_uv",
       "b8_24h@ros_#",
       "b8_24h@ros_minus_#",
       "b8_24h@rovn_#",
@@ -344,7 +317,15 @@ scorer-config = {
       "b8_3h@rovn_#",
       "b8_3h@str",
       "b8_3h@str_plus",
+      "b9_1h@is_share",
+      "b9_1h@return_n_uv",
+      "b9_1h@ros_#",
+      "b9_1h@ros_minus_#",
+      "b9_1h@rovn_#",
+      "b9_1h@str",
+      "b9_1h@str_plus",
       "b9_24h@is_share",
+      "b9_24h@return_n_uv",
       "b9_24h@ros_#",
       "b9_24h@ros_minus_#",
       "b9_24h@rovn_#",
@@ -391,32 +372,32 @@ scorer-config = {
       "c9@m_s_cnt",
       "c9@r_pv",
       "c9@r_uv",
-      "c9@ros_#",
-      "c9@ros_minus_#",
+      "c9@ros",
+      "c9@ros_minus",
       "c9@ros_one",
       "c9@s_cnt",
       "c9@s_pv",
       "c9_c1s@mu",
-      "c9_c1s@ros_#",
-      "c9_c1s@ros_minus_#",
+      "c9_c1s@ros",
+      "c9_c1s@ros_minus",
       "c9_c1s@ros_one",
       "c9_c1s@rp",
       "c9_c1s@ru",
       "c9_c1s@sp",
       "c9_c2s@mu",
-      "c9_c2s@ros_#",
-      "c9_c2s@ros_minus_#",
+      "c9_c2s@ros",
+      "c9_c2s@ros_minus",
       "c9_c2s@ros_one",
       "c9_c2s@rp",
       "c9_c2s@ru",
       "c9_c2s@sp",
       "c9_l1s@mu",
-      "c9_l1s@ros_#",
-      "c9_l1s@ros_minus_#",
+      "c9_l1s@ros",
+      "c9_l1s@ros_minus",
       "c9_l1s@sp",
       "c9_l2s@mu",
-      "c9_l2s@ros_#",
-      "c9_l2s@ros_minus_#",
+      "c9_l2s@ros",
+      "c9_l2s@ros_minus",
       "c9_l2s@sp",
       "c9_lrs@1@title",
       "c9_lrs@1@ts",