Wenn Sie schon einmal eine E-Mail wie „Ich konnte die gesuchten Informationen auf Ihrer Website nicht finden, können Sie mir helfen?“ erhalten haben, dann haben Sie bereits einen guten Anwendungsfall für einen KI-gestützten Mini-Chat. Ziel ist es nicht, Ihr Support-Team zu ersetzen, sondern eine hilfreiche erste Antwort basierend auf den Inhalten Ihrer Website zu geben. ohne Installieren Plugin.
Der Bedarf / Der Anwendungsfall
Ein „einfacher“ KI-Chatbot auf WordPress Hauptzweck ist es, wiederkehrende Fragen zu beantworten: Öffnungszeiten, Rückerstattungsrichtlinien, „Wo finde ich diesen oder jenen Artikel?“ Lebensläufe einer ausführlichen Anleitung oder Navigationshilfe („Ich suche ein Tutorial zu Permalinks“). Auf einem Blog habe ich oft festgestellt, dass sich 80 % der eingehenden Anfragen auf 10 Fragen reduzieren lassen.
Was KI hier leistet: eine minimalistische Chat-Oberfläche und eine sichere WordPress-AJAX-Route, die die Frage an eine KI-API (z. B. OpenAI) sendet und eine verwertbare Antwort zurückgibt. Wir halten es bewusst einfach: keine Drittanbieter-Widgets, kein SDK, keine Composer-Abhängigkeiten.
Am Ende werden Sie es wissen:
- Anzeige einer kleinen Katze (HTML/CSS/JS) über einen WordPress-Shortcode.
- Sende die Frage mit einer Nonce (Anti-CSRF-Token) an deinen WordPress-Server (AJAX).
- Aufruf einer KI-API mit
wp_remote_post(), Fehler/Timeouts behandeln und Antworten mit Transients zwischenspeichern. - Schützen Sie Ihren API-Schlüssel (gespeichert in
wp-config.phpund vermeiden Sie es, es dem Browser zugänglich zu machen.
Kurze Zusammenfassung
- Sie fügen eine API-Konstante hinzu in
wp-config.php(nie themenbezogen). - Sie erstellen eine Mu-Plugin (Plugin „must-use“), das einen Shortcode bereitstellt
[ai_chatbot]. - Die Front (JS)-Anrufe
admin-ajax.phpmit einer Nonce, nicht direkt über die KI-API. - Der Server ruft die API auf über
wp_remote_post()kurzes Timeout, saubere Fehlerbehandlung. - Zwischenspeicherung von Antworten (Transienten) zur Kostenreduzierung.
- Bereinigter Antwort vor der Anzeige (
wp_kses()) um XSS-Risiken zu begrenzen.
Wann sollte man KI dafür einsetzen?
Verwenden Sie diesen Chatbot-Typ, wenn:
- Sie erhalten immer wiederkehrende Fragen und möchten sofort eine „erste Antwort“.
- Ihre Inhalte sind bereits umfangreich (FAQ, „Über uns“-Seiten, Anleitungen) und KI kann den Benutzer führen.
- Sie akzeptieren, dass eine Antwort manchmal unvollkommen sein kann, und fügen eine klare Nachricht hinzu („automatische Antwort“).
- Sie wünschen sich einen leichtgewichtigen, kontrollierbaren Chatbot, ohne auf ein undurchsichtiges Plugin angewiesen zu sein, das überall externe Skripte einfügt.
Ich empfehle es oft für Tutorial-Blogs, Showcase-Websites oder Nischen, in denen die Fragen sehr konstant sind (z. B. „Wie buche ich?“, „Fristen“, „Preise“).
Wann man KI NICHT einsetzen sollte
Verzichten Sie auf KI, wenn eine klassische WordPress-Lösung eine bessere, einfachere und kostengünstigere Arbeit leistet:
- Recherche interne Beginnen Sie mit der Verbesserung der Suchfunktion (Auszüge, Kategorien, Sitemap). Eine KI kann teuer sein, nur um Ihnen zu raten, die Suchfunktion zu benutzen.
- Sensible Unterstützung (Gesundheit, Recht, Finanzen): Das Risiko von Halluzinationen ist real. In diesen Fällen ist ein Kontaktformular in Kombination mit einer Wissensdatenbank sicherer.
- Transaktionsfragen (Befehle, Konten): Wenn die Antwort von privaten Daten abhängt, sollten Sie nicht einfach einen „einfachen“ KI-Chat zusammenbasteln. Sie benötigen echte Authentifizierung, Berechtigungen und ein Bedrohungsmodell.
- Knappes Budget Bei geringem Traffic ist alles in Ordnung. Bei 50.000 Besuchen pro Tag können die Kosten jedoch sprunghaft ansteigen, wenn kein Cache oder keine Ratenbegrenzung eingerichtet ist.
Voraussetzungen
Zielversionen : WordPress 6.9.4 (April 2026) und PHP 8.1+.
Ein KI-API-Schlüssel Ich verwende OpenAI als Beispiel, weil die API stabil und gut dokumentiert ist, aber die Architektur wird bei Anthropic/Mistral/Google die gleiche sein.
Kurz erklärt: API- und HTTP-Aufrufe
Einen halben Tag API ist eine Schnittstelle, die es Ihnen ermöglicht, etwas von einem Dienst anzufordern (hier: „Beantworten Sie diese Frage“). Technisch gesehen senden Sie eine HTTP-Anfrage (oft in einer URL). jetzt lesen) mit JSON, und Sie erhalten JSON als Antwort.
In WordPress erfolgt dies korrekt über die HTTP-API: wp_remote_post() (Und wp_remote_get()Dies ist die empfohlene Methode, da sie Transport (cURL, Streams), Proxys handhabt und sich in WordPress integriert.
Offizielle Quelle: WordPress HTTP-API.
Wo soll der API-Schlüssel gespeichert werden (erforderlich)
Sie werden den Schlüssel speichern in wp-config.phpNicht in einer Theme-Datei und schon gar nicht in JavaScript. Wenn Ihr Schlüssel im Browser landet, wird er innerhalb von 30 Sekunden kopiert und auf Ihre Kosten verwendet.
Füge dies hinzu zu wp-config.php (idealerweise kurz vor der Zeile „Das war’s, Schluss mit dem Bearbeiten!“):
define('BPCAB_OPENAI_API_KEY', 'VOTRE_CLE_ICI');
Kosten Jede an die API gesendete Nachricht verursacht Kosten (pro Token). Selbst wenn es sich nur um wenige Cent handelt, können sich die Kosten summieren. Daher implementieren wir serverseitig Caching und eine Ratenbegrenzung.
Wo soll der Code eingefügt werden?
- Empfohlene Option : a Mu-Plugin (Aufladung automatiquement). Weg :
wp-content/mu-plugins/. - Alternative : ein klassisches benutzerdefiniertes Plugin in
wp-content/plugins/. - Zu vermeiden :
functions.phpdes Designs (insbesondere wenn Sie das Design ändern oder wenn ein Builder Dateien aktualisiert).
Offizielle mu-plugins-Dokumentation: Unbedingt zu verwendende Plugins.
Lösungsarchitektur
Hier ist der Feed in einfachen Worten:
- Der Besucher öffnet eine Seite, die Folgendes enthält
[ai_chatbot]. - Der Shortcode zeigt die Benutzeroberfläche (ein kleines Fenster) an und lädt ein JS-Skript.
- Wenn der Benutzer eine Nachricht sendet, sendet das JavaScript eine POST-Anfrage an
admin-ajax.phpavec:- die Nachricht
- eine WordPress-Nonce (Anti-Forged-Query)
- WordPress empfängt die Anfrage über einen AJAX-Hook, validiert die Nonce, wendet ein Ratenlimit an und ruft dann die KI-API auf.
wp_remote_post(). - WordPress bereinigt die Antwort, speichert sie im Cache (transient) und sendet sie dann als JSON an den Browser zurück.
- Der Browser zeigt die Antwort an.
Warum WordPress AJAX verwenden (und nicht die KI-API in JS aufrufen)?
Weil Ihr Schlüssel serverseitig verbleiben muss. Wenn Sie OpenAI direkt vom Browser aus aufrufen, geben Sie den Schlüssel preis… und verlieren die Kontrolle (Kosten, Missbrauch, Scraping).
Der vollständige Code – Schritt für Schritt
Wir werden ein MU-Plugin entwickeln, das Folgendes leistet:
- Füge einen Shortcode hinzu
[ai_chatbot] - CSS/JS korrekt einbinden
- Erstellt eine AJAX-Aktion für angemeldete und abgemeldete Benutzer
- ruft die OpenAI-API auf
- speichert die Antworten
Schritt 1 – Erstellen Sie das mu-Plugin
Ordner erstellen wp-content/mu-plugins/ Falls die Datei nicht existiert, erstellen Sie sie:
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';
}
Schritt 2 – Laden von Shortcode + CSS/JS
Un Kurzwahlnummern ist ein Tag zwischen Klammern (z.B. [ai_chatbot]WordPress ersetzt diesen Code durch HTML. Das ist praktisch bei Divi 5, Elementor und Avada: Sie alle wissen, wie man einen Shortcode einfügt (Modul „Code“, Widget „Shortcode“ usw.).
/**
* 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');
Ein häufiger Fehler ist, diesen Code in die falsche Datei einzufügen (ein Builder-Snippet oder ein Snippet-Plugin, das zu spät ausgeführt wird). Mit einem mu-Plugin vermeiden Sie viele unangenehme Überraschungen.
Schritt 3 – CSS/JS-Dateien erstellen
In derselben Datei mu-plugins, erstellen:
bpcab-ai-chatbot.cssbpcab-ai-chatbot.js
Minimales CSS (Sie können es an das Theme/den Builder anpassen):
.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;
}
Minimal JS : AJAX-Anfragen an WordPress senden, Nachrichten anzeigen, Status verwalten.
(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);
});
})();
Eine häufige Fehlerquelle: „JS wird nicht geladen.“ In 90 % der Fälle ist das der falsche Ansatz. plugins_url()oder ein Cache (Plugin/CDN), der eine alte Version ausliefert. Ändern Sie die Version. BPCAB_CHATBOT_VERSION Um ein Neuladen zu erzwingen oder den Cache zu leeren.
Schritt 4 – AJAX-Endpunkt + Nonce + Ratenbegrenzung
Un Haken WordPress ist ein Einstiegspunkt. Es gibt zwei Arten:
- Aktion : „Hier etwas tun“ (z.B.
wp_enqueue_scripts). - Filter : „diesen Wert ändern“ (z.B.
the_content).
Hier verwenden wir AJAX-Aktionen: wp_ajax_* (verbunden) und wp_ajax_nopriv_* (nicht verbunden).
/**
* 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');
Schritt 5 – OpenAI-Aufruf über wp_remote_post() + Cache-Transient
Wir werden:
- Erstelle einen Cache-Schlüssel basierend auf der Frage (Hash).
- Legen Sie eine Gültigkeitsdauer (z. B. 24 Stunden) fest, um zu vermeiden, dass Sie für dieselbe Antwort erneut bezahlen müssen.
- Um zu verhindern, dass PHP sich aufhängt, sollte ein kurzes Timeout (z. B. 20 Sekunden) hinzugefügt werden.
- Bereinigen Sie die Antwort (nur eine sehr begrenzte Teilmenge von HTML ist zulässig).
/**
* 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;
}
Zwei Fehler, die ich oft sehe:
- Ein Semikolon vergessen in
wp-config.phpoder im MU-Plugin: Ergebnis: weißer Bildschirm (schwerwiegender Fehler). AktivierenWP_DEBUGBei der Inszenierung, nicht in der Produktion. - Kopiere ein altes Tutorial Diese verwendet eine veraltete API-Struktur oder ein überholtes Modell. Hier basiert alles auf WordPress 6.9.4 und PHP 8.1, und der Endpunkt wird nur angepasst, wenn OpenAI seine Empfehlung ändert.
Der vollständige kompilierte Code
Nachfolgend finden Sie die vollständige Datei. Mu-PluginSie müssen außerdem die oben gezeigten CSS/JS-Dateien erstellen. Fügen Sie den Schlüssel nicht in diese Datei ein.
<?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');
Code-Erklärung
Warum ein MU-Plugin?
Ein MU-Plugin lädt automatisch ohne Aktivierung und ist weniger fehleranfällig als ein Code-Snippet, das in ein Theme eingefügt wird. Meiner Erfahrung nach ist es so, dass Anfänger, wenn sie den Code in … einfügen, … functions.php und wenn dann das Design geändert (oder ein übergeordnetes Design aktualisiert) wird, verschwindet der Chatbot.
Warum wp_localize_script()
Wir verwenden es hier, um zu JS zu wechseln:
- die AJAX-URL (
admin-ajax.php) - ein Nuntius
- eine Längenbegrenzung
Dadurch wird das "Festcodieren" der URL vermieden, und es funktioniert auch dann, wenn WordPress in einem Unterordner installiert ist.
Dokument: wp_localize_script ().
Nonce: Was es tatsächlich schützt
Eine WordPress-Nonce verhindert, dass andere Websites in Ihrem Namen Anfragen über den Browser Ihrer Besucher senden (CSRF-Angriff). Sie ersetzt kein Authentifizierungssystem, ist aber für einen öffentlichen Chatbot eine Mindestvoraussetzung.
Dokument: WordPress-Nonces.
Cache-Transienten: Warum sie so wichtig sind
Die Transienten Hierbei handelt es sich um Schlüssel-Wert-Caches mit einem Ablaufdatum. Fragt ein Besucher beispielsweise 200 Mal im Monat nach den Öffnungszeiten, möchten Sie nicht 200 KI-Aufrufe bezahlen. Sie bezahlen nur einen Aufruf und liefern die Antwort anschließend aus dem Cache.
Dokument: API-Transienten.
Bereinigung der Reaktion
Eine KI-API kann unerwarteten Text (oder sogar HTML) zurückgeben. Wenn Sie diesen direkt in das DOM einfügen, öffnen Sie die Tür für XSS-Schwachstellen. Hier:
- Wir erlauben nur
a,strong,em,br - Alles andere wird gelöscht
Verwendete Funktion: wp_kses()Dokument: wp_kses().
API-Kosten und Optimierung
Die genauen Preise hängen vom Modell und Ihrem Vertrag ab. Folgendes ist für die Kostenschätzung wichtig:
- die Anzahl der Nachrichten pro Monat
- die durchschnittliche Länge der Fragen und Antworten (Tokens)
- die Cache-Trefferrate (wie viele identische Fragen wiederholt werden)
Schätzung vor Ort (Größenordnung)
Bei einem einfachen „Orientierungs“-Chatbot beobachte ich oft Folgendes:
- Frage: 20 bis 60 Token
- Antwort: 60 bis 200 Token
- Insgesamt: 100 bis 300 Token pro Interaktion
Bei 2.000 Interaktionen pro Monat entspricht das 200.000 bis 600.000 Tokens monatlich. Je nach Modell kann das angemessen sein … oder auch nicht. Caching und ein „Mini“-Modell machen einen großen Unterschied.
Sofortige Optimierungen
- 24-Stunden-Cache (Bereits vorhanden). Für stabile FAQs können Sie das Intervall auf 7 Tage verlängern.
- Kürzere Antworten : Ergänzen Sie die Aufforderung um „Antworten Sie in maximal 5 Sätzen“.
- Niedrige Temperatur Weniger Variationen, mehr Cache-Treffer.
- Begrenzen Sie die Länge Meldungen sowohl auf der JS- als auch auf der PHP-Seite (bereits vorhanden). Immer doppelte Sicherheitsbarriere.
Erweiterte Varianten und Anwendungsfälle
Variante 1 — Fügen Sie einen „Site-Kontext“ (ohne RAG) hinzu
Ohne Ihre Artikel durchsuchen zu müssen, können Sie einen kurzen, statischen Kontext angeben (z. B. Ihre Öffnungszeiten, die URL Ihrer FAQ). Dies ist nützlich für eine Präsentationswebsite.
// 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),
);
Variante 2 – „Artikelzusammenfassungsmodus“ über ein Shortcode-Attribut
Sie können dies zulassen [ai_chatbot mode="resume"] und senden Sie den aktuellen Seitenausschnitt an die KI. Hinweis: Mehr Token bedeuten höhere Kosten.
// 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 '...';
}
Um es klarzustellen: Dieser Codeausschnitt ist unvollständig. Damit er funktioniert, muss auch der JavaScript-Code angepasst werden (senden). mode) und dem AJAX-Handler (ändert die Eingabeaufforderung entsprechend dem Modus).
Variante 3 – Divi 5 / Elementor / Avada-Integration
- Divi 5 Fügen Sie ein „Code“- oder „Shortcode“-Modul hinzu und fügen Sie es ein.
[ai_chatbot]Wenn Divi minimiert/verkettet, leeren Sie seinen statischen Cache, falls das CSS/JS nicht aktualisiert wird. - Elementor Widget „Shortcode“ →
[ai_chatbot]Wenn Sie die Asset-Optimierung von Elementor verwenden, überprüfen Sie, ob das Skript nicht aggressiv verzögert wird (andernfallsDOMContentLoadedkann vor der Injektion erfolgen: In diesem Fall initialisieren Sie auch aufelementor/frontend/init). - Avada Das „Shortcode“-Element in Fusion Builder. Falls Avada-Caching aktiviert ist, sollte dieses nach dem Hinzufügen der CSS/JS-Dateien geleert werden.
Sicherheit und bewährte Verfahren
Geben Sie den API-Schlüssel niemals clientseitig preis.
Einfache Regel: Der Schlüssel bleibt drin wp-config.phpDer Browser sollte es niemals sehen. Selbst wenn es in einem JS-Bundle "versteckt" ist, kann es immer noch wiederhergestellt werden.
Einträge prüfen und begrenzen
- Maximale Länge sowohl auf der JS-Seite (UX) als auch auf der PHP-Seite (Sicherheit).
- Leere Nachrichten ablehnen.
- Ratenbegrenzung nach IP-Adresse (bereits eingerichtet). Zusätzlich kann die Begrenzung auch nach Sitzung/Cookie erfolgen.
Reinigen Sie die Ausgänge.
Die Antwort als Text anzeigen (wie in JS mit textContent) oder stark bereinigen, wenn HTML zugelassen ist. Hier machen wir beides: Wir bereinigen serverseitig und zeigen clientseitig Text an. Das ist bewusst so gewählt.
DSGVO: Daten, die an Dritte übermittelt werden
Wenn ein Besucher personenbezogene Daten eingibt, werden diese möglicherweise an einen Dienstanbieter (OpenAI) übermittelt. Planen Sie entsprechend:
- eine klare Botschaft („Geben Sie keine sensiblen Informationen weiter.“).
- eine Erwähnung in Ihrer Datenschutzerklärung
- Einstellungen des Anbieters (Aufbewahrung, Abmeldung usw.) gemäß Ihrem Vertrag
Testen Sie nicht in der Produktionsumgebung ohne Backup.
Ein MU-Plugin mit einem PHP-Fehler kann die gesamte Website lahmlegen (da es automatisch geladen wird). Arbeiten Sie daher zunächst mit einer Testumgebung. Falls Sie in der Produktionsumgebung arbeiten müssen, halten Sie FTP-/SSH-Zugriff bereit, um die Datei im Falle eines weißen Bildschirms löschen zu können.
Wie man testet und Fehler behebt
Protokollierung ordnungsgemäß aktivieren
Aktivieren Sie in einer Testumgebung Folgendes:
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
Dokument: Debugging in WordPress.
Überprüfen Sie die AJAX-Anfrage.
- Öffnen Sie die Entwicklertools des Browsers → Registerkarte „Netzwerk“ → Filtern Sie nach
admin-ajax.php. - Prüfen Sie den HTTP-Status (200/403/429/500) und die JSON-Antwort.
- Wenn Sie anstelle von JSON eine HTML-Seite sehen, handelt es sich häufig um einen schwerwiegenden PHP-Fehler (oder um ein Sicherheits-Plugin, das den Vorgang abfängt).
Testen der KI-API „im Probelauf“
Vor der Integration des JS können Sie Folgendes aufrufen: bpcab_get_ai_answer() Führen Sie den Code (vorübergehend) in einem Administratorkontext aus und protokollieren Sie die Antwort. Lassen Sie diesen Code nicht dauerhaft vorhanden.
Wenn es nicht funktioniert
| Symptom | Mögliche Ursache | Überprüfung | Lösung |
|---|---|---|---|
| Auf der Seite wird nichts angezeigt. | Shortcode fehlt oder wird nicht interpretiert | Prüfen Sie, ob die Seite Folgendes enthält [ai_chatbot] (nicht in einem "Code"-Block, der die Klammern maskiert) |
Verwenden Sie einen „Shortcode“-Block (Elementor) / ein Shortcode-Modul (Divi/Avada). |
| Das Chatfenster erscheint, aber die Schaltfläche reagiert nicht. | JavaScript nicht geladen (Cache, Pfad, Konflikt) | Entwicklertools → Konsole (Fehler) + Netzwerk (JS-Datei 404?) | Scheck plugins_url()Cache leeren, inkrementieren BPCAB_CHATBOT_VERSION |
| Antwort „ungültige keine“ | Abgelaufene Nonce, aggressiver Seitencache oder JavaScript erhält eine alte Nonce | Schauen Sie sich den Wert an BPCAB_CHATBOT.nonce in der Konsole |
Verringern Sie den Cache auf Seiten mit Chat oder generieren Sie die Nonce beim Laden neu (fortgeschrittenerer Ansatz). |
| Fehler 429 „Zu viele Anfragen“ | zu strenge Ratenbegrenzungen oder wiederholte Tests | Reproduktion im privaten Browsermodus / mit anderer IP-Adresse | Erhöhen Sie das Zeitfenster/Limit oder beschränken Sie es auf angemeldete Benutzer. |
| Fehler „KI-API nicht verfügbar (Code 401/403)“ | Ungültiger Schlüssel, Schlüssel nicht geladen, Berechtigungen | Scheck BPCAB_OPENAI_API_KEY in wp-config.php (ohne Leerzeichen) |
Generieren Sie den Schlüssel neu und überprüfen Sie die Abrechnung/das Kontingent beim Lieferanten. |
| Fehler „Ungültiges JSON“ | Abgeschnittene API-Antwort (Proxy, WAF) oder geänderter Endpunkt | Einloggen $raw im Staging-Bereich (beachten Sie die Daten) |
Passen Sie den Endpunkt/das Format gemäß der offiziellen Dokumentation an und erhöhen Sie gegebenenfalls das Timeout. |
| Weißer Bildschirm nach dem Hinzufügen des mu-Plugins | PHP-Fehler (Klammern, Semikolon), PHP-Version zu alt | Konsultieren wp-content/debug.log oder Serverprotokolle |
Korrigieren Sie die Syntax, prüfen Sie, ob PHP 8.1 oder höher erforderlich ist, und löschen Sie die Datei gegebenenfalls per FTP. |
Realistische Probleme und schnelle Lösungen
- Der Code wurde an der falschen Stelle eingefügt. Wenn Sie den Code im Abschnitt „Benutzerdefinierter Code“ eines Builders platziert haben, wird er möglicherweise gefiltert/maskiert. Verwenden Sie stattdessen das mu-Plugin.
- Unangemessener Haken : wenn Sie die JS-Abfrage auf
initSie riskieren, es zu früh/falsch zu laden. Hier verwenden wirwp_enqueue_scripts. - Cache-Konflikt Manche Caches speichern den HTML-Code, der die Nonce enthält. Folge: Für alle ist die Nonce veraltet. Lösung: Die Seite vom Cache ausschließen oder eine Route implementieren, die eine aktuelle Nonce zurückgibt (fortgeschrittene Variante).
- Permalinks Nicht direkt verwandt, aber ich habe Seiten gesehen, auf denen
admin_url()Die Anfrage wird durch ein falsch konfiguriertes Sicherheits-Plugin gefiltert. Falls AJAX nicht reagiert, überprüfen Sie die URL./wp-admin/admin-ajax.phpdirekt im Browser.
Ressourcen
- WordPress HTTP-API (wp_remote_post)
- Transients API (Cache)
- Nonces (Sicherheit)
- wp_kses() (Sanitärreinigung)
- WordPress-Debug (WP_DEBUG)
- OpenAI API-Dokumentation
- PHP mbstring (mb_strlen)
- Offizielles WordPress-Repository (wordpress-develop)
FAQ
Ist es wirklich „pluginfrei“, wenn ich ein mu-Plugin hinzufüge?
Sie installieren kein Drittanbieter-Plugin von wordpress.org, fügen aber technisch gesehen Code in Form eines Plugins hinzu. Dies ist beabsichtigt: Es ist die sauberste Methode, den Code unabhängig vom Theme zu halten.
Kann ich den Code einfügen? functions.php ?
Ja, aber ich rate davon ab. Beim Ändern des Designs besteht die Gefahr, dass der Chatbot verloren geht, und ein Theme-Builder kann die Fehlersuche erschweren. Falls Sie es trotzdem versuchen, verwenden Sie ein Child-Theme.
Warum verwenden? admin-ajax.php statt der REST-API?
Beides funktioniert. Für Anfänger ist AJAX WordPress schnell eingerichtet. Wer einen moderneren Ansatz bevorzugt, sollte auf eine REST-Route umsteigen. register_rest_route() und Rückrufberechtigungen. Der Kern (Aufruf wp_remote_post()(Cache, Sicherheit) bleibt unverändert.
Die Nonce funktioniert nicht mehr mit meinem Caching-Plugin: Was kann ich tun?
Die Seite kann entweder vom Cache ausgeschlossen werden (einfache Lösung) oder ein Endpunkt implementiert werden, der bei Anfrage eine Nonce zurückgibt. Anschließend kann das JavaScript aktualisiert werden, um diese abzurufen. Bei stark zwischengespeicherten Websites (aggressives CDN) ist dies ein gängiges Verfahren.
Wie kann ich den Chatbot auf bestimmte Seiten beschränken?
Der Shortcode bietet Ihnen diese Kontrolle bereits: Sie fügen ihn nur dort ein, wo Sie ihn benötigen. Und der Code fragt JS/CSS nur dann ab, wenn der Shortcode vorhanden ist.
Wie können wir verhindern, dass KI Unsinn von sich gibt?
Eine hundertprozentige Garantie gibt es nicht. Reduzieren Sie das Risiko:
- kurze Antworten
- niedrige Temperatur
- Die Botschaft ist klar: „Wenn du es nicht weißt, sag es.“
- Weiterleitung zu Ihren FAQs/Kontakt
Kann ich in der Antwort anklickbare Links anzeigen?
Ja, aber gehen Sie dabei vorsichtig vor. In diesem Tutorial reinigen wir mit wp_kses() durch Genehmigung aAuf der JS-Seite zeigen wir Folgendes an: textContent (also kein HTML). Wenn Sie klickbare Links wünschen, müssen Sie diese in HTML anzeigen (innerHTML) und besondere Vorsicht walten zu lassen (strenge Hygiene, automatische Zugabe von rel="noopener nofollow").
Ist es kompatibel mit Divi 5 / Elementor / Avada?
Ja: Verwenden Sie ein „Shortcode“-Modul/Widget und fügen Sie es ein. [ai_chatbot]Der Code ist themenunabhängig. Probleme treten üblicherweise nur beim Caching/der Minifizierung auf: Das Löschen von Dateien nach dem Hinzufügen ist erforderlich.
Warum ist die Zeichenanzahl auf 400 begrenzt?
Um übermäßige Eingabeaufforderungen zu vermeiden, Kosten zu senken und Missbrauch einzuschränken, können Sie die Anzahl der Eingabeaufforderungen erhöhen, sollten sich dabei aber der Konsequenzen (Tokens, Latenz, Abrechnung) voll bewusst sein.
Wie kann das KI-Modell geändert werden?
Ändern Sie den Wert model in $bodyHalten Sie ein „Mini“-Modell für einen Chatbot zur Benutzerführung bereit. Wenn Sie die Implementierung skalieren, überwachen Sie Kosten und Latenzzeiten.
Ich erhalte die Fehlermeldung „HTTP-Fehler: cURL-Fehler 28“.
Es ist eine Auszeit. Prüfen:
- Netzwerkzugriff vom Hosting-Server (Firewall) erlaubt
- DNS
- erhöhen, ansteigen
timeout(z. B. 30) wenn Ihr Server langsam ist
Bei einem Shared-Hosting-Paket mit Netzsperre ist dies üblich.