137 lines
4.5 KiB
PHP
137 lines
4.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Classifier\Lib;
|
|
|
|
use Classifier\Lib\Discriminant\AlertAnalyzer;
|
|
use Classifier\Lib\Enum\MessageType;
|
|
use Classifier\Lib\Model\MessageQualifier;
|
|
|
|
class MessageClassifier
|
|
{
|
|
/** @var AlertAnalyzer */
|
|
private $analyzer;
|
|
|
|
/** @var MessageQualifier */
|
|
private $qualifier;
|
|
|
|
/** @var MessageType */
|
|
private $defaultAlertType;
|
|
|
|
// * Seuil en dessous duquel on ne fait pas confiance à la machine
|
|
private const MACHINE_CONFIDENCE_THRESHOLD = 0.65;
|
|
|
|
// * Seuil pour forcer une validation humaine sur une alerte critique
|
|
private const CRITICAL_SAFETY_BAR = 0.85;
|
|
|
|
public function __construct(
|
|
AlertAnalyzer $analyzer,
|
|
MessageQualifier $qualifier,
|
|
MessageType $defaultAlertType
|
|
) {
|
|
$this->analyzer = $analyzer;
|
|
$this->qualifier = $qualifier;
|
|
$this->defaultAlertType = $defaultAlertType;
|
|
}
|
|
|
|
public function classify(string $message): MessageClassificationResult
|
|
{
|
|
// * Analyse Déterministe (Priorité Absolue)
|
|
$dictResult = $this->analyzer->analyze($message);
|
|
|
|
if ($dictResult->isBlacklisted) {
|
|
return new MessageClassificationResult(
|
|
MessageType::INFO(),
|
|
ClassificationMethod::DICTIONARY(),
|
|
1.0,
|
|
[$dictResult->reason => 1.0]
|
|
);
|
|
}
|
|
|
|
// * Si score >= 1.0 (ex: mot "attentat" ou "seveso"), c'est une certitude.
|
|
if ($dictResult->score >= 1.0) {
|
|
return new MessageClassificationResult(
|
|
MessageType::ALERTE(),
|
|
ClassificationMethod::DICTIONARY(),
|
|
1.0,
|
|
array_map(function (): int {
|
|
// * Tous les mots trouvés sont considérés comme des preuves à 100%
|
|
return 1;
|
|
}, array_flip($dictResult->keywordsFound))
|
|
);
|
|
}
|
|
|
|
// * Analyse Probabiliste (Machine Learning)
|
|
$mlResult = $this->qualifier->predictWithConfidence($message);
|
|
|
|
$predictedMessageType = $mlResult->decision;
|
|
$confidence = $mlResult->confidence;
|
|
$scores = $mlResult->scores;
|
|
|
|
// * Arbitrage Logique
|
|
|
|
// ? La machine n'est pas assez sûre d'elle
|
|
if ($confidence < self::MACHINE_CONFIDENCE_THRESHOLD) {
|
|
return new MessageClassificationResult(
|
|
$this->defaultAlertType,
|
|
ClassificationMethod::MACHINE(),
|
|
$confidence,
|
|
$scores,
|
|
$predictedMessageType
|
|
);
|
|
}
|
|
|
|
// ? Si la machine doute, on applique la règle de sécurité par défaut
|
|
if ($predictedMessageType->equals(MessageType::DOUBT())) {
|
|
return new MessageClassificationResult(
|
|
$this->defaultAlertType,
|
|
ClassificationMethod::MACHINE(),
|
|
$confidence,
|
|
$scores,
|
|
$predictedMessageType
|
|
);
|
|
}
|
|
|
|
// * La machine est confiante et ne doute pas. On regarde ce qu'elle a trouvé.
|
|
|
|
// ? Si la machine dit "INFO", on lui fait confiance (risque faible)
|
|
if ($predictedMessageType->equals(MessageType::INFO())) {
|
|
return new MessageClassificationResult(
|
|
MessageType::INFO(),
|
|
ClassificationMethod::MACHINE(),
|
|
$confidence,
|
|
$scores,
|
|
$predictedMessageType
|
|
);
|
|
}
|
|
|
|
// ? Si la machine dit "ALERTE", on applique une sécurité supplémentaire
|
|
if ($predictedMessageType->equals(MessageType::ALERTE())) {
|
|
// ? Si c'est une alerte mais que la confiance est "moyenne" (ex: 70%)
|
|
if ($confidence < self::CRITICAL_SAFETY_BAR) {
|
|
return new MessageClassificationResult(
|
|
// * "Alerte probable, mais besoin de validation"
|
|
$this->defaultAlertType,
|
|
ClassificationMethod::MACHINE(),
|
|
$confidence,
|
|
$scores,
|
|
$predictedMessageType
|
|
);
|
|
}
|
|
|
|
// * Sinon, c'est une ALERTE validée par la machine (> 85%)
|
|
return new MessageClassificationResult(
|
|
$predictedMessageType,
|
|
ClassificationMethod::MACHINE(),
|
|
$confidence,
|
|
$scores,
|
|
$predictedMessageType
|
|
);
|
|
}
|
|
|
|
throw new \RuntimeException(
|
|
'Cas non prévu pour la classification du message.'
|
|
);
|
|
}
|
|
}
|