Ако някога сте получавали имейл от рода на „Не можах да намеря информацията на вашия сайт, можете ли да ми помогнете?“, вече имате добър пример за употреба на мини-чат с изкуствен интелект. Целта тук не е да „замените“ екипа си за поддръжка, а да предоставите полезен първоначален отговор въз основа на съдържанието на вашия уебсайт. без инсталирам плъгин.
Нуждата / Случаят на употреба
„Прост“ чатбот с изкуствен интелект WordPress Основната му цел е да абсорбира повтарящи се въпроси: работно време, политика за възстановяване на суми, „къде да намеря еди-кой си артикул“ и др. резюмета на дълго ръководство или помощ за навигация („Търся урок за постоянни връзки“). В един блог често виждах, че 80% от входящите заявки се свеждат до 10 въпроса.
Какво предлага изкуственият интелект тук: минималистичен интерфейс за чат + защитен AJAX маршрут за WordPress, който изпраща въпроса към AI API (напр. OpenAI) и връща използваем отговор. Умишлено се стремим да опростим нещата: без джаджи от трети страни, без SDK, без зависимости от Composer.
В крайна сметка ще знаете:
- Показване на малка котка (HTML/CSS/JS) чрез шорткод на WordPress.
- Изпратете въпроса към вашия WordPress сървър (AJAX) с nonce (anti-CSRF токен).
- Извикване на AI API с
wp_remote_post(), обработва грешки/изтичания на времето и кешира отговори с преходни процеси. - Защитете вашия API ключ (съхраняван в
wp-config.php) и избягвайте да го показвате на браузъра.
Бързо обобщение
- Добавяте API константа в
wp-config.php(никога по темата). - Вие създавате mu-плъгин (плъгин „задължителен за употреба“), който предоставя кратък код
[ai_chatbot]. - Фронтът (JS) се обажда
admin-ajax.phpс еднократен номер, а не директно с AI 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 повиквания
Une API е интерфейс, който ви позволява да заявите нещо от услуга (тук: „отговорете на този въпрос“). Технически, вие изпращате HTTP заявка (често в ПУСНИ) с 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, струва пари (за токен). Дори да са „няколко цента“, те могат да се натрупат. Затова ще внедрим кеширане и ограничение на скоростта от страна на сървъра.
Къде да поставите кода
- Препоръчителна опция : A mu-плъгин (такса automatiquement). Път:
wp-content/mu-plugins/. - Алтернатива : класически персонализиран плъгин в
wp-content/plugins/. - Да избегна :
functions.phpна темата (особено ако променяте теми или ако конструктор актуализира файлове).
Официална документация за mu-plugins: Задължителни плъгини.
Архитектура на решението
Ето е емисията, на разбираем език:
- Посетителят отваря страница, съдържаща
[ai_chatbot]. - Шорткодът показва потребителския интерфейс (малък прозорец) + зарежда JS код.
- Когато потребителят изпрати съобщение, JS прави POST заявка към
admin-ajax.phpс:- съобщението
- еднократен номер на WordPress (заявка, защитена от фалшифициране)
- WordPress получава заявката чрез AJAX hook, валидира nonce, прилага ограничение на скоростта и след това извиква AI API чрез
wp_remote_post(). - WordPress дезинфекцира отговора, кешира го (временно) и след това изпраща JSON обратно към браузъра.
- Браузърът показва отговора.
Защо да използваме AJAX в WordPress (а не да извикваме AI API в JS)?
Защото вашият ключ трябва да остане на сървъра. Ако извикате OpenAI директно от браузъра, ще разкриете ключа... и ще загубите контрол (цена, злоупотреба, извличане на данни).
Пълният код — стъпка по стъпка
Ще изградим mu-плъгин, който:
- добавете кратък код
[ai_chatbot] - CSS/JS чисто поставяне в опашката
- създава AJAX действие за влезли и невлезли потребители
- извиква OpenAI API
- кешира отговорите
Стъпка 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');
Често срещан капан е поставянето на този код в грешен файл (фрагмент на builder или плъгин за фрагмент, който се изпълнява твърде късно). С 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;
}
Минимален JS Изпращане на 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 промени препоръката си.
Пълният асемблиран код
По-долу е пълният файл mu-плъгинЩе трябва да създадете и 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 и след това промени темата (или актуализира родителска тема), чатботът изчезва.
Pourquoi wp_localize_script()
Използваме го тук, за да превключим към JS:
- AJAX URL адресът (
admin-ajax.php) - нунций
- ограничение на дължината
Това избягва „твърдо кодиране“ на URL адреса и работи дори ако WordPress е инсталиран в подпапка.
Документ: wp_localize_script ().
Nonce: какво всъщност защитава
WordPress nonce не позволява на друг сайт да изпраща заявки от ваше име през браузъра на вашия посетител (CSRF атака). Той не замества система за удостоверяване, но за публичен чатбот е минимално изискване.
Документ: Известия за WordPress.
Преходни процеси в кеша: Защо е жизненоважно
Les Преходни Това са кешове ключ/стойност с дата на изтичане. Ако посетителят попита „Какво е работното ви време?“ 200 пъти в месеца, не искате да плащате за 200 AI повиквания. Плащате за едно повикване, след което обслужвате отговора от кеша.
Документ: Преходни процеси на API.
Почистване на реакцията
AI 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 изречения“.
- Ниска температура по-малко вариации, повече кеш посещения.
- Ограничете дължината Съобщения от двете страни на JS и 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 '...';
}
Нека уточня: този фрагмент е непълен. За да работи, JS също трябва да бъде модифициран (изпрати mode) и AJAX манипулатора (променете подканата според режима).
Вариант 3 — Интеграция на Divi 5 / Elementor / Avada
- Диви 5 Добавете модул „Код“ или „Кратък код“ и го поставете
[ai_chatbot]Ако Divi минифицира/конкатенира, изчистете статичния му кеш, ако CSS/JS не се актуализира. - Elementor уиджет „Кратък код“ →
[ai_chatbot]Ако използвате оптимизацията на активи на Elementor, проверете дали скриптът не се отлага агресивно (в противен случайDOMContentLoadedможе да се случи преди инжектиране: в този случай, инициализирайте и наelementor/frontend/init). - Avada Елемент „Shortcode“ във Fusion Builder. Ако имате кеширане в Avada, изчистете го след добавяне на CSS/JS файловете.
Безопасност и най-добри практики
Никога не разкривайте API ключа от страна на клиента
Просто правило: ключът остава вътре wp-config.phpБраузърът никога не би трябвало да го вижда. Дори „скрит“ в JS пакет, той все още може да бъде възстановен.
Валидиране и ограничаване на записи
- Максимална дължина от страна на JS (UX) + от страна на PHP (сигурност).
- Отхвърляйте празни съобщения.
- Ограничение на скоростта по IP адрес (вече е налице). За да стигнете до още по-голямо ниво, можете да ограничите и по сесия/бисквитка.
Почистете изходите
Показване на отговора като текст (както в JS с textContent) или силно дезинфекцираме, ако разрешите HTML. Тук правим и двете: дезинфекцираме от страна на сървъра и показваме като текст от страна на клиента. Това е умишлено „параноично“.
GDPR: данни, изпратени на трета страна
Ако посетителят въведе лични данни, е възможно да ги изпратите на доставчик на услуги (OpenAI). Планирайте съответно:
- ясно послание („Не споделяйте чувствителна информация.“)
- споменаване във вашата политика за поверителност
- Настройки на доставчика (задържане, отказване и др.) съгласно вашия договор
Не тествайте в продукция без резервно копие.
mu-плъгин с PHP грешка прекъсва работата на целия сайт (защото се зарежда автоматично). Първо работете върху тестово копие. Ако трябва да работите в производствен режим, поддържайте FTP/SSH достъп, за да изтриете файла в случай на бял екран.
Как да тествате и отстранявате грешки
Правилно активиране на регистрационните файлове
В тестова среда активирайте:
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
Документ: Отстраняване на грешки в WordPress.
Проверете AJAX заявката
- Отворете DevTools в браузъра → раздела Network → включете филтъра
admin-ajax.php. - Проверете HTTP статуса (200/403/429/500) и JSON отговора.
- Ако видите HTML страница вместо JSON, това често е фатална PHP грешка (или плъгин за сигурност, който прехваща).
Тестване на AI API „на сухо“
Преди да интегрирате JS, можете да извикате bpcab_get_ai_answer() в администраторски контекст (временно) и запишете отговора. Не оставяйте този код на място.
Ако това не проработи
| симптом | Вероятна причина | проверка | Решение |
|---|---|---|---|
| Нищо не се показва на страницата | Липсва или не се интерпретира кратък код | Проверете дали страницата съдържа [ai_chatbot] (не в блок "Код", който екранира скобите) |
Използвайте блок „Shortcode“ (Elementor) / модул за Shortcode (Divi/Avada) |
| Прозорецът за чат се появява, но бутонът не реагира | JavaScript не е зареден (кеш, път, конфликт) | DevTools → Конзола (грешки) + Мрежа (JS файл 404?) | Проверка plugins_url()изчистване на кеша, увеличаване BPCAB_CHATBOT_VERSION |
| Отговор „невалиден, няма“ | Изтекъл nonce, агресивен кеш на страници или JS получава стар nonce | Вижте стойността BPCAB_CHATBOT.nonce в конзолата |
Намалете кеша на страници с чат или регенерирайте nonce при зареждане (по-напреднал подход) |
| Грешка 429 „Твърде много заявки“ | Твърде строги ограничения на честотата или повтарящи се тестове | Възпроизвеждане в режим на частно сърфиране / различен IP адрес | Увеличете прозореца/лимита или ограничете до влезли потребители |
| Грешка „AI 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 вместо това.
- Неподходяща кука : ако направите заявка към JS на
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)
- Документация за OpenAI API
- PHP mbstring (mb_strlen)
- Официално хранилище на WordPress (wordpress-develop)
ЧЗВ
Наистина ли е „без плъгини“, ако добавя mu-плъгин?
Не инсталирате плъгин на трета страна от wordpress.org, но технически добавяте код под формата на плъгин. Това е умишлено: това е най-чистият начин да запазите кода независим от темата.
Мога ли да въведа кода functions.php ?
Да, но не го препоръчвам. Рискувате да загубите чатбота, когато сменяте теми, а конструкторът на теми може да усложни отстраняването на грешки. Ако все пак го направите, използвайте дъщерна тема.
Защо да използвам admin-ajax.php а не REST API?
И двете работят. За начинаещи, AJAX WordPress е бърз за настройване. Ако искате по-модерен подход, преминете към REST маршрут с register_rest_route() и разрешения за обратно извикване. Ядрото (извикване wp_remote_post()(кеш, сигурност) остава същото.
Еднократният код се поврежда с моя кеширащ плъгин: какво да правя?
Изключете страницата от кеша (просто решение) или имплементирайте крайна точка, която връща nonce при заявка, след което актуализирайте JS, за да го извлечете. На сайтове с голяма кеширана информация (агресивна CDN) това е класически подход.
Как мога да огранича чатбота до определени страници?
Шорткодът вече ви дава този контрол: добавяте го само където ви е необходим. А кодът запитва JS/CSS само ако шорткодът е наличен.
Как можем да попречим на изкуствения интелект да говори глупости?
Не можете да го гарантирате на 100%. Намалете риска:
- кратки отговори
- ниска температура
- Посланието е ясно: „Ако не знаеш, кажи.“
- Пренасочване към вашите ЧЗВ/контакт
Мога ли да показвам кликаеми връзки в отговора?
Да, но го правете внимателно. В този урок ще почистим с wp_kses() чрез упълномощаване aОт страна на JS, ние показваме с textContent (така че без HTML). Ако искате кликаеми връзки, трябва да ги покажете в HTML (innerHTML) и да се проявяват допълнителни предпазни мерки (строга санитария, автоматично добавяне на rel="noopener nofollow").
Съвместим ли е с Divi 5 / Elementor / Avada?
Да: използвайте модул/уиджет за „Shortcode“ и го поставете [ai_chatbot]Кодът е независим от темата. Единствените проблеми обикновено възникват от кеширането/минифицирането: изчистване след добавяне на файлове.
Защо го ограничавате до 400 знака?
За да избегнете огромния брой подкани, да намалите разходите и да ограничите злоупотребите, можете да увеличите броя им, но го направете с пълна осъзнатост за последствията (токени, латентност, фактуриране).
Как да променим модела на изкуствения интелект?
Промяна на стойността model в $bodyЗапазете „мини“ модел за чатбот за насоки. Ако мащабирате, следете разходите и латентността.
Получавам „HTTP грешка: cURL грешка 28“
Това е таймаут. Проверете:
- Достъпът до мрежата е разрешен от хостинг сървъра (защитна стена)
- DNS
- увеличение
timeout(напр. 30), ако сървърът ви е бавен
Ако сте на заключен споделен хостинг план, това е често срещано.