Files
crm.clientright.ru/aiassist/gptAssistant.php
Fedor ac7467f0b4 Major CRM updates: AI Assistant, Court Status API, S3 integration improvements, and extensive file storage system
- 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.
2025-10-16 11:17:21 +03:00

1345 lines
62 KiB
PHP
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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

<?php
// 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" .
"• Суть спора (в 23 предложениях)\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" .
"• Суть спора (в 23 предложениях)\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']; // Получаем последний ответ ассистента
}
?>