- Add intermediate saves (v1.0-v1.4) to Generated_Articles_Versioned - Fix compiled_text pipeline (generatedTexts object structure) - Add /api/workflow-modulaire endpoint with version tracking - Create test-modulaire.html interface with real-time logs - Support parent-child linking via Parent_Article_ID
692 lines
25 KiB
JavaScript
692 lines
25 KiB
JavaScript
// ========================================
|
||
// VALIDATEUR ANTI-DÉTECTION IA
|
||
// Détection des patterns typiques d'IA et évaluation de naturalité
|
||
// ========================================
|
||
|
||
// Import du LLMManager pour validation IA
|
||
let LLMManager;
|
||
try {
|
||
const llmModule = require('../../lib/LLMManager');
|
||
if (llmModule && typeof llmModule.callLLM === 'function') {
|
||
LLMManager = llmModule;
|
||
}
|
||
} catch (error) {
|
||
console.warn('LLMManager non disponible pour AntiDetectionValidator:', error.message);
|
||
}
|
||
|
||
/**
|
||
* Validateur spécialisé dans la détection d'empreintes IA
|
||
* Score de naturalité et détection de patterns suspects
|
||
*/
|
||
class AntiDetectionValidator {
|
||
|
||
// Base de données des empreintes IA suspectes
|
||
static AI_FINGERPRINTS = {
|
||
// Mots/expressions typiquement IA (niveau critique)
|
||
critical: {
|
||
english: [
|
||
'comprehensive', 'robust', 'seamless', 'innovative', 'cutting-edge',
|
||
'state-of-the-art', 'furthermore', 'moreover', 'in conclusion',
|
||
'it is important to note', 'it\'s worth noting', 'additionally'
|
||
],
|
||
french: [
|
||
'complet et exhaustif', 'robuste et fiable', 'innovant et révolutionnaire',
|
||
'de pointe', 'il est important de souligner', 'il convient de noter',
|
||
'en outre', 'par ailleurs', 'en définitive', 'en fin de compte'
|
||
]
|
||
},
|
||
|
||
// Patterns de structure suspects (niveau élevé)
|
||
high: {
|
||
patterns: [
|
||
/^(Premièrement|Deuxièmement|Troisièmement).+(Deuxièmement|Troisièmement|Enfin)/s,
|
||
/En conclusion.+(il est|nous pouvons|il convient)/i,
|
||
/(Il est important|Il faut souligner|Il convient de noter).+(que|de)/gi,
|
||
/^\d+\.\s.+\n\d+\.\s.+\n\d+\.\s/m // Listes numérotées systématiques
|
||
],
|
||
phrases: [
|
||
'permet de', 'contribue à', 'favorise le développement',
|
||
'optimise les performances', 'améliore significativement'
|
||
]
|
||
},
|
||
|
||
// Indicateurs moyens (niveau modéré)
|
||
moderate: {
|
||
overused: [
|
||
'cependant', 'néanmoins', 'toutefois', 'ainsi', 'donc',
|
||
'par conséquent', 'en effet', 'effectivement', 'naturellement'
|
||
],
|
||
formal: [
|
||
'il s\'avère que', 'force est de constater', 'il ressort que',
|
||
'on peut considérer', 'il est possible de'
|
||
]
|
||
}
|
||
};
|
||
|
||
// Patterns de transitions trop parfaites
|
||
static PERFECT_TRANSITIONS = [
|
||
/\.\s+(Cependant|Néanmoins|Toutefois|Par ailleurs),/g,
|
||
/\.\s+(En outre|De plus|Par ailleurs),/g,
|
||
/\.\s+(Ainsi|Donc|Par conséquent),/g,
|
||
/\.\s+(En effet|Effectivement|Naturellement),/g
|
||
];
|
||
|
||
// Structures de phrases trop régulières
|
||
static REGULAR_PATTERNS = [
|
||
/^[A-Z][^.]{50,80}\./gm, // Phrases de longueur trop uniforme
|
||
/^[A-Z][^.]+, [^.]+, [^.]+\./gm, // Structure triple répétitive
|
||
];
|
||
|
||
/**
|
||
* 🚫 VALIDATION ANTI-DÉTECTION PRINCIPALE - ORCHESTRATEUR GLOBAL
|
||
*
|
||
* CE QUI EST TESTÉ :
|
||
* ✅ Analyse statique 4 métriques : empreintes IA, variabilité, naturalité, erreurs humaines
|
||
* ✅ Score pondéré : empreintes(30%) + variabilité(25%) + naturalité(25%) + erreurs(20%)
|
||
* ✅ Classification risque détection (LOW/MODERATE/HIGH/CRITICAL)
|
||
* ✅ Validation LLM complémentaire optionnelle (fusion 70% statique + 30% IA)
|
||
*
|
||
* ALGORITHMES EXÉCUTÉS :
|
||
* - 4 analyses parallèles : patterns suspects, diversité syntaxique, markers humains
|
||
* - Protection NaN avec fallback scores à 50
|
||
* - Pondération sophistiquée privilégiant détection patterns critiques
|
||
* - Fusion intelligente avec validation IA si disponible
|
||
*/
|
||
static async validateAntiDetection(content, options = {}) {
|
||
// Analyse statique (patterns)
|
||
const staticAnalysis = {
|
||
fingerprintScore: this.analyzeFingerprintScore(content),
|
||
variabilityScore: this.analyzeVariabilityScore(content),
|
||
naturalness: this.analyzeNaturalness(content),
|
||
humanErrors: this.detectHumanLikeErrors(content),
|
||
overallScore: 0,
|
||
details: {}
|
||
};
|
||
|
||
// Calcul score global pondéré avec vérification NaN
|
||
const fp = isNaN(staticAnalysis.fingerprintScore?.score) ? 50 : staticAnalysis.fingerprintScore.score;
|
||
const vs = isNaN(staticAnalysis.variabilityScore) ? 50 : staticAnalysis.variabilityScore;
|
||
const nat = isNaN(staticAnalysis.naturalness) ? 50 : staticAnalysis.naturalness;
|
||
const he = isNaN(staticAnalysis.humanErrors) ? 50 : staticAnalysis.humanErrors;
|
||
|
||
staticAnalysis.overallScore = Math.round(
|
||
fp * 0.3 + vs * 0.25 + nat * 0.25 + he * 0.2
|
||
);
|
||
|
||
// Classification du niveau de détection
|
||
staticAnalysis.detectionRisk = this.calculateDetectionRisk(staticAnalysis.overallScore);
|
||
|
||
// Validation IA complémentaire (si LLM disponible)
|
||
let aiAnalysis = null;
|
||
if (LLMManager && options.enableLLM !== false) {
|
||
try {
|
||
aiAnalysis = await this.validateWithLLM(content);
|
||
|
||
// Fusion des scores (moyenne pondérée)
|
||
const combinedScore = Math.round(staticAnalysis.overallScore * 0.7 + aiAnalysis.score * 0.3);
|
||
|
||
return {
|
||
...staticAnalysis,
|
||
overallScore: combinedScore,
|
||
aiAnalysis: aiAnalysis,
|
||
detectionRisk: this.calculateDetectionRisk(combinedScore)
|
||
};
|
||
|
||
} catch (error) {
|
||
console.warn('Erreur validation LLM anti-détection:', error.message);
|
||
}
|
||
}
|
||
|
||
return staticAnalysis;
|
||
}
|
||
|
||
/**
|
||
* 🤖 VALIDATION LLM QUALITATIVE - ANALYSE IA vs HUMAIN
|
||
*
|
||
* CE QUI EST TESTÉ :
|
||
* ✅ Appel OpenAI pour détection qualitative empreintes IA
|
||
* ✅ Évaluation 4 critères : empreintes, variabilité, naturalité, fluidité
|
||
* ✅ Score probabilité humain (0=clairement IA, 100=clairement humain)
|
||
*
|
||
* ALGORITHMES EXÉCUTÉS :
|
||
* - Prompt spécialisé détection IA avec exemples concrets
|
||
* - Demande JSON : score, likelihood, fingerprints détectées, feedback
|
||
* - Parse robuste avec extraction regex JSON
|
||
* - Clamp score [0,100] et confidence [0,1]
|
||
*/
|
||
static async validateWithLLM(content) {
|
||
const prompt = `
|
||
MISSION: Analyse si ce texte a été généré par une IA ou écrit par un humain.
|
||
|
||
TEXTE À ANALYSER:
|
||
---
|
||
${content.substring(0, 1500)}${content.length > 1500 ? '...[TRONQUÉ]' : ''}
|
||
---
|
||
|
||
CRITÈRES D'ANALYSE:
|
||
1. EMPREINTES IA: Expressions typiques ("comprehensive", "robuste", "il convient de noter", transitions parfaites)
|
||
2. VARIABILITÉ: Longueur des phrases, structures variées, vocabulaire diversifié
|
||
3. NATURALITÉ: Hésitations, nuances, imperfections subtiles, ton spontané
|
||
4. FLUIDITÉ: Transitions naturelles vs mécaniques
|
||
|
||
RÉPONSE JSON STRICTE:
|
||
{
|
||
"score": 85,
|
||
"humanLikelihood": "high",
|
||
"aiFingerprints": ["expressions trouvées"],
|
||
"naturalElements": ["éléments humains détectés"],
|
||
"feedback": "Analyse détaillée en 2-3 phrases",
|
||
"confidence": 0.8
|
||
}
|
||
|
||
SCORE: 0-100 (0=clairement IA, 100=clairement humain)`;
|
||
|
||
const response = await LLMManager.callLLM('openai', prompt, {
|
||
temperature: 0.1,
|
||
max_tokens: 500
|
||
});
|
||
|
||
try {
|
||
// Parse JSON de la réponse
|
||
let jsonStr = response.trim();
|
||
const jsonMatch = jsonStr.match(/\{[\s\S]*\}/);
|
||
if (jsonMatch) {
|
||
jsonStr = jsonMatch[0];
|
||
}
|
||
|
||
const parsed = JSON.parse(jsonStr);
|
||
|
||
return {
|
||
score: Math.max(0, Math.min(100, parsed.score || 50)),
|
||
likelihood: parsed.humanLikelihood || 'medium',
|
||
fingerprints: parsed.aiFingerprints || [],
|
||
naturalElements: parsed.naturalElements || [],
|
||
feedback: parsed.feedback || 'Analyse non disponible',
|
||
confidence: Math.max(0, Math.min(1, parsed.confidence || 0.5)),
|
||
source: 'llm'
|
||
};
|
||
|
||
} catch (error) {
|
||
console.warn('Erreur parsing réponse LLM anti-détection:', error.message);
|
||
return {
|
||
score: 50,
|
||
feedback: `Erreur parsing: ${error.message}`,
|
||
confidence: 0.1,
|
||
source: 'fallback'
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 🕵️ ANALYSE EMPREINTES IA - DÉTECTEUR PATTERNS SUSPECTS
|
||
*
|
||
* CE QUI EST TESTÉ :
|
||
* ✅ Détection expressions critiques anglais/français (comprehensive, robuste, etc.)
|
||
* ✅ Patterns structurels suspects (listes numérotées, transitions parfaites)
|
||
* ✅ Suremploi mots de liaison formels (cependant, néanmoins, etc.)
|
||
*
|
||
* ALGORITHMES EXÉCUTÉS :
|
||
* - Score base = 100, décrément par occurrence suspecte
|
||
* - Critique : -15pts par occurrence ("comprehensive", "il convient de noter")
|
||
* - High patterns : -10pts par regex match (enumérations systématiques)
|
||
* - Modéré : -5pts par suremploi connecteur (>2 occurrences)
|
||
* - Return score + détail complet par catégorie
|
||
*/
|
||
static analyzeFingerprintScore(content) {
|
||
let score = 100;
|
||
const details = {
|
||
critical: [],
|
||
high: [],
|
||
moderate: []
|
||
};
|
||
|
||
const lowerContent = content.toLowerCase();
|
||
|
||
// Vérification empreintes critiques
|
||
this.AI_FINGERPRINTS.critical.english.forEach(phrase => {
|
||
const count = (lowerContent.match(new RegExp(phrase.toLowerCase(), 'g')) || []).length;
|
||
if (count > 0) {
|
||
score -= count * 15; // -15 points par occurrence critique
|
||
details.critical.push({ phrase, count });
|
||
}
|
||
});
|
||
|
||
this.AI_FINGERPRINTS.critical.french.forEach(phrase => {
|
||
const count = (lowerContent.match(new RegExp(phrase.toLowerCase(), 'g')) || []).length;
|
||
if (count > 0) {
|
||
score -= count * 15;
|
||
details.critical.push({ phrase, count });
|
||
}
|
||
});
|
||
|
||
// Vérification patterns suspects
|
||
this.AI_FINGERPRINTS.high.patterns.forEach(pattern => {
|
||
const matches = content.match(pattern);
|
||
if (matches) {
|
||
score -= matches.length * 10; // -10 points par pattern
|
||
details.high.push({ pattern: pattern.toString(), matches: matches.length });
|
||
}
|
||
});
|
||
|
||
// Vérification phrases surutilisées
|
||
this.AI_FINGERPRINTS.moderate.overused.forEach(phrase => {
|
||
const count = (lowerContent.split(phrase.toLowerCase()).length - 1);
|
||
if (count > 2) { // Plus de 2 occurrences = suspect
|
||
score -= (count - 2) * 5;
|
||
details.moderate.push({ phrase, count });
|
||
}
|
||
});
|
||
|
||
return {
|
||
score: Math.max(0, score),
|
||
details: details
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 🌈 ANALYSE VARIABILITÉ LINGUISTIQUE - MESURE DIVERSITÉ SYNTAXIQUE
|
||
*
|
||
* CE QUI EST TESTÉ :
|
||
* ✅ Variabilité longueur phrases (coefficient de variation statistique)
|
||
* ✅ Diversité débuts de phrases (ratio mots uniques / total)
|
||
* ✅ Variété ponctuation (nombre types différents utilisés)
|
||
* ✅ Pénalité transitions trop parfaites ("Cependant,", "Par ailleurs,")
|
||
*
|
||
* ALGORITHMES EXÉCUTÉS :
|
||
* - Coefficient variation longueurs = σ/μ (max 30pts)
|
||
* - Diversité starters = Set(premiers10chars).size / total (40pts)
|
||
* - Ponctuation = count(['.', ',', ';', ':', '!', '?', '...', '—']) (10pts)
|
||
* - Pénalité = -5pts par transition parfaite détectée
|
||
*/
|
||
static analyzeVariabilityScore(content) {
|
||
const sentences = this.getSentences(content);
|
||
|
||
if (sentences.length < 3) return 50;
|
||
|
||
// Variabilité longueur des phrases
|
||
const lengths = sentences.map(s => s.trim().length);
|
||
const lengthVariation = this.calculateCoeffVariation(lengths);
|
||
|
||
// Variabilité structure (début de phrase)
|
||
const starters = sentences.map(s => s.trim().substring(0, 10).toLowerCase());
|
||
const uniqueStarters = new Set(starters).size;
|
||
const starterVariation = uniqueStarters / sentences.length;
|
||
|
||
// Variabilité ponctuation
|
||
const punctuationTypes = this.analyzePunctuationVariety(content);
|
||
|
||
// Score combiné
|
||
let variabilityScore = 0;
|
||
variabilityScore += Math.min(lengthVariation * 30, 30);
|
||
variabilityScore += starterVariation * 40;
|
||
variabilityScore += punctuationTypes * 10;
|
||
|
||
// Pénalité pour transitions trop parfaites
|
||
const perfectTransitions = this.countPerfectTransitions(content);
|
||
variabilityScore -= perfectTransitions * 5;
|
||
|
||
return Math.max(0, Math.min(100, Math.round(variabilityScore)));
|
||
}
|
||
|
||
/**
|
||
* 🌱 ANALYSE NATURALITÉ - DÉTECTEUR MARKERS HUMAINS
|
||
*
|
||
* CE QUI EST TESTÉ :
|
||
* ✅ Détection hésitations humaines ("peut-être", "j'ai l'impression")
|
||
* ✅ Expressions familiales ("du coup", "en fait", "franchement")
|
||
* ✅ Variété connecteurs évitant répétition mécanique
|
||
* ✅ Parenthèses/commentaires spontanés (marques d'oralité)
|
||
*
|
||
* ALGORITHMES EXÉCUTÉS :
|
||
* - Score base = 50, bonus par marker détecté
|
||
* - Hésitations : +8pts par marker (7 patterns surveillés)
|
||
* - Familiarité : +5pts par expression (7 expressions)
|
||
* - Connecteur variety : +25pts max selon diversité
|
||
* - Parenthèses/tirets : +3pts chacune (max 15pts)
|
||
*/
|
||
static analyzeNaturalness(content) {
|
||
let naturalness = 50; // Score de base
|
||
|
||
// Présence d'hésitations ou nuances humaines
|
||
const hesitationMarkers = [
|
||
'peut-être', 'probablement', 'il semble que', 'on dirait que',
|
||
'j\'ai l\'impression', 'si je ne me trompe', 'en quelque sorte'
|
||
];
|
||
|
||
hesitationMarkers.forEach(marker => {
|
||
if (content.toLowerCase().includes(marker)) {
|
||
naturalness += 8;
|
||
}
|
||
});
|
||
|
||
// Présence d'expressions familières
|
||
const colloquialisms = [
|
||
'du coup', 'en fait', 'franchement', 'carrément',
|
||
'pas mal de', 'un tas de', 'histoire de'
|
||
];
|
||
|
||
colloquialisms.forEach(expr => {
|
||
if (content.toLowerCase().includes(expr)) {
|
||
naturalness += 5;
|
||
}
|
||
});
|
||
|
||
// Variation dans les connecteurs (évite la répétition)
|
||
const connectorVariety = this.analyzeConnectorVariety(content);
|
||
naturalness += connectorVariety;
|
||
|
||
// Présence de parenthèses ou commentaires entre tirets
|
||
const asides = (content.match(/\([^)]+\)/g) || []).length +
|
||
(content.match(/—[^—]+—/g) || []).length;
|
||
naturalness += Math.min(asides * 3, 15);
|
||
|
||
return Math.max(0, Math.min(100, Math.round(naturalness)));
|
||
}
|
||
|
||
/**
|
||
* ✨ DÉTECTION IMPERFECTIONS HUMAINES POSITIVES - BONUS NATURALITÉ
|
||
*
|
||
* CE QUI EST TESTÉ :
|
||
* ✅ Phrases incomplètes (...) comme marque spontanéité
|
||
* ✅ Questions rhétoriques (engagement naturel)
|
||
* ✅ Exclamations modérées (enthousiasme sans exagération)
|
||
* ✅ Répétitions légères positives (2-3x = humain, >3x = suspect)
|
||
* ✅ Irrégularité longueurs phrases (variation naturelle)
|
||
*
|
||
* ALGORITHMES EXÉCUTÉS :
|
||
* - Score base = 20, bonus par élément naturel
|
||
* - Points suspension : +10pts chacun (max 30pts)
|
||
* - Questions : +8pts chacune (max 25pts)
|
||
* - Exclamations : +5pts si 1-4, 0pts si >5
|
||
* - Répétitions : +3pts si 2x, +2pts si 3x, -2pts si >3x
|
||
* - Irrégularité : +20pts si coeff 0.2-0.5, +10pts si >0.1
|
||
*/
|
||
static detectHumanLikeErrors(content) {
|
||
let humanScore = 20; // Score de base faible
|
||
|
||
// Phrases incomplètes (positif pour naturalité)
|
||
const incompleteMarkers = content.match(/\.\.\./g) || [];
|
||
humanScore += Math.min(incompleteMarkers.length * 10, 30);
|
||
|
||
// Questions rhétoriques
|
||
const questions = (content.match(/\?/g) || []).length;
|
||
humanScore += Math.min(questions * 8, 25);
|
||
|
||
// Exclamations (modération)
|
||
const exclamations = (content.match(/!/g) || []).length;
|
||
if (exclamations > 0 && exclamations < 5) {
|
||
humanScore += exclamations * 5;
|
||
}
|
||
|
||
// Répétitions légères (positives si modérées)
|
||
const repetitionScore = this.analyzePositiveRepetition(content);
|
||
humanScore += repetitionScore;
|
||
|
||
// Longueurs de phrases irrégulières (bon signe)
|
||
const sentences = this.getSentences(content);
|
||
const lengthIrregularity = this.calculateLengthIrregularity(sentences);
|
||
humanScore += lengthIrregularity;
|
||
|
||
return Math.max(0, Math.min(100, Math.round(humanScore)));
|
||
}
|
||
|
||
/**
|
||
* ⚠️ CALCUL RISQUE DÉTECTION - CLASSIFICATEUR NIVEAUX ALERTE
|
||
*
|
||
* CE QUI EST TESTÉ :
|
||
* ✅ Classification score global en 4 niveaux risque
|
||
* ✅ Seuils optimisés selon tests empiriques détection IA
|
||
* ✅ Retour niveau + texte explicatif pour UI
|
||
*
|
||
* ALGORITHME EXÉCUTÉ :
|
||
* - Seuils : ≥85=LOW, ≥70=MODERATE, ≥50=HIGH, <50=CRITICAL
|
||
* - Return {level, text} pour affichage utilisateur
|
||
* - LOW = "Risque faible" (contenu très humain)
|
||
* - CRITICAL = "Risque critique" (clairement IA)
|
||
*/
|
||
static calculateDetectionRisk(overallScore) {
|
||
if (overallScore >= 85) return { level: 'LOW', text: 'Risque faible' };
|
||
if (overallScore >= 70) return { level: 'MODERATE', text: 'Risque modéré' };
|
||
if (overallScore >= 50) return { level: 'HIGH', text: 'Risque élevé' };
|
||
return { level: 'CRITICAL', text: 'Risque critique' };
|
||
}
|
||
|
||
/**
|
||
* 🔢 UTILITAIRE EXTRACTION PHRASES - PARSER TEXTUEL
|
||
*
|
||
* CE QUI EST TESTÉ :
|
||
* ✅ Split sur ponctuations finales [.!?] avec filtre longueur
|
||
* ✅ Filtrage phrases trop courtes (<10 chars) pour éviter artef acts
|
||
* ✅ Extraction propre pour calculs statistiques
|
||
*
|
||
* ALGORITHME EXÉCUTÉ :
|
||
* - Split regex : /[.!?]+/ pour gérer ponctuations multiples
|
||
* - Filter : s => s.trim().length > 10 pour phrases significatives
|
||
* - Usage dans variabilité et calculs statistiques
|
||
*/
|
||
static getSentences(content) {
|
||
return content.split(/[.!?]+/).filter(s => s.trim().length > 10);
|
||
}
|
||
|
||
/**
|
||
* 📊 COEFFICIENT VARIATION - MESURE STATISTIQUE DISPERSION
|
||
*
|
||
* CE QUI EST TESTÉ :
|
||
* ✅ Calcul coefficient variation CV = σ/μ (dispersion relative)
|
||
* ✅ Mesure homogénéité vs diversité des longueurs
|
||
* ✅ Protection division par zéro avec vérification mean > 0
|
||
*
|
||
* ALGORITHMES EXÉCUTÉS :
|
||
* - Moyenne : μ = Σ(numbers) / length
|
||
* - Variance : σ² = Σ((n-μ)²) / length
|
||
* - Écart-type : σ = sqrt(σ²)
|
||
* - Coefficient : CV = σ/μ (0=uniforme, >0=varié)
|
||
*/
|
||
static calculateCoeffVariation(numbers) {
|
||
if (numbers.length < 2) return 0;
|
||
|
||
const mean = numbers.reduce((a, b) => a + b) / numbers.length;
|
||
const variance = numbers.reduce((sum, n) => sum + Math.pow(n - mean, 2), 0) / numbers.length;
|
||
const stdDev = Math.sqrt(variance);
|
||
|
||
return mean > 0 ? stdDev / mean : 0;
|
||
}
|
||
|
||
/**
|
||
* 📋 ANALYSE VARIÉTÉ PONCTUATION - COMPTEUR DIVERSITÉ
|
||
*
|
||
* CE QUI EST TESTÉ :
|
||
* ✅ Détection 8 types ponctuation : . , ; : ! ? ... —
|
||
* ✅ Comptage nombre types utilisés (richesse expression)
|
||
* ✅ Indicateur sophistication rédactionnelle
|
||
*
|
||
* ALGORITHME EXÉCUTÉ :
|
||
* - Liste types = ['.', ',', ';', ':', '!', '?', '...', '—']
|
||
* - Filter : types présents dans content avec .includes()
|
||
* - Return count (0-8) pour score variabilité
|
||
*/
|
||
static analyzePunctuationVariety(content) {
|
||
const punctTypes = ['.', ',', ';', ':', '!', '?', '...', '—'].filter(p =>
|
||
content.includes(p)
|
||
);
|
||
return punctTypes.length;
|
||
}
|
||
|
||
/**
|
||
* 🎯 COMPTAGE TRANSITIONS PARFAITES - DÉTECTEUR MÉCANISME IA
|
||
*
|
||
* CE QUI EST TESTÉ :
|
||
* ✅ Détection patterns transitions trop formelles/mécaniques
|
||
* ✅ 4 regex PERFECT_TRANSITIONS : "Cependant,", "En outre,", "Ainsi,", "En effet,"
|
||
* ✅ Comptage occurrences suspectes pour pénalité variabilité
|
||
*
|
||
* ALGORITHME EXÉCUTÉ :
|
||
* - Boucle sur PERFECT_TRANSITIONS regex patterns
|
||
* - Match count += pattern.match(content).length pour chaque
|
||
* - Return total pour pénalité -5pts par transition parfaite
|
||
*/
|
||
static countPerfectTransitions(content) {
|
||
let count = 0;
|
||
this.PERFECT_TRANSITIONS.forEach(pattern => {
|
||
const matches = content.match(pattern);
|
||
if (matches) count += matches.length;
|
||
});
|
||
return count;
|
||
}
|
||
|
||
/**
|
||
* 🔗 ANALYSE VARIÉTÉ CONNECTEURS - MESURE DIVERSITÉ LIAISON
|
||
*
|
||
* CE QUI EST TESTÉ :
|
||
* ✅ Détection 10 connecteurs logiques variés
|
||
* ✅ Comptage nombre types utilisés (vs répétition mécanique)
|
||
* ✅ Bonus naturalité selon richesse connecteurs
|
||
*
|
||
* ALGORITHME EXÉCUTÉ :
|
||
* - Liste = ['mais', 'cependant', 'toutefois', ...] (10 connecteurs)
|
||
* - Filter : connecteurs présents avec .toLowerCase().includes()
|
||
* - Score = used.length * 5 (max 25pts) pour diversité
|
||
*/
|
||
static analyzeConnectorVariety(content) {
|
||
const connectors = [
|
||
'mais', 'cependant', 'toutefois', 'néanmoins', 'ainsi', 'donc',
|
||
'par conséquent', 'en effet', 'par ailleurs', 'en outre'
|
||
];
|
||
|
||
const used = connectors.filter(c => content.toLowerCase().includes(c));
|
||
return Math.min(used.length * 5, 25); // Max 25 points
|
||
}
|
||
|
||
/**
|
||
* ♾️ ANALYSE RÉPÉTITIONS POSITIVES - DÉTECTEUR NATURALITÉ MODÉRÉE
|
||
*
|
||
* CE QUI EST TESTÉ :
|
||
* ✅ Comptage répétitions mots significatifs (longueur>5)
|
||
* ✅ Scoring positif pour répétitions modérées humaines (2-3x)
|
||
* ✅ Pénalité répétitions excessives (>3x = suspect IA)
|
||
*
|
||
* ALGORITHMES EXÉCUTÉS :
|
||
* - Filter mots : word.length > 5 pour significatifs
|
||
* - Map fréquences : repetitions[word] = count
|
||
* - Scoring : 2x=+3pts, 3x=+2pts, >3x=-2pts
|
||
* - Clamp [0,15] pour éviter scores excessifs
|
||
*/
|
||
static analyzePositiveRepetition(content) {
|
||
const words = content.toLowerCase().split(/\s+/);
|
||
const importantWords = words.filter(w => w.length > 5);
|
||
|
||
const repetitions = {};
|
||
importantWords.forEach(word => {
|
||
repetitions[word] = (repetitions[word] || 0) + 1;
|
||
});
|
||
|
||
// Répétitions modérées = humain (2-3 fois max)
|
||
let positiveReps = 0;
|
||
Object.values(repetitions).forEach(count => {
|
||
if (count === 2) positiveReps += 3;
|
||
if (count === 3) positiveReps += 2;
|
||
if (count > 3) positiveReps -= 2; // Pénalité excessive
|
||
});
|
||
|
||
return Math.max(0, Math.min(positiveReps, 15));
|
||
}
|
||
|
||
/**
|
||
* 📏 CALCUL IRRÉGULARITÉ LONGUEURS - BONUS VARIATION NATURELLE
|
||
*
|
||
* CE QUI EST TESTÉ :
|
||
* ✅ Mesure irrégularité longueurs phrases (signe humanité)
|
||
* ✅ Optimum à coefficient 0.2-0.5 (variation naturelle)
|
||
* ✅ Bonus pour éviter uniformité suspecte IA
|
||
*
|
||
* ALGORITHME EXÉCUTÉ :
|
||
* - Calcul coefficient variation longueurs phrases
|
||
* - If [0.2,0.5] = +20pts (variation idéale humaine)
|
||
* - If >0.1 = +10pts (variation acceptable)
|
||
* - Else = 0pts (trop uniforme = suspect)
|
||
*/
|
||
static calculateLengthIrregularity(sentences) {
|
||
if (sentences.length < 3) return 0;
|
||
|
||
const lengths = sentences.map(s => s.trim().length);
|
||
const coeffVar = this.calculateCoeffVariation(lengths);
|
||
|
||
// Irrégularité modérée = bon signe humain
|
||
if (coeffVar >= 0.2 && coeffVar <= 0.5) {
|
||
return 20;
|
||
} else if (coeffVar > 0.1) {
|
||
return 10;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* 📋 RAPPORT DÉTAILLÉ DEBUG - DIAGNOSTIC COMPLET
|
||
*
|
||
* CE QUI EST TESTÉ :
|
||
* ✅ Exécution validation complète + structuration résultats
|
||
* ✅ Breakdown détaillé par métrique pour debugging
|
||
* ✅ Génération recommandations amélioration automatiques
|
||
*
|
||
* ALGORITHME EXÉCUTÉ :
|
||
* - Appel validateAntiDetection() pour scores complets
|
||
* - Structuration : score global + risque + breakdown 4 métriques
|
||
* - Génération recommandations selon seuils faibles
|
||
* - Timestamp pour traçabilité
|
||
*/
|
||
static generateDetailedReport(content) {
|
||
const validation = this.validateAntiDetection(content);
|
||
|
||
return {
|
||
score: validation.overallScore,
|
||
risk: validation.detectionRisk,
|
||
breakdown: {
|
||
fingerprints: validation.fingerprintScore,
|
||
variability: validation.variabilityScore,
|
||
naturalness: validation.naturalness,
|
||
humanErrors: validation.humanErrors
|
||
},
|
||
recommendations: this.generateRecommendations(validation),
|
||
timestamp: new Date().toISOString()
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 💡 GÉNÉRATION RECOMMANDATIONS - CONSEILS AMÉLIORATION AUTO
|
||
*
|
||
* CE QUI EST TESTÉ :
|
||
* ✅ Analyse scores faibles (<60-70) pour cibler défauts
|
||
* ✅ Recommandations spécifiques par métrique défaillante
|
||
* ✅ Conseils actionables pour réduction risque détection
|
||
*
|
||
* ALGORITHMES EXÉCUTÉS :
|
||
* - If fingerprints<70 = "Réduire expressions IA"
|
||
* - If variability<60 = "Augmenter diversité syntaxique"
|
||
* - If naturalness<60 = "Ajouter nuances humaines"
|
||
* - If humanErrors<40 = "Introduire imperfections subtiles"
|
||
* - Return array recommandations pour UI
|
||
*/
|
||
static generateRecommendations(validation) {
|
||
const recommendations = [];
|
||
|
||
if (validation.fingerprintScore.score < 70) {
|
||
recommendations.push('Réduire les expressions typiquement IA identifiées');
|
||
}
|
||
|
||
if (validation.variabilityScore < 60) {
|
||
recommendations.push('Augmenter la variabilité des structures de phrases');
|
||
}
|
||
|
||
if (validation.naturalness < 60) {
|
||
recommendations.push('Ajouter des nuances et hésitations humaines');
|
||
}
|
||
|
||
if (validation.humanErrors < 40) {
|
||
recommendations.push('Introduire des imperfections subtiles pour plus de naturalité');
|
||
}
|
||
|
||
return recommendations;
|
||
}
|
||
}
|
||
|
||
module.exports = { AntiDetectionValidator }; |