|
|
@@ -426,8 +426,9 @@ def _build_author_assets(
|
|
|
sample_count = len(items)
|
|
|
qualified_count = len(qualified_decisions)
|
|
|
qualified_ratio = qualified_count / sample_count if sample_count else 0
|
|
|
- age_level = _best_age_level(decisions)
|
|
|
- if not _author_asset_eligible(sample_count, qualified_count, qualified_ratio, age_level):
|
|
|
+ # 优质老年作者入池门:纯 50+ 画像(来自 fifty_plus,仅抖音有)占比>30% 且 TGI>120。
|
|
|
+ elderly_ratio, elderly_tgi = _best_fifty_plus(decisions)
|
|
|
+ if not _author_asset_eligible(elderly_ratio, elderly_tgi):
|
|
|
continue
|
|
|
|
|
|
author_asset_id = _stable_id("author_asset", platform, author_id)
|
|
|
@@ -455,7 +456,8 @@ def _build_author_assets(
|
|
|
"sample_count": sample_count,
|
|
|
"qualified_content_count": qualified_count,
|
|
|
"qualified_content_ratio": qualified_ratio,
|
|
|
- "age_50_plus_level": age_level,
|
|
|
+ "elderly_ratio": elderly_ratio,
|
|
|
+ "elderly_tgi": elderly_tgi,
|
|
|
}
|
|
|
summary = {
|
|
|
"author_asset_id": author_asset_id,
|
|
|
@@ -481,8 +483,8 @@ def _build_author_assets(
|
|
|
"source_type": source_type,
|
|
|
"validation_status": "rule_validated",
|
|
|
"eligible_as_source": 1,
|
|
|
- "elderly_ratio": None,
|
|
|
- "elderly_tgi": None,
|
|
|
+ "elderly_ratio": elderly_ratio,
|
|
|
+ "elderly_tgi": elderly_tgi,
|
|
|
"content_tags": tags,
|
|
|
"source_run_id": run_id,
|
|
|
"source_policy_run_id": policy_run_id,
|
|
|
@@ -499,7 +501,7 @@ def _build_author_assets(
|
|
|
"author_asset_id": author_asset_id,
|
|
|
"role": role,
|
|
|
"role_status": "active",
|
|
|
- "role_reason_code": "p7_author_asset_eligible",
|
|
|
+ "role_reason_code": "elderly_50plus_profile_pass",
|
|
|
"assigned_by": "system",
|
|
|
"source_run_id": run_id,
|
|
|
"raw_payload": {"evidence_refs": evidence_refs},
|
|
|
@@ -509,20 +511,38 @@ def _build_author_assets(
|
|
|
return summaries, asset_rows, role_rows
|
|
|
|
|
|
|
|
|
-def _author_asset_eligible(
|
|
|
- sample_count: int,
|
|
|
- qualified_count: int,
|
|
|
- qualified_ratio: float,
|
|
|
- age_level: str,
|
|
|
-) -> bool:
|
|
|
+def _author_asset_eligible(elderly_ratio: float | None, elderly_tgi: float | None) -> bool:
|
|
|
+ """优质老年作者入池门:纯 50+ 占比 > 30% 且 50+ TGI > 120(仅抖音有画像,天然只抖音入池)。"""
|
|
|
return (
|
|
|
- sample_count >= 9
|
|
|
- and qualified_count >= 3
|
|
|
- and qualified_ratio >= 1 / 3
|
|
|
- and age_level in {"medium", "strong"}
|
|
|
+ elderly_ratio is not None
|
|
|
+ and elderly_tgi is not None
|
|
|
+ and elderly_ratio > 30
|
|
|
+ and elderly_tgi > 120
|
|
|
)
|
|
|
|
|
|
|
|
|
+def _best_fifty_plus(decisions: list[dict[str, Any]]) -> tuple[float | None, float | None]:
|
|
|
+ """从作者各 decision 的 scorecard 取 50+ 画像(status=ok),按 50+占比取最强的一条。
|
|
|
+
|
|
|
+ 返回 (band_percent=纯50+占比, band_tgi=纯50+TGI);无 ok 画像 → (None, None)。
|
|
|
+ """
|
|
|
+ best: tuple[float, float] | None = None
|
|
|
+ for decision in decisions:
|
|
|
+ scorecard = decision.get("scorecard")
|
|
|
+ if not isinstance(scorecard, dict) or scorecard.get("fifty_plus_status") != "ok":
|
|
|
+ continue
|
|
|
+ components = scorecard.get("fifty_plus_components")
|
|
|
+ if not isinstance(components, dict):
|
|
|
+ continue
|
|
|
+ pct = components.get("band_percent")
|
|
|
+ tgi = components.get("band_tgi")
|
|
|
+ if not isinstance(pct, (int, float)) or not isinstance(tgi, (int, float)):
|
|
|
+ continue
|
|
|
+ if best is None or pct > best[0]:
|
|
|
+ best = (float(pct), float(tgi))
|
|
|
+ return best if best is not None else (None, None)
|
|
|
+
|
|
|
+
|
|
|
def _best_age_level(decisions: list[dict[str, Any]]) -> str:
|
|
|
priority = {"strong": 3, "medium": 2, "weak": 1, "missing": 0}
|
|
|
levels = [decision.get("age_50_plus_level", "missing") for decision in decisions]
|