73 lines
2.8 KiB
Python
73 lines
2.8 KiB
Python
|
|
# ============================================================
|
|||
|
|
# 偏好时间衰减加权黄金测试(plan.md §8.3 / 矩阵 66 行剩余项)
|
|||
|
|
# 覆盖:新样本主导偏好(衰减)、久远样本权重趋近 0、
|
|||
|
|
# 舍入后当天信号近似整数、既有计数语义兼容。
|
|||
|
|
# ============================================================
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import datetime
|
|||
|
|
from pathlib import Path
|
|||
|
|
|
|||
|
|
from server.knowledge.preferences import PreferenceStore
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _store(tmp_path: Path) -> PreferenceStore:
|
|||
|
|
return PreferenceStore(str(tmp_path / "pref.json"))
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _sample(strategy: str, *, days_ago: float, project_id: str | None = "p1") -> dict:
|
|||
|
|
at = (datetime.datetime.now() - datetime.timedelta(days=days_ago)).strftime("%Y-%m-%d %H:%M")
|
|||
|
|
return {"strategy": strategy, "source": "schedule.run", "actor": "u1",
|
|||
|
|
"projectId": project_id, "at": at}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_recent_signal_dominates_stale(tmp_path: Path):
|
|||
|
|
"""时间衰减:30 天前的旧偏好不应压过今天的信号(偏好漂移)。"""
|
|||
|
|
ps = _store(tmp_path)
|
|||
|
|
ps.samples = [
|
|||
|
|
_sample("DELIVERY_FIRST", days_ago=30),
|
|||
|
|
_sample("CAPACITY_BALANCE", days_ago=0),
|
|||
|
|
]
|
|||
|
|
scores = ps._decayed_scores("p1")
|
|||
|
|
assert scores["CAPACITY_BALANCE"] > scores["DELIVERY_FIRST"]
|
|||
|
|
assert scores["DELIVERY_FIRST"] < 0.5 # 30 天(>1 半衰期)显著衰减
|
|||
|
|
strategy, _ = ps.preferred_strategy(project_id="p1")
|
|||
|
|
assert strategy == "CAPACITY_BALANCE"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_same_source_two_recent_samples_half_life(tmp_path: Path):
|
|||
|
|
"""半衰期:14 天前样本权重约为今天的 0.5。"""
|
|||
|
|
ps = _store(tmp_path)
|
|||
|
|
ps.samples = [
|
|||
|
|
_sample("A", days_ago=14),
|
|||
|
|
_sample("B", days_ago=0),
|
|||
|
|
]
|
|||
|
|
scores = ps._decayed_scores("p1")
|
|||
|
|
ratio = scores["A"] / scores["B"]
|
|||
|
|
assert 0.45 <= ratio <= 0.55, f"半衰期比值应≈0.5,got {ratio}"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_today_scores_round_to_ints(tmp_path: Path):
|
|||
|
|
"""当天样本(同源同策略×2)评分≈2(保留既有计数语义兼容)。"""
|
|||
|
|
ps = _store(tmp_path)
|
|||
|
|
ps.samples = [
|
|||
|
|
_sample("DELIVERY_FIRST", days_ago=0),
|
|||
|
|
_sample("DELIVERY_FIRST", days_ago=0),
|
|||
|
|
]
|
|||
|
|
strategy, scores = ps.preferred_strategy(project_id="p1")
|
|||
|
|
assert strategy == "DELIVERY_FIRST"
|
|||
|
|
assert abs(scores["DELIVERY_FIRST"] - 2.0) < 0.01
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_decay_preserves_apply_weight(tmp_path: Path):
|
|||
|
|
"""来源权重叠加衰减:采用(2×decay) > 试排(1×decay) 同时间。"""
|
|||
|
|
ps = _store(tmp_path)
|
|||
|
|
ps.samples = [
|
|||
|
|
_sample("CAPACITY_BALANCE", days_ago=0),
|
|||
|
|
_sample("DELIVERY_FIRST", days_ago=0),
|
|||
|
|
]
|
|||
|
|
# 同时间同来源权重应相等;改其中一个为采用验证权重
|
|||
|
|
ps.samples[0]["source"] = "scenario.apply"
|
|||
|
|
scores = ps._decayed_scores("p1")
|
|||
|
|
assert abs(scores["CAPACITY_BALANCE"] - 2 * scores["DELIVERY_FIRST"]) < 0.05
|