|
|
@@ -939,13 +939,25 @@ async def get_ads_for_review(
|
|
|
bid_amount = float(row.get("bid_amount", 0) or 0)
|
|
|
|
|
|
# 零消耗待关停:7日均消耗 < 10元,几乎无活动(强规则,仍保留)
|
|
|
+ # ⚠️ 但需要年龄保护:≤7天的广告不适用零消耗规则
|
|
|
if cost_7d_avg < min_spend_for_class_a:
|
|
|
- zero_spend_ads.append({
|
|
|
- "ad_id": int(row["ad_id"]),
|
|
|
- "ad_name": str(row.get("ad_name", "")),
|
|
|
- "cost_7d_avg": round(cost_7d_avg, 2),
|
|
|
- })
|
|
|
- continue
|
|
|
+ # 检查广告年龄
|
|
|
+ if ad_age is not None and ad_age <= EARLY_GROWTH_DAYS:
|
|
|
+ # 4-7天(早期成长期)或≤3天(冷启动期):保护,不关停
|
|
|
+ normal_ads_count += 1
|
|
|
+ logger.debug(
|
|
|
+ f"广告 {row['ad_id']} 年龄{ad_age}天≤{EARLY_GROWTH_DAYS}天,"
|
|
|
+ f"虽消耗低({cost_7d_avg:.2f}元),但年龄保护不关停"
|
|
|
+ )
|
|
|
+ continue
|
|
|
+ else:
|
|
|
+ # >7天的低消耗广告:正常应用零消耗规则
|
|
|
+ zero_spend_ads.append({
|
|
|
+ "ad_id": int(row["ad_id"]),
|
|
|
+ "ad_name": str(row.get("ad_name", "")),
|
|
|
+ "cost_7d_avg": round(cost_7d_avg, 2),
|
|
|
+ })
|
|
|
+ continue
|
|
|
|
|
|
# 待优化评估:ROI 偏低 或 衰退信号 或 出价调整候选(需要智能判断)
|
|
|
roi_low = (not pd.isna(f_roi)) and (f_roi < roi_mean * roi_review_factor)
|
|
|
@@ -994,24 +1006,33 @@ async def get_ads_for_review(
|
|
|
age_protected_skip = True
|
|
|
|
|
|
# 早期成长期(4-7天):仅允许提价和扩量评估
|
|
|
- # ⚠️ 关键修复:完全阻断非提价/扩量候选,无论何种候选标志
|
|
|
+ # ⚠️ 核心修复:强制清除所有负向候选标志,无论是否有提价标志
|
|
|
elif ad_age <= EARLY_GROWTH_DAYS:
|
|
|
- # 只有提价候选或扩量候选才允许进入LLM评估
|
|
|
- # 其他所有候选标志(roi_low, decay_signal, bid_down_candidate)都被排除
|
|
|
- if not (bid_up_candidate or scale_up_candidate):
|
|
|
- # 检查是否有任何候选标志(即使不是提价/扩量)
|
|
|
- has_any_candidate = roi_low or decay_signal or bid_down_candidate
|
|
|
- if has_any_candidate:
|
|
|
- # 有候选标志但不是提价/扩量 → 直接排除
|
|
|
- normal_ads_count += 1
|
|
|
- logger.debug(
|
|
|
- f"广告 {row['ad_id']} 处于早期成长期({ad_age}天,4-{EARLY_GROWTH_DAYS}天),"
|
|
|
- f"年龄保护规则:仅允许提价/扩量评估,其他候选已排除"
|
|
|
- f"(roi_low={roi_low}, decay={decay_signal}, bid_down={bid_down_candidate})"
|
|
|
- )
|
|
|
- age_protected_skip = True
|
|
|
- # else: 无任何候选标志,正常计入normal_ads_count
|
|
|
- # else: 是提价或扩量候选,允许进入评估
|
|
|
+ # 检查原始候选状态(用于日志)
|
|
|
+ has_negative_flags = roi_low or decay_signal or bid_down_candidate
|
|
|
+ has_positive_flags = bid_up_candidate or scale_up_candidate
|
|
|
+
|
|
|
+ # 强制清除负向候选标志(即使同时有提价标志)
|
|
|
+ if has_negative_flags:
|
|
|
+ logger.debug(
|
|
|
+ f"广告 {row['ad_id']} 处于早期成长期({ad_age}天),"
|
|
|
+ f"年龄保护强制清除负向候选标志:"
|
|
|
+ f"roi_low={roi_low}→False, decay={decay_signal}→False, "
|
|
|
+ f"bid_down={bid_down_candidate}→False"
|
|
|
+ )
|
|
|
+ roi_low = False
|
|
|
+ decay_signal = False
|
|
|
+ bid_down_candidate = False
|
|
|
+
|
|
|
+ # 如果清除后没有任何候选标志 → 排除
|
|
|
+ if not has_positive_flags:
|
|
|
+ normal_ads_count += 1
|
|
|
+ logger.debug(
|
|
|
+ f"广告 {row['ad_id']} 处于早期成长期({ad_age}天),"
|
|
|
+ f"无提价/扩量候选标志,已排除"
|
|
|
+ )
|
|
|
+ age_protected_skip = True
|
|
|
+ # else: 有提价或扩量候选,允许进入评估(负向标志已清除)
|
|
|
|
|
|
# 年龄保护排除的广告,直接跳过
|
|
|
if age_protected_skip:
|
|
|
@@ -1210,7 +1231,19 @@ async def apply_decisions(
|
|
|
df_metrics = pd.read_csv(metrics_csv)
|
|
|
for _, row in df_metrics.iterrows():
|
|
|
cost_7d_avg = float(row.get("cost_7d_avg", 0) or 0)
|
|
|
+ ad_age = row.get("ad_age_days") # 获取广告年龄
|
|
|
+
|
|
|
+ # ⚠️ 年龄保护:≤7天的广告不适用零消耗规则
|
|
|
if cost_7d_avg < 10.0:
|
|
|
+ # 检查广告年龄
|
|
|
+ if ad_age is not None and ad_age <= EARLY_GROWTH_DAYS:
|
|
|
+ # 4-7天或≤3天:保护,跳过
|
|
|
+ logger.debug(
|
|
|
+ f"零消耗规则跳过广告 {row['ad_id']}:年龄{ad_age}天≤{EARLY_GROWTH_DAYS}天"
|
|
|
+ )
|
|
|
+ continue
|
|
|
+
|
|
|
+ # >7天的低消耗广告:正常应用零消耗规则
|
|
|
# 优化reason表达:避免"0.00元"显示,改用"几乎无消耗"
|
|
|
if cost_7d_avg == 0:
|
|
|
reason_text = "7日几乎无消耗,长期无活动"
|