feat(management): 评分引擎——三类标准化/维度归一/样本保护/等级/四对比明细+期末active_friends接口修订
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -58,7 +58,8 @@ METRIC_DEFINITIONS: dict[str, dict] = {
|
||||
# 阈值全部为试算起点(v2 §0 决议:全默认+试算标记);type 三种:
|
||||
# target : floor..target 线性 0..100(方向已含在 target>floor)
|
||||
# reverse : ≤target_ms 100 分,≥floor_ms 0 分,中间线性降
|
||||
# band : 区间内 80..100,低于下限不评分(None),超上限不再加分
|
||||
# band : 区间内 100,超上限 80(不再加分),低于下限 None(负载
|
||||
# 不足→不评分)——得分取值范围 80..100
|
||||
DEFAULT_SCORE_CONFIG: dict = {
|
||||
"version": "v1.0",
|
||||
"name": "一期试算(3维可用)",
|
||||
@@ -79,9 +80,11 @@ DEFAULT_SCORE_CONFIG: dict = {
|
||||
"action": {"weight": 20, "name": "行动执行",
|
||||
"status": "pending_data"}, # 批次E 接 followups/任务
|
||||
"workload": {"weight": 15, "name": "有效负载", "metrics": {
|
||||
# basis=period_distinct:用周期 distinct active_friends(B1 口径),
|
||||
# 比单日期末值更抗单日噪声(2026-09-09 B2 决议)
|
||||
"active_friends_band": {"type": "band", "metric": "active_friends",
|
||||
"low": 5, "high": 80,
|
||||
"basis": "period_daily_avg"},
|
||||
"basis": "period_distinct"},
|
||||
"call_band": {"type": "band", "metric": "call_count",
|
||||
"low": 1, "high": 200, "basis": "period_total",
|
||||
"note": "通话低不扣分仅提示;band 得分下限 80"},
|
||||
@@ -93,7 +96,14 @@ DEFAULT_SCORE_CONFIG: dict = {
|
||||
|
||||
|
||||
def validate_config(config: dict) -> list[str]:
|
||||
"""配置校验:权重总和/结构/引用的 metric_code 有元数据。返回问题清单。"""
|
||||
"""配置校验:权重总和/结构/引用的 metric_code 有元数据/阈值形状。
|
||||
|
||||
阈值形状(随评分引擎落地的 B0 评审预埋要求):
|
||||
- type 须为 target/reverse/band 三者之一
|
||||
- target : floor < target
|
||||
- reverse: target_ms < floor_ms(100 分线在 0 分线之前)
|
||||
- band : low < high
|
||||
"""
|
||||
problems: list[str] = []
|
||||
dims = config.get("dimensions", {})
|
||||
total = sum(d.get("weight", 0) for d in dims.values())
|
||||
@@ -106,6 +116,29 @@ def validate_config(config: dict) -> list[str]:
|
||||
src = spec.get("metric", code)
|
||||
if src not in METRIC_DEFINITIONS:
|
||||
problems.append(f"{key}.{code} 引用未定义指标 {src}")
|
||||
mtype = spec.get("type")
|
||||
if mtype not in ("target", "reverse", "band"):
|
||||
problems.append(
|
||||
f"{key}.{code} 类型非法:{mtype!r}(须 target/reverse/band)"
|
||||
)
|
||||
elif mtype == "target" and not spec.get("floor", 0) < spec.get("target", 0):
|
||||
problems.append(
|
||||
f"{key}.{code} target 型须 floor<target"
|
||||
f"(当前 floor={spec.get('floor')} target={spec.get('target')})"
|
||||
)
|
||||
elif mtype == "reverse" and not (
|
||||
spec.get("target_ms", 0) < spec.get("floor_ms", 0)
|
||||
):
|
||||
problems.append(
|
||||
f"{key}.{code} reverse 型须 target_ms<floor_ms"
|
||||
f"(当前 target_ms={spec.get('target_ms')}"
|
||||
f" floor_ms={spec.get('floor_ms')})"
|
||||
)
|
||||
elif mtype == "band" and not spec.get("low", 0) < spec.get("high", 0):
|
||||
problems.append(
|
||||
f"{key}.{code} band 型须 low<high"
|
||||
f"(当前 low={spec.get('low')} high={spec.get('high')})"
|
||||
)
|
||||
return problems
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user