↩️ Назад

Категории

От счётчика ошибок к умному анализу с Qwen 2.5 + PHP + HTML

16.07.2026 | Статья из категории: Нейросети

Интеграция AI-аналитики в панель умного дома

Вы уже считаете количество ошибок в логах. Но что, если нейросеть сможет не просто посчитать, а понять, что происходит в вашем доме? В этой статье мы:

  • Настроим локальную нейросеть Qwen 2.5 7B (GGUF) на Linux
  • Напишем PHP-скрипт, который анализирует логи Zigbee-устройств
  • Сделаем красивую HTML-панель для отображения результатов
  • Настроим автоматический сбор и анализ логов

1. Установка нейросети на Linux

Мы будем использовать llama.cpp — это самый производительный движок для запуска GGUF-моделей на видеокартах NVIDIA.

1.1 Устанавливаем llama.cpp с поддержкой CUDA

# Клонируем репозиторий
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp

# Собираем с поддержкой CUDA (для вашей RTX 5060)
LLAMA_CUDA=1 make -j4

# Проверяем, что сборка прошла
./llama-cli -h

1.2 Скачиваем модель Qwen 2.5 7B (Q4_K_M)

Для вашей видеокарты с 8 ГБ памяти идеально подходит версия Q4_K_M — около 4.3 ГБ.

# Скачиваем через huggingface-cli (рекомендуется)
pip install -U "huggingface_hub[cli]"
huggingface-cli download tensorblock/Qwen2.5-7B-Instruct-GGUF \
    --include "Qwen2.5-7B-Instruct-Q4_K_M.gguf" \
    --local-dir ./models/qwen
💡 Альтернатива: если ссылка недоступна, используйте репозиторий paultimothymooney/Qwen2.5-7B-Instruct-Q4_K_M-GGUF.

1.3 Запускаем модель как сервер (API)

Для интеграции с PHP удобнее всего запустить модель в режиме сервера. Он будет висеть в фоне и ждать запросов.

# Запускаем сервер на порту 8080
./llama-server -m ./models/qwen/Qwen2.5-7B-Instruct-Q4_K_M.gguf \
    -c 4096 \
    -ngl 999 \
    --flash-attn \
    --port 8080
⚙️ Важные параметры:
-c 4096 — размер контекста (для логов хватит)
-ngl 999 — загрузить все слои в видеопамять
--flash-attn — экономия памяти

2. Архитектура агента: сбор логов → анализ → вывод

Вот как будет работать вся система:

Zigbee-устройства → логи → PHP-агент → Qwen (API) → анализ → HTML-панель

📥 Сбор логов

Ваш Zigbee-контроллер (например, zigbee2mqtt) пишет логи в файл /var/log/zigbee.log.

🧠 Анализ

PHP-скрипт читает логи, формирует промпт и отправляет запрос к нейросети.

📊 Результат

Нейросеть возвращает структурированный ответ с рекомендациями и критическими событиями.

🖥️ Панель

HTML-страница с карточками, графиками и статусами — всё в реальном времени.

3. PHP-агент для анализа логов

Этот скрипт читает логи, выделяет ошибки, предупреждения и отправляет их в нейросеть. Результат кешируется, чтобы не нагружать видеокарту.

3.1 Класс для работы с API нейросети

<?php
// agent.php — ядро аналитики

class QwenAgent {
    private $apiUrl = 'http://localhost:8080/v1/chat/completions';
    private $cacheFile = '/tmp/agent_cache.json';
    private $cacheTTL = 300; // 5 минут

    public function analyzeLogs($logText) {
        // Проверяем кеш
        if ($this->getCache($logText)) {
            return $this->getCache($logText);
        }

        // Формируем системный промпт
        $systemPrompt = "Ты — эксперт по умному дому. Анализируй логи Zigbee-устройств.
Выдели критичные ошибки, нестабильные устройства и дай рекомендации.
В ответе используй JSON-структуру:
{
  \"critical\": [\"список критических событий\"],
  \"warnings\": [\"список предупреждений\"],
  \"summary\": \"краткое резюме\",
  \"devices_offline\": [\"устройства, которые не отвечают\"]
}";

        $payload = [
            'messages' => [
                ['role' => 'system', 'content' => $systemPrompt],
                ['role' => 'user', 'content' => "Проанализируй эти логи:\n" . $logText]
            ],
            'temperature' => 0.2,
            'max_tokens' => 1024
        ];

        $ch = curl_init($this->apiUrl);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
        curl_setopt($ch, CURLOPT_TIMEOUT, 60);

        $response = curl_exec($ch);
        curl_close($ch);

        if (!$response) {
            return ['error' => 'Нейросеть не отвечает'];
        }

        $data = json_decode($response, true);
        $result = $data['choices'][0]['message']['content'] ?? '';

        // Сохраняем в кеш
        $this->setCache($logText, $result);
        return $result;
    }

    private function getCache($text) {
        if (!file_exists($this->cacheFile)) return false;
        $cache = json_decode(file_get_contents($this->cacheFile), true);
        if (!$cache) return false;
        if (time() - $cache['time'] > $this->cacheTTL) return false;
        if (md5($text) !== $cache['hash']) return false;
        return $cache['data'];
    }

    private function setCache($text, $data) {
        file_put_contents($this->cacheFile, json_encode([
            'hash' => md5($text),
            'data' => $data,
            'time' => time()
        ]));
    }
}

// Использование:
$agent = new QwenAgent();
$logs = file_get_contents('/var/log/zigbee.log'); // последние 1000 строк
$analysis = $agent->analyzeLogs($logs);
echo $analysis;
?>

3.2 Скрипт для сбора статистики по ключевым словам (без нейросети)

Если нейросеть ещё не установлена, можно использовать «упрощённый» анализатор. Он считает ключевые слова и подготавливает данные для нейросети.

<?php
// stat_collector.php — собирает статистику по ключевым словам

function collectStats($logPath) {
    $keywords = ['error' => 0, 'warning' => 0, 'failed' => 0, 'offline' => 0];
    $lines = file($logPath, FILE_IGNORE_NEW_LINES);
    $lines = array_slice($lines, -200); // последние 200 строк

    foreach ($lines as $line) {
        $lower = strtolower($line);
        foreach ($keywords as $key => &$count) {
            if (strpos($lower, $key) !== false) {
                $count++;
            }
        }
    }

    // Формируем текст для нейросети
    $logText = implode("\n", $lines);
    return [
        'stats' => $keywords,
        'log_text' => $logText,
        'total_lines' => count($lines)
    ];
}

// Сохраняем статистику в JSON для панели
$data = collectStats('/var/log/zigbee.log');
file_put_contents('/var/www/html/stats.json', json_encode($data));
?>

4. HTML-панель для отображения результатов

Вот готовая страница, которая показывает статистику ошибок, результат анализа нейросети и статус устройств. Обновляется автоматически каждые 30 секунд.

4.1 Полный HTML-код панели

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>🏠 Умный дом — AI-аналитика</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: 'Segoe UI', system-ui, sans-serif;
            background: #f0f4f8;
            padding: 2rem 1rem;
            color: #1e293b;
        }
        .dashboard {
            max-width: 1200px;
            margin: 0 auto;
            display: grid;
            grid-template-columns: 1fr 1fr 1fr;
            gap: 1.5rem;
        }
        .card {
            background: white;
            border-radius: 24px;
            padding: 1.5rem;
            box-shadow: 0 4px 16px rgba(0,0,0,0.06);
            border: 1px solid #e2e8f0;
        }
        .card-full { grid-column: 1 / -1; }
        .card h3 {
            font-size: 1rem;
            text-transform: uppercase;
            letter-spacing: 0.04em;
            color: #64748b;
            margin-bottom: 0.75rem;
        }
        .stat-number {
            font-size: 2.6rem;
            font-weight: 700;
            color: #0f172a;
        }
        .stat-label { color: #475569; font-size: 0.95rem; }
        .badge-error { background: #fef2f2; color: #b91c1c; padding: 0.2rem 0.8rem; border-radius: 30px; font-size: 0.8rem; }
        .badge-warning { background: #fffbeb; color: #92400e; padding: 0.2rem 0.8rem; border-radius: 30px; font-size: 0.8rem; }
        .badge-ok { background: #ecfdf5; color: #065f46; padding: 0.2rem 0.8rem; border-radius: 30px; font-size: 0.8rem; }
        .ai-response {
            background: #f8fafc;
            padding: 1.2rem;
            border-radius: 16px;
            margin-top: 0.8rem;
            white-space: pre-wrap;
            font-size: 0.95rem;
            border-left: 4px solid #3b82f6;
        }
        .device-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 0.5rem;
            margin-top: 0.5rem;
        }
        .device-item {
            background: #f8fafc;
            padding: 0.6rem 1rem;
            border-radius: 12px;
            display: flex;
            justify-content: space-between;
        }
        .footer { margin-top: 2rem; text-align: center; color: #94a3b8; font-size: 0.85rem; }
        @media (max-width: 800px) {
            .dashboard { grid-template-columns: 1fr; }
        }
    </style>
</head>
<body>
    <div class="dashboard" id="dashboard">
        <!-- Карточка ошибок -->
        <div class="card">
            <h3>🔴 Ошибки (error)</h3>
            <div class="stat-number" id="errorCount">--</div>
            <div class="stat-label">за последние 200 строк</div>
        </div>

        <!-- Карточка предупреждений -->
        <div class="card">
            <h3>🟡 Предупреждения (warning)</h3>
            <div class="stat-number" id="warningCount">--</div>
            <div class="stat-label">за последние 200 строк</div>
        </div>

        <!-- Карточка офлайн-устройств -->
        <div class="card">
            <h3>📡 Офлайн устройства</h3>
            <div class="stat-number" id="offlineCount">--</div>
            <div class="stat-label">не отвечают</div>
        </div>

        <!-- Анализ нейросети (полная ширина) -->
        <div class="card card-full">
            <h3>🧠 Анализ нейросети (Qwen 2.5)</h3>
            <div class="ai-response" id="aiAnalysis">
                ⏳ Ожидание данных...
            </div>
            <div style="margin-top: 0.8rem; font-size: 0.85rem; color: #64748b;">
                Последнее обновление: <span id="updateTime">--</span>
            </div>
        </div>

        <!-- Устройства (пример) -->
        <div class="card card-full">
            <h3>🔌 Состояние устройств Zigbee</h3>
            <div class="device-grid" id="deviceGrid">
                <div class="device-item"><span>Датчик двери (спальня)</span> <span class="badge-ok">Онлайн</span></div>
                <div class="device-item"><span>Датчик движения (кухня)</span> <span class="badge-ok">Онлайн</span></div>
                <div class="device-item"><span>Умная розетка (гостиная)</span> <span class="badge-warning">Нестабильно</span></div>
                <div class="device-item"><span>Датчик температуры (ванна)</span> <span class="badge-error">Офлайн</span></div>
            </div>
        </div>
    </div>

    <div class="footer">
        🤖 Локальная нейросеть Qwen 2.5 7B · Обновление каждые 30 секунд
    </div>

    <script>
        // Обновление панели через AJAX
        async function updateDashboard() {
            try {
                const response = await fetch('stats.json?t=' + Date.now());
                const data = await response.json();

                document.getElementById('errorCount').textContent = data.stats?.error || 0;
                document.getElementById('warningCount').textContent = data.stats?.warning || 0;
                document.getElementById('offlineCount').textContent = data.stats?.offline || 0;

                // Если есть анализ от нейросети (кэшируется в файле)
                const aiResponse = await fetch('ai_result.json?t=' + Date.now()).catch(() => null);
                if (aiResponse && aiResponse.ok) {
                    const aiData = await aiResponse.json();
                    if (aiData.analysis) {
                        document.getElementById('aiAnalysis').textContent = aiData.analysis;
                    }
                }

                document.getElementById('updateTime').textContent = new Date().toLocaleTimeString();
            } catch (e) {
                console.warn('Ошибка обновления:', e);
            }
        }

        updateDashboard();
        setInterval(updateDashboard, 30000); // каждые 30 секунд
    </script>
</body>
</html>

5. Настройка Cron для автоматического анализа

Добавьте задачу в crontab, чтобы логи анализировались каждые 15 минут, а результат сохранялся в JSON для панели.

# Открываем crontab
crontab -e

# Добавляем две задачи:
*/15 * * * * /usr/bin/php /var/www/html/stat_collector.php
*/15 * * * * /usr/bin/php /var/www/html/agent.php > /var/www/html/ai_result.json

🎯 Итог: что у нас получилось

✅ Собрали логи

PHP-скрипт читает последние 200 строк из /var/log/zigbee.log.

✅ Проанализировали нейросетью

Qwen 2.5 выдаёт структурированный ответ с критическими событиями.

✅ Отобразили в панели

HTML-страница с карточками, статистикой и AI-блоком обновляется каждые 30 секунд.

✅ Автоматизировали

Cron запускает анализ каждые 15 минут — вы всегда в курсе.

🚀 Дальнейшие улучшения:
• Добавить Telegram-бота для уведомлений о критических ошибках
• Настроить анализ истории (тренды ошибок по дням)
• Интегрировать с Home Assistant через REST API
👍 Оцените статью
Всего голосов: 0



Категории:

Категории

Комментарии

Пока нет комментариев. Будьте первым!

Оставить комментарий

← Назад к списку статей

Посетителей сегодня: 0
о блоге | карта блога | 📡 Подписаться на RSS

© Digital Specialist | Не являемся сотрудниками Google, Яндекса и NASA