cii-text-analysis/Lib/MessageClassifier.php

138 lines
4.5 KiB
PHP
Raw Permalink Normal View History

2026-02-10 16:19:59 +01:00
<?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.'
);
}
}