Or copy link
В этой статье рассмотрим процесс интеграции ChatGPT в Битрикс24 в виде чат-бота. Начнем с аренды облачного сервера и настройки LAMP-стека (Linux, Apache, MySQL, PHP), что обеспечит работу приложения. Далее создадим чат-бота в Битрикс24 и настроим базу данных для хранения данных переписки. На заключительном этапе напишем само приложение для обработки запросов. Во второй части статьи займемся контейнеризацией приложения с помощью Docker, что облегчит его развёртывание и управление.
Поскольку доступ к ChatGPT в России ограничен, мы будем использовать зарубежный хостинг для обхода блокировок. Вы также можете развернуть приложение на сервере внутри своей сети, при условии, что ваш IP-адрес не заблокирован OpenAI.
Аренда облачного сервера:
Услуги -> Виртуальные серверы -> Нидерланды
Lite
ОФОРМИТЬ ЗАКАЗ
После оплаты заказа потребуется подождать активации сервера, что обычно занимает около 5 минут. Как только сервер будет активирован, на почту придёт письмо с данными для доступа:
Подключаемся к серверу по SSH, используя предоставленные данные через PuTTy или другой SSH-клиент:
В терминале вводим логин и пароль (при вводе пароля символы не отображаются):
После успешной авторизации увидим следующий вывод:
Поскольку наше приложение будет обрабатывать HTTP-запросы, потребуется установить несколько компонентов: веб-сервер, интерпретатор PHP и базу данных. Для этого мы будем использовать стек LAMP (Linux, Apache, MySQL, PHP). Этот стек включает:
Настраиваем часовой пояс на сервере:
timedatectl set-timezone Europe/Moscow
Проверяем дату и время:
date
Обновляем пакеты:
apt update
Открываем порты для HTTP и HTTPS запросов:
ufw allow 80 ufw allow 443
Устанавливаем веб-сервера Apache2:
apt install apache2 -y
Добавляем в автозагрузку и запускаем демон:
systemctl enable --now apache2
Теперь при открытии IP-адреса сервера в браузере должна отобразиться стандартная страница Apache2:
Также необходимо отключить отображение списка файлов в каталогах в конфигурации веб-сервера. Для этого откроем конфигурационный файл веб-сервера:
nano /etc/apache2/apache2.conf
Нужно найти следующий фрагмент:
Options Indexes FollowSymLinks AllowOverride None Require all granted
И заменить на этот:
Options -Indexes AllowOverride None Require all granted
Перечитываем конфигурацию веб-сервера:
apachectl graceful
Установка базы данных MySQL:
apt install mysql-server -y
Запускаем скрипт для настройки безопасности базы данных:
mysql_secure_installation
Соглашаемся с настройкой компонента проверки паролей:
VALIDATE PASSWORD COMPONENT can be used to test passwords and improve security. It checks the strength of password and allows the users to set only those passwords which are secure enough. Would you like to setup VALIDATE PASSWORD component? Press y|Y for Yes, any other key for No: Y
Устанавливаем уровень сложности паролей:
There are three levels of password validation policy: LOW Length >= 8 MEDIUM Length >= 8, numeric, mixed case, and special characters STRONG Length >= 8, numeric, mixed case, special characters and dictionary file Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 0
Удаляем анонимных пользователей:
By default, a MySQL installation has an anonymous user, allowing anyone to log into MySQL without having to have a user account created for them. This is intended only for testing, and to make the installation go a bit smoother. You should remove them before moving into a production environment. Remove anonymous users? (Press y|Y for Yes, any other key for No) : Y
Запрещаем удалённый доступ для пользователя root:
Normally, root should only be allowed to connect from 'localhost'. This ensures that someone cannot guess at the root password from the network. Disallow root login remotely? (Press y|Y for Yes, any other key for No) : Y
Удаляем тестовые базы данных:
By default, MySQL comes with a database named 'test' that anyone can access. This is also intended only for testing, and should be removed before moving into a production environment. Remove test database and access to it? (Press y|Y for Yes, any other key for No) : Y
Перезагружаем привилегии:
Reloading the privilege tables will ensure that all changes made so far will take effect immediately. Reload privilege tables now? (Press y|Y for Yes, any other key for No) : Y
Устанавливаем PHP и дополнительные расширения:
apt install php php-mysql libapache2-mod-php php-curl -y
Перезагружаем демон:
systemctl restart apache2
Создаём файл для проверки работоспособности:
nano /var/www/html/index.php
И вставляем следующий код:
Теперь при открытии веб-страницы в браузере по адресу http://IP_SERVER/index.php должна отобразиться страница с конфигурацией PHP:
http://IP_SERVER/index.php
В качестве веб-интерфейса для взаимодействия с ChatGPT мы будем использовать Битрикс24. Давайте создадим чат-бота, так же в документации есть посвященная этому тема.
Создание чат-бота:
Разработчикам -> Добавить чат-бот -> Передавать боту сообщения из чата
СОХРАНИТЬ
XXX/XXXXXXXXXXXXXXXXXXX/
Чтобы ChatGPT мог поддерживать диалог, необходимо сохранять предыдущие вопросы и ответы. Для этого потребуется база данных, в которой будут храниться вопросы и ответы для каждого пользователя Битрикс24.
Подключаемся к оболочке управления MySQL:
mysql -u root
Если на шаге с настройкой безопасности указывали пароль от пользователя root, то команда будет следующей:
mysql -u root -p
Отобразится приветственное сообщение:
Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 12 Server version: 8.0.39-0ubuntu0.22.04.1 (Ubuntu) Copyright (c) 2000, 2024, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
Создаём базу данных:
CREATE DATABASE gpt;
Выбираем созданную базу данных:
USE gpt;
Создаём таблицу:
CREATE TABLE `context` ( `ID` int NOT NULL AUTO_INCREMENT, `USER_ID` int NOT NULL, `DIALOG_ID` int NOT NULL, `ROLE` varchar(32) NOT NULL, `MESSAGE` mediumtext NOT NULL, `TIMESTAMP` timestamp NOT NULL, PRIMARY KEY (`ID`) ) ENGINE=InnoDB AUTO_INCREMENT=1471 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Описание структуры таблицы:
Создаём пользователя базы данных:
CREATE USER 'gpt'@'localhost' IDENTIFIED BY 'PASSWORD';
gpt
GRANT ALL PRIVILEGES ON gpt.* TO 'gpt'@'localhost';
Обновляем права доступа:
FLUSH PRIVILEGES;
Выходим из оболочки:
quit
Удаляем лишние файлы:
rm /var/www/html/*
Создаём конфигурационный файл:
nano /var/www/html/config_DOAMIN.php
Здесь DOMAIN замените на адрес вашего портала Битрикс24, например, адрес портала crm.example.ru, то файл будет называться config_crm_example_ru.php
Таким образом, если мы хотим, чтобы наше приложение обрабатывало запросы с нескольких серверов, нужно создать отдельный конфигурационный файл для каждого обрабатываемого сервера.
'XXX/XXXXXXXXXXXXXXXX/', 'OPENAI_TOKEN' => array( 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' ), 'DATABASE' => array( 'HOST' => 'localhost', 'PORT' => 3306, 'USER' => 'gpt', 'PASSWORD' => 'XXXXXXXXXXXXXXXX', 'DB' => 'gpt' ), 'CONTEXT' => 4, 'MODEL' => "gpt-4o" ); ?>
Для работы приложения необходимо заполнить конфигурационный файл, рассмотрим его более детально:
3306
Создаём основной файл работы приложения:
nano /var/www/html/handler.php
$_REQUEST['data']['PARAMS']['DIALOG_ID'])); $messages['messages'][] = array('role' => 'user', 'content' => $_REQUEST['data']['PARAMS']['MESSAGE']); foreach ($rows as $row) { array_unshift($messages['messages'], array('role' => $row['ROLE'], 'content' => $row['MESSAGE'])); } $arReport = ask($appsConfig['OPENAI_TOKEN'][array_rand($appsConfig['OPENAI_TOKEN'])], $appsConfig['MODEL'], $messages); } // send answer message $result = restCommand($appsConfig['BITRIX_TOKEN'], 'imbot.message.add', array( 'BOT_ID' => array_shift($_REQUEST['data']['BOT'])['BOT_ID'], 'CLIENT_ID' => $_REQUEST['auth']['application_token'], 'DIALOG_ID' => $_REQUEST['data']['PARAMS']['DIALOG_ID'], 'MESSAGE' => $arReport['message'], 'ATTACH' => isset($arReport['attach']) ? $arReport['attach'] : null ), $_REQUEST['auth']); if (isset($result['result']) && !isset($arReport['failed'])) { query($appsConfig['DATABASE'], 'INSERT INTO context (USER_ID, DIALOG_ID, ROLE, MESSAGE, TIMESTAMP) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP())', array($_REQUEST['data']['PARAMS']['FROM_USER_ID'], $_REQUEST['data']['PARAMS']['DIALOG_ID'], "user", $_REQUEST['data']['PARAMS']['MESSAGE'])); query($appsConfig['DATABASE'], 'INSERT INTO context (USER_ID, DIALOG_ID, ROLE, MESSAGE, TIMESTAMP) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP())', array($_REQUEST['data']['PARAMS']['FROM_USER_ID'], $_REQUEST['data']['PARAMS']['DIALOG_ID'], "assistant", $arReport['message'])); } } // receive event "open private dialog with bot" or "join bot to group chat" else { if ($_REQUEST['event'] == 'ONIMBOTJOINCHAT') { // check the event - register this application or not if (!isset($appsConfig['BITRIX_TOKEN']) or empty($appsConfig['BITRIX_TOKEN'])) { return false; } // send help message how to use chat-bot. For private chat and for group chat need send different instructions. $result = restCommand($appsConfig['BITRIX_TOKEN'], 'imbot.message.add', array( 'BOT_ID' => array_shift($_REQUEST['data']['BOT'])['BOT_ID'], 'CLIENT_ID' => $_REQUEST['auth']['application_token'], 'DIALOG_ID' => $_REQUEST['data']['PARAMS']['DIALOG_ID'], 'MESSAGE' => 'Привет! Чем могу помочь?' ), $_REQUEST['auth']); } } /** * Send rest query to Bitrix24. * * @param $method - Rest method, ex: methods * @param array $params - Method params, ex: array() * @param array $auth - Authorize data, ex: array('domain' => 'https://test.bitrix24.com', 'access_token' => '7inpwszbuu8vnwr5jmabqa467rqur7u6') * * @return mixed */ function restCommand($token, $method, array $params = array(), array $auth = array()) { $queryUrl = 'https://' . $auth['domain'] . '/rest/' . $token . '/' . $method; $queryData = http_build_query($params); #writeToLog(array('URL' => $queryUrl, 'PARAMS' => $params)), 'ReportBot send data'); $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_POST => 1, CURLOPT_HEADER => 0, CURLOPT_RETURNTRANSFER => 1, CURLOPT_URL => $queryUrl, CURLOPT_POSTFIELDS => $queryData, )); $result = curl_exec($curl); curl_close($curl); $result = json_decode($result, 1); return $result; } /** * Write data to log file. * * @param mixed $data * @param string $title * * @return bool */ function writeToLog($data, $title = '') { $log = "\n------------------------\n"; $log .= date("Y.m.d G:i:s") . "\n"; $log .= (strlen($title) > 0 ? $title : 'DEBUG') . "\n"; $log .= print_r($data, 1); $log .= "\n------------------------\n"; file_put_contents('/var/log/imbot.log', $log, FILE_APPEND); return true; } function ask($token, $model, $messages) { $data = [ "model" => $model, 'temperature' => 0.5, "max_tokens" => 1000, "top_p" => 1.0, "frequency_penalty" => 0.52, "presence_penalty" => 0.5, "stop" => ["11."], ]; $data['messages'] = $messages['messages']; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://api.openai.com/v1/chat/completions'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0); curl_setopt($ch, CURLOPT_TIMEOUT, 300); //timeout in seconds curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); $headers = []; $headers[] = 'Content-Type: application/json'; $headers[] = 'Authorization: Bearer '.$token; curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); $response = curl_exec($ch); if (curl_errno($ch)) { echo 'Error:' . curl_error($ch); } curl_close($ch); $response = json_decode($response, 1); if (isset($response['error'])) { $arResult['message'] = "Ой, что-то пошло не так ..."; $arResult['attach'][] = array('MESSAGE' => $response['error']['message']); $arResult['failed'] = 1; writeToLog(array('TOKEN' => $token, 'ERROR' => $response), 'ERROR'); } else if (isset($response['choices'][0]['message']['content'])) { $arResult['message'] = $response['choices'][0]['message']['content']; } else { $arResult['message'] = "Ой, что-то пошло не так ..."; $arResult['failed'] = 1; writeToLog(array('TOKEN' => $token, 'ERROR' => $response), 'ERROR'); } return $arResult; } function query(array $config, $sql, array $params) { try { $conn = new PDO('mysql:host=' . $config['HOST'] . ';port=' . $config['PORT'] . ';dbname=' . $config['DB'], $config['USER'], $config['PASSWORD']); // установка режима вывода ошибок $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $stmt = $conn->prepare($sql); // через массив передаем значения параметрам по позиции $result = $stmt->execute($params); return $result; } catch (PDOException $e) { writeToLog($e, 'DATABASE EXCEPTION'); } } function fetch(array $config, $sql, array $params) { try { $conn = new PDO('mysql:host=' . $config['HOST'] . ';port=' . $config['PORT'] . ';dbname=' . $config['DB'], $config['USER'], $config['PASSWORD']); $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $stmt = $conn->prepare($sql); $stmt->execute($params); return $stmt; } catch (PDOException $e) { writeToLog($e, 'DATABASE EXCEPTION'); } }
Так же создадим файл лога:
touch /var/log/imbot.log
И выдадим соответствующие права:
chmod 777 /var/log/imbot.log
Следующий шаг не является обязательным, но настоятельно рекомендуется: обеспечение защиты передаваемых данных с помощью SSL-сертификата.
Для этого нужно будет приобрести домен и создать DNS A-запись, указывающую на сервер с приложением. Предполагаем, что эти действия уже выполнены.
Я буду использовать поддомен третьего уровня chat.winhexs.ru, замените его на свой . Отключаем стандартный сайт:
chat.winhexs.ru
a2dissite 000-default.conf
Включаем модуль SSL:
a2enmod ssl
Включаем модуль rewrite:
a2enmod rewrite
Перезапускаем демон веб-сервера:
Создаём отдельный виртуальный хост:
nano /etc/apache2/sites-available/chat-winhexs-ru.conf
* где, chat-winhexs-ru заменяем на свой домен.
И вставляем следующее содержимое:
ServerName chat.winhexs.ru ServerAlias www.chat.winhexs.ru ServerAdmin webmaster@localhost DocumentRoot /var/www/html ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined
* где, домен в опциях ServerName и ServerAlias заменяем на свой.
Включаем виртуальный сайт:
a2ensite chat-winhexs-ru.conf
Перечитываем конфигурацию:
Устанавливаем утилиту Certbot:
apt install certbot -y
А также пакет для работы Certbot с Apache:
apt install python3-certbot-apache
Выпускаем сертификат:
certbot --apache -d chat.winhexs.ru -d www.chat.winhexs.ru
Указываем почтовый ящик для уведомлений:
Enter email address (used for urgent renewal and security notices) (Enter 'c' to cancel): winhexs@mail.ru
Соглашаемся с условиями обслуживания:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Please read the Terms of Service at https://letsencrypt.org/documents/LE-SA-v1.4-April-3-2024.pdf. You must agree in order to register with the ACME server. Do you agree? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (Y)es/(N)o: Y
Отказываемся от подписки на рассылку на наш электронный адрес:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Would you be willing, once your first certificate is successfully issued, to share your email address with the Electronic Frontier Foundation, a founding partner of the Let's Encrypt project and the non-profit organization that develops Certbot? We'd like to send you email about our work encrypting the web, EFF news, campaigns, and ways to support digital freedom. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (Y)es/(N)o: N
В случае успешного выпуска сертификата увидим следующий вывод:
Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/chat.winhexs.ru/fullchain.pem Key is saved at: /etc/letsencrypt/live/chat.winhexs.ru/privkey.pem This certificate expires on 2024-12-03. These files will be updated when the certificate renews. Certbot has set up a scheduled task to automatically renew this certificate in the background. Deploying certificate Successfully deployed certificate for chat.winhexs.ru to /etc/apache2/sites-available/chat-winhexs-ru-le-ssl.conf Successfully deployed certificate for www.chat.winhexs.ru to /etc/apache2/sites-available/chat-winhexs-ru-le-ssl.conf Congratulations! You have successfully enabled HTTPS on https://chat.winhexs.ru and https://www.chat.winhexs.ru
А также автоматически будет создан виртуальный хост.
Настроим автоматическое продление сертификата, открываем планировщик задач:
nano /etc/crontab
И в самый низ добавляем следующую строку:
30 2 * * 1 /usr/bin/certbot renew >> /var/log/le-renew.log
Задание настроено на запуск Certbot каждую неделю по понедельникам в 2:30.
Также нужно обновить URL-адрес до приложения в настройках чат-бота в Битрикс24.
Теперь в Битрикс24 можно находить ChatGPT через поиск чатов и вести с ним переписку. Приложение поддерживает только текстовые сообщения, отправка файлов не предусмотрена. Во второй части статьи рассмотрим процесс контейнеризации приложения с использованием Docker.
Сохранить моё имя, email и адрес сайта в этом браузере для последующих моих комментариев.