- Added comprehensive AI Assistant system (aiassist/ directory): * Vector search and embedding capabilities * Typebot proxy integration * Elastic search functionality * Message classification and chat history * MCP proxy for external integrations - Implemented Court Status API (GetCourtStatus.php): * Real-time court document status checking * Integration with external court systems * Comprehensive error handling and logging - Enhanced S3 integration: * Improved file backup system with metadata * Batch processing capabilities * Enhanced error logging and recovery * Copy operations with URL fixing - Added Telegram contact creation API - Improved error logging across all modules - Enhanced callback system for AI responses - Extensive backup file storage with timestamps - Updated documentation and README files - File storage improvements: * Thousands of backup files with proper metadata * Fix operations for broken file references * Project-specific backup and recovery systems * Comprehensive file integrity checking Total: 26,461+ files added/modified including AWS SDK, vendor dependencies, and extensive backup system.
1345 lines
62 KiB
PHP
1345 lines
62 KiB
PHP
<?php
|
||
// aiassist/gptAssistant.php
|
||
require_once 'config.php';
|
||
require_once 'logger.php';
|
||
|
||
|
||
function createNewThread() {
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
CURLOPT_URL => OPENAI_THREADS_API,
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_POST => true,
|
||
CURLOPT_HTTPHEADER => [
|
||
'Authorization: Bearer ' . OPENAI_API_KEY,
|
||
'OpenAI-Beta: assistants=v2'
|
||
]
|
||
]);
|
||
$response = curl_exec($curl);
|
||
$decoded = json_decode($response, true);
|
||
curl_close($curl);
|
||
|
||
if (isset($decoded['id'])) {
|
||
logMessage("🔄 Создан новый thread_id: " . $decoded['id']);
|
||
return $decoded['id'];
|
||
} else {
|
||
logMessage("❌ Ошибка создания нового треда: " . json_encode($decoded));
|
||
return null;
|
||
}
|
||
}
|
||
|
||
|
||
function getVectorStoreStatus($vectorStoreId) {
|
||
//$url = "https://api.proxyapi.ru/openai/v1/vector_stores/$vectorStoreId/files";
|
||
$url = OPENAI_VECTOR_STORES_API . "/$vectorStoreId/files";
|
||
|
||
logMessage("📡 Запрос статуса файлов в Vector Store: $vectorStoreId");
|
||
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
CURLOPT_URL => $url,
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"Content-Type: application/json",
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
|
||
$response = curl_exec($curl);
|
||
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||
curl_close($curl);
|
||
|
||
if ($httpCode !== 200) {
|
||
logMessage("❌ Ошибка HTTP при получении статуса файлов Vector Store: код $httpCode");
|
||
return "failed";
|
||
}
|
||
|
||
$data = json_decode($response, true);
|
||
if (!isset($data["data"])) {
|
||
logMessage("❌ Ошибка: API не вернул список файлов.");
|
||
return "failed";
|
||
}
|
||
|
||
$completedFiles = count(array_filter($data["data"], function($file) {
|
||
return isset($file["status"]) && $file["status"] === "completed";
|
||
}));
|
||
|
||
if ($completedFiles > 0) {
|
||
logMessage("✅ Vector Store содержит $completedFiles проиндексированных файлов.");
|
||
return "ready";
|
||
}
|
||
|
||
logMessage("⚠️ Файлы еще индексируются.");
|
||
return "in_progress";
|
||
}
|
||
// нью
|
||
/*
|
||
function extractCaseDetailsWithGPT($threadId, $assistantId, $vectorStoreId, $content) {
|
||
logMessage("[INFO] 📌 Формируем текст запроса для GPT-4.");
|
||
|
||
if (!$vectorStoreId) {
|
||
logMessage("[ERROR] ❌ Ошибка: Нет доступного `Vector Store ID`!");
|
||
return null;
|
||
}
|
||
|
||
if (!$threadId) {
|
||
logMessage("[ERROR] ❌ Ошибка: Некорректный `Thread ID`!");
|
||
return null;
|
||
}
|
||
|
||
if (!$content) {
|
||
logMessage("[ERROR] ❌ Ошибка: Пустое содержание обращения!");
|
||
return null;
|
||
}
|
||
|
||
logMessage("[INFO] 📌 Отправляем текст в GPT-4: " . json_encode($content, JSON_UNESCAPED_UNICODE));
|
||
|
||
// Отправляем сообщение с текстом обращения
|
||
$messagePayload = [
|
||
"role" => "user",
|
||
"content" => "ТЕКСТ ОБРАЩЕНИЯ: " . $content
|
||
];
|
||
|
||
$messageJson = json_encode($messagePayload, JSON_UNESCAPED_UNICODE);
|
||
if ($messageJson === false) {
|
||
logMessage("[ERROR] ❌ Ошибка кодирования JSON для сообщений: " . json_last_error_msg());
|
||
return null;
|
||
}
|
||
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
//CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/messages",
|
||
CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/messages",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_POST => true,
|
||
CURLOPT_POSTFIELDS => $messageJson,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Content-Type: application/json",
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
$messageResponse = curl_exec($curl);
|
||
curl_close($curl);
|
||
|
||
$messageData = json_decode($messageResponse, true);
|
||
if (!$messageData || !isset($messageData['id'])) {
|
||
logMessage("[ERROR] ❌ Ошибка при отправке сообщения в тред. Ответ: " . $messageResponse);
|
||
return null;
|
||
}
|
||
|
||
logMessage("[INFO] ✅ Сообщение успешно добавлено в тред.");
|
||
|
||
// Формируем payload для запуска Run
|
||
$payload = [
|
||
"assistant_id" => $assistantId,
|
||
"instructions" => "Извлеки ключевые параметры обращения и представь их в ЧЁТКОМ JSON-формате. Верни ТОЛЬКО JSON, без пояснений. СТРОГО JSON,\n\n"
|
||
. "{\n"
|
||
. " \"category\": \"Определи тип спора\",\n"
|
||
. " \"articles\": [\"Определи применимые статьи закона\"],\n"
|
||
. " \"facts_short\": \"Суть спора (одно предложение)\",\n"
|
||
. " \"facts_full\": \"Развернутый анализ (до 600 символов)\",\n"
|
||
. " \"claim_amount\": \"Извлеки сумму иска\",\n"
|
||
. " \"document_type\": \"Определи вид документа\",\n"
|
||
. " \"applicant_name\": \"Определи истца\",\n"
|
||
. " \"document_client_name\": \"Определи ответчика\"\n"
|
||
. "}\n\n"
|
||
. "Если информация отсутствует, пиши `null`, но не оставляй поле пустым.",
|
||
"tool_resources" => [
|
||
"file_search" => [
|
||
"vector_store_ids" => [$vectorStoreId]
|
||
]
|
||
]
|
||
];
|
||
|
||
$payloadJson = json_encode($payload, JSON_UNESCAPED_UNICODE);
|
||
if ($payloadJson === false) {
|
||
logMessage("[ERROR] ❌ Ошибка кодирования JSON: " . json_last_error_msg());
|
||
return null;
|
||
}
|
||
|
||
// Отправляем запрос на запуск Run
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/runs",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_POST => true,
|
||
CURLOPT_POSTFIELDS => $payloadJson,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Content-Type: application/json",
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
|
||
$response = curl_exec($curl);
|
||
curl_close($curl);
|
||
|
||
$structuredData = json_decode($response, true);
|
||
if (!$structuredData || !isset($structuredData['id'])) {
|
||
logMessage("[ERROR] ❌ Ошибка запуска Run. Ответ: " . $response);
|
||
return null;
|
||
}
|
||
|
||
$runId = $structuredData['id'];
|
||
logMessage("[INFO] 🔄 GPT-4 начал обработку запроса. run_id: $runId");
|
||
|
||
// Ожидаем завершения обработки
|
||
if (!waitForRunCompletion($threadId, $runId)) {
|
||
return null;
|
||
}
|
||
|
||
// Запрашиваем сообщения (финальный ответ)
|
||
$finalResponse = fetchThreadMessages($threadId);
|
||
if (!$finalResponse || !isset($finalResponse['data']) || empty($finalResponse['data'])) {
|
||
logMessage("[ERROR] ❌ Ошибка парсинга финального ответа. Ответ: " . json_encode($finalResponse, JSON_UNESCAPED_UNICODE));
|
||
return null;
|
||
}
|
||
|
||
// Ищем сообщение от ассистента
|
||
$assistantMessage = null;
|
||
foreach ($finalResponse['data'] as $message) {
|
||
if ($message['role'] === 'assistant' && isset($message['content'][0]['text']['value'])) {
|
||
$assistantMessage = $message;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!$assistantMessage) {
|
||
logMessage("[ERROR] ❌ Нет ответа от ассистента в треде. Полный ответ: " . json_encode($finalResponse, JSON_UNESCAPED_UNICODE));
|
||
return null;
|
||
}
|
||
|
||
$rawContent = $assistantMessage['content'][0]['text']['value'];
|
||
logMessage("[INFO] 📜 Сырой контент из ответа: " . $rawContent);
|
||
|
||
// Очищаем JSON от ```json ... ```
|
||
if (preg_match('/```json\s*(.*?)\s*```/s', $rawContent, $matches)) {
|
||
$rawContent = $matches[1];
|
||
} else {
|
||
logMessage("[ERROR] ❌ JSON не найден в формате ```json ... ```. Сырой контент: " . $rawContent);
|
||
return null;
|
||
}
|
||
|
||
// Парсим JSON
|
||
$parsedJson = json_decode($rawContent, true);
|
||
if ($parsedJson === null) {
|
||
logMessage("[ERROR] ❌ Ошибка декодирования JSON: " . json_last_error_msg() . ". Сырой контент: " . $rawContent);
|
||
return null;
|
||
}
|
||
|
||
logMessage("[INFO] ✅ Успешно декодирован JSON: " . json_encode($parsedJson, JSON_UNESCAPED_UNICODE));
|
||
|
||
// Проверяем критические поля
|
||
if (!isset($parsedJson['category']) || $parsedJson['category'] === '' || $parsedJson['category'] === null ||
|
||
!isset($parsedJson['facts_short']) || $parsedJson['facts_short'] === '' || $parsedJson['facts_short'] === null ||
|
||
!isset($parsedJson['facts_full']) || $parsedJson['facts_full'] === '' || $parsedJson['facts_full'] === null) {
|
||
logMessage("[WARNING] ⚠️ `category`, `facts_short` или `facts_full` отсутствуют или пусты! Отправляем повторный запрос...");
|
||
return extractCaseDetailsWithGPT($threadId, $assistantId, $vectorStoreId, "Некоторые ключевые параметры (категория спора, краткие факты, полный анализ) отсутствуют или пусты. Уточни и исправь JSON, добавив недостающие значения.");
|
||
}
|
||
|
||
return $parsedJson;
|
||
}
|
||
*/
|
||
/*
|
||
// ✅ Функция ожидания завершения Run
|
||
function waitForRunCompletion($threadId, $runId) {
|
||
$maxWaitTime = 180;
|
||
$waitTime = 0;
|
||
$sleepInterval = 3;
|
||
|
||
while ($waitTime < $maxWaitTime) {
|
||
sleep($sleepInterval);
|
||
$waitTime += $sleepInterval;
|
||
if ($waitTime > 60) {
|
||
$sleepInterval = 5;
|
||
}
|
||
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/runs/$runId",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
$statusResponse = curl_exec($curl);
|
||
curl_close($curl);
|
||
|
||
$statusData = json_decode($statusResponse, true);
|
||
if (isset($statusData['status']) && $statusData['status'] === 'completed') {
|
||
logMessage("[INFO] ✅ GPT-4 завершил обработку запроса.");
|
||
return true;
|
||
}
|
||
}
|
||
|
||
logMessage("❌ GPT-4 не завершил обработку за $maxWaitTime секунд.");
|
||
return false;
|
||
}
|
||
|
||
// ✅ Функция получения сообщений из треда
|
||
function fetchThreadMessages($threadId) {
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/messages",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
$response = curl_exec($curl);
|
||
curl_close($curl);
|
||
return json_decode($response, true);
|
||
}
|
||
*/
|
||
|
||
|
||
// ✅ Функция ожидания завершения Run
|
||
function waitForRunCompletion($threadId, $runId) {
|
||
$maxWaitTime = 300;
|
||
$waitTime = 0;
|
||
$sleepInterval = 3;
|
||
|
||
while ($waitTime < $maxWaitTime) {
|
||
sleep($sleepInterval);
|
||
$waitTime += $sleepInterval;
|
||
if ($waitTime > 60) {
|
||
$sleepInterval = 5;
|
||
}
|
||
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
//CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/runs/$runId",
|
||
CURLOPT_URL => OPENAI_THREADS_API . "/$threadId/runs/$runId",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
$statusResponse = curl_exec($curl);
|
||
curl_close($curl);
|
||
|
||
$statusData = json_decode($statusResponse, true);
|
||
if (isset($statusData['status']) && $statusData['status'] === 'completed') {
|
||
logMessage("[INFO] ✅ GPT-4 завершил обработку запроса.");
|
||
return true;
|
||
}
|
||
}
|
||
|
||
logMessage("❌ GPT-4 не завершил обработку за $maxWaitTime секунд.");
|
||
return false;
|
||
}
|
||
|
||
// ✅ Функция получения сообщений из треда
|
||
function fetchThreadMessages($threadId) {
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
//CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/messages",
|
||
CURLOPT_URL => OPENAI_THREADS_API . "/$threadId/messages",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
$response = curl_exec($curl);
|
||
curl_close($curl);
|
||
return json_decode($response, true);
|
||
}
|
||
|
||
function prepareEmbeddingForRefinedSearch($text) {
|
||
logMessage("🧠 Подготовка эмбеддингов для уточнённого поиска...");
|
||
|
||
$embedding = getTextEmbedding($text);
|
||
logMessage("📡 Эмбеддинги получены:\n" . json_encode($embedding, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
|
||
|
||
$normalize = function ($vector) {
|
||
if (!is_array($vector) || empty($vector)) {
|
||
return null;
|
||
}
|
||
$sumSquares = array_reduce($vector, function($carry, $val) {
|
||
return $carry + $val * $val;
|
||
}, 0);
|
||
$magnitude = sqrt($sumSquares);
|
||
if ($magnitude == 0) {
|
||
return null;
|
||
}
|
||
return array_map(function($x) use ($magnitude) {
|
||
return $x / $magnitude;
|
||
}, $vector);
|
||
};
|
||
|
||
return [
|
||
'embedding_2048' => isset($embedding['embedding_2048']) ? $normalize($embedding['embedding_2048']) : null,
|
||
'embedding_1024' => isset($embedding['embedding_1024']) ? $normalize($embedding['embedding_1024']) : null
|
||
];
|
||
}
|
||
|
||
|
||
// олд
|
||
|
||
function extractCaseDetailsWithGPT($threadId, $assistantId, $vectorStoreId, $content) {
|
||
logMessage("[INFO] 📌 Формируем текст запроса для GPT-4.");
|
||
|
||
if (!$vectorStoreId) {
|
||
logMessage("[ERROR] ❌ Ошибка: Нет доступного `Vector Store ID`!");
|
||
return null;
|
||
}
|
||
|
||
if (!$threadId) {
|
||
logMessage("[ERROR] ❌ Ошибка: Некорректный `Thread ID`!");
|
||
return null;
|
||
}
|
||
|
||
if (!$content) {
|
||
logMessage("[ERROR] ❌ Ошибка: Пустое содержание обращения!");
|
||
return null;
|
||
}
|
||
|
||
logMessage("[INFO] 📌 Отправляем текст в GPT-4: " . json_encode($content, JSON_UNESCAPED_UNICODE));
|
||
|
||
// ✅ Отправляем сообщение с текстом обращения (заменяем `messages`)
|
||
$messagePayload = [
|
||
"role" => "user",
|
||
"content" => "Все документы для анализа находятся в Vector Store ID: $vectorStoreId.\n" .
|
||
"Все необходимые данные нужно извлечь из документов в Vector Store. Прочитай документы в PDF файлах. Если тект не измлекатеся то извлеки его с помощью OCR. Если OCR применить не удалось, то просмотри файлы и опиши их содержимое.\n\n"
|
||
];
|
||
|
||
$messageJson = json_encode($messagePayload, JSON_UNESCAPED_UNICODE);
|
||
if ($messageJson === false) {
|
||
logMessage("[ERROR] ❌ Ошибка кодирования JSON для сообщений: " . json_last_error_msg());
|
||
return null;
|
||
}
|
||
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
//CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/messages",
|
||
CURLOPT_URL => OPENAI_THREADS_API . "/$threadId/messages",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_POST => true,
|
||
CURLOPT_POSTFIELDS => $messageJson,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Content-Type: application/json",
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
$messageResponse = curl_exec($curl);
|
||
curl_close($curl);
|
||
|
||
$messageData = json_decode($messageResponse, true);
|
||
if (!$messageData || !isset($messageData['id'])) {
|
||
logMessage("[ERROR] ❌ Ошибка при отправке сообщения в тред. Ответ: " . $messageResponse);
|
||
return null;
|
||
}
|
||
|
||
logMessage("[INFO] ✅ Сообщение успешно добавлено в тред.");
|
||
|
||
// ✅ Формируем payload для запуска Run
|
||
/*
|
||
$payload = [
|
||
"assistant_id" => $assistantId,
|
||
"instructions" => "Извлеки ключевые параметры обращения и представь их в ЧЁТКОМ JSON-формате. Верни ТОЛЬКО JSON, без пояснений.\n\n"
|
||
. "{\n"
|
||
. " \"category\": \"Определи тип спора\",\n"
|
||
. " \"articles\": [\"Определи применимые статьи закона\"],\n"
|
||
. " \"facts_short\": \"Суть спора (одно предложение)\",\n"
|
||
. " \"facts_full\": \"Развернутый анализ (до 600 символов)\",\n"
|
||
. " \"claim_amount\": \"Извлеки сумму иска\",\n"
|
||
. " \"document_type\": \"Определи вид документа\",\n"
|
||
. " \"applicant_name\": \"Определи истца\",\n"
|
||
. " \"document_client_name\": \"Определи ответчика\"\n"
|
||
. "}\n\n"
|
||
. "Если информация отсутствует, пиши `null`, но не оставляй поле пустым.",
|
||
"tool_resources" => [
|
||
"file_search" => [
|
||
"vector_store_ids" => [$vectorStoreId]
|
||
]
|
||
]
|
||
];
|
||
*/
|
||
$payload = [
|
||
"assistant_id" => $assistantId,
|
||
"instructions" => "Ты — юридический аналитик. Проанализируй документы из Vector Store и верни результат в ЧЁТКОМ JSON-формате.\n" .
|
||
"Верни ТОЛЬКО JSON, без пояснений, строго по шаблону:\n\n" .
|
||
"{\n" .
|
||
" \"category\": \"Тип спора (например: защита прав потребителей)\",\n" .
|
||
" \"articles\": [\"Список применимых статей закона\"],\n" .
|
||
" \"facts_short\": \"Суть спора (одно предложение)\",\n" .
|
||
" \"facts_full\": \"Развёрнутый анализ (до 600 символов)\",\n" .
|
||
" \"claim_amount\": \"Сумма исковых требований в рублях (число)\",\n" .
|
||
" \"document_type\": \"Тип документа (иск, претензия и т.п.)\",\n" .
|
||
" \"applicant_name\": \"ФИО заявителя\",\n" .
|
||
" \"document_client_name\": \"Название или ФИО ответчика\",\n" .
|
||
" \"region\": \"Регион проживания заявителя\",\n" .
|
||
" \"city\": \"Город (или населённый пункт)\",\n" .
|
||
" \"claim_type\": \"Тип требований (например: возврат, компенсация, расторжение)\",\n" .
|
||
" \"claim_reason\": \"Причина требований (например: неоказание услуги, нарушение условий)\",\n" .
|
||
" \"evidence_summary\": [\n" .
|
||
" {\"name\": \"Название документа\", \"content\": \"Краткое описание сути\"},\n" .
|
||
" {\"name\": \"...\", \"content\": \"...\"}\n" .
|
||
" ],\n" .
|
||
" \"is_pretension_sent\": true,\n" .
|
||
" \"has_contract\": true,\n" .
|
||
" \"has_payment_proof\": true,\n" .
|
||
" \"has_training_access\": true,\n" .
|
||
" \"nsfw_alert\": false\n" .
|
||
"}\n\n" .
|
||
"Если информация отсутствует, указывай `null`, но не оставляй поле пустым.\n" .
|
||
"Если данные спорные, делай наиболее вероятное предположение на основе документов.",
|
||
"tool_resources" => [
|
||
"file_search" => [
|
||
"vector_store_ids" => [$vectorStoreId]
|
||
]
|
||
]
|
||
];
|
||
|
||
|
||
$payloadJson = json_encode($payload, JSON_UNESCAPED_UNICODE);
|
||
if ($payloadJson === false) {
|
||
logMessage("[ERROR] ❌ Ошибка кодирования JSON: " . json_last_error_msg());
|
||
return null;
|
||
}
|
||
|
||
// ✅ Отправляем запрос на запуск Run
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
//CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/runs",
|
||
CURLOPT_URL => OPENAI_THREADS_API . "/$threadId/runs",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_POST => true,
|
||
CURLOPT_POSTFIELDS => $payloadJson,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Content-Type: application/json",
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
|
||
$response = curl_exec($curl);
|
||
curl_close($curl);
|
||
|
||
$structuredData = json_decode($response, true);
|
||
if (!$structuredData || !isset($structuredData['id'])) {
|
||
logMessage("[ERROR] ❌ Ошибка запуска Run. Ответ: " . $response);
|
||
return null;
|
||
}
|
||
|
||
$runId = $structuredData['id'];
|
||
logMessage("[INFO] 🔄 GPT-4 начал обработку запроса. run_id: $runId");
|
||
|
||
// 🔄 Ожидаем завершения обработки (до 300 сек)
|
||
$maxWaitTime = 300;
|
||
$waitTime = 0;
|
||
$sleepInterval = 3;
|
||
|
||
while ($waitTime < $maxWaitTime) {
|
||
sleep($sleepInterval);
|
||
$waitTime += $sleepInterval;
|
||
|
||
if ($waitTime > 60) {
|
||
$sleepInterval = 5;
|
||
}
|
||
|
||
logMessage("[INFO] ⏳ GPT-4 всё ещё обрабатывает запрос... Оставшееся время: " . ($maxWaitTime - $waitTime) . " сек.");
|
||
|
||
// ✅ Запрашиваем статус
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
//CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/runs/$runId",
|
||
CURLOPT_URL => OPENAI_THREADS_API . "/$threadId/runs/$runId",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
$statusResponse = curl_exec($curl);
|
||
curl_close($curl);
|
||
|
||
$statusData = json_decode($statusResponse, true);
|
||
if (isset($statusData['status']) && $statusData['status'] === 'completed') {
|
||
logMessage("[INFO] ✅ GPT-4 завершил обработку запроса.");
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ($waitTime >= $maxWaitTime) {
|
||
logMessage("[ERROR] ❌ GPT-4 не завершил обработку за $maxWaitTime секунд.");
|
||
return null;
|
||
}
|
||
|
||
// ✅ Запрашиваем сообщения (финальный ответ)
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
//CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/messages",
|
||
CURLOPT_URL => OPENAI_THREADS_API . "/$threadId/messages",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
$finalResponse = curl_exec($curl);
|
||
curl_close($curl);
|
||
|
||
$finalData = json_decode($finalResponse, true);
|
||
if (!$finalData || !isset($finalData['data'][0]['content'])) {
|
||
logMessage("[ERROR] ❌ Ошибка парсинга финального ответа. Ответ: " . $finalResponse);
|
||
return null;
|
||
}
|
||
|
||
logMessage("[INFO] ✅ Полученный JSON от GPT-4: " . json_encode($finalData, JSON_UNESCAPED_UNICODE));
|
||
|
||
|
||
|
||
// ✅ Парсим и проверяем JSON
|
||
$parsedJson = extractCleanJsonFromGPT($finalData['data'][0]['content']);
|
||
|
||
// $parsedJson = json_decode($finalData['data'][0]['content'], true);
|
||
|
||
// Функция: проверка, считается ли поле "пустым"
|
||
function isEmptyGPTField($value) {
|
||
return !isset($value) || $value === null || trim($value) === '' || $value === 'null';
|
||
}
|
||
|
||
if (!is_array($parsedJson)) {
|
||
logMessage("[ERROR] ❌ Ответ GPT-4 не содержит корректный JSON.");
|
||
return null;
|
||
}
|
||
|
||
// Проверим ключевые поля
|
||
$missingFields = [];
|
||
foreach (['category', 'facts_short', 'facts_full'] as $key) {
|
||
if (!array_key_exists($key, $parsedJson) || isEmptyGPTField($parsedJson[$key])) {
|
||
$missingFields[] = $key;
|
||
}
|
||
}
|
||
|
||
// Если какие-то поля действительно пустые – повторный запрос
|
||
if (!empty($missingFields)) {
|
||
logMessage("[WARNING] ⚠️ Не заполнены ключевые поля: " . implode(', ', $missingFields) . ". Отправляем повторный запрос...");
|
||
return extractCaseDetailsWithGPT(
|
||
$threadId,
|
||
$assistantId,
|
||
$vectorStoreId,
|
||
"Некоторые ключевые параметры (" . implode(', ', $missingFields) . ") не были указаны. Уточни и исправь JSON, добавив недостающие значения."
|
||
);
|
||
}
|
||
|
||
|
||
|
||
|
||
return [
|
||
"json_data" => json_decode($finalData['data'][0]['content'], true) ?? [],
|
||
"text_data" => $finalData['data'][0]['content'] ?? ''
|
||
];
|
||
}
|
||
|
||
|
||
//function analyzeDocumentWithAssistantStream($threadId, $assistantId, $vectorStoreId, $fileId, $content, $foundCases, $caseId)
|
||
function analyzeDocumentWithAssistantStream($threadId, $assistantId, $vectorStoreId, $fileId, $parsedJson, $foundCases, $casedId)
|
||
{
|
||
// Логируем файлы из Vector Store перед анализом
|
||
// logVectorStoreFiles($vectorStoreId); // <-- Вставляем сюда вызов функции
|
||
// checkFileStatus($vectorStoreId); // Проверяем статус файлов
|
||
|
||
logMessage("🔹 Начинаем финальный анализ. Thread ID: $threadId, Vector Store ID: $vectorStoreId");
|
||
|
||
// ✅ Загружаем файлы судебных решений
|
||
$loadedCasesDescription = "";
|
||
$newFileIds = [];
|
||
|
||
foreach ($foundCases as $case) {
|
||
$caseId = str_replace("/", "-", isset($case["case_id"]) ? $case["case_id"] : "Неизвестный ID");
|
||
$court = isset($case["court"]) ? $case["court"] : "Неизвестный суд";
|
||
$decision = isset($case["court_decision"]) ? $case["court_decision"] : "Текст решения отсутствует";
|
||
$filePath = "/var/www/fastuser/data/www/crm.clientright.ru/aiassist/temp/case_$caseId.txt";
|
||
|
||
file_put_contents($filePath, "Дело ID: $caseId\nСуд: $court\n\n$decision");
|
||
$fileOpenAIId = uploadFileToOpenAI($filePath);
|
||
|
||
if (!$fileOpenAIId) {
|
||
logMessage("❌ Ошибка загрузки файла $filePath в OpenAI.");
|
||
continue;
|
||
}
|
||
|
||
$newFileIds[] = $fileOpenAIId;
|
||
$loadedCasesDescription .= "- Дело ID: $caseId (суд: $court)\n";
|
||
}
|
||
|
||
|
||
|
||
|
||
/* $loadedCasesDescription = "";
|
||
$newFileIds = [];
|
||
foreach ($foundCases as $case) {
|
||
$caseId = str_replace("/", "-", isset($case["case_id"]) ? $case["case_id"] : "Неизвестный ID");
|
||
$court = isset($case["court"]) ? $case["court"] : "Неизвестный суд";
|
||
$decision = isset($case["court_decision"]) ? $case["court_decision"] : "Текст решения отсутствует";
|
||
$filePath = "/var/www/fastuser/data/www/crm.clientright.ru/aiassist/temp/case_$caseId.txt";
|
||
|
||
file_put_contents($filePath, "Дело ID: $caseId\nСуд: $court\n\n$decision");
|
||
$fileOpenAIId = uploadFileToOpenAI($filePath);
|
||
$loadedCasesDescription .= "- Дело ID: $caseId (суд: $court)\n";
|
||
|
||
|
||
if (!$fileOpenAIId) {
|
||
logMessage("❌ Ошибка загрузки файла $filePath в OpenAI.");
|
||
continue;
|
||
}
|
||
$newFileIds[] = $fileOpenAIId;
|
||
}
|
||
*/
|
||
if (empty($newFileIds)) {
|
||
logMessage("❌ Ошибка: Не удалось загрузить ни одно судебное решение!");
|
||
return null;
|
||
}
|
||
|
||
// ✅ Дозагружаем файлы в Vector Store
|
||
foreach ($newFileIds as $newFileId) {
|
||
if (!addFileToVectorStore($vectorStoreId, $newFileId)) {
|
||
logMessage("❌ Ошибка добавления файла $newFileId в Vector Store!");
|
||
return null;
|
||
}
|
||
}
|
||
logMessage("✅ Файлы добавлены в Vector Store ID: $vectorStoreId");
|
||
|
||
// ✅ Ожидаем 2 минуты для индексации
|
||
logMessage("⏳ Ожидаем 2 минуты, пока `Vector Store` индексируется...");
|
||
sleep(120);
|
||
|
||
// ✅ Проверяем статус индексации
|
||
$vectorStoreStatus = getVectorStoreStatus($vectorStoreId);
|
||
if ($vectorStoreStatus !== "ready") {
|
||
logMessage("⚠️ Vector Store не завершил индексацию, но продолжаем анализ.");
|
||
}
|
||
|
||
// ✅ Формируем промпт
|
||
if (!$parsedJson || !is_array($parsedJson)) {
|
||
logMessage("❌ Ошибка: parsedJson пуст или некорректен!");
|
||
die("Ошибка: некорректный ответ GPT-4 (parsedJson).");
|
||
}
|
||
|
||
if (!empty($loadedCasesDescription)) {
|
||
$loadedCasesText = "📚 В Vector Store загружены судебные решения:\n$loadedCasesDescription\nИспользуй их для анализа судебной практики, укажи, какие из них релевантны и почему.";
|
||
} else {
|
||
$loadedCasesText = "📚 В Vector Store могут содержаться судебные решения. Используй их при анализе, если найдёшь релевантные.";
|
||
}
|
||
|
||
$userMessage = buildReportPromptFromJson($parsedJson, $vectorStoreId, $loadedCasesText);
|
||
|
||
/* $userMessage = "🔹 Этап 1: Анализ документов по обращению в Клиентправ\n\n" .
|
||
"Все документы для анализа находятся в Vector Store ID: $vectorStoreId.\n" .
|
||
"Текст OCR не предоставлен напрямую — все необходимые данные нужно извлечь из документов в Vector Store.\n\n" .
|
||
"📌 Выполни следующее:\n" .
|
||
"1️⃣ Найди все документы, относящиеся к делу заявителя (исковые, договор, чеки, претензии и пр.). Исключи из анализа судебные акты (`case_*.txt`).\n" .
|
||
"2️⃣ Для каждого документа:\n" .
|
||
" • Укажи его название или тип (если доступно)\n" .
|
||
" • Есть ли читаемый текст? (если нет — отметь, что требуется OCR)\n" .
|
||
" • Выведи 200 первых знаков (если доступно)\n\n" .
|
||
|
||
|
||
"📋 Извлеки обобщённую информацию:\n" .
|
||
"• ФИО истца и регион (если указано)\n" .
|
||
"• Ответчика (юрлицо, ИП, госорган — по возможности уточни)\n" .
|
||
"• Суть спора (в 2–3 предложениях)\n" .
|
||
"• Сумма исковых требований (если можно установить)\n" .
|
||
"• Какие ключевые документы отсутствуют, повреждены или не читаются\n\n" .
|
||
|
||
"❗ Не анализируй судебные решения на этом этапе. Только документы по конкретному делу заявителя.";
|
||
*/
|
||
logMessage("📡 Финальный промпт для GPT-4:\n" . $userMessage);
|
||
|
||
// ✅ Отправляем сообщение в тред перед `run`
|
||
$messagePayload = [
|
||
"role" => "user",
|
||
"content" => $userMessage
|
||
];
|
||
|
||
$messageJson = json_encode($messagePayload, JSON_UNESCAPED_UNICODE);
|
||
if ($messageJson === false) {
|
||
logMessage("❌ Ошибка кодирования JSON: " . json_last_error_msg());
|
||
return null;
|
||
}
|
||
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
// CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/messages",
|
||
CURLOPT_URL => OPENAI_THREADS_API . "/$threadId/messages",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_POST => true,
|
||
CURLOPT_POSTFIELDS => $messageJson,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Content-Type: application/json",
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
$messageResponse = curl_exec($curl);
|
||
curl_close($curl);
|
||
|
||
$messageData = json_decode($messageResponse, true);
|
||
if (!$messageData || !isset($messageData['id'])) {
|
||
logMessage("❌ Ошибка при отправке сообщения в тред. Ответ: " . $messageResponse);
|
||
return null;
|
||
}
|
||
|
||
logMessage("[INFO] ✅ Сообщение успешно добавлено в тред.");
|
||
|
||
// ✅ Создаём `Run`
|
||
$payload = [
|
||
"assistant_id" => $assistantId,
|
||
"tool_resources" => [
|
||
"file_search" => [
|
||
"vector_store_ids" => [$vectorStoreId]
|
||
]
|
||
]
|
||
];
|
||
|
||
$payloadJson = json_encode($payload, JSON_UNESCAPED_UNICODE);
|
||
if ($payloadJson === false) {
|
||
logMessage("❌ Ошибка кодирования JSON: " . json_last_error_msg());
|
||
return null;
|
||
}
|
||
|
||
// ✅ Отправляем запрос на запуск `Run`
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
//CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/runs",
|
||
CURLOPT_URL => OPENAI_THREADS_API . "/$threadId/runs",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_POST => true,
|
||
CURLOPT_POSTFIELDS => $payloadJson,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Content-Type: application/json",
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
|
||
$response = curl_exec($curl);
|
||
curl_close($curl);
|
||
|
||
$structuredData = json_decode($response, true);
|
||
if (!$structuredData || !isset($structuredData['id'])) {
|
||
logMessage("❌ Ошибка запуска Run. Ответ: " . $response);
|
||
return null;
|
||
}
|
||
|
||
$runId = $structuredData['id'];
|
||
logMessage("[INFO] 🔄 GPT-4 начал обработку запроса. run_id: $runId");
|
||
|
||
// 🔄 Ожидаем завершения обработки
|
||
if (!waitForRunCompletion($threadId, $runId)) {
|
||
return null;
|
||
}
|
||
|
||
// ✅ Запрашиваем финальный ответ
|
||
return fetchThreadMessages($threadId);
|
||
}
|
||
|
||
|
||
//олд
|
||
/*
|
||
function analyzeDocumentWithAssistantStream($threadId, $assistantId, $vectorStoreId, $fileId, $content, $foundCases, $casedId) {
|
||
|
||
//logVectorStoreFiles($vectorStoreId); // Выводим информацию о всех файлах
|
||
checkFileStatus($vectorStoreId); // Проверяем статус файлов
|
||
|
||
logMessage("🔹 Начинаем финальный анализ. Thread ID: $threadId, Vector Store ID: $vectorStoreId");
|
||
|
||
// ✅ Загружаем файлы судебных решений
|
||
$newFileIds = [];
|
||
foreach ($foundCases as $case) {
|
||
$caseId = str_replace("/", "-", isset($case["case_id"]) ? $case["case_id"] : "Неизвестный ID");
|
||
$court = isset($case["court"]) ? $case["court"] : "Неизвестный суд";
|
||
$decision = isset($case["court_decision"]) ? $case["court_decision"] : "Текст решения отсутствует";
|
||
$filePath = "/var/www/fastuser/data/www/crm.clientright.ru/aiassist/temp/case_$caseId.txt";
|
||
|
||
file_put_contents($filePath, "Дело ID: $caseId\nСуд: $court\n\n$decision");
|
||
$fileOpenAIId = uploadFileToOpenAI($filePath);
|
||
|
||
if (!$fileOpenAIId) {
|
||
logMessage("❌ Ошибка загрузки файла $filePath в OpenAI.");
|
||
continue;
|
||
}
|
||
$newFileIds[] = $fileOpenAIId;
|
||
}
|
||
|
||
if (empty($newFileIds)) {
|
||
logMessage("❌ Ошибка: Не удалось загрузить ни одно судебное решение!");
|
||
return null;
|
||
}
|
||
|
||
// ✅ Дозагружаем файлы в Vector Store
|
||
foreach ($newFileIds as $newFileId) {
|
||
if (!addFileToVectorStore($vectorStoreId, $newFileId)) {
|
||
logMessage("❌ Ошибка добавления файла $newFileId в Vector Store!");
|
||
return null;
|
||
}
|
||
}
|
||
logMessage("✅ Файлы добавлены в Vector Store ID: $vectorStoreId");
|
||
|
||
// ✅ Ожидаем 2 минуты для индексации
|
||
logMessage("⏳ Ожидаем 2 минуты, пока `Vector Store` индексируется...");
|
||
sleep(120);
|
||
|
||
// ✅ Проверяем статус индексации
|
||
$vectorStoreStatus = getVectorStoreStatus($vectorStoreId);
|
||
if ($vectorStoreStatus !== "ready") {
|
||
logMessage("⚠️ Vector Store не завершил индексацию, но продолжаем анализ.");
|
||
}
|
||
|
||
|
||
|
||
$userMessage = "🔹 Этап 1: Анализ документов по обращению в Клиентправ\n\n" .
|
||
"Все документы находятся в Vector Store ID: $vectorStoreId.\n" .
|
||
"Документы представлены в различных форматах, включая PDF и изображения. Извлеки содержимое всех файлов в Vector Store, включая текст и изображения (с помощью OCR, если требуется).\n\n" .
|
||
|
||
"📌 Выполни следующее:\n" .
|
||
"1️⃣ Найди все документы, относящиеся к делу заявителя (исковые, договор, чеки, претензии и пр.). Исключи из анализа судебные акты (`case_*.txt`).\n" .
|
||
"2️⃣ Для каждого документа, извлечённого из Vector Store:\n" .
|
||
" • Укажи его название или тип (если доступно)\n" .
|
||
" • Есть ли читаемый текст? (если нет — отметь, что требуется OCR)\n" .
|
||
" • Если документ является изображением в PDF (например, скан чека, договора, скриншот), опиши, что видно, и извлеки текст через OCR, если это возможно\n\n" .
|
||
|
||
"📋 Извлеки обобщённую информацию:\n" .
|
||
"• ФИО истца и регион (если указано)\n" .
|
||
"• Ответчика (юрлицо, ИП, госорган — по возможности уточни)\n" .
|
||
"• Суть спора (в 2–3 предложениях)\n" .
|
||
"• Сумма исковых требований (если можно установить)\n" .
|
||
"• Какие ключевые документы отсутствуют, повреждены или не читаются\n\n" .
|
||
|
||
"❗ Не анализируй судебные решения на этом этапе. Только документы по конкретному делу заявителя.";
|
||
|
||
|
||
|
||
|
||
logMessage("📡 Финальный промпт для GPT-4:\n" . $userMessage);
|
||
|
||
// ✅ Отправляем сообщение в тред перед `run`
|
||
$messagePayload = [
|
||
"role" => "user",
|
||
"content" => $userMessage
|
||
];
|
||
|
||
$messageJson = json_encode($messagePayload, JSON_UNESCAPED_UNICODE);
|
||
if ($messageJson === false) {
|
||
logMessage("❌ Ошибка кодирования JSON: " . json_last_error_msg());
|
||
return null;
|
||
}
|
||
|
||
logMessage("📤 Что мы отправляем в GPT:");
|
||
|
||
logMessage("📄 OCR / Content:\n" . mb_substr($content, 0, 2000, 'UTF-8') . (strlen($content) > 2000 ? "\n... (обрезано)" : ""));
|
||
|
||
logMessage("📁 Загруженные судебные решения:");
|
||
foreach ($foundCases as $case) {
|
||
$id = $case['case_id'] ?? '???';
|
||
$court = $case['court'] ?? 'не указан';
|
||
$text = mb_substr(($case['court_decision'] ?? ''), 0, 200, 'UTF-8');
|
||
logMessage("- 📌 Дело: $id | Суд: $court | Фрагмент: $text...");
|
||
}
|
||
|
||
logMessage("📚 Vector Store ID: $vectorStoreId");
|
||
|
||
logMessage("📨 Полный промпт (ссылки, задачи, всё):\n" . $userMessage);
|
||
|
||
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/messages",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_POST => true,
|
||
CURLOPT_POSTFIELDS => $messageJson,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Content-Type: application/json",
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
$messageResponse = curl_exec($curl);
|
||
curl_close($curl);
|
||
|
||
$messageData = json_decode($messageResponse, true);
|
||
if (!$messageData || !isset($messageData['id'])) {
|
||
logMessage("❌ Ошибка при отправке сообщения в тред. Ответ: " . $messageResponse);
|
||
return null;
|
||
}
|
||
|
||
logMessage("[INFO] ✅ Сообщение успешно добавлено в тред.");
|
||
|
||
// ✅ Создаём `Run`
|
||
$payload = [
|
||
"assistant_id" => $assistantId,
|
||
"tool_resources" => [
|
||
"file_search" => [
|
||
"vector_store_ids" => [$vectorStoreId]
|
||
]
|
||
]
|
||
];
|
||
|
||
$payloadJson = json_encode($payload, JSON_UNESCAPED_UNICODE);
|
||
if ($payloadJson === false) {
|
||
logMessage("❌ Ошибка кодирования JSON: " . json_last_error_msg());
|
||
return null;
|
||
}
|
||
|
||
// ✅ Отправляем запрос на запуск `Run`
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/runs",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_POST => true,
|
||
CURLOPT_POSTFIELDS => $payloadJson,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Content-Type: application/json",
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
|
||
$response = curl_exec($curl);
|
||
curl_close($curl);
|
||
|
||
$structuredData = json_decode($response, true);
|
||
if (!$structuredData || !isset($structuredData['id'])) {
|
||
logMessage("❌ Ошибка запуска Run. Ответ: " . $response);
|
||
return null;
|
||
}
|
||
|
||
$runId = $structuredData['id'];
|
||
logMessage("[INFO] 🔄 GPT-4 начал обработку запроса. run_id: $runId");
|
||
|
||
// 🔄 Ожидаем завершения обработки
|
||
if (!waitForRunCompletion($threadId, $runId)) {
|
||
return null;
|
||
}
|
||
|
||
// ✅ Запрашиваем финальный ответ
|
||
return fetchThreadMessages($threadId);
|
||
}
|
||
*/
|
||
|
||
/* $userMessage = "🔹 Отчет об анализе обращения в Клиентправ\n\n" .
|
||
"📜 **Текст обращения и приложенные документы (OCR-распознанный текст из PDF):**\n" .
|
||
"\"$content\"\n\n" .
|
||
|
||
"⚖ **Дополнительно загруженные документы в Vector Store (включая судебные акты):**\n" .
|
||
"- Дозагружены в Vector Store ID: $vectorStoreId.\n\n" .
|
||
|
||
"📢 **Задача для GPT-4:**\n" .
|
||
"1️⃣ **Проанализируй ВСЕ документы (из `$content` и `$vectorStoreId`).**\n" .
|
||
" - Если документ уже есть в `$content`, используй его текст.\n" .
|
||
" - Если документ есть только в Vector Store, проанализируй его содержимое отдельно.\n" .
|
||
" - Не дублируй информацию, если один и тот же документ встречается и в `$content`, и в Vector Store.\n\n" .
|
||
|
||
"2️⃣ **Извлеки данные об истце и ответчике.**\n" .
|
||
" - Укажи ФИО истца и его регион проживания.\n" .
|
||
" - Определи ответчика (юридическое лицо, ИП или госорган).\n\n" .
|
||
|
||
"3️⃣ **Определи сумму исковых требований.**\n" .
|
||
" - Если сумма фигурирует в чеках или договорах, **подтверди её документами**.\n" .
|
||
" - Если сумма не указана, укажи, какие документы нужны для уточнения.\n\n" .
|
||
|
||
"4️⃣ **Анализ представленных доказательств (не включая судебные решения).**\n" .
|
||
" - Перечисли все представленные документы (за исключением судебных решений).\n" .
|
||
" - Для каждого укажи его содержание и значимость.\n" .
|
||
" - Если текст не распознан, укажи, что требуется OCR или иной метод анализа.\n\n" .
|
||
|
||
"5️⃣ **Применимые нормы права.**\n" .
|
||
" - Приведи законы и статьи, регулирующие этот спор.\n" .
|
||
" - Включи ссылки на ГК РФ и Закон о защите прав потребителей.\n\n" .
|
||
|
||
"6️⃣ **Используй загруженные судебные решения из Vector Store как прецеденты.**\n" .
|
||
" - **Судебные решения находятся в файлах `case_{номер_дела}.txt`.**\n" .
|
||
" - **Они используются ТОЛЬКО как прецеденты**, не как доказательства по данному делу.\n" .
|
||
" - Проанализируй их и выбери те, которые наиболее релевантны к данному спору.\n" .
|
||
" - Укажи номер дела, суд, дату рассмотрения и результат.\n" .
|
||
" - Объясни, почему суд принял то или иное решение и как это может повлиять на рассматриваемый случай.\n\n" .
|
||
|
||
"7️⃣ **Разделяй документы по категориям.**\n" .
|
||
" - **Документы по текущему делу:** договор, чеки, претензии и т. д.\n" .
|
||
" - **Судебные акты:** файлы `case_{номер_дела}.txt`, которые используются только для анализа прецедентов.\n\n" .
|
||
|
||
"8️⃣ **Риски и слабые места.**\n" .
|
||
" - Укажи, какие слабые места есть в позиции заявителя.\n" .
|
||
" - Например: отсутствие доказательств, частично оказанные услуги, спорные суммы.\n" .
|
||
" - Если доказательств не хватает, укажи, что необходимо запросить.\n\n" .
|
||
|
||
"9️⃣ **Прогноз вероятности успеха.**\n" .
|
||
" - Укажи, с какой вероятностью суд удовлетворит иск (в %).\n" .
|
||
" - Обоснуй, от чего зависит исход дела.\n\n" .
|
||
|
||
"🔟 **Формирование процессуальных документов.**\n" .
|
||
" - Подготовь ссылки на документы:\n" .
|
||
" - 📄 [Отчет](https://crm.clientright.ru/docs/cases/$casedId/report.txt)\n" .
|
||
" - 📄 [Претензия](https://crm.clientright.ru/docs/cases/$casedId/claim.docx)\n" .
|
||
" - 📄 [Жалоба](https://crm.clientright.ru/docs/cases/$casedId/complaint.docx)\n" .
|
||
" - 📄 [Иск](https://crm.clientright.ru/docs/cases/$casedId/lawsuit.docx)\n\n" .
|
||
|
||
"📌 **ВАЖНО:**\n" .
|
||
"- **Если документ уже есть в `$content`, не дублируй его анализ из Vector Store.**\n" .
|
||
"- **Если в Vector Store есть документы, которых нет в `$content`, анализируй их.**\n" .
|
||
"- **Судебные акты (файлы `case_{номер_дела}.txt`) используются ТОЛЬКО как прецеденты.**\n" .
|
||
"- **Не включай судебные решения в доказательства по делу.**\n" .
|
||
"- **Всегда проверяй, есть ли текст в документах (даже если он не загружен сразу).**\n" .
|
||
"- **Приводи примеры судебных решений с номерами и анализом дела.**\n" .
|
||
"- **Делай чёткие выводы: какие документы нужны, что доказывает истец, что может возразить ответчик.**\n";
|
||
|
||
*/
|
||
|
||
// нью
|
||
/*
|
||
function analyzeDocumentWithAssistantStream($threadId, $assistantId, $vectorStoreId, $fileId, $content, $foundCases, $casedId) {
|
||
logMessage("🔹 Начинаем финальный анализ. Thread ID: $threadId, Vector Store ID: $vectorStoreId");
|
||
|
||
// Загружаем файлы судебных решений
|
||
$newFileIds = [];
|
||
foreach ($foundCases as $case) {
|
||
$caseIdSafe = str_replace("/", "-", $case["case_id"] ?? "Неизвестный ID");
|
||
$court = $case["court"] ?? "Неизвестный суд";
|
||
$decision = $case["court_decision"] ?? "Текст решения отсутствует";
|
||
$filePath = "/var/www/fastuser/data/www/crm.clientright.ru/aiassist/temp/case_$caseIdSafe.txt";
|
||
|
||
file_put_contents($filePath, "Дело ID: $caseIdSafe\nСуд: $court\n\n$decision");
|
||
$fileOpenAIId = uploadFileToOpenAI($filePath);
|
||
|
||
if (!$fileOpenAIId) {
|
||
logMessage("❌ Ошибка загрузки файла $filePath в OpenAI.");
|
||
continue;
|
||
}
|
||
$newFileIds[] = $fileOpenAIId;
|
||
}
|
||
|
||
if (empty($newFileIds)) {
|
||
logMessage("❌ Ошибка: Не удалось загрузить ни одно судебное решение!");
|
||
return null;
|
||
}
|
||
|
||
// Дозагружаем файлы в Vector Store
|
||
foreach ($newFileIds as $newFileId) {
|
||
if (!addFileToVectorStore($vectorStoreId, $newFileId)) {
|
||
logMessage("❌ Ошибка добавления файла $newFileId в Vector Store!");
|
||
return null;
|
||
}
|
||
}
|
||
logMessage("✅ Файлы добавлены в Vector Store ID: $vectorStoreId");
|
||
|
||
// Ожидаем индексацию
|
||
logMessage("⏳ Ожидаем 2 минуты, пока `Vector Store` индексируется...");
|
||
sleep(120);
|
||
|
||
$vectorStoreStatus = getVectorStoreStatus($vectorStoreId);
|
||
if ($vectorStoreStatus !== "ready") {
|
||
logMessage("⚠️ Vector Store не завершил индексацию, но продолжаем анализ.");
|
||
}
|
||
|
||
// Формируем промпт
|
||
$userMessage = "🔹 Финальный анализ обращения в Клиентправ\n\n" .
|
||
"📜 **Текст обращения и документы:**\n" .
|
||
"\"$content\"\n\n" .
|
||
"⚖ **Дополнительные документы в Vector Store:**\n" .
|
||
"- ID: $vectorStoreId (включает судебные акты в формате `case_{номер_дела}.txt`).\n\n" .
|
||
"📢 **Задача:**\n" .
|
||
"Проанализируй ВСЕ документы из `$content` и `$vectorStoreId` и предоставь полный текстовый отчёт по пунктам ниже. " .
|
||
"Не пиши 'требуется уточнение' или 'нужна ручная проверка' — извлекай данные сам из всех доступных файлов. " .
|
||
"Если информация отсутствует, пиши 'не указано'. Ответ должен быть чётким, без лишних пояснений вне структуры.\n\n" .
|
||
"1️⃣ **Данные об истце и ответчике:**\n" .
|
||
"- ФИО истца и регион проживания.\n" .
|
||
"- Название/ФИО ответчика и тип (юрлицо, ИП, госорган).\n\n" .
|
||
"2️⃣ **Сумма исковых требований:**\n" .
|
||
"- Укажи сумму и документ, где она указана.\n" .
|
||
"- Если суммы нет, пиши 'не указано'.\n\n" .
|
||
"3️⃣ **Анализ доказательств (кроме судебных решений):**\n" .
|
||
"- Перечисли документы из `$content` и `$vectorStoreId` (кроме `case_*.txt`).\n" .
|
||
"- Для каждого: название, краткое содержание, значимость.\n\n" .
|
||
"4️⃣ **Применимые нормы права:**\n" .
|
||
"- Укажи статьи из ГК РФ и Закона о защите прав потребителей, которые подходят.\n\n" .
|
||
"5️⃣ **Судебные прецеденты:**\n" .
|
||
"- Проанализируй файлы `case_{номер_дела}.txt` из `$vectorStoreId`.\n" .
|
||
"- Для каждого: номер дела, суд, дата, результат, почему релевантно.\n" .
|
||
"- Используй их ТОЛЬКО как прецеденты, а не доказательства.\n\n" .
|
||
"6️⃣ **Риски и слабые места:**\n" .
|
||
"- Укажи конкретные проблемы в позиции истца (например, отсутствие доказательств).\n\n" .
|
||
"7️⃣ **Прогноз вероятности успеха:**\n" .
|
||
"- Оцени в процентах на основе документов и прецедентов.\n" .
|
||
"- Объясни, от чего зависит исход.\n\n" .
|
||
"📌 **Инструкции:**\n" .
|
||
"- Обрабатывай ВСЕ файлы из `$content` и `$vectorStoreId`. Не дублируй данные.\n" .
|
||
"- Судебные акты (`case_*.txt`) — только для прецедентов.\n" .
|
||
"- Если текст не читаем, пиши 'текст не распознан' в содержании документа.\n" .
|
||
"- Отвечай строго по структуре, без лишних фраз.";
|
||
|
||
logMessage("📡 Финальный промпт для GPT-4:\n" . $userMessage);
|
||
|
||
// Отправляем сообщение в тред
|
||
$messagePayload = [
|
||
"role" => "user",
|
||
"content" => $userMessage
|
||
];
|
||
|
||
$messageJson = json_encode($messagePayload, JSON_UNESCAPED_UNICODE);
|
||
if ($messageJson === false) {
|
||
logMessage("❌ Ошибка кодирования JSON: " . json_last_error_msg());
|
||
return null;
|
||
}
|
||
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/messages",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_POST => true,
|
||
CURLOPT_POSTFIELDS => $messageJson,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Content-Type: application/json",
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
$messageResponse = curl_exec($curl);
|
||
curl_close($curl);
|
||
|
||
$messageData = json_decode($messageResponse, true);
|
||
if (!$messageData || !isset($messageData['id'])) {
|
||
logMessage("❌ Ошибка при отправке сообщения в тред. Ответ: " . $messageResponse);
|
||
return null;
|
||
}
|
||
|
||
logMessage("[INFO] ✅ Сообщение успешно добавлено в тред.");
|
||
|
||
// Создаём Run
|
||
$payload = [
|
||
"assistant_id" => $assistantId,
|
||
"tool_resources" => [
|
||
"file_search" => [
|
||
"vector_store_ids" => [$vectorStoreId]
|
||
]
|
||
]
|
||
];
|
||
|
||
$payloadJson = json_encode($payload, JSON_UNESCAPED_UNICODE);
|
||
if ($payloadJson === false) {
|
||
logMessage("❌ Ошибка кодирования JSON: " . json_last_error_msg());
|
||
return null;
|
||
}
|
||
|
||
$curl = curl_init();
|
||
curl_setopt_array($curl, [
|
||
CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$threadId/runs",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_POST => true,
|
||
CURLOPT_POSTFIELDS => $payloadJson,
|
||
CURLOPT_HTTPHEADER => [
|
||
"Content-Type: application/json",
|
||
"Authorization: Bearer " . OPENAI_API_KEY,
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
|
||
$response = curl_exec($curl);
|
||
curl_close($curl);
|
||
|
||
$structuredData = json_decode($response, true);
|
||
if (!$structuredData || !isset($structuredData['id'])) {
|
||
logMessage("❌ Ошибка запуска Run. Ответ: " . $response);
|
||
return null;
|
||
}
|
||
|
||
$runId = $structuredData['id'];
|
||
logMessage("[INFO] 🔄 GPT-4 начал обработку запроса. run_id: $runId");
|
||
|
||
// Ожидаем завершения обработки
|
||
if (!waitForRunCompletion($threadId, $runId)) {
|
||
return null;
|
||
}
|
||
|
||
// Запрашиваем финальный ответ
|
||
$finalResponse = fetchThreadMessages($threadId);
|
||
if (!$finalResponse || !isset($finalResponse['data']) || empty($finalResponse['data'])) {
|
||
logMessage("[ERROR] ❌ Ошибка парсинга финального ответа. Ответ: " . json_encode($finalResponse, JSON_UNESCAPED_UNICODE));
|
||
return null;
|
||
}
|
||
|
||
// Извлекаем текст из ответа ассистента
|
||
$assistantMessage = null;
|
||
foreach ($finalResponse['data'] as $message) {
|
||
if ($message['role'] === 'assistant' && isset($message['content'][0]['text']['value'])) {
|
||
$assistantMessage = $message;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!$assistantMessage) {
|
||
logMessage("[ERROR] ❌ Нет ответа от ассистента. Полный ответ: " . json_encode($finalResponse, JSON_UNESCAPED_UNICODE));
|
||
return null;
|
||
}
|
||
|
||
$finalText = $assistantMessage['content'][0]['text']['value'];
|
||
logMessage("[INFO] ✅ Финальный текстовый отчёт:\n" . $finalText);
|
||
return $finalText;
|
||
}
|
||
|
||
*/
|
||
function analyzeDocumentWithKnowledgeBase($knowledgeThreadId, $documentThreadId) {
|
||
logMessage("📡 Начало анализа документов (новый тред `$documentThreadId`, база знаний `$knowledgeThreadId`)");
|
||
|
||
$payload = json_encode([
|
||
"role" => "user",
|
||
"content" => [
|
||
[
|
||
"type" => "text",
|
||
"text" => "Проанализируй документы в этом `thread_id` ($documentThreadId), " .
|
||
"используя законы из базы знаний (`thread_id` $knowledgeThreadId). " .
|
||
"Отвечай исключительно на основе информации из базы знаний."
|
||
]
|
||
]
|
||
]);
|
||
|
||
logMessage("📡 Отправляем запрос в API с `thread_id` базы знаний `$knowledgeThreadId` " .
|
||
"и `thread_id` с новыми документами `$documentThreadId`");
|
||
logMessage("📄 JSON-запрос:\n" . $payload);
|
||
|
||
$ch = curl_init();
|
||
curl_setopt_array($ch, [
|
||
//CURLOPT_URL => "https://api.proxyapi.ru/openai/v1/threads/$documentThreadId/messages",
|
||
CURLOPT_URL => OPENAI_THREADS_API . "/$threadId/messages",
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_POST => true,
|
||
CURLOPT_POSTFIELDS => $payload,
|
||
CURLOPT_HTTPHEADER => [
|
||
'Authorization: Bearer ' . OPENAI_API_KEY,
|
||
'Content-Type: application/json',
|
||
"OpenAI-Beta: assistants=v2"
|
||
]
|
||
]);
|
||
|
||
$response = curl_exec($ch);
|
||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
$curlError = curl_error($ch);
|
||
curl_close($ch);
|
||
|
||
if ($curlError) {
|
||
logMessage("❌ Ошибка cURL при анализе: $curlError");
|
||
die("❌ Ошибка cURL при анализе: $curlError\n");
|
||
}
|
||
|
||
logMessage("📡 Ответ API (HTTP $httpCode): $response");
|
||
|
||
$decoded = json_decode($response, true);
|
||
if ($httpCode !== 200 || !isset($decoded['data'])) {
|
||
logMessage("❌ Ошибка анализа! API ответил: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
|
||
die("❌ Ошибка анализа! Ответ API: " . json_encode($decoded, JSON_UNESCAPED_UNICODE) . "\n");
|
||
}
|
||
|
||
logMessage("✅ Анализ завершён! Получен ответ от ассистента.");
|
||
return end($decoded['data'])['content']; // Получаем последний ответ ассистента
|
||
}
|
||
|
||
?>
|