2026-09-04 10:20:40 +08:00

72 lines
1.9 KiB
Python

"""Replaceable baseline risk scoring engine."""
from dataclasses import dataclass
from enum import StrEnum
class RiskLevel(StrEnum):
LOW = "low"
MODERATELY_LOW = "moderately_low"
MEDIUM = "medium"
HIGH = "high"
VERY_HIGH = "very_high"
@dataclass(frozen=True, slots=True)
class IndicatorValue:
name: str
value: float
weight: float
@dataclass(frozen=True, slots=True)
class FactorContribution:
name: str
value: float
weight: float
contribution: float
@dataclass(frozen=True, slots=True)
class RiskScore:
score: float
level: RiskLevel
factors: tuple[FactorContribution, ...]
class BaselineRiskEngine:
"""Weighted safety-score baseline following the PRD scoring direction.
Input indicator values represent risk intensity (0 = no observed risk,
100 = maximum risk). The public score is the inverse safety score, so a
higher result always means a safer assessment.
"""
model_version = "baseline-safety-v2"
def predict(self, indicators: list[IndicatorValue]) -> RiskScore:
total_weight = sum(item.weight for item in indicators)
factors = tuple(
FactorContribution(
name=item.name,
value=item.value,
weight=item.weight,
contribution=round((100 - item.value) * item.weight / total_weight, 2),
)
for item in indicators
)
score = round(sum(item.contribution for item in factors), 2)
return RiskScore(score=score, level=self._classify(score), factors=factors)
@staticmethod
def _classify(score: float) -> RiskLevel:
if score >= 80:
return RiskLevel.LOW
if score >= 60:
return RiskLevel.MODERATELY_LOW
if score >= 40:
return RiskLevel.MEDIUM
if score >= 20:
return RiskLevel.HIGH
return RiskLevel.VERY_HIGH