修正粉丝日报信息区重复与热点区空白问题

1. 移除今日重点信息中的重复摘要列表,只保留卡片展示\n2. 为粉丝日报模板补充新旧载荷结构兼容逻辑,支持从llm_compact、peak_buckets、repeated_messages等字段自动兜底\n3. 热点窗口与共识梗区域增加空数据说明块,避免页面出现整块空白
This commit is contained in:
liuwei
2026-04-29 15:28:48 +08:00
parent 7de1dc9ee3
commit 2a651a5c85
2 changed files with 114 additions and 21 deletions

View File

@@ -474,6 +474,95 @@ def _render_fans_metric_cards(metrics: List[Dict[str, str]]) -> str:
return "".join(blocks)
def _get_compact_scene_material(payload: Dict[str, Any]) -> Dict[str, Any]:
"""
兼容两种来源的粉丝日报载荷:
1. 新版 prompt 材料已经预先组装好的 compact_scene_material
2. 正式渲染链路里仍然保留在 llm_compact 下的压缩材料。
这样模板层可以向后兼容,不会因为字段尚未完全迁移就出现空版块。
"""
compact = payload.get("compact_scene_material", {}) or {}
if compact:
return compact
return payload.get("llm_compact", {}) or {}
def _get_topic_evidence_clusters(payload: Dict[str, Any]) -> List[Dict[str, Any]]:
"""
兼容“已展开主题簇”和“仍放在 llm_compact.semantic_fact_hints.topic_clusters”两种结构。
"""
clusters = payload.get("topic_evidence_clusters", []) or []
if clusters:
return clusters
compact = _get_compact_scene_material(payload)
fact_hints = compact.get("semantic_fact_hints", {}) or {}
normalized = []
for item in (fact_hints.get("topic_clusters", []) or [])[:6]:
normalized.append({
"label": str(item.get("label") or "").strip(),
"count": int(item.get("match_count", item.get("count", 0)) or 0),
"user_count": int(item.get("user_count", 0) or 0),
"time_range": (
f"{str(item.get('first_hm') or '').strip()}-{str(item.get('last_hm') or '').strip()}"
).strip("-"),
"keywords": item.get("keywords", []) or [],
"samples": item.get("samples", []) or [],
})
return normalized
def _get_hero_mentions(payload: Dict[str, Any]) -> List[Dict[str, Any]]:
compact = _get_compact_scene_material(payload)
return (
compact.get("semantic_fact_hints", {})
.get("hero_mentions", [])
or []
)
def _build_local_stats(payload: Dict[str, Any]) -> Dict[str, Any]:
"""
兼容新旧载荷的本地统计字段。
如果没有 top-level local_stats就从 peak_buckets / repeated_messages / burst_terms / content_cues 临时拼一份,
保证“热点窗口与共识梗”始终有内容可用。
"""
local_stats = payload.get("local_stats", {}) or {}
if local_stats:
return local_stats
compact = _get_compact_scene_material(payload)
content_cues = compact.get("content_cues", []) or []
return {
"message_count": int((payload.get("report_meta", {}) or {}).get("message_count", 0) or 0),
"unique_user_count": int((payload.get("report_meta", {}) or {}).get("unique_user_count", 0) or 0),
"top_emotion_bursts": [
{
"text": str(item.get("text") or "").strip(),
"count": int(item.get("count", 0) or 0),
}
for item in content_cues
if str(item.get("kind") or "").strip() == "emotion" and str(item.get("text") or "").strip()
][:8],
"top_repeated_messages": [
{
"text": str(item.get("text") or "").strip(),
"count": int(item.get("count", 0) or 0),
"user_count": int(item.get("user_count", 0) or 0),
}
for item in (payload.get("repeated_messages", []) or [])[:8]
if str(item.get("text") or "").strip()
],
"peak_windows": [
{
"start_time": str(item.get("start_time") or "").strip(),
"message_count": int(item.get("message_count", 0) or 0),
"user_count": int(item.get("user_count", 0) or 0),
}
for item in (payload.get("peak_buckets", []) or [])[:6]
],
}
def _build_fans_effective_info_lines(payload: Dict[str, Any], limit: int = 6) -> List[str]:
"""
为粉丝日报补一层“有效信息速览”。
@@ -490,7 +579,7 @@ def _build_fans_effective_info_lines(payload: Dict[str, Any], limit: int = 6) ->
seen.add(value)
lines.append(value)
for item in (payload.get("topic_evidence_clusters", []) or [])[:6]:
for item in _get_topic_evidence_clusters(payload)[:6]:
label = str(item.get("label") or "").strip()
count = int(item.get("count", 0) or 0)
time_range = str(item.get("time_range") or "").strip()
@@ -506,13 +595,13 @@ def _build_fans_effective_info_lines(payload: Dict[str, Any], limit: int = 6) ->
if len(lines) >= limit:
return lines[:limit]
hero_mentions = payload.get("compact_scene_material", {}).get("semantic_fact_hints", {}).get("hero_mentions", []) or []
hero_mentions = _get_hero_mentions(payload)
if hero_mentions:
hero_names = [str(item.get("hero") or "").strip() for item in hero_mentions[:4] if str(item.get("hero") or "").strip()]
if hero_names:
push(f"英雄讨论主要集中在 {''.join(hero_names)}")
hot_windows = payload.get("local_stats", {}).get("peak_windows", []) or []
hot_windows = _build_local_stats(payload).get("peak_windows", []) or []
if hot_windows:
top_window = hot_windows[0]
push(
@@ -537,7 +626,7 @@ def _build_local_topic_focus_lines(payload: Dict[str, Any], limit: int = 4) -> L
seen.add(value)
lines.append(value)
for item in (payload.get("topic_evidence_clusters", []) or [])[:4]:
for item in _get_topic_evidence_clusters(payload)[:4]:
label = str(item.get("label") or "").strip()
keywords = [str(keyword).strip() for keyword in (item.get("keywords", []) or [])[:5] if str(keyword).strip()]
count = int(item.get("count", 0) or 0)
@@ -555,12 +644,7 @@ def _build_local_hero_focus_lines(payload: Dict[str, Any], limit: int = 4) -> Li
为“英雄与对局焦点”准备本地兜底。
这部分直接复用英雄提及聚类,优先强调出现频次和代表发言,方便粉丝快速看懂今天在聊什么英雄。
"""
hero_mentions = (
payload.get("compact_scene_material", {})
.get("semantic_fact_hints", {})
.get("hero_mentions", [])
or []
)
hero_mentions = _get_hero_mentions(payload)
lines: List[str] = []
seen = set()
@@ -687,11 +771,20 @@ def _render_hot_window_cards(hot_windows: List[Dict[str, Any]]) -> str:
f'<div class="fans-hot-window-meta">{message_count} 条弹幕 / {user_count} 人参与</div>'
'</div>'
)
# 页面层兜底:
# 如果热窗没有任何卡片,也返回一个说明块,避免整个区域看起来像渲染坏了。
if not blocks:
blocks.append(
'<div class="fans-hot-window-card">'
'<div class="fans-hot-window-time">--:--</div>'
'<div class="fans-hot-window-meta">当前载荷里没有可展示的热点窗口数据</div>'
'</div>'
)
return "".join(blocks)
def _render_repeat_digest(payload: Dict[str, Any]) -> str:
local_stats = payload.get("local_stats", {}) or {}
local_stats = _build_local_stats(payload)
repeated_messages = local_stats.get("top_repeated_messages", []) or []
emotion_bursts = local_stats.get("top_emotion_bursts", []) or []
blocks = []
@@ -715,6 +808,13 @@ def _render_repeat_digest(payload: Dict[str, Any]) -> str:
f'<span class="repeat-chip-count">{count} 次</span>'
'</div>'
)
if not blocks:
blocks.append(
'<div class="repeat-chip">'
'<span class="repeat-chip-text">暂无共识梗数据</span>'
'<span class="repeat-chip-count">0 次</span>'
'</div>'
)
return "".join(blocks)
@@ -1054,14 +1154,9 @@ def render_fans_daily_report_html(
lead_text = str(sections.get("lead") or "").strip()
if not lead_text:
lead_text = "今天这场直播的弹幕主打一个集体上头,观众一边盯着画面,一边忙着把梗越刷越离谱。"
topic_clusters = payload.get("topic_evidence_clusters", []) or []
hero_mentions = (
payload.get("compact_scene_material", {})
.get("semantic_fact_hints", {})
.get("hero_mentions", [])
or []
)
local_stats = payload.get("local_stats", {}) or {}
topic_clusters = _get_topic_evidence_clusters(payload)
hero_mentions = _get_hero_mentions(payload)
local_stats = _build_local_stats(payload)
renderer = HtmlTemplateRenderer()
return renderer.render(
@@ -1072,7 +1167,6 @@ def render_fans_daily_report_html(
"lead_text": lead_text,
# 粉丝版不再只做“乐子文案展示”,而是补进本地提纯后的有效信息区。
"fans_metrics_html": Markup(_render_fans_metric_cards(_build_fans_fun_metrics(payload))),
"effective_summary_html": Markup(_render_list(effective_info_lines, item_class="section-summary-list")),
"effective_info_html": Markup(_render_fans_info_cards(effective_info_lines)),
"topic_focus_html": Markup(_render_list(topic_focus_lines, item_class="section-summary-list")),
"topic_clusters_html": Markup(_render_topic_clusters(topic_clusters)),

View File

@@ -398,7 +398,6 @@
<div class="section">
<div class="section-title"><span class="icon"></span><span>今日重点信息</span></div>
{{ effective_summary_html }}
<div class="info-grid">{{ effective_info_html }}</div>
</div>