analyzeTemplateSentenceRisk($templateStats), $this->analyzeRepeatedPhraseRisk($repeatedStats), $this->analyzeSentenceLengthRisk($sentenceStats), ]; $overallScore = 0; $levelRank = ['low' => 1, 'medium' => 2, 'high' => 3]; $maxRank = 1; $flags = []; foreach ($dimensions as $dimension) { $overallScore += intval($dimension['score'] ?? 0); $rank = $levelRank[$dimension['level'] ?? 'low'] ?? 1; if ($rank > $maxRank) { $maxRank = $rank; } foreach ((array) ($dimension['flags'] ?? []) as $flag) { if ($flag !== '') { $flags[] = $flag; } } } $overallLevel = array_search($maxRank, $levelRank, true) ?: 'low'; return [ 'overall_level' => $overallLevel, 'overall_score' => min(100, $overallScore), 'summary' => $this->buildSummary($overallLevel, $dimensions, $flags), 'dimensions' => $dimensions, 'flags' => array_values(array_unique($flags)), ]; } private function analyzeTemplateSentenceRisk(array $stats): array { $items = (array) ($stats['items'] ?? []); $totalMatches = intval($stats['total_matches'] ?? 0); $matchedRuleCount = intval($stats['matched_rule_count'] ?? 0); $score = 0; $flags = []; foreach ($items as $item) { $count = intval($item['count'] ?? 0); $riskLevel = max(1, intval($item['risk_level'] ?? 1)); $weight = max(1, intval($item['weight'] ?? 1)); $score += $count * $riskLevel * $weight; if ($count > 0) { $section = (string) ($item['section'] ?? ''); $remark = trim((string) ($item['remark'] ?? '')); $label = $remark !== '' ? $remark : (string) ($item['sentence_pattern'] ?? ''); $flags[] = sprintf('%s段命中模版句:%s(%d次)', $section, $label, $count); } } $level = $this->scoreToLevel($score, 15, 40); return [ 'key' => 'template_sentence', 'name' => '模版句特征', 'level' => $level, 'score' => min(100, $score), 'summary' => $totalMatches > 0 ? sprintf('命中 %d 条模版句规则,共 %d 处', $matchedRuleCount, $totalMatches) : '未检测到典型 AI 模版句', 'metrics' => [ 'total_matches' => $totalMatches, 'matched_rule_count' => $matchedRuleCount, ], 'details' => $stats, 'flags' => array_slice($flags, 0, 8), ]; } private function analyzeRepeatedPhraseRisk(array $stats): array { $items = (array) ($stats['suspicious_items'] ?? []); if (empty($items)) { $items = array_values(array_filter((array) ($stats['items'] ?? []), function ($item) { return ($item['category'] ?? '') === 'suspicious'; })); } $uniqueCount = count($items); $expectedCount = count((array) ($stats['expected_topic_items'] ?? [])); $topCount = 0; $flags = []; foreach (array_slice($items, 0, 5) as $item) { $count = intval($item['count'] ?? 0); if ($count > $topCount) { $topCount = $count; } if ($count >= 4) { $flags[] = sprintf('可疑重复短语「%s」出现 %d 次', (string) ($item['phrase'] ?? ''), $count); } } $score = min(100, $uniqueCount * 3 + max(0, $topCount - 3) * 6); $level = 'low'; if ($uniqueCount >= 10 || $topCount >= 8) { $level = 'high'; } elseif ($uniqueCount >= 4 || $topCount >= 5) { $level = 'medium'; } $summary = '未发现可疑机械重复短语'; if ($uniqueCount > 0) { $summary = sprintf('发现 %d 组可疑重复短语,最高重复 %d 次', $uniqueCount, $topCount); } elseif ($expectedCount > 0) { $summary = sprintf('主题词正常重复 %d 组,未触发可疑短语', $expectedCount); } return [ 'key' => 'repeated_phrase', 'name' => '重复短语', 'level' => $level, 'score' => $score, 'summary' => $summary, 'metrics' => [ 'suspicious_count' => $uniqueCount, 'expected_topic_count' => $expectedCount, 'top_count' => $topCount, ], 'details' => $stats, 'flags' => $flags, ]; } private function analyzeSentenceLengthRisk(array $stats): array { $avg = floatval($stats['avg'] ?? 0); $std = floatval($stats['std'] ?? 0); $max = intval($stats['max'] ?? 0); $sentenceCount = intval($stats['sentence_count'] ?? 0); $score = 0; $flags = []; $level = 'low'; if ($sentenceCount >= 8) { if ($avg >= 32) { $score += 20; $flags[] = sprintf('平均句长偏高(%.1f 词/句)', $avg); } if ($std > 0 && $std <= 5 && $avg >= 12 && $avg <= 28) { $score += 25; $flags[] = sprintf('句长分布过于均匀(均值 %.1f,标准差 %.1f)', $avg, $std); } if ($max >= 50) { $score += 10; $flags[] = sprintf('存在超长句(最长 %d 词)', $max); } } $level = $this->scoreToLevel($score, 10, 25); return [ 'key' => 'sentence_length', 'name' => '句长特征', 'level' => $level, 'score' => min(100, $score), 'summary' => $sentenceCount > 0 ? sprintf('平均句长 %.1f,标准差 %.1f,共 %d 句', $avg, $std, $sentenceCount) : '正文句长数据不足', 'metrics' => [ 'avg' => $avg, 'std' => $std, 'max' => $max, 'min' => intval($stats['min'] ?? 0), 'sentence_count' => $sentenceCount, ], 'details' => $stats, 'flags' => $flags, ]; } private function scoreToLevel(int $score, int $mediumThreshold, int $highThreshold): string { if ($score >= $highThreshold) { return 'high'; } if ($score >= $mediumThreshold) { return 'medium'; } return 'low'; } private function buildSummary(string $overallLevel, array $dimensions, array $flags): string { $levelText = [ 'low' => '低风险', 'medium' => '中等风险', 'high' => '较高风险', ]; $parts = [$levelText[$overallLevel] ?? '低风险']; foreach ($dimensions as $dimension) { if (($dimension['level'] ?? 'low') === 'low') { continue; } $parts[] = $dimension['name'] . '需关注'; } if (!empty($flags)) { $parts[] = '重点:' . $flags[0]; } return implode(';', $parts); } }