610 lines
27 KiB
PHP
610 lines
27 KiB
PHP
|
|
<?php
|
|||
|
|
ini_set('display_errors', 1);
|
|||
|
|
ini_set('display_startup_errors', 1);
|
|||
|
|
error_reporting(E_ALL);
|
|||
|
|
ini_set('log_errors', 1);
|
|||
|
|
ini_set('error_log', 'logs/php_errors.log');
|
|||
|
|
|
|||
|
|
define('LOG_FILE', 'logs/CheckPDF779.log');
|
|||
|
|
define('OPENAI_API_KEY', 'sk-GS24OxHQYfq8ErW5CRLoN5F1CfJPxNsY');
|
|||
|
|
//define('OPENAI_API_URL', 'https://api.proxyapi.ru/openai/v1/chat/completions');
|
|||
|
|
define('OPENAI_API_URL', 'https://api.proxyapi.ru/openai/v1/chat/completions');
|
|||
|
|
|
|||
|
|
|
|||
|
|
// Подсчет токенов перед отправкой в GPT
|
|||
|
|
function countTokens($text) {
|
|||
|
|
$escapedText = escapeshellarg($text);
|
|||
|
|
$command = "echo $escapedText | python3 count_tokens.py"; // Вызываем Python-скрипт
|
|||
|
|
$output = shell_exec($command);
|
|||
|
|
$data = json_decode($output, true);
|
|||
|
|
return $data['tokens'] ?? strlen($text) / 4; // Фолбэк, если токены не посчитались
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Разбиение текста на части (если документ слишком длинный)
|
|||
|
|
function splitText($text, $maxTokens = 4000) {
|
|||
|
|
$chunks = [];
|
|||
|
|
while (mb_strlen($text, 'UTF-8') > 0) {
|
|||
|
|
$chunk = mb_substr($text, 0, $maxTokens * 4, 'UTF-8');
|
|||
|
|
$chunks[] = trim($chunk);
|
|||
|
|
$text = mb_substr($text, $maxTokens * 4, null, 'UTF-8');
|
|||
|
|
}
|
|||
|
|
return $chunks;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
function logMessage($message) {
|
|||
|
|
$message = mb_convert_encoding($message, 'UTF-8', 'auto');
|
|||
|
|
file_put_contents(LOG_FILE, date('Y-m-d H:i:s') . " - " . $message . "\n", FILE_APPEND | LOCK_EX);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function transliterate($text) {
|
|||
|
|
$cyr = ['а','б','в','г','д','е','ё','ж','з','и','й','к','л','м','н','о','п','р','с','т','у','ф','х','ц','ч','ш','щ','ъ','ы','ь','э','ю','я'];
|
|||
|
|
$lat = ['a','b','v','g','d','e','yo','zh','z','i','y','k','l','m','n','o','p','r','s','t','u','f','h','ts','ch','sh','shch','','y','','e','yu','ya'];
|
|||
|
|
return str_replace($cyr, $lat, mb_strtolower($text));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function classifyImage($imagePath) {
|
|||
|
|
$absolutePath = realpath($imagePath);
|
|||
|
|
if (!$absolutePath) {
|
|||
|
|
logMessage("ERROR: Не удалось получить абсолютный путь для " . $imagePath);
|
|||
|
|
return [];
|
|||
|
|
}
|
|||
|
|
logMessage("DEBUG: Абсолютный путь для классификации: " . $absolutePath);
|
|||
|
|
|
|||
|
|
$escapedPath = escapeshellarg($absolutePath);
|
|||
|
|
logMessage("DEBUG: Экранированный путь для классификации: " . $escapedPath);
|
|||
|
|
|
|||
|
|
// Используем json.dumps для корректного JSON-вывода
|
|||
|
|
$command = "python3 -c \"import json; from nudenet import NudeClassifier; classifier = NudeClassifier(); print(json.dumps(classifier.classify($escapedPath)))\"";
|
|||
|
|
logMessage("DEBUG: Выполнение команды: " . $command);
|
|||
|
|
|
|||
|
|
$output = shell_exec($command);
|
|||
|
|
logMessage("DEBUG: Вывод команды: " . $output);
|
|||
|
|
|
|||
|
|
if ($output === null) {
|
|||
|
|
logMessage("ERROR: shell_exec вернул null при выполнении NudeClassifier");
|
|||
|
|
return [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return json_decode(trim($output), true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function renameFileForProcessing($filePath, $targetFolder = "scanpdf") {
|
|||
|
|
$pathInfo = pathinfo($filePath);
|
|||
|
|
$newName = transliterate($pathInfo['filename']) . "_" . uniqid() . "." . $pathInfo['extension'];
|
|||
|
|
$newPath = rtrim($targetFolder, "/") . "/" . $newName;
|
|||
|
|
if (copy($filePath, $newPath)) {
|
|||
|
|
logMessage("Файл переименован: $filePath -> $newPath");
|
|||
|
|
return $newPath;
|
|||
|
|
} else {
|
|||
|
|
logMessage("Ошибка при копировании файла: $filePath");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function extractTextFromPDF($pdfPath) {
|
|||
|
|
$text = shell_exec("pdftotext -layout -enc UTF-8 " . escapeshellarg($pdfPath) . " -");
|
|||
|
|
$text = mb_convert_encoding($text, 'UTF-8', 'auto');
|
|||
|
|
logMessage("DEBUG: Извлеченный текст: " . substr($text, 0, 500));
|
|||
|
|
return trim($text);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function convertPdfToImages($pdfPath, $outputDir) {
|
|||
|
|
if (!file_exists($outputDir)) {
|
|||
|
|
mkdir($outputDir, 0777, true);
|
|||
|
|
logMessage("Создана директория для изображений: $outputDir");
|
|||
|
|
}
|
|||
|
|
$imagePattern = $outputDir . '/page-%03d.jpg';
|
|||
|
|
$command = "convert -density 300 " . escapeshellarg($pdfPath) . " -quality 90 " . escapeshellarg($imagePattern);
|
|||
|
|
logMessage("Выполняем команду: " . $command);
|
|||
|
|
exec($command . " 2>&1", $output, $returnVar);
|
|||
|
|
logMessage("DEBUG: Вывод convert: " . implode("\n", $output));
|
|||
|
|
if ($returnVar !== 0) {
|
|||
|
|
logMessage("Ошибка при конвертации PDF в изображения.");
|
|||
|
|
return [];
|
|||
|
|
}
|
|||
|
|
return glob($outputDir . '/*.jpg');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function deleteFolderAndContents($folderPath) {
|
|||
|
|
if (is_dir($folderPath)) {
|
|||
|
|
$files = array_diff(scandir($folderPath), array('.', '..'));
|
|||
|
|
foreach ($files as $file) {
|
|||
|
|
$filePath = $folderPath . DIRECTORY_SEPARATOR . $file;
|
|||
|
|
if (is_dir($filePath)) {
|
|||
|
|
deleteFolderAndContents($filePath);
|
|||
|
|
} else {
|
|||
|
|
unlink($filePath);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
rmdir($folderPath);
|
|||
|
|
logMessage("Удалена подпапка и её содержимое: $folderPath");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Подключение к БД
|
|||
|
|
$dbconfig = [
|
|||
|
|
'db_server' => 'localhost',
|
|||
|
|
'db_port' => '3306',
|
|||
|
|
'db_username' => 'ci20465_72new',
|
|||
|
|
'db_password' => 'EcY979Rn',
|
|||
|
|
'db_name' => 'ci20465_72new'
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
$conn = new mysqli(
|
|||
|
|
$dbconfig['db_server'],
|
|||
|
|
$dbconfig['db_username'],
|
|||
|
|
$dbconfig['db_password'],
|
|||
|
|
$dbconfig['db_name'],
|
|||
|
|
$dbconfig['db_port']
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if ($conn->connect_error) {
|
|||
|
|
logMessage("Ошибка подключения к БД: " . $conn->connect_error);
|
|||
|
|
die(json_encode(["status" => "error", "message" => "Ошибка подключения к БД."]));
|
|||
|
|
}
|
|||
|
|
$conn->set_charset("utf8mb4");
|
|||
|
|
|
|||
|
|
$id = $_POST['id'] ?? null;
|
|||
|
|
if (!$id || !is_numeric($id)) {
|
|||
|
|
logMessage("Ошибка: Некорректный ID.");
|
|||
|
|
die(json_encode(["status" => "error", "message" => "Некорректный ID."]));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$sql = "
|
|||
|
|
SELECT n.title,
|
|||
|
|
CASE WHEN a.storedname IS NOT NULL
|
|||
|
|
THEN CONCAT(a.path, a.attachmentsid, '_', a.storedname)
|
|||
|
|
ELSE CONCAT(a.path, a.attachmentsid, '_', a.name)
|
|||
|
|
END AS filepath
|
|||
|
|
FROM vtiger_senotesrel r
|
|||
|
|
LEFT JOIN vtiger_notes n ON n.notesid = r.notesid
|
|||
|
|
LEFT JOIN vtiger_crmentity e ON e.crmid = r.notesid
|
|||
|
|
LEFT JOIN vtiger_seattachmentsrel r2 ON r2.crmid = r.notesid
|
|||
|
|
LEFT JOIN vtiger_attachments a ON a.attachmentsid = r2.attachmentsid
|
|||
|
|
WHERE r.crmid = ? AND e.deleted = 0
|
|||
|
|
AND (a.type = 'application/pdf' OR a.type = 'application/octet-stream')
|
|||
|
|
";
|
|||
|
|
|
|||
|
|
$stmt = $conn->prepare($sql);
|
|||
|
|
$stmt->bind_param("i", $id);
|
|||
|
|
$stmt->execute();
|
|||
|
|
$result = $stmt->get_result();
|
|||
|
|
|
|||
|
|
if ($result->num_rows == 0) {
|
|||
|
|
logMessage("Ошибка: Данные не найдены в БД.");
|
|||
|
|
die(json_encode(["status" => "error", "message" => "Нет данных."]));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Создаем уникальную рабочую папку для текущего запуска
|
|||
|
|
$uniqueFolder = "scanpdf/" . uniqid('run_', true);
|
|||
|
|
if (!file_exists($uniqueFolder)) {
|
|||
|
|
mkdir($uniqueFolder, 0777, true);
|
|||
|
|
logMessage("Создана подпапка для работы: $uniqueFolder");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$files_data = [];
|
|||
|
|
$maxTokens = 128000; // Лимит контекста GPT-4-Turbo
|
|||
|
|
$usedTokens = 0;
|
|||
|
|
$splitDocuments = [];
|
|||
|
|
$files_data = [];
|
|||
|
|
|
|||
|
|
while ($row = $result->fetch_assoc()) {
|
|||
|
|
$title = $row["title"];
|
|||
|
|
$filePath = $row["filepath"];
|
|||
|
|
logMessage("Обрабатываем файл: " . $filePath);
|
|||
|
|
|
|||
|
|
$tempFilePath = renameFileForProcessing($filePath, $uniqueFolder);
|
|||
|
|
if (!$tempFilePath) continue;
|
|||
|
|
|
|||
|
|
$text = extractTextFromPDF($tempFilePath);
|
|||
|
|
$data = ["title" => $title];
|
|||
|
|
|
|||
|
|
if (!empty(trim($text)) && mb_strlen(trim($text), 'UTF-8') >= 100) {
|
|||
|
|
logMessage("DEBUG: Текст извлечён из $title: " . substr($text, 0, 500));
|
|||
|
|
$data["text"] = $text;
|
|||
|
|
} else {
|
|||
|
|
logMessage("PDF '$title' пуст или содержит слишком мало текста, конвертируем в изображения.");
|
|||
|
|
$outputDir = $uniqueFolder . "/pdf_images_" . uniqid();
|
|||
|
|
$images = convertPdfToImages($tempFilePath, $outputDir);
|
|||
|
|
|
|||
|
|
if (!empty($images)) {
|
|||
|
|
logMessage("DEBUG: Изображения созданы из $title: " . implode(", ", $images));
|
|||
|
|
$recognizedText = "";
|
|||
|
|
$data["nsfw_alert"] = false;
|
|||
|
|
|
|||
|
|
foreach ($images as $imagePath) {
|
|||
|
|
logMessage("DEBUG: Отправляем изображение '$imagePath' на проверку NSFW.");
|
|||
|
|
$classification = classifyImage($imagePath);
|
|||
|
|
if ($classification === null) {
|
|||
|
|
logMessage("DEBUG: Классификатор вернул null для '$imagePath'. NSFW-метка установлена.");
|
|||
|
|
$data["nsfw_alert"] = true;
|
|||
|
|
} elseif (isset($classification[$imagePath]) && $classification[$imagePath]['unsafe'] > 0.8) {
|
|||
|
|
logMessage("⚠️ Обнаружено NSFW-изображение: $imagePath (unsafe = " . $classification[$imagePath]['unsafe'] . ")");
|
|||
|
|
$data["nsfw_alert"] = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$ocrText = shell_exec("tesseract " . escapeshellarg($imagePath) . " stdout -l rus+eng --oem 1");
|
|||
|
|
if (!empty(trim($ocrText))) {
|
|||
|
|
$recognizedText .= trim($ocrText) . "\n";
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($data["nsfw_alert"]) {
|
|||
|
|
logMessage("DEBUG: NSFW-контент найден, передаем изображения вместо текста.");
|
|||
|
|
unset($data["text"]);
|
|||
|
|
$data["images"] = $images;
|
|||
|
|
} else {
|
|||
|
|
if (!empty(trim($recognizedText)) && mb_strlen(trim($recognizedText), 'UTF-8') >= 100) {
|
|||
|
|
logMessage("DEBUG: OCR-текст извлечён: " . substr($recognizedText, 0, 500));
|
|||
|
|
$data["text"] = $recognizedText;
|
|||
|
|
} else {
|
|||
|
|
logMessage("PDF '$title' остаётся без читаемого текста, передаем изображения.");
|
|||
|
|
$data["images"] = $images;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
logMessage("Ошибка: PDF пуст, OCR не дал результатов, изображения не созданы.");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 🔹 Разбиваем текст на части, если слишком длинный
|
|||
|
|
if (isset($data["text"]) && !empty(trim($data["text"]))) {
|
|||
|
|
$tokens = countTokens($data["text"]);
|
|||
|
|
|
|||
|
|
logMessage("DEBUG: Документ '$title' содержит $tokens токенов.");
|
|||
|
|
if ($tokens > 4000) {
|
|||
|
|
logMessage("⚠️ Документ '$title' слишком длинный ($tokens токенов), он будет разбит на части.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
if ($tokens + $usedTokens > $maxTokens) {
|
|||
|
|
logMessage("⚠️ Превышен лимит токенов, часть данных не будет отправлена.");
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($tokens > 4000) {
|
|||
|
|
logMessage("⚠️ Документ '$title' слишком длинный, разбиваем на части.");
|
|||
|
|
$chunks = splitText($data["text"]);
|
|||
|
|
foreach ($chunks as $chunk) {
|
|||
|
|
$splitDocuments[] = ["title" => $title, "text" => $chunk, "nsfw_alert" => $data["nsfw_alert"]];
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
$splitDocuments[] = $data;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$usedTokens += $tokens;
|
|||
|
|
} else {
|
|||
|
|
$splitDocuments[] = $data;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 🔹 Добавляем данные в $files_data[]
|
|||
|
|
if (!empty($splitDocuments)) {
|
|||
|
|
foreach ($splitDocuments as $splitDoc) {
|
|||
|
|
$files_data[] = $splitDoc;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
$files_data[] = $data;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 🔹 Логируем NSFW-обнаружение
|
|||
|
|
if (isset($data["nsfw_alert"]) && $data["nsfw_alert"] === true) {
|
|||
|
|
logMessage("DEBUG: Обнаружен NSFW-контент в документе '$title'.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 🔹 Проверяем, прошла ли модерация
|
|||
|
|
if (isset($data["nsfw_alert"]) && $data["nsfw_alert"] === true) {
|
|||
|
|
$data["moderation_passed"] = false;
|
|||
|
|
} else {
|
|||
|
|
$data["moderation_passed"] = !empty(trim($data["text"]));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
/* while ($row = $result->fetch_assoc()) {
|
|||
|
|
$title = $row["title"];
|
|||
|
|
$filePath = $row["filepath"];
|
|||
|
|
logMessage("Обрабатываем файл: " . $filePath);
|
|||
|
|
|
|||
|
|
$tempFilePath = renameFileForProcessing($filePath, $uniqueFolder);
|
|||
|
|
if (!$tempFilePath) continue;
|
|||
|
|
|
|||
|
|
$text = extractTextFromPDF($tempFilePath);
|
|||
|
|
$data = ["title" => $title];
|
|||
|
|
|
|||
|
|
if (!empty(trim($text)) && mb_strlen(trim($text), 'UTF-8') >= 100) {
|
|||
|
|
logMessage("DEBUG: Текст извлечён из $title: " . substr($text, 0, 500));
|
|||
|
|
$data["text"] = $text;
|
|||
|
|
} else {
|
|||
|
|
logMessage("PDF '$title' пуст или содержит слишком мало текста, конвертируем в изображения.");
|
|||
|
|
$outputDir = $uniqueFolder . "/pdf_images_" . uniqid();
|
|||
|
|
$images = convertPdfToImages($tempFilePath, $outputDir);
|
|||
|
|
|
|||
|
|
if (!empty($images)) {
|
|||
|
|
logMessage("DEBUG: Изображения созданы из $title: " . implode(", ", $images));
|
|||
|
|
$recognizedText = "";
|
|||
|
|
$data["nsfw_alert"] = false; // Инициализируем флаг NSFW
|
|||
|
|
|
|||
|
|
foreach ($images as $imagePath) {
|
|||
|
|
logMessage("DEBUG: Отправляем изображение '$imagePath' на проверку NSFW.");
|
|||
|
|
$absImagePath = realpath($imagePath);
|
|||
|
|
logMessage("DEBUG: Абсолютный путь для проверки NSFW: " . $absImagePath);
|
|||
|
|
$classification = classifyImage($imagePath);
|
|||
|
|
if ($classification === null) {
|
|||
|
|
logMessage("DEBUG: Классификатор вернул null для изображения '$absImagePath'. Устанавливаем nsfw_alert в true.");
|
|||
|
|
$data["nsfw_alert"] = true;
|
|||
|
|
} elseif (isset($classification[$absImagePath])) {
|
|||
|
|
$unsafeProbability = $classification[$absImagePath]['unsafe'];
|
|||
|
|
logMessage("DEBUG: Для изображения '$absImagePath' получено unsafeProbability = " . $unsafeProbability);
|
|||
|
|
if ($unsafeProbability > 0.8) {
|
|||
|
|
logMessage("⚠️ Обнаружено NSFW-изображение: $absImagePath (unsafe = " . $unsafeProbability . ")");
|
|||
|
|
$data["nsfw_alert"] = true;
|
|||
|
|
} else {
|
|||
|
|
logMessage("DEBUG: unsafeProbability для '$absImagePath' ниже порогового значения (0.8). Значение = " . $unsafeProbability);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
logMessage("DEBUG: Нет данных проверки NSFW для изображения '$absImagePath'.");
|
|||
|
|
}
|
|||
|
|
$ocrText = shell_exec("tesseract " . escapeshellarg($imagePath) . " stdout -l rus+eng --oem 1 --psm 3");
|
|||
|
|
|
|||
|
|
// $ocrText = shell_exec("tesseract " . escapeshellarg($imagePath) . " stdout -l rus+eng --oem 1");
|
|||
|
|
if (!empty(trim($ocrText))) {
|
|||
|
|
$recognizedText .= trim($ocrText) . "\n";
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Если обнаружен NSFW-контент, отдаем изображения вместо OCR-текста
|
|||
|
|
if (isset($data["nsfw_alert"]) && $data["nsfw_alert"] === true) {
|
|||
|
|
logMessage("DEBUG: Обнаружен NSFW-контент, поэтому вместо OCR-текста передаем изображения.");
|
|||
|
|
unset($data["text"]);
|
|||
|
|
$data["images"] = $images;
|
|||
|
|
} else {
|
|||
|
|
if (!empty(trim($recognizedText)) && mb_strlen(trim($recognizedText), 'UTF-8') >= 100) {
|
|||
|
|
logMessage("DEBUG: OCR-текст (LSTM) извлечён из изображений $title: " . substr($recognizedText, 0, 500));
|
|||
|
|
$data["text"] = $recognizedText;
|
|||
|
|
} else {
|
|||
|
|
logMessage("PDF '$title' остаётся без читаемого текста, отправляем изображения в GPT.");
|
|||
|
|
$data["images"] = $images;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
logMessage("Ошибка: PDF пуст, OCR не дал результатов, изображения не созданы.");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Добавляем новое поле moderation_passed:
|
|||
|
|
// Если NSFW обнаружен, модерация не пройдена, иначе, если присутствует текст, считаем, что модерация пройдена.
|
|||
|
|
if (isset($data["nsfw_alert"]) && $data["nsfw_alert"] === true) {
|
|||
|
|
$data["moderation_passed"] = false;
|
|||
|
|
} else {
|
|||
|
|
if (isset($data["text"]) && !empty(trim($data["text"]))) {
|
|||
|
|
$data["moderation_passed"] = true;
|
|||
|
|
} else {
|
|||
|
|
$data["moderation_passed"] = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
$files_data[] = $data;
|
|||
|
|
|
|||
|
|
if (isset($data["nsfw_alert"]) && $data["nsfw_alert"] === true) {
|
|||
|
|
logMessage("DEBUG: Обнаружен NSFW-контент в документе '$title'.");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
|
|||
|
|
logMessage("DEBUG: JSON, отправляемый в OpenAI: " . json_encode($files_data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
|
|||
|
|
|
|||
|
|
$task = '**🔹 Инструкция для анализа документов**
|
|||
|
|
**Задача:**
|
|||
|
|
Проанализируй загруженные документы и подготовь детальный отчет.
|
|||
|
|
Отвечай **строго по структуре**, не пропуская важные данные.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## **1️⃣ Список файлов и проверка соответствия названий**
|
|||
|
|
- Перечисли **все загруженные файлы**.
|
|||
|
|
- Проверь, соответствуют ли их названия содержимому.
|
|||
|
|
- Если файл содержит изображение:
|
|||
|
|
- **Попробуй извлечь текст** и проанализируй его.
|
|||
|
|
- Если текст плохо читается или частично распознан – укажи это.
|
|||
|
|
- Если текста нет – **опиши, что изображено**.
|
|||
|
|
- Обрати внимание на **NSFW-контент** (если `nsfw_alert: true`).
|
|||
|
|
- Проверь такие файлы **более внимательно**.
|
|||
|
|
- Если контент сомнительный, напиши: **"ТРЭШ-контент, требуется ручная проверка"**.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## **2️⃣ Краткий анализ спора**
|
|||
|
|
- Определи **истца** (потребителя) и **ответчика** (организацию, на которую подана жалоба).
|
|||
|
|
- Опиши **суть спора**:
|
|||
|
|
- Что произошло?
|
|||
|
|
- Какая проблема заявлена?
|
|||
|
|
- Какое требование выдвигает истец?
|
|||
|
|
- Укажи **основные аргументы сторон**:
|
|||
|
|
- **Позиция истца:**
|
|||
|
|
- **Возможные возражения ответчика:**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## **3️⃣ Проверка на цензуру**
|
|||
|
|
- Проанализируй текст на **ненормативную лексику** и **оскорбления**.
|
|||
|
|
- Проверь изображения на **нецензурный контент, запрещенную символику, сцены насилия**.
|
|||
|
|
- Если обнаружены нарушения, **укажи, какие именно**.
|
|||
|
|
- Если требуется ручная проверка, **отметь приоритетные файлы**.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
|
|||
|
|
# **4️⃣ Выдача итогового вердикта**
|
|||
|
|
- Если всё соответствует, укажи: \"Вердикт: Прошло модерацию.\"
|
|||
|
|
- Если имеются несоответствия или потенциальные проблемы, укажи, что требуется ручная проверка.
|
|||
|
|
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## **5️⃣ Характер спора**
|
|||
|
|
Определи, к какой категории относится спор. Примеры:
|
|||
|
|
- **"Характер спора: некачественный товар"**
|
|||
|
|
- **"Характер спора: товар не привезли"**
|
|||
|
|
- **"Характер спора: отказ в возврате денег"**
|
|||
|
|
- **"Характер спора: некачественная услуга"**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## **6️⃣ Вероятность решения в пользу истца**
|
|||
|
|
**Вероятность положительного решения спора:** _**__%**_
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## **7️⃣ Чего не хватает для полного анализа?**
|
|||
|
|
- Перечисли **документы или данные**, которые нужно запросить дополнительно.
|
|||
|
|
- Используй формат: **"Запросить: ____"**.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### **📌 Важно**
|
|||
|
|
- Отчет должен быть **структурированным, четким и лаконичным**.
|
|||
|
|
- Если требуется ручная проверка, **укажи, какие файлы нужно проверить в первую очередь**.
|
|||
|
|
- Укажи, **какими нормами права РФ** регулируется данный спор (например, Закон РФ «О защите прав потребителей», ГК РФ и т.д.).';
|
|||
|
|
|
|||
|
|
|
|||
|
|
logMessage("DEBUG: Начинаем формирование JSON для отправки в GPT-4 Turbo.");
|
|||
|
|
|
|||
|
|
// 🔹 Формируем массив сообщений перед кодированием в JSON
|
|||
|
|
$messages = [
|
|||
|
|
["role" => "system", "content" => "Ты юридический аналитик. Проанализируй материалы согласно инструкции."],
|
|||
|
|
["role" => "user", "content" => $task]
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
foreach ($splitDocuments as $doc) {
|
|||
|
|
$messages[] = ["role" => "user", "content" => json_encode($doc, JSON_UNESCAPED_UNICODE)];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 🔹 Кодируем JSON корректно (теперь $body создается после массива $messages)
|
|||
|
|
$body = json_encode([
|
|||
|
|
"model" => "gpt-4-turbo",
|
|||
|
|
//"model" => "gpt-4",
|
|||
|
|
"messages" => $messages,
|
|||
|
|
"temperature" => 0.7,
|
|||
|
|
"max_completion_tokens" => 4000
|
|||
|
|
], JSON_UNESCAPED_UNICODE);
|
|||
|
|
|
|||
|
|
logMessage("DEBUG: Итоговый JSON перед отправкой: " . $body);
|
|||
|
|
|
|||
|
|
logMessage("DEBUG: Общая сумма токенов перед отправкой: " . $usedTokens);
|
|||
|
|
|
|||
|
|
if ($usedTokens > 128000) {
|
|||
|
|
logMessage("⚠️ Превышен лимит токенов, отправка будет урезана!");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 🔹 Отправка запроса в OpenAI API
|
|||
|
|
$curl = curl_init();
|
|||
|
|
curl_setopt_array($curl, [
|
|||
|
|
CURLOPT_URL => OPENAI_API_URL,
|
|||
|
|
CURLOPT_RETURNTRANSFER => true,
|
|||
|
|
CURLOPT_POST => true,
|
|||
|
|
CURLOPT_POSTFIELDS => $body,
|
|||
|
|
CURLOPT_HTTPHEADER => [
|
|||
|
|
'Content-Type: application/json',
|
|||
|
|
'Authorization: Bearer ' . OPENAI_API_KEY
|
|||
|
|
]
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
// 🚀 ОТКЛЮЧАЕМ реальную отправку в GPT
|
|||
|
|
// $response = curl_exec($curl);
|
|||
|
|
/* ДЛЯ ТЕСТА!!!!!
|
|||
|
|
$http_code = 200; // Симулируем успешный HTTP-код
|
|||
|
|
$curl_error = null;
|
|||
|
|
$response = '{"choices":[{"message":{"content":"Вердикт: Прошло модерацию."}}]}'; // Фейковый ответ GPT
|
|||
|
|
|
|||
|
|
curl_close($curl);
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
|
|||
|
|
$response = curl_exec($curl);
|
|||
|
|
$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|||
|
|
$curl_error = curl_error($curl);
|
|||
|
|
curl_close($curl);
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 🔹 Логируем ответ API
|
|||
|
|
logMessage("DEBUG: HTTP-код ответа от API: " . $http_code);
|
|||
|
|
logMessage("DEBUG: Ошибка CURL (если есть): " . ($curl_error ?: "Нет ошибок"));
|
|||
|
|
logMessage("DEBUG: Ответ от GPT-4-Turbo: " . $response);
|
|||
|
|
|
|||
|
|
// 🔹 Проверяем, не пустой ли ответ от API
|
|||
|
|
if ($response === false || empty($response)) {
|
|||
|
|
logMessage("Ошибка: пустой ответ от GPT-4 Turbo.");
|
|||
|
|
die(json_encode(["status" => "error", "message" => "Ошибка: пустой ответ от GPT-4 Turbo."]));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 🔹 Проверяем, пришел ли корректный JSON
|
|||
|
|
$gptAnalysis = json_decode($response, true);
|
|||
|
|
if ($gptAnalysis === null) {
|
|||
|
|
logMessage("Ошибка: JSON-декодирование не удалось.");
|
|||
|
|
die(json_encode(["status" => "error", "message" => "Ошибка: JSON-декодирование не удалось."]));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 🔹 Проверяем, есть ли в ответе 'choices'
|
|||
|
|
if (!isset($gptAnalysis['choices'][0]['message']['content'])) {
|
|||
|
|
logMessage("Ошибка: в JSON-ответе отсутствует ключ 'choices'.");
|
|||
|
|
die(json_encode(["status" => "error", "message" => "Ошибка: в JSON-ответе отсутствует ключ 'choices'."]));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 🔹 Получаем текстовый ответ от GPT
|
|||
|
|
|
|||
|
|
$content = $gptAnalysis['choices'][0]['message']['content'] ?? null;
|
|||
|
|
if (!$content) {
|
|||
|
|
logMessage("Ошибка: контент не найден в ответе от GPT.");
|
|||
|
|
die(json_encode(["status" => "error", "message" => "Ошибка: контент не найден в ответе от GPT."]));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//logMessage("DEBUG: Извлеченный контент: " . $content);
|
|||
|
|
|
|||
|
|
// Извлекаем модерационный вердикт из ответа GPT.
|
|||
|
|
// Ищем строку, начинающуюся с "Вердикт:" и берем все, что идет после неё.
|
|||
|
|
|
|||
|
|
$moderationVerdict = "";
|
|||
|
|
|
|||
|
|
// Попробуем найти строку с вердиктом
|
|||
|
|
if (preg_match('/Вердикт:\s*(Прошло модерацию|Не прошло модерацию)/ui', $content, $matches)) {
|
|||
|
|
$moderationVerdict = trim($matches[1]); // Получаем сам текст вердикта
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Логируем извлеченный вердикт модерации
|
|||
|
|
logMessage("DEBUG: Извлеченный вердикт модерации: " . ($moderationVerdict ?: "Не найден"));
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
$final_output = [
|
|||
|
|
"status" => "complete",
|
|||
|
|
"content" => $content,
|
|||
|
|
"moderationVerdict" => $moderationVerdict,
|
|||
|
|
"files_data" => $files_data
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
logMessage("DEBUG: Извлеченный контент: " . $content);
|
|||
|
|
logMessage("DEBUG: Извлеченный вердикт модерации: " . $moderationVerdict);
|
|||
|
|
|
|||
|
|
echo json_encode($final_output, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
|||
|
|
|
|||
|
|
|
|||
|
|
//echo json_encode(["status" => "complete", "content" => $content], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
logMessage("Отправка запроса в GPT завершена.");
|
|||
|
|
|
|||
|
|
deleteFolderAndContents($uniqueFolder);
|
|||
|
|
|
|||
|
|
?>
|