feat(classify): tighten scoring with reaction co-occurrence and date negatives
强正向 reaction 需标题同时含 require_title;移除听/看等宽泛词,新增弱正向组;charge 命中>=2 记+3、==1 记+1;负向补充录播/录像并新增标题带日期强负向;时长仅在已命中 reaction/charge 时加权;discover 关键词改为单个咻咻满、pages=3、window_days=7,notify.min_score=4;同步示例配置、project.md/README 与相关测试。
This commit is contained in:
1 parent
e04908ccad
commit
742c67a1c3
9 files changed
+210
-107
No files matched your search
+71
-20
@@ -10,6 +10,8 @@ sys.stdout.reconfigure(encoding="utf-8")
|
||||
from reaction_board.classify import ( # noqa: E402
|
||||
clean_title,
|
||||
contains_any,
|
||||
date_hits,
|
||||
matched_words,
|
||||
normalize_title,
|
||||
parse_duration,
|
||||
passes_required_title,
|
||||
@@ -19,13 +21,22 @@ from reaction_board.classify import ( # noqa: E402
|
||||
CLASSIFY = {
|
||||
"require_title": "咻咻满",
|
||||
"self_mid": 37754047,
|
||||
"positive": ["reaction", "re", "反应", "看", "听", "初听", "首次", "点评", "锐评", "路人", "震惊", "双人"],
|
||||
"charge": ["充电", "加急", "专场"],
|
||||
"negative": ["歌切", "切片", "纯享", "搬运", "完整版"],
|
||||
"reaction": ["reaction", "反应"],
|
||||
"charge": ["加急", "特急", "充电", "专场", "转场"],
|
||||
"weak_positive": ["双人", "路人", "第一次", "首次"],
|
||||
"negative": ["歌切", "切片", "纯享", "搬运", "完整版", "录播", "录像"],
|
||||
"date_negative": True,
|
||||
"duration_boost": {"gt_30min": 1, "gt_60min": 2},
|
||||
}
|
||||
|
||||
|
||||
def score(title, up_mid=100, duration=0, classify=CLASSIFY):
|
||||
return score_item(
|
||||
title=title, up_mid=up_mid,
|
||||
duration_seconds=duration, classify=classify,
|
||||
)
|
||||
|
||||
|
||||
def test_title_helpers():
|
||||
assert normalize_title("咻咻满 REACTION") == "咻咻满 reaction"
|
||||
assert clean_title('<em class="keyword">咻咻满</em> reaction') == "咻咻满 reaction"
|
||||
@@ -34,6 +45,8 @@ def test_title_helpers():
|
||||
assert not passes_required_title("其他 UP 的 reaction", "咻咻满")
|
||||
assert contains_any("咻咻满 REACTION", ["reaction"])
|
||||
assert not contains_any("咻咻满", [])
|
||||
assert matched_words("咻咻满 REACTION 反应", ["reaction", "反应"]) == ["reaction", "反应"]
|
||||
assert matched_words("咻咻满", []) == []
|
||||
|
||||
|
||||
def test_parse_duration():
|
||||
@@ -46,32 +59,70 @@ def test_parse_duration():
|
||||
assert parse_duration(-5) == 0
|
||||
|
||||
|
||||
def test_score_rules():
|
||||
def score(title, up_mid=100, duration=0):
|
||||
return score_item(
|
||||
title=title, up_mid=up_mid,
|
||||
duration_seconds=duration, classify=CLASSIFY,
|
||||
)
|
||||
def test_date_hits():
|
||||
assert date_hits("咻咻满-2026-10-28-素颜满") == ["2026-10-28"]
|
||||
assert date_hits("【咻咻满】10月25日直播片段") == ["10月25日"]
|
||||
assert date_hits("咻咻满 ☆ 2026.10.26") == ["2026.10.26"]
|
||||
assert date_hits("【咻咻满】2026/10/25直播回放") == ["2026/10/25"]
|
||||
assert date_hits("咻咻满:《发如雪》2024年10月5日直播歌切") == ["2024年10月5日"]
|
||||
assert date_hits("咻咻满 reaction") == []
|
||||
|
||||
assert score("咻咻满 reaction", duration=60) == 2
|
||||
assert score("咻咻满 充电", duration=60) == 2
|
||||
|
||||
def test_score_strong_reaction_requires_title():
|
||||
# 强正向 reaction 只在标题含 require_title 时成立。
|
||||
assert score("咻咻满 reaction", duration=60) == 3
|
||||
assert score("咻咻满 反应", duration=60) == 3
|
||||
assert score("其他 UP 的 reaction", duration=61 * 60) == 0
|
||||
|
||||
|
||||
def test_score_charge_levels():
|
||||
assert score("咻咻满 充电", duration=60) == 1
|
||||
assert score("咻咻满 加急专场", duration=60) == 3
|
||||
assert score("咻咻满 reaction 充电", duration=60) == 4
|
||||
assert score("咻咻满 reaction", duration=61 * 60) == 4
|
||||
assert score("咻咻满 reaction", duration=31 * 60) == 3
|
||||
assert score("咻咻满 reaction", duration=30 * 60) == 2
|
||||
assert score("咻咻满 切片", duration=60) == -5
|
||||
assert score("咻咻满 reaction 加急充电专场", duration=60) == 6
|
||||
|
||||
|
||||
def test_score_weak_positive():
|
||||
assert score("咻咻满 双人", duration=60) == 1
|
||||
assert score("咻咻满 reaction 双人", duration=60) == 4
|
||||
|
||||
|
||||
def test_duration_only_counts_with_signal():
|
||||
assert score("咻咻满 reaction", duration=61 * 60) == 5
|
||||
assert score("咻咻满 reaction", duration=31 * 60) == 4
|
||||
assert score("咻咻满 reaction", duration=30 * 60) == 3
|
||||
# 无 reaction / charge 信号时,时长不给分(避免纯长录像进池)。
|
||||
assert score("咻咻满", duration=61 * 60) == 0
|
||||
assert score("咻咻满 双人", duration=61 * 60) == 1
|
||||
|
||||
|
||||
def test_score_negative_and_self():
|
||||
assert score("咻咻满 切片", duration=60) == -6
|
||||
assert score("咻咻满 reaction 切片", duration=60) == -3
|
||||
assert score("咻咻满 reaction", up_mid=37754047, duration=60) == -1
|
||||
assert score("咻咻满", duration=0) == 0
|
||||
assert score("咻咻满", duration=61 * 60) == 2
|
||||
assert score("咻咻满 reaction 直播录像", duration=60) == -3
|
||||
assert score("咻咻满 reaction", up_mid=37754047, duration=60) == 0
|
||||
|
||||
|
||||
def test_score_date_negative():
|
||||
assert score("咻咻满 reaction 2026-10-28 完整录播", duration=60) == -9
|
||||
assert score("咻咻满 reaction 2026-10-28", duration=60) == -3
|
||||
assert score("咻咻满 充电 10月25日", duration=60) == -5
|
||||
disabled = dict(CLASSIFY, date_negative=False)
|
||||
assert score("咻咻满 reaction 2026-10-28", duration=60, classify=disabled) == 3
|
||||
|
||||
|
||||
def main():
|
||||
test_title_helpers()
|
||||
test_parse_duration()
|
||||
test_score_rules()
|
||||
test_date_hits()
|
||||
test_score_strong_reaction_requires_title()
|
||||
test_score_charge_levels()
|
||||
test_score_weak_positive()
|
||||
test_duration_only_counts_with_signal()
|
||||
test_score_negative_and_self()
|
||||
test_score_date_negative()
|
||||
print("OK: 候选标题归一化、时长解析与分类打分测试全部通过")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
Reference in new issue
Block a user