Бесплатная проверка комментариев с помощью API LLM
LLM прекрасный помощник для многих дел. Ниже расскажу, как я присобачил его (полностью бесплатно) для предварительного модерирования комментариев. Итак, нам необходимо, чтобы система предварительно проверяла комментарии и сразу давала первую оценку и причину – если содержимое некорректно. Все в формате json.
Регистрируетесь здесь openrouter.ai и создаете ключ. Дальше выбираете бесплатную модель
Проект работает на шустром и простом фреймворке Flight. Контроллер
namespace App\Controllers;
use Flight;
class ModerationController extends Controller
{
public function moderateText()
{
$data = Flight::request()->data;
$text = trim($data->text ?? '');
if ($text === '') {
Flight::json(['error' => 'Поле "text" обязательно'], 400);
return;
}
// Ограничение длины (опционально)
if (mb_strlen($text) > 1000) {
Flight::json(['error' => 'Текст слишком длинный (макс. 1000 символов)'], 400);
return;
}
$apiKey = 'ключ';
if (!$apiKey) {
error_log('OpenRouter API key not configured');
Flight::json(['error' => 'Сервис временно недоступен'], 500);
return;
}
$prompt = <<<PROMPT
Проанализируй следующий текст на наличие неуместного содержания: мат, оскорбления, спам, реклама, призывы к насилию, дискриминация или другая токсичность.
Ответь строго в формате JSON без пояснений:
Если текст уместный — верни: {"is_inappropriate": false}
Если неуместный — верни: {"is_inappropriate": true, "reason": "причина"}
Текст для анализа:
"""
{$text}
"""
PROMPT;
$payload = [
'model' => 'arcee-ai/trinity-large-preview:free',
'messages' => [
['role' => 'user', 'content' => $prompt]
],
'temperature' => 0.0,
'max_tokens' => 100
];
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://openrouter.ai/api/v1/chat/completions',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $apiKey,
'HTTP-Referer: ' . Flight::get('flight.base_url'), // требуется OpenRouter
'X-Title: ' . Flight::get('app.name') // опционально
],
CURLOPT_TIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => true
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
error_log("OpenRouter cURL error: $error");
Flight::json(['error' => 'Ошибка при вызове модерации'], 500);
return;
}
if ($httpCode !== 200) {
error_log("OpenRouter HTTP $httpCode: $response");
Flight::json(['error' => 'Сервис модерации недоступен'], 500);
return;
}
$decoded = json_decode($response, true);
$rawContent = $decoded['choices'][0]['message']['content'] ?? '';
// Очистка: иногда модель оборачивает JSON в блок кода
$cleanJson = trim(preg_replace('/^```json\s*|\s*```$/i', '', $rawContent));
$result = json_decode($cleanJson, true);
if (json_last_error() !== JSON_ERROR_NONE || !isset($result['is_inappropriate'])) {
// Если модель не вернула валидный JSON — считаем безопасным или логируем
error_log("Invalid JSON from OpenRouter: $rawContent");
Flight::json([
'is_inappropriate' => false,
'warning' => 'Не удалось распознать ответ модели'
]);
return;
}
Flight::json($result);
}
public function showForm()
{
// Просто рендерим view
require_once 'views/moderation/form.php';
}
}
Шаблон страницы
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Модерация текста</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 700px;
margin: 40px auto;
padding: 0 20px;
background-color: #f9f9f9;
color: #333;
}
h1 {
text-align: center;
margin-bottom: 24px;
color: #2c3e50;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
}
textarea {
width: 100%;
height: 150px;
padding: 12px;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 15px;
resize: vertical;
box-sizing: border-box;
}
button {
background-color: #3498db;
color: white;
border: none;
padding: 12px 24px;
font-size: 16px;
border-radius: 6px;
cursor: pointer;
transition: background-color 0.2s;
}
button:hover {
background-color: #2980b9;
}
button:disabled {
background-color: #bdc3c7;
cursor: not-allowed;
}
#result {
margin-top: 24px;
padding: 16px;
border-radius: 6px;
display: none;
}
.result-safe {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.result-risk {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.loading {
display: inline-block;
margin-left: 10px;
font-style: italic;
color: #666;
}
</style>
</head>
<body>
<h1>Проверка текста на неуместное содержание</h1>
<div class="form-group">
<label for="textInput">Введите текст для модерации:</label>
<textarea id="textInput" placeholder="Например: Привет! Как дела?"></textarea>
</div>
<button id="submitBtn">Проверить</button>
<span id="loading" class="loading" style="display:none;">Отправка...</span>
<div id="result"></div>
<script>
document.getElementById('submitBtn').addEventListener('click', async () => {
const text = document.getElementById('textInput').value.trim();
const resultDiv = document.getElementById('result');
const loading = document.getElementById('loading');
const btn = document.getElementById('submitBtn');
if (!text) {
alert('Пожалуйста, введите текст.');
return;
}
// Сброс результата
resultDiv.style.display = 'none';
resultDiv.className = '';
btn.disabled = true;
loading.style.display = 'inline';
try {
const response = await fetch('/api/moderate', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ text: text })
});
const data = await response.json();
let message = '';
if (response.ok) {
if (data.is_inappropriate) {
resultDiv.className = 'result-risk';
message = `<strong>⚠️ Неуместный контент</strong><br>Причина: ${data.reason || 'не указана'}`;
} else {
resultDiv.className = 'result-safe';
message = '<strong>✅ Текст безопасен</strong>';
}
} else {
resultDiv.className = 'result-risk';
message = `<strong>❌ Ошибка</strong><br>${data.error || 'Неизвестная ошибка'}`;
}
resultDiv.innerHTML = message;
resultDiv.style.display = 'block';
} catch (err) {
resultDiv.className = 'result-risk';
resultDiv.innerHTML = '<strong>❌ Ошибка подключения</strong><br>Проверьте интернет или попробуйте позже.';
resultDiv.style.display = 'block';
} finally {
btn.disabled = false;
loading.style.display = 'none';
}
});
</script>
</body>
</html>
Не забудем роут
// Показ формы модерации
Flight::route('GET /moderate', ['App\Controllers\ModerationController', 'showForm']);
// API: отправка текста на модерацию
Flight::route('POST /api/moderate', ['App\Controllers\ModerationController', 'moderateText']);
Итог
Вот так просто и бесплатно можно с помощью LLM проверять комментарии на корретность.
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.
Программы на заказ
Отзывы
Контакты