Files
crm-ms1/v4_ci4/app/Libraries/Radar/RadarAnalyzeService.php

96 lines
3.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Libraries\Radar;
/**
* MSOP-3 — analyse métier dossards + delta vs mails précédents du même expéditeur.
* LIA signale ; elle napprouve jamais (pas de Go / tampon).
*/
class RadarAnalyzeService
{
public function __construct(private GeminiClient $gemini)
{
}
public function isEnabled(): bool
{
return $this->gemini->isConfigured();
}
/**
* @param list<array{subject:string,summary:string,analysis:?string,received_at:?string}> $priors
* @return array{analysis:string, delta:string, confidence:int}
*/
public function analyzeDossards(
string $subject,
string $fromEmail,
string $body,
array $priors,
): array {
$hist = '';
foreach ($priors as $i => $p) {
$n = $i + 1;
$hist .= "--- Mail dossards #{$n} ({$p['received_at']}) ---\n"
. 'Sujet: ' . ($p['subject'] ?? '') . "\n"
. 'Analyse/snippet: ' . (($p['analysis'] ?: $p['summary']) ?? '') . "\n\n";
}
$isFirst = $priors === [];
$prompt = <<<PROMPT
Tu es l'assistant Radar du CRM MS1 Timing (chronométrage / dossards).
Tu SIGNALES seulement. Tu n'APPROUVES JAMAIS une impression ni un Go.
Réponds UNIQUEMENT en JSON valide UTF-8, sans markdown autour, schéma :
{"analysis":"string","delta":"string","confidence":0}
- analysis : analyse métier dossards courte (FR), points utiles : logos, séquences, imprimeur, délais, fichiers, questions ouvertes, signaux faibles vs forts. Pas de copier-coller du mail. Max ~1200 caractères.
- delta : si 1er mail → chaîne vide "". Sinon : ce qui change / stable / nouveau / contradictoire vs l'historique. Max ~800 caractères.
- confidence : entier 0-100 (fiabilité de ton analyse, pas un feu vert).
Contexte :
Expéditeur : {$fromEmail}
Sujet : {$subject}
Premier mail dossards pour cet expéditeur : {$this->boolFr($isFirst)}
Historique dossards déjà en Radar (du plus ancien au plus récent) :
{$hist}
Mail courant (corps) :
{$body}
PROMPT;
$out = $this->gemini->generate($prompt);
$text = $out['text'];
// Extraire JSON si le modèle ajoute du bruit
if (\preg_match('/\{.*\}/s', $text, $m)) {
$text = $m[0];
}
$json = \json_decode($text, true);
if (! \is_array($json)) {
return [
'analysis' => \mb_substr($out['text'], 0, 2000),
'delta' => $isFirst ? '' : 'Delta non structuré — voir analyse.',
'confidence' => 40,
];
}
$analysis = \trim((string) ($json['analysis'] ?? ''));
$delta = \trim((string) ($json['delta'] ?? ''));
$conf = (int) ($json['confidence'] ?? 50);
$conf = \max(0, \min(100, $conf));
if ($isFirst) {
$delta = '';
}
return [
'analysis' => \mb_substr($analysis !== '' ? $analysis : $out['text'], 0, 4000),
'delta' => \mb_substr($delta, 0, 2000),
'confidence' => $conf,
];
}
private function boolFr(bool $v): string
{
return $v ? 'oui' : 'non';
}
}