Если вы когда-либо получали электронные письма типа: «Я не смог найти информацию на вашем сайте, не могли бы вы мне помочь?», то у вас уже есть хороший пример применения мини-чата с использованием ИИ. Цель здесь не в том, чтобы «заменить» вашу службу поддержки, а в том, чтобы предоставить полезный первоначальный ответ, основанный на содержании вашего сайта. без установить плагин.
Потребность / Вариант использования
«Простой» чат-бот с искусственным интеллектом на WordPress Его основная цель — ответить на повторяющиеся вопросы: часы работы, правила возврата, «где найти тот или иной товар». вОЗОБНОВЛЯЕТ Длинное руководство или справка по навигации («Мне нужен учебник по постоянным ссылкам»). В одном блоге я часто видел, что 80% входящих запросов сводятся к 10 вопросам.
Что предлагает нам ИИ: минималистичный интерфейс чата + безопасный AJAX-маршрут WordPress, который отправляет вопрос в API ИИ (например, OpenAI) и возвращает полезный ответ. Мы намеренно упростили всё: никаких сторонних виджетов, никаких SDK, никаких зависимостей от Composer.
В конце концов, вы узнаете:
- Отображение изображения маленькой кошки (HTML/CSS/JS) с помощью шорткода WordPress.
- Отправьте вопрос на свой сервер WordPress (AJAX) с помощью nonce (токена защиты от CSRF-атак).
- Вызов API ИИ с помощью
wp_remote_post()Обрабатывать ошибки/тайм-ауты и кэшировать ответы с помощью Transients. - Защитите свой API-ключ (хранящийся в
wp-config.php) и избегайте предоставления к нему доступа браузеру.
Краткое резюме
- Вы добавляете константу API в
wp-config.php(никогда не по теме). - Вы создаете мю-плагин (плагин "must-use"), предоставляющий короткий код
[ai_chatbot]. - Фронт (JS) вызывает
admin-ajax.phpс использованием одноразового числа (nonce), а не напрямую API ИИ. - Сервер вызывает API через
wp_remote_post()короткий тайм-аут, корректная обработка ошибок. - Кэш ответов (временных данных) для снижения затрат.
- Очищенный ответ перед отображением (
wp_kses()) для ограничения рисков XSS.
Когда следует использовать ИИ для этого?
Используйте этот тип чат-бота, если:
- Вам задают повторяющиеся вопросы, и вы хотите получить немедленный «первый ответ».
- Ваш контент уже достаточно обширен (часто задаваемые вопросы, страницы «О нас», руководства), и ИИ может помочь пользователю.
- Вы признаете, что ответ иногда может быть не совсем корректным, и добавляете четкое сообщение («автоматический ответ»).
- Вам нужен легкий, управляемый чат-бот, не полагающийся на непрозрачный плагин, который внедряет внешние скрипты повсюду.
Я часто рекомендую его для обучающих блогов, сайтов-витрин или ниш, где вопросы очень актуальны (например, «как забронировать», «сроки», «цены»).
Когда НЕ следует использовать ИИ
Избегайте использования ИИ, если классическое решение для WordPress справится с задачей лучше, проще и дешевле:
- Внутренние исследования Начните с улучшения функциональности поиска (выдержки, категории, страница «Карта сайта»). Искусственный интеллект может обойтись дорого, если он просто посоветует вам «воспользоваться функцией поиска».
- Поддержка по деликатным вопросам (Здравоохранение, юриспруденция, финансы): Риск галлюцинаций реален. В таких случаях форма обратной связи + база знаний будут безопаснее.
- Вопросы сделок (команды, учетные записи): если ответ зависит от личных данных, не стоит наспех создавать "простой" чат с использованием ИИ. Вам необходима реальная аутентификация, права доступа и модель угроз.
- Жесткий бюджет Если у вас небольшой трафик, всё в порядке. Но если у вас 50 000 посещений в день, затраты могут резко возрасти, если у вас нет ограничения на кэширование/количество запросов.
Предпосылки
Целевые версии : WordPress 6.9.4 (апрель 2026 г.) и PHP 8.1+.
Ключ API для ИИ Я использую OpenAI в качестве примера, потому что его API стабилен и хорошо документирован, но архитектура будет одинаковой и у Anthropic/Mistral/Google.
Краткое описание: вызовы API и HTTP.
Один API Это интерфейс, позволяющий запрашивать что-либо у сервиса (в данном случае: «ответьте на этот вопрос»). Технически, вы отправляете HTTP-запрос (часто в POST) в формате JSON, и вы получаете JSON в ответ.
В WordPress это корректно реализуется через HTTP API: wp_remote_post() (И wp_remote_get()Это рекомендуемый метод, поскольку он обрабатывает передачу данных (cURL, потоки), прокси-серверы и интегрируется с WordPress.
Официальный источник: HTTP API WordPress.
Где хранить ключ API (обязательно)
Вы будете хранить ключ в wp-config.phpНи в файле темы, ни тем более в JavaScript. Если ваш ключ окажется в браузере, он будет скопирован через 30 секунд и использован за ваш счёт.
Добавить это в wp-config.php (в идеале непосредственно перед фразой «Вот и всё, хватит редактировать!»):
define('BPCAB_OPENAI_API_KEY', 'VOTRE_CLE_ICI');
расходы Каждое сообщение, отправленное в API, стоит денег (за токен). Даже если это «несколько центов», сумма может накопиться. Поэтому мы реализуем кэширование и ограничение скорости запросов на стороне сервера.
Куда вставить код
- Рекомендуемый вариант : un
мю-плагин (заряжать автоматически). Путь :
wp-content/mu-plugins/. - Альтернатива : классический пользовательский плагин в
wp-content/plugins/. - Избегать :
functions.phpтемы оформления (особенно если вы меняете тему или если конструктор обновляет файлы).
Официальная документация mu-плагинов: Обязательные плагины.
Архитектура решения
Вот ссылка на ленту новостей, изложенная простым языком:
- Посетитель открывает страницу, содержащую
[ai_chatbot]. - Этот шорткод отображает пользовательский интерфейс (небольшое окно) и загружает JavaScript.
- Когда пользователь отправляет сообщение, JavaScript отправляет POST-запрос.
admin-ajax.phpвместе с:- сообщение
- nonce WordPress (запрос для защиты от подделки)
- WordPress получает запрос через AJAX-хук, проверяет значение nonce, применяет ограничение скорости запросов, а затем вызывает API искусственного интеллекта.
wp_remote_post(). - WordPress проверяет полученный ответ, кэширует его (временно), а затем отправляет JSON обратно в браузер.
- Браузер отображает ответ.
Почему следует использовать WordPress AJAX (а не вызывать API ИИ с помощью JavaScript)?
Потому что ваш ключ должен оставаться на стороне сервера. Если вы вызываете OpenAI напрямую из браузера, вы раскрываете ключ… и теряете контроль (из-за затрат, злоупотреблений, парсинга).
Полный код — шаг за шагом
Мы собираемся создать mu-плагин, который:
- добавить шорткод
[ai_chatbot] - CSS/JS подключаются без ошибок
- Создает AJAX-запрос для авторизованных и неавторизованных пользователей.
- вызывает API OpenAI
- кэширует ответы
Шаг 1 — Создайте mu-плагин
Создайте папку wp-content/mu-plugins/ Если такого файла не существует, создайте его:
wp-content/mu-plugins/bpcab-ai-chatbot.php
<?php
/**
* Plugin Name: BPCAB — Chatbot IA simple (sans plugin)
* Description: Ajoute un chatbot IA minimal via shortcode, avec appel serveur (wp_remote_post), cache transient, et sécurité de base.
* Author: Vous
* Version: 1.0.0
*/
if (!defined('ABSPATH')) {
exit;
}
define('BPCAB_CHATBOT_VERSION', '1.0.0');
/**
* Petit helper : récupère l'IP (approx) pour rate limiting.
* Attention : derrière un proxy/CDN, l'IP peut être celle du proxy si mal configuré.
*/
function bpcab_get_client_ip(): string {
$keys = array('HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR');
foreach ($keys as $key) {
if (!empty($_SERVER[$key])) {
$ip = sanitize_text_field(wp_unslash($_SERVER[$key]));
// X-Forwarded-For peut contenir plusieurs IP : on prend la première.
if (str_contains($ip, ',')) {
$parts = explode(',', $ip);
$ip = trim($parts[0]);
}
return $ip;
}
}
return '0.0.0.0';
}
Шаг 2 — Загрузка шорткода + CSS/JS
Un короткий код — это тег, заключенный в скобки (например, [ai_chatbot]), который WordPress заменяет HTML. Это удобно для Divi 5, Elementor и Avada: все они умеют вставлять шорткод (модуль «Код», виджет «Шорткод» и т. д.).
/**
* Enqueue des assets uniquement si le shortcode est présent sur la page.
*/
function bpcab_maybe_enqueue_assets(): void {
if (!is_singular()) {
return;
}
global $post;
if (!$post instanceof WP_Post) {
return;
}
if (!has_shortcode($post->post_content, 'ai_chatbot')) {
return;
}
$handle = 'bpcab-ai-chatbot';
wp_register_style(
$handle,
plugins_url('bpcab-ai-chatbot.css', __FILE__),
array(),
BPCAB_CHATBOT_VERSION
);
wp_register_script(
$handle,
plugins_url('bpcab-ai-chatbot.js', __FILE__),
array(),
BPCAB_CHATBOT_VERSION,
true
);
wp_enqueue_style($handle);
wp_enqueue_script($handle);
// Données côté JS (sans clé API !)
wp_localize_script($handle, 'BPCAB_CHATBOT', array(
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('bpcab_chatbot_nonce'),
'maxLen' => 400,
));
}
add_action('wp_enqueue_scripts', 'bpcab_maybe_enqueue_assets');
/**
* Shortcode [ai_chatbot]
*/
function bpcab_ai_chatbot_shortcode(): string {
// HTML volontairement simple, facile à styliser.
ob_start();
?>
<div class="bpcab-chatbot" data-bpcab-chatbot="1">
<div class="bpcab-chatbot__header">
<strong>Assistant</strong>
<span class="bpcab-chatbot__status" aria-live="polite">Prêt</span>
</div>
<div class="bpcab-chatbot__messages" role="log" aria-live="polite"></div>
<form class="bpcab-chatbot__form">
<input class="bpcab-chatbot__input" type="text" name="message" placeholder="Posez votre question…" autocomplete="off" />
<button class="bpcab-chatbot__send" type="submit">Envoyer</button>
</form>
<p class="bpcab-chatbot__hint">
<em>Réponse automatique. Ne partagez pas d’informations sensibles.</em>
</p>
</div>
<?php
return (string) ob_get_clean();
}
add_shortcode('ai_chatbot', 'bpcab_ai_chatbot_shortcode');
Распространенная ошибка — вставка этого кода в неправильный файл (фрагмент кода сборщика или плагин фрагмента кода, который запускается слишком поздно). С помощью mu-плагина вы избежите многих неожиданностей.
Шаг 3 — Создайте файлы CSS/JS
В том же файле mu-plugins, создавать:
bpcab-ai-chatbot.cssbpcab-ai-chatbot.js
Минималистичный CSS (Вы можете адаптировать его под тему/конструктор):
.bpcab-chatbot{
max-width: 520px;
border: 1px solid rgba(0,0,0,.12);
border-radius: 12px;
overflow: hidden;
background: #fff;
}
.bpcab-chatbot__header{
display:flex;
justify-content: space-between;
align-items:center;
padding: 12px 14px;
background: #111827;
color: #fff;
}
.bpcab-chatbot__messages{
padding: 12px 14px;
min-height: 220px;
max-height: 360px;
overflow:auto;
background: #f9fafb;
}
.bpcab-chatbot__msg{
margin: 0 0 10px 0;
padding: 10px 12px;
border-radius: 10px;
line-height: 1.35;
}
.bpcab-chatbot__msg--user{
background: #dbeafe;
}
.bpcab-chatbot__msg--bot{
background: #fff;
border: 1px solid rgba(0,0,0,.08);
}
.bpcab-chatbot__form{
display:flex;
gap: 8px;
padding: 12px 14px;
border-top: 1px solid rgba(0,0,0,.08);
background: #fff;
}
.bpcab-chatbot__input{
flex:1;
padding: 10px 12px;
border: 1px solid rgba(0,0,0,.18);
border-radius: 10px;
}
.bpcab-chatbot__send{
padding: 10px 14px;
border-radius: 10px;
border: 0;
background: #111827;
color: #fff;
cursor: pointer;
}
.bpcab-chatbot__hint{
padding: 0 14px 14px 14px;
margin: 0;
color: #6b7280;
font-size: 13px;
}
Минимальный JavaScript Отправка AJAX-запросов в WordPress, отображение сообщений, управление статусом.
(function () {
function qs(root, sel) { return root.querySelector(sel); }
function addMsg(messagesEl, text, who) {
var p = document.createElement('p');
p.className = 'bpcab-chatbot__msg bpcab-chatbot__msg--' + who;
p.textContent = text;
messagesEl.appendChild(p);
messagesEl.scrollTop = messagesEl.scrollHeight;
}
function setStatus(root, text) {
var s = qs(root, '.bpcab-chatbot__status');
if (s) s.textContent = text;
}
function initChatbot(root) {
var form = qs(root, '.bpcab-chatbot__form');
var input = qs(root, '.bpcab-chatbot__input');
var messages = qs(root, '.bpcab-chatbot__messages');
if (!form || !input || !messages) return;
addMsg(messages, "Bonjour ! Posez votre question, je vais essayer de vous orienter.", "bot");
form.addEventListener('submit', function (e) {
e.preventDefault();
var msg = (input.value || '').trim();
if (!msg) return;
if (msg.length > (window.BPCAB_CHATBOT?.maxLen || 400)) {
addMsg(messages, "Message trop long. Essayez de faire plus court.", "bot");
return;
}
addMsg(messages, msg, "user");
input.value = '';
setStatus(root, 'Réflexion…');
var data = new FormData();
data.append('action', 'bpcab_chatbot_ask');
data.append('nonce', window.BPCAB_CHATBOT?.nonce || '');
data.append('message', msg);
fetch(window.BPCAB_CHATBOT?.ajaxUrl || '', {
method: 'POST',
credentials: 'same-origin',
body: data
})
.then(function (r) { return r.json(); })
.then(function (payload) {
if (!payload || !payload.success) {
var err = payload?.data?.message || "Erreur côté serveur.";
addMsg(messages, err, "bot");
setStatus(root, 'Erreur');
return;
}
addMsg(messages, payload.data.answer, "bot");
setStatus(root, 'Prêt');
})
.catch(function () {
addMsg(messages, "Impossible de contacter le serveur. Réessayez.", "bot");
setStatus(root, 'Hors ligne');
});
});
}
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('[data-bpcab-chatbot="1"]').forEach(initChatbot);
});
})();
Распространенная ошибка: «JS не загружается». В 90% случаев это неправильный путь. plugins_url()или кэш (плагин/CDN), который обслуживает старую версию. Измените версию. BPCAB_CHATBOT_VERSION Чтобы принудительно перезагрузить страницу или очистить кэш.
Шаг 4 — AJAX-конечная точка + nonce + ограничение скорости запросов
Un крючок WordPress — это точка входа. Существует два типа:
- действие : «сделайте здесь что-нибудь» (например,
wp_enqueue_scripts). - фильтры : «изменить это значение» (например,
the_content).
Здесь мы используем AJAX-запросы: wp_ajax_* (подключенные) и wp_ajax_nopriv_* (не подключено).
/**
* Rate limit très simple : X requêtes par minute par IP.
* Ce n'est pas une protection parfaite, mais ça évite les abus basiques.
*/
function bpcab_rate_limit_or_die(string $ip, int $limit = 10, int $window_seconds = 60): void {
$key = 'bpcab_rl_' . md5($ip);
$hits = (int) get_transient($key);
if ($hits >= $limit) {
wp_send_json_error(array(
'message' => 'Trop de requêtes. Attendez une minute et réessayez.'
), 429);
}
set_transient($key, $hits + 1, $window_seconds);
}
/**
* Handler AJAX : reçoit la question et renvoie une réponse IA.
*/
function bpcab_ajax_chatbot_ask(): void {
// Vérification nonce (anti-CSRF)
$nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
if (!wp_verify_nonce($nonce, 'bpcab_chatbot_nonce')) {
wp_send_json_error(array('message' => 'Requête refusée (nonce invalide).'), 403);
}
$ip = bpcab_get_client_ip();
bpcab_rate_limit_or_die($ip, 10, 60);
$message = isset($_POST['message']) ? (string) wp_unslash($_POST['message']) : '';
$message = trim($message);
if ($message === '') {
wp_send_json_error(array('message' => 'Message vide.'), 400);
}
if (mb_strlen($message) > 400) {
wp_send_json_error(array('message' => 'Message trop long (max 400 caractères).'), 400);
}
$answer = bpcab_get_ai_answer($message);
if (is_wp_error($answer)) {
wp_send_json_error(array(
'message' => $answer->get_error_message()
), 500);
}
wp_send_json_success(array(
'answer' => $answer,
));
}
add_action('wp_ajax_bpcab_chatbot_ask', 'bpcab_ajax_chatbot_ask');
add_action('wp_ajax_nopriv_bpcab_chatbot_ask', 'bpcab_ajax_chatbot_ask');
Шаг 5 — Вызов OpenAI через wp_remote_post() + временный кэш
Мы собираемся:
- Создайте ключ кэша на основе вопроса (хеша).
- Установите значение TTL (например, 24 часа), чтобы избежать повторной оплаты за тот же ответ.
- Добавьте короткий тайм-аут (например, 20 секунд), чтобы предотвратить «зависание» PHP.
- Упростите ответ (допускается очень ограниченное количество HTML-кода).
/**
* Appelle l'API IA (OpenAI) et renvoie une réponse texte.
* IMPORTANT : la clé API doit être définie dans wp-config.php via BPCAB_OPENAI_API_KEY
*/
function bpcab_get_ai_answer(string $user_message) {
if (!defined('BPCAB_OPENAI_API_KEY') || BPCAB_OPENAI_API_KEY === '') {
return new WP_Error('bpcab_missing_key', 'Clé API manquante. Ajoutez BPCAB_OPENAI_API_KEY dans wp-config.php.');
}
// Cache : même question => même réponse pendant 24h
$cache_key = 'bpcab_ai_' . md5(mb_strtolower(trim($user_message)));
$cached = get_transient($cache_key);
if (is_string($cached) && $cached !== '') {
return $cached;
}
/**
* Prompt système : court, orienté "site".
* Ici, on ne fait PAS encore de RAG (recherche dans vos contenus).
* On limite la responsabilité : pas de conseils médicaux/juridiques.
*/
$system = "Vous êtes un assistant pour un site WordPress. Répondez en français, de façon courte et pratique. "
. "Si la question demande des infos sensibles (santé/juridique/finance), refusez et conseillez de contacter le propriétaire du site. "
. "Si vous ne savez pas, dites-le et proposez une piste (ex: consulter la page FAQ).";
/**
* API OpenAI (Chat Completions style Responses API selon évolution).
* En avril 2026, l'API a évolué plusieurs fois. Le principe reste :
* - endpoint HTTPS
* - JSON en entrée
* - JSON en sortie
*
* Si votre compte OpenAI recommande un endpoint différent, adaptez l'URL et le parsing.
* Référez-vous à la doc officielle.
*/
$endpoint = 'https://api.openai.com/v1/chat/completions';
$body = array(
'model' => 'gpt-4.1-mini',
'temperature' => 0.2,
'messages' => array(
array('role' => 'system', 'content' => $system),
array('role' => 'user', 'content' => $user_message),
),
);
$args = array(
'headers' => array(
'Authorization' => 'Bearer ' . BPCAB_OPENAI_API_KEY,
'Content-Type' => 'application/json',
),
'timeout' => 20,
'body' => wp_json_encode($body),
);
$response = wp_remote_post($endpoint, $args);
if (is_wp_error($response)) {
return new WP_Error('bpcab_http_error', 'Erreur HTTP : ' . $response->get_error_message());
}
$code = (int) wp_remote_retrieve_response_code($response);
$raw = (string) wp_remote_retrieve_body($response);
if ($code < 200 || $code >= 300) {
// On évite d'afficher tout le raw (peut contenir des détails techniques).
return new WP_Error('bpcab_api_error', 'API IA indisponible (code ' . $code . '). Vérifiez votre clé/quota.');
}
$data = json_decode($raw, true);
if (!is_array($data)) {
return new WP_Error('bpcab_json_error', 'Réponse API illisible (JSON invalide).');
}
// Parsing "chat.completions"
$text = $data['choices'][0]['message']['content'] ?? '';
$text = is_string($text) ? trim($text) : '';
if ($text === '') {
return new WP_Error('bpcab_empty_answer', 'Réponse vide de l’API IA.');
}
/**
* Assainissement :
* - on autorise un peu de HTML basique (liens, strong, em, br)
* - on supprime le reste
*/
$allowed = array(
'a' => array(
'href' => true,
'target' => true,
'rel' => true,
),
'strong' => array(),
'em' => array(),
'br' => array(),
);
$safe = wp_kses($text, $allowed);
// Cache 24h
set_transient($cache_key, $safe, DAY_IN_SECONDS);
return $safe;
}
Две ошибки, которые я часто вижу:
- Забыли поставить точку с запятой в
wp-config.phpили в mu-плагине: результат — белый экран (фатальная ошибка). АктивироватьWP_DEBUGНа этапе подготовки, а не на этапе производства. - Скопировать старый учебник который использует старую структуру API или устаревшую модель. Здесь же используется база WordPress 6.9.4 + PHP 8.1, и вы адаптируете конечную точку только в том случае, если OpenAI изменит свои рекомендации.
Полный собранный код
Ниже приведён полный текст файла. мю-плагинВам также потребуется создать файлы CSS/JS (показаны выше). Не добавляйте ключ в этот файл.
<?php
/**
* Plugin Name: BPCAB — Chatbot IA simple (sans plugin)
* Description: Chatbot IA minimal via shortcode, AJAX WordPress, appel OpenAI avec wp_remote_post(), cache transient, sécurité de base.
* Author: Vous
* Version: 1.0.0
*/
if (!defined('ABSPATH')) {
exit;
}
define('BPCAB_CHATBOT_VERSION', '1.0.0');
function bpcab_get_client_ip(): string {
$keys = array('HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR');
foreach ($keys as $key) {
if (!empty($_SERVER[$key])) {
$ip = sanitize_text_field(wp_unslash($_SERVER[$key]));
if (str_contains($ip, ',')) {
$parts = explode(',', $ip);
$ip = trim($parts[0]);
}
return $ip;
}
}
return '0.0.0.0';
}
function bpcab_maybe_enqueue_assets(): void {
if (!is_singular()) {
return;
}
global $post;
if (!$post instanceof WP_Post) {
return;
}
if (!has_shortcode($post->post_content, 'ai_chatbot')) {
return;
}
$handle = 'bpcab-ai-chatbot';
wp_register_style(
$handle,
plugins_url('bpcab-ai-chatbot.css', __FILE__),
array(),
BPCAB_CHATBOT_VERSION
);
wp_register_script(
$handle,
plugins_url('bpcab-ai-chatbot.js', __FILE__),
array(),
BPCAB_CHATBOT_VERSION,
true
);
wp_enqueue_style($handle);
wp_enqueue_script($handle);
wp_localize_script($handle, 'BPCAB_CHATBOT', array(
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('bpcab_chatbot_nonce'),
'maxLen' => 400,
));
}
add_action('wp_enqueue_scripts', 'bpcab_maybe_enqueue_assets');
function bpcab_ai_chatbot_shortcode(): string {
ob_start();
?>
<div class="bpcab-chatbot" data-bpcab-chatbot="1">
<div class="bpcab-chatbot__header">
<strong>Assistant</strong>
<span class="bpcab-chatbot__status" aria-live="polite">Prêt</span>
</div>
<div class="bpcab-chatbot__messages" role="log" aria-live="polite"></div>
<form class="bpcab-chatbot__form">
<input class="bpcab-chatbot__input" type="text" name="message" placeholder="Posez votre question…" autocomplete="off" />
<button class="bpcab-chatbot__send" type="submit">Envoyer</button>
</form>
<p class="bpcab-chatbot__hint">
<em>Réponse automatique. Ne partagez pas d’informations sensibles.</em>
</p>
</div>
<?php
return (string) ob_get_clean();
}
add_shortcode('ai_chatbot', 'bpcab_ai_chatbot_shortcode');
function bpcab_rate_limit_or_die(string $ip, int $limit = 10, int $window_seconds = 60): void {
$key = 'bpcab_rl_' . md5($ip);
$hits = (int) get_transient($key);
if ($hits >= $limit) {
wp_send_json_error(array(
'message' => 'Trop de requêtes. Attendez une minute et réessayez.'
), 429);
}
set_transient($key, $hits + 1, $window_seconds);
}
function bpcab_get_ai_answer(string $user_message) {
if (!defined('BPCAB_OPENAI_API_KEY') || BPCAB_OPENAI_API_KEY === '') {
return new WP_Error('bpcab_missing_key', 'Clé API manquante. Ajoutez BPCAB_OPENAI_API_KEY dans wp-config.php.');
}
$cache_key = 'bpcab_ai_' . md5(mb_strtolower(trim($user_message)));
$cached = get_transient($cache_key);
if (is_string($cached) && $cached !== '') {
return $cached;
}
$system = "Vous êtes un assistant pour un site WordPress. Répondez en français, de façon courte et pratique. "
. "Si la question demande des infos sensibles (santé/juridique/finance), refusez et conseillez de contacter le propriétaire du site. "
. "Si vous ne savez pas, dites-le et proposez une piste (ex: consulter la page FAQ).";
$endpoint = 'https://api.openai.com/v1/chat/completions';
$body = array(
'model' => 'gpt-4.1-mini',
'temperature' => 0.2,
'messages' => array(
array('role' => 'system', 'content' => $system),
array('role' => 'user', 'content' => $user_message),
),
);
$args = array(
'headers' => array(
'Authorization' => 'Bearer ' . BPCAB_OPENAI_API_KEY,
'Content-Type' => 'application/json',
),
'timeout' => 20,
'body' => wp_json_encode($body),
);
$response = wp_remote_post($endpoint, $args);
if (is_wp_error($response)) {
return new WP_Error('bpcab_http_error', 'Erreur HTTP : ' . $response->get_error_message());
}
$code = (int) wp_remote_retrieve_response_code($response);
$raw = (string) wp_remote_retrieve_body($response);
if ($code < 200 || $code >= 300) {
return new WP_Error('bpcab_api_error', 'API IA indisponible (code ' . $code . '). Vérifiez votre clé/quota.');
}
$data = json_decode($raw, true);
if (!is_array($data)) {
return new WP_Error('bpcab_json_error', 'Réponse API illisible (JSON invalide).');
}
$text = $data['choices'][0]['message']['content'] ?? '';
$text = is_string($text) ? trim($text) : '';
if ($text === '') {
return new WP_Error('bpcab_empty_answer', 'Réponse vide de l’API IA.');
}
$allowed = array(
'a' => array('href' => true, 'target' => true, 'rel' => true),
'strong' => array(),
'em' => array(),
'br' => array(),
);
$safe = wp_kses($text, $allowed);
set_transient($cache_key, $safe, DAY_IN_SECONDS);
return $safe;
}
function bpcab_ajax_chatbot_ask(): void {
$nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
if (!wp_verify_nonce($nonce, 'bpcab_chatbot_nonce')) {
wp_send_json_error(array('message' => 'Requête refusée (nonce invalide).'), 403);
}
$ip = bpcab_get_client_ip();
bpcab_rate_limit_or_die($ip, 10, 60);
$message = isset($_POST['message']) ? (string) wp_unslash($_POST['message']) : '';
$message = trim($message);
if ($message === '') {
wp_send_json_error(array('message' => 'Message vide.'), 400);
}
if (mb_strlen($message) > 400) {
wp_send_json_error(array('message' => 'Message trop long (max 400 caractères).'), 400);
}
$answer = bpcab_get_ai_answer($message);
if (is_wp_error($answer)) {
wp_send_json_error(array(
'message' => $answer->get_error_message()
), 500);
}
wp_send_json_success(array('answer' => $answer));
}
add_action('wp_ajax_bpcab_chatbot_ask', 'bpcab_ajax_chatbot_ask');
add_action('wp_ajax_nopriv_bpcab_chatbot_ask', 'bpcab_ajax_chatbot_ask');
Код Пояснение
Зачем нужен mu-плагин?
Плагин mu загружается автоматически, без активации, и менее «хрупок», чем фрагмент кода, вставленный в тему оформления. По моему опыту, когда новичок вставляет код в тему, это происходит быстрее. functions.php А если затем сменить тему оформления (или обновить родительскую тему), чат-бот исчезнет.
Почему wp_localize_script()
Здесь мы используем его для переключения на JavaScript:
- URL AJAX (
admin-ajax.php) - нунций
- ограничение длины
Это позволяет избежать "жесткого кодирования" URL-адреса и работает даже если WordPress установлен в подпапке.
Док: wp_localize_script ().
Nonce: что именно он защищает
Nonce в WordPress предотвращает отправку запросов от имени другого сайта через браузер посетителя (CSRF-атака). Он не заменяет систему аутентификации, но для публичного чат-бота это минимальное требование.
Док: WordPress Noces.
Временные данные кэша: почему это важно
. Перепады Это кэши типа «ключ/значение» с ограниченным сроком действия. Если посетитель спрашивает «Какое у вас время работы?» 200 раз в месяц, вы не захотите платить за 200 вызовов ИИ. Вы платите за один вызов, а затем предоставляете ответ из кэша.
Док: Временные данные API.
Очистка ответа
API искусственного интеллекта может возвращать неожиданный текст (или даже HTML). Если внедрить его непосредственно в DOM, это откроет путь к XSS-уязвимостям. Вот здесь:
- мы разрешаем только
a,strong,em,br - всё остальное удалено
Используемая функция: wp_kses()Док: wp_kses().
Стоимость и оптимизация API
Точные цены зависят от модели и вашего договора. Вот что важно для составления сметы:
- количество сообщений в месяц
- средняя длина вопросов и ответов (токенов)
- Коэффициент попадания в кэш (количество повторений одинаковых вопросов)
Оценка "на месте" (порядок величины)
При работе с простым чат-ботом для ознакомления с работой системы я часто наблюдаю следующее:
- Вопрос: от 20 до 60 жетонов
- Ответ: от 60 до 200 токенов
- Всего: от 100 до 300 токенов за взаимодействие.
Если у вас 2000 взаимодействий в месяц, это от 200 000 до 600 000 токенов в месяц. В зависимости от модели, это может быть разумно… или нет. Кэширование и «мини»-модель имеют огромное значение.
Немедленная оптимизация
- 24-часовой тайник (уже есть). Для стабильной работы раздела часто задаваемых вопросов можно увеличить интервал до 7 дней.
- Более короткие ответы Добавьте к запросу «ответить максимум в 5 предложениях».
- Низкая температура Чем меньше вариантов, тем больше попаданий в кэш.
- Ограничьте длину Сообщения на стороне JavaScript и PHP (уже настроены). Всегда двойной барьер.
Расширенные варианты и сценарии использования
Вариант 1 — Добавить «контекст сайта» (без RAG)
Не прибегая к поиску по всем статьям, вы можете предоставить краткую статическую информацию (например, часы работы, URL раздела часто задаваемых вопросов). Это полезно для демонстрационного сайта.
// Exemple : ajoutez ceci avant $body dans bpcab_get_ai_answer()
$site_context = "Contexte du site :n"
. "- FAQ : https://example.com/faq/n"
. "- Contact : https://example.com/contact/n"
. "- Horaires : lun-ven 9h-18hn";
$body['messages'] = array(
array('role' => 'system', 'content' => $system . "nn" . $site_context),
array('role' => 'user', 'content' => $user_message),
);
Вариант 2 — Режим «Краткое содержание статьи» с помощью атрибута шорткода.
Вы можете разрешить [ai_chatbot mode="resume"] и отправить фрагмент текущей страницы ИИ. Примечание: больше токенов = выше стоимость.
// Exemple (incomplet) : détecter un attribut et injecter le contenu
function bpcab_ai_chatbot_shortcode($atts = array()): string {
$atts = shortcode_atts(array('mode' => 'chat'), $atts, 'ai_chatbot');
// ... même HTML qu'avant, mais vous pourriez ajouter data-mode
// <div data-mode="chat"> etc.
// Ce snippet est volontairement partiel : il faut adapter le JS pour envoyer le mode.
return '...';
}
Хочу сразу уточнить: этот фрагмент кода неполный. Для его корректной работы необходимо также изменить JavaScript-код (отправить). mode) и обработчик AJAX (измените приглашение в соответствии с режимом).
Вариант 3 — интеграция Divi 5 / Elementor / Avada
- Дива 5 Добавьте модуль «Код» или «Шорткод» и вставьте его.
[ai_chatbot]Если Divi выполняет миниляцию/конкатенацию, очистите его статический кэш, если CSS/JS не обновляются. - Elementor виджет «Шорткод» →
[ai_chatbot]Если вы используете оптимизацию ресурсов Elementor, убедитесь, что выполнение скрипта не откладывается слишком часто (в противном случае...).DOMContentLoadedЭто может произойти до инъекции: в этом случае также выполняется инициализация.elementor/frontend/init). - Авада : Элемент «Шорткод» в Fusion Builder. Если у вас включено кэширование Avada, очистите кэш после добавления файлов CSS/JS.
Безопасность и передовой опыт
Никогда не раскрывайте ключ API на стороне клиента.
Простое правило: ключ остается внутри. wp-config.phpБраузер никогда не должен его увидеть. Даже "скрытый" в JS-пакете, он всё равно может быть восстановлен.
Проверка и ограничение количества записей
- Максимальная длина на стороне JavaScript (UX) + на стороне PHP (безопасность).
- Отклонять пустые сообщения.
- Ограничение скорости по IP-адресу (уже реализовано). Для большей гибкости можно также ограничить скорость по сессии/cookie.
Приберитесь у выходов
Отобразить ответ в виде текста (как в JavaScript) textContent) или тщательно очищать, если вы разрешаете HTML. Здесь мы делаем и то, и другое: очищаем на стороне сервера и отображаем как текст на стороне клиента. Это сделано намеренно «параноично».
GDPR: данные, передаваемые третьей стороне.
Если посетитель вводит личные данные, вы потенциально отправляете их поставщику услуг (OpenAI). Планируйте свои действия соответствующим образом:
- Чёткое сообщение («Не разглашайте конфиденциальную информацию»).
- упоминание в вашей политике конфиденциальности
- Настройки поставщика (сохранение данных, отказ от получения информации и т. д.) в соответствии с вашим договором.
Не проводите тестирование в рабочей среде без резервной копии.
Ошибка PHP в mu-плагине приводит к сбою всего сайта (поскольку он загружается автоматически). Сначала поработайте на тестовой копии. Если вам нужно работать в продакшене, сохраните доступ по FTP/SSH, чтобы удалить файл в случае появления белого экрана.
Как тестировать и отлаживать
Включите корректное ведение журналов.
В тестовой среде включите:
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
Док: Отладка в WordPress.
Проверьте AJAX-запрос.
- Откройте вкладку «Инструменты разработчика» в браузере → вкладка «Сеть» → отфильтруйте по...
admin-ajax.php. - Проверьте HTTP-статус (200/403/429/500) и JSON-ответ.
- Если вместо JSON вы видите HTML-страницу, это часто означает фатальную ошибку PHP (или перехват данных плагином безопасности).
Тестирование API ИИ в «пробном запуске»
Перед интеграцией JavaScript вы можете вызвать bpcab_get_ai_answer() В административном контексте (временно) запишите ответ в лог. Не оставляйте этот код без изменений.
Если это не сработает
| симптом | Причина вероятна | проверка | Решение |
|---|---|---|---|
| На странице ничего не отображается. | Отсутствует или не интерпретируется короткий код. | Убедитесь, что страница содержит [ai_chatbot] (не в блоке "Код", который экранирует скобки) |
Используйте блок «Шорткод» (Elementor) / модуль «Шорткод» (Divi/Avada). |
| Окно чата отображается, но кнопка не реагирует. | JavaScript не загружен (кэш, путь, конфликт) | Инструменты разработчика → Консоль (ошибки) + Сеть (JS-файл 404?) | Проверить plugins_url()очистить кэш, увеличить BPCAB_CHATBOT_VERSION |
| Ответ «недействительный, отсутствует» | Истек срок действия одноразового кода (nonce), агрессивное кэширование страниц или JavaScript использует старый одноразовый код. | Посмотрите на значение BPCAB_CHATBOT.nonce в консоли |
Уменьшите размер кэша на страницах с чатом или сгенерируйте nonce заново при загрузке (более продвинутый подход). |
| Ошибка 429 «Слишком много запросов» | Слишком строгие ограничения скорости или повторные проверки | Воспроизвести в режиме приватного просмотра / с другого IP-адреса. | Увеличьте размер окна/лимита или ограничьте доступ только для авторизованных пользователей. |
| Ошибка «API ИИ недоступен (код 401/403)» | Недействительный ключ, ключ не загружен, права доступа | Проверить BPCAB_OPENAI_API_KEY в wp-config.php (без пробелов) |
Сгенерируйте ключ заново, проверьте выставление счетов/квоту на стороне поставщика. |
| Ошибка «Недопустимый JSON» | Усеченный ответ API (прокси, WAF) или измененная конечная точка | Авторизоваться $raw на этапе (обратите внимание на данные) |
Настройте конечную точку/формат в соответствии с официальной документацией, при необходимости увеличьте время ожидания. |
| Белый экран после добавления mu-плагина | Ошибка PHP (скобки, точка с запятой), слишком старая версия PHP. | советоваться wp-content/debug.log или журналы сервера |
Исправьте синтаксис, проверьте PHP 8.1+, при необходимости удалите файл через FTP. |
Реалистичные проблемы и быстрые решения
- Код вставлен не в то место. Если вы разместили его в разделе «Пользовательский код» конструктора, он может быть отфильтрован/экранирован. Используйте вместо него mu-plugin.
- Неподходящий зацеп : если вы зададите вопрос по JavaScript
initВы рискуете загрузить его слишком рано/неправильно. Здесь мы используемwp_enqueue_scripts. - Конфликт кэша Некоторые кэши кэшируют HTML-код, содержащий nonce. Результат: устаревший nonce для всех. Решение: исключить страницу из кэша или реализовать маршрут, который возвращает «свежий» nonce (расширенный вариант).
- Постоянные Это не совсем по теме, но я видел сайты, где
admin_url()Фильтрация происходит из-за неправильно настроенного плагина безопасности. Если AJAX не отвечает, проверьте URL-адрес./wp-admin/admin-ajax.phpнепосредственно в браузере.
Ресурсы
- HTTP API WordPress (wp_remote_post)
- API для работы с временными данными (кэш)
- Нонсы (безопасность)
- wp_kses() (санитария)
- Отладка WordPress (WP_DEBUG)
- Документация API OpenAI
- PHP mbstring (mb_strlen)
- Официальный репозиторий WordPress (wordpress-develop)
FAQ
Действительно ли добавление mu-плагина позволит обойтись без каких-либо дополнительных плагинов?
Вы не устанавливаете сторонний плагин с wordpress.org, но технически добавляете код в виде плагина. Это сделано намеренно: это самый чистый способ сохранить независимость кода от темы.
Могу ли я вставить код? functions.php ?
Да, но я не рекомендую этого делать. Вы рискуете потерять чат-бота при смене тем, а конструктор тем может усложнить отладку. Если вы все же решите это сделать, используйте дочернюю тему.
Зачем использовать admin-ajax.php а не REST API?
Оба варианта работают. Для новичка настройка AJAX в WordPress занимает немного времени. Если же вам нужен более современный подход, переключитесь на REST-маршрут. register_rest_route() и разрешения на обратный вызов. Ядро (вызов) wp_remote_post()(Кэш, безопасность) остаются неизменными.
Мой плагин кэширования нарушает работу nonce: что мне делать?
Исключите страницу из кэша (простое решение) или реализуйте конечную точку, которая возвращает nonce по запросу, а затем обновите JavaScript для его получения. На сайтах с большим объемом кэширования (агрессивные CDN) это классический подход.
Как ограничить доступ чат-бота к определенным страницам?
Шорткод уже предоставляет вам этот контроль: вы добавляете его только там, где это необходимо. И код запрашивает данные из JS/CSS только в том случае, если шорткод присутствует.
Как предотвратить распространение ИИ бессмысленных высказываний?
Гарантировать это на 100% невозможно. Снизьте риск:
- краткие ответы
- низкая температура
- Послание ясно: «Если вы не знаете, скажите об этом».
- Перенаправить на страницу часто задаваемых вопросов/контактной информации.
Можно ли отображать кликабельные ссылки в ответе?
Да, но делайте это осторожно. В этом уроке мы будем чистить с помощью wp_kses() разрешив aНа стороне JavaScript мы отображаем с помощью textContent (Поэтому HTML не нужен). Если вам нужны кликабельные ссылки, вам необходимо отображать их в HTML.innerHTML) и проявлять особую осторожность (строгая санитария, автоматическое добавление rel="noopener nofollow").
Совместимо ли оно с Divi 5 / Elementor / Avada?
Да: используйте модуль/виджет «Шорткод» и вставьте его. [ai_chatbot]Код не зависит от темы оформления. Единственные проблемы обычно возникают из-за кэширования/минификации: очистка после добавления файлов.
Почему вы ограничиваете текст 400 символами?
Чтобы избежать чрезмерного количества запросов, снизить затраты и ограничить злоупотребления, вы можете увеличить количество запросов, но делайте это с полным пониманием последствий (токены, задержка, выставление счетов).
Как изменить модель искусственного интеллекта?
Измените значение model в $bodyДля чат-бота-консультанта используйте "мини"-модель. При масштабировании отслеживайте затраты и задержку.
Я получаю сообщение об ошибке «HTTP Error: cURL error 28».
Тайм-аут. Проверка:
- Доступ к сети разрешен с хост-сервера (брандмауэр).
- DNS
- увеличивать
timeout(например, 30), если ваш сервер работает медленно.
Если у вас заблокированный тарифный план общего хостинга, это распространенная ситуация.