Wer schon einmal um Mitternacht hastig einen Artikelauszug in eine Meta-Beschreibung eingefügt hat, kennt das Problem: Er ist repetitiv, oft zu lang und manchmal themenfremd. Automatische Zusammenfassungen hingegen können zuverlässig sein … vorausgesetzt, sie sind korrekt verknüpft. WordPress 6.9.4, mit Caching, Sicherheitsvorkehrungen und ohne offengelegte API-Schlüssel.

Der Bedarf / Der Anwendungsfall

eine Website WordPress Es lebt vom Inhalt. Doch der Leser überfliegt ihn nur. Eine kurze, prägnante Zusammenfassung (2–3 Sätze) ist daher hilfreich:

  • Kategorieseiten (homogene Übersicht der Artikel).
  • Newsletter (Kurze Teaser ohne manuelle Überarbeitung).
  • Die „Empfohlenen“ Seiten (setzen (weiter vorwärts besser lesbar).
  • Websites mit mehreren Autoren : gleicher Stil der Zusammenfassung, gleiche Länge.

Am Ende erhalten Sie ein System, das Folgendes leistet:

  • generiert eine KI-Zusammenfassung beim Speichern eines Artikels.
  • bewahre es auf in Ziel (in der WordPress-Datenbank)
  • Es speichert die Daten (Transients) zwischen, um eine doppelte Zahlung zu vermeiden.
  • Es wird über einen Shortcode angezeigt (kompatibel mit Divi 5, Elementor und Avada).
  • Behandelt Fehler (Timeout, Kontingentüberschreitung, ungültiges JSON), ohne Ihre Website zu beeinträchtigen.

Kurze Zusammenfassung

  • Sie speichern den API-Schlüssel in wp-config.php (niemals fest codiert).
  • Un Mu-Plugin generiert eine Zusammenfassung über wp_remote_post() zur OpenAI-API.
  • Die Zusammenfassung wird aufgezeichnet in Post-Meta (_ai_summaryund wiederverwendet.
  • Un vorübergehend Dadurch werden wiederholte Anrufe vermieden und die Kosten begrenzt.
  • Ein Kurzcode [ai_summary] ermöglicht die Anzeige in Divi 5 / Elementor / Avada.
  • Sie haben einen Debug-Modus, übersichtliche Protokolle und ein Diagnose-Dashboard.

Wann sollte man KI dafür einsetzen?

Ich aktiviere diese Art von automatischer Zusammenfassung, wenn die Website mindestens eine der folgenden Einschränkungen aufweist:

  • Viele Artikel (umfangreiche Archive, sehr aktive Kategorien).
  • Mehrere Redakteure und die Notwendigkeit eines einheitlichen Formats.
  • Langer Inhalt (Leitfäden, Dateien, Studien), wo der Mensch oft vergessen wird.
  • recyclage : Newsletter, Push-Benachrichtigungen, Seiten mit den „Top-Artikeln“.

Es funktioniert auch sehr gut für Websites, die Divi 5, Elementor oder Avada verwenden: Wir zeigen die Zusammenfassung über einen Shortcode an, ohne den Builder zu berühren.

Wann man KI NICHT einsetzen sollte

Bezahlen Sie nicht für eine API, wenn eine native Lösung ausreicht. Typische Fälle:

  • Sie haben bereits einen WordPress-Codeausschnitt. (Feld „Extrakt“) und Ihr Team füllt es korrekt aus.
  • Ihre Artikel sind kurz (300–500 Wörter): Ein gekürzter Auszug kann genügen.
  • Sie unterliegen starken rechtlichen Beschränkungen. (medizinischer/juristischer Inhalt): Eine KI-Zusammenfassung kann Fehler „interpretieren“ und Fehler einbringen.
  • Sie wünschen eine 100% deterministische Zusammenfassung Die Leistung von KI variiert, selbst bei strengen Parametern.

Eine einfache Alternative (ohne KI): den Auszug anzeigen oder eine „Zusammenfassung“ erstellen, indem der Inhalt gekürzt wird (z. B. wp_trim_words()Es ist kostenlos, sofort verfügbar und oft ausreichend.

Voraussetzungen

Versionen und Umgebung

  • WordPress : 6.9.4 (April 2026) oder später.
  • PHP 8.1+ (empfohlen). Bei Version 7.x treten in modernem Code schwerwiegende Fehler auf.
  • HTTPS auf der Websiteseite (dringend empfohlen), um die Übertragung von Administratorsitzungen im Klartext zu vermeiden.

Eine API: eine einfache Definition (nützlich vor dem Schreiben von Code)

Einen halben Tag API Eine Programmierschnittstelle (API) ist ein Dienst, der über HTTP zugänglich ist. In der Praxis sendet WordPress eine HTTP-Anfrage (oft im JSON-Format) an eine URL, der Dienst antwortet mit JSON, und Ihr Code wandelt diese Antwort in verwendbaren Text um.

In WordPress erfolgt dies korrekt über die HTTP-API: wp_remote_post() et wp_remote_get()Offizielle Quelle: HTTP-API (developer.wordpress.org).

API-Schlüssel und Speicher (wp-config.php)

Sie benötigen einen API-Schlüssel von einem KI-Anbieter. Hier zeige ich OpenAI, da die API stabil und gut dokumentiert ist, aber die Struktur ist für Anthropic/Mistral/Google ähnlich.

Fügen Sie als Nächstes den Schlüssel hinzu zu wp-config.php (über „Das war’s, Bearbeitung beenden!“). Niemals in ein Plugin, niemals in einen geteilten Code-Schnipsel, niemals in ein Git-Repository einfügen.

define( 'BPCAB_OPENAI_API_KEY', 'sk-votre-cle-ici' );

Aufmerksamkeit, Kosten: Jede Zusammenfassung ist ein kostenpflichtiger Anruf. Sie können dies durch Caching und die Zusammenfassung nur bei bestimmten Ereignissen (Veröffentlichung/Aktualisierung) begrenzen.

Wo soll der Code eingefügt werden?

Für diese Art von Funktionalität vermeide ich functions.php (Zu anfällig für Fehler bei einem Theme-Update). Zwei robuste Optionen:

  • Mu-Plugin (empfohlen): Datei in wp-content/mu-plugins/, automatisch geladen.
  • benutzerdefinierte Plugins : Datei in wp-content/plugins/, kann aktiviert/deaktiviert werden.

Ich reise ab Mu-Pluginweil ich schon oft erlebt habe, dass Webseiten ihre Snippets "verlieren". Après eine Änderung des Designs oder ein deaktiviertes Snippets-Plugin.


Lösungsarchitektur

Ablauf (textuelles Schema)

WordPress-Editor → Artikel speichern → Haken save_post → Abrufen von Inhalten → Prüfungen (Typ, Status, automatisches Speichern) → Temporärer Cache (Vermeidung doppelter Aufrufe) → wp_remote_post() → KI-API → JSON-Parsing → Bereinigung → Speicherung von Beitragsmetadaten _ai_summary → Anzeige über Shortcode.

Warum dieses Design?

  • In Meta speichern Die Zusammenfassung ist persistent, indexierbar und wiederverwendbar, ohne dass die API bei jeder Anzeige aufgerufen werden muss.
  • vergänglich : Vermeidet Mehrfachaufrufe, wenn Sie mehrmals auf „Aktualisieren“ klicken oder wenn ein Plugin mehrere Backups auslöst.
  • Kurzcode Kompatibel mit Divi 5 / Elementor / Avada ohne spezielle Entwicklung.

Der vollständige Code – Schritt für Schritt

Schritt 1 – Erstellen Sie das mu-Plugin

Ordner erstellen wp-content/mu-plugins/ Falls die Datei nicht existiert, fügen Sie sie hinzu:

  • wp-content/mu-plugins/ai-auto-summary.php

Fügen Sie die folgende Basis ein:

<?php
/**
 * Plugin Name: AI Auto Summary (mu-plugin)
 * Description: Génère et stocke un résumé IA des articles WordPress.
 * Version: 1.0.0
 * Requires at least: 6.9
 * Requires PHP: 8.1
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

Schritt 2 – Konstanten und Einstellungen definieren

Die Einstellungen sind zentralisiert: Länge, Modell, Timeout. Sie können diese anpassen, ohne die gesamte Logik zu ändern.

const BPCAB_AI_SUMMARY_META_KEY = '_ai_summary';
const BPCAB_AI_SUMMARY_MODEL    = 'gpt-4.1-mini'; // Modèle “mini” : bon compromis coût/qualité (à ajuster)
const BPCAB_AI_TIMEOUT_SECONDS  = 20;
const BPCAB_AI_MAX_INPUT_CHARS  = 12000; // Limite simple pour éviter d'envoyer des romans entiers

Schritt 3 – Fügen Sie den Generierungs-Hook zum richtigen Zeitpunkt hinzu.

Un Haken ist ein Erweiterungspunkt. Aktion Führt Code zu einem bestimmten Zeitpunkt aus (z. B. zum Speichern eines Beitrags). Filter verändert einen Wert (z. B. den Inhalt vor der Anzeige). Hier verwenden wir ein Aktion : save_post.

Klassische Falle: verwenden the_content Um die Zusammenfassung zu generieren, wird die KI bei jeder Anzeige ausgelöst. Die Folge: API-Gebühren und eine langsame Website. Die Zusammenfassung wird beim Speichern generiert, nicht beim Rendern.

add_action( 'save_post', 'bpcab_ai_maybe_generate_summary', 20, 3 );

/**
 * Déclenche la génération du résumé lors de l'enregistrement.
 *
 * @param int     $post_id ID du post.
 * @param WP_Post $post    Objet post.
 * @param bool    $update  True si mise à jour, false si création.
 */
function bpcab_ai_maybe_generate_summary( int $post_id, $post, bool $update ): void {
	// 1) Éviter les autosaves/révisions (sinon vous payez pour rien)
	if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
		return;
	}

	// 2) Vérifier le type de contenu : ici, uniquement les articles
	if ( $post->post_type !== 'post' ) {
		return;
	}

	// 3) Générer seulement quand l'article est publié (ou en passe de l'être)
	$status = get_post_status( $post_id );
	if ( $status !== 'publish' ) {
		return;
	}

	// 4) Si un résumé existe déjà, ne pas écraser (vous pourrez ajouter un bouton “Regénérer” plus tard)
	$existing = get_post_meta( $post_id, BPCAB_AI_SUMMARY_META_KEY, true );
	if ( is_string( $existing ) && $existing !== '' ) {
		return;
	}

	// 5) Vérifier la présence de la clé API
	if ( ! defined( 'BPCAB_OPENAI_API_KEY' ) || ! is_string( BPCAB_OPENAI_API_KEY ) || BPCAB_OPENAI_API_KEY === '' ) {
		error_log( '[AI Summary] Clé API absente. Ajoutez BPCAB_OPENAI_API_KEY dans wp-config.php.' );
		return;
	}

	$content = bpcab_ai_get_post_text_for_summary( $post_id );
	if ( $content === '' ) {
		return;
	}

	// 6) Anti double-facturation : transient de verrouillage pendant 10 minutes
	$lock_key = 'bpcab_ai_summary_lock_' . $post_id;
	if ( get_transient( $lock_key ) ) {
		return;
	}
	set_transient( $lock_key, 1, 10 * MINUTE_IN_SECONDS );

	$summary = bpcab_ai_generate_summary_via_openai( $content, get_the_title( $post_id ) );

	// En cas d'échec, on libère le lock plus tôt (sinon il expire tout seul)
	if ( $summary === '' ) {
		delete_transient( $lock_key );
		return;
	}

	// 7) Sanitation : on stocke du texte simple
	$summary = sanitize_text_field( $summary );

	update_post_meta( $post_id, BPCAB_AI_SUMMARY_META_KEY, $summary );
}

Schritt 4 – Extrahieren Sie einen „bereinigten“ Text aus dem Artikel

WordPress-Inhalte können Blöcke, HTML und Shortcodes enthalten. Wenn man diese Rohdaten an die KI sendet, entstehen oft seltsame Zusammenfassungen (sie „fasst“ Layout-Ausschnitte zusammen). Wir bereinigen das:

  • Entfernung von Kurzcodes
  • Entfernung von HTML-Tags
  • Größenbeschränkung.
/**
 * Récupère un texte propre à résumer (sans HTML/shortcodes).
 */
function bpcab_ai_get_post_text_for_summary( int $post_id ): string {
	$post = get_post( $post_id );
	if ( ! $post instanceof WP_Post ) {
		return '';
	}

	$text = $post->post_content;

	// Retirer les shortcodes (souvent présents avec Divi/Avada et certains builders)
	$text = strip_shortcodes( $text );

	// Retirer le HTML
	$text = wp_strip_all_tags( $text );

	// Normaliser les espaces
	$text = preg_replace( '/s+/', ' ', $text );
	$text = trim( (string) $text );

	if ( $text === '' ) {
		return '';
	}

	// Limiter la taille envoyée à l'API (simple et efficace)
	if ( mb_strlen( $text ) > BPCAB_AI_MAX_INPUT_CHARS ) {
		$text = mb_substr( $text, 0, BPCAB_AI_MAX_INPUT_CHARS );
	}

	return $text;
}

Schritt 5 – Aufruf der OpenAI-API mit wp_remote_post()

Wir senden eine HTTP-POST-Anfrage an den Endpunkt „Responses“ (moderne API). Wir verarbeiten Folgendes:

  • Timeout (andernfalls könnte Ihr Speicherstand als "festgefahren" erscheinen.)
  • HTTP-Codes (401, 429, 500…),
  • Ungültiges JSON,
  • Cache-Speicher (vorübergehend basierend auf dem Inhalts-Hash).
/**
 * Génère un résumé via l'API OpenAI.
 *
 * @param string $content Texte à résumer.
 * @param string $title   Titre de l'article (aide au contexte).
 * @return string Résumé ou chaîne vide si échec.
 */
function bpcab_ai_generate_summary_via_openai( string $content, string $title = '' ): string {
	// Cache “fonctionnel” : si le contenu est identique, on réutilise le résumé
	$hash      = hash( 'sha256', $title . '|' . $content );
	$cache_key = 'bpcab_ai_summary_' . substr( $hash, 0, 32 );

	$cached = get_transient( $cache_key );
	if ( is_string( $cached ) && $cached !== '' ) {
		return $cached;
	}

	$endpoint = 'https://api.openai.com/v1/responses';

	// Prompt : court, contraint, stable
	$instruction = "Vous êtes un assistant de rédaction. Résumez l'article en français en 2 à 3 phrases maximum. " .
		"Ton neutre. Pas de listes. Pas de titre. Pas de conclusion. " .
		"Ne mentionnez pas que vous êtes une IA.";

	$input = "Titre : " . $title . "nnTexte :n" . $content;

	$body = array(
		'model' => BPCAB_AI_SUMMARY_MODEL,
		'input' => array(
			array(
				'role'    => 'system',
				'content' => $instruction,
			),
			array(
				'role'    => 'user',
				'content' => $input,
			),
		),
		// Paramètres de génération : limiter la longueur et la variabilité
		'max_output_tokens' => 160,
		'temperature'       => 0.2,
	);

	$args = array(
		'timeout' => BPCAB_AI_TIMEOUT_SECONDS,
		'headers' => array(
			'Authorization' => 'Bearer ' . BPCAB_OPENAI_API_KEY,
			'Content-Type'  => 'application/json',
		),
		'body'    => wp_json_encode( $body ),
	);

	$response = wp_remote_post( $endpoint, $args );

	if ( is_wp_error( $response ) ) {
		error_log( '[AI Summary] wp_remote_post error: ' . $response->get_error_message() );
		return '';
	}

	$code = (int) wp_remote_retrieve_response_code( $response );
	$raw  = (string) wp_remote_retrieve_body( $response );

	if ( $code < 200 || $code >= 300 ) {
		error_log( '[AI Summary] HTTP ' . $code . ' - Réponse: ' . $raw );
		return '';
	}

	$data = json_decode( $raw, true );
	if ( ! is_array( $data ) ) {
		error_log( '[AI Summary] JSON invalide. Body: ' . $raw );
		return '';
	}

	/*
	 * Parsing : l'API Responses peut renvoyer un tableau "output" avec des items.
	 * On récupère le texte de sortie de manière prudente, sans supposer un format unique.
	 */
	$summary = bpcab_ai_extract_text_from_openai_responses( $data );
	$summary = trim( (string) $summary );

	if ( $summary === '' ) {
		error_log( '[AI Summary] Résumé vide. Body: ' . $raw );
		return '';
	}

	// Sanitation côté affichage : on stocke du texte simple, mais on reste prudent
	$summary = wp_strip_all_tags( $summary );

	// Cache 30 jours : un résumé ne change pas souvent
	set_transient( $cache_key, $summary, 30 * DAY_IN_SECONDS );

	return $summary;
}

/**
 * Extrait le texte depuis la réponse OpenAI "Responses API".
 * Cette fonction est volontairement défensive.
 */
function bpcab_ai_extract_text_from_openai_responses( array $data ): string {
	// Cas fréquent : output[0].content[0].text
	if ( isset( $data['output'] ) && is_array( $data['output'] ) ) {
		foreach ( $data['output'] as $output_item ) {
			if ( ! is_array( $output_item ) ) {
				continue;
			}
			if ( ! isset( $output_item['content'] ) || ! is_array( $output_item['content'] ) ) {
				continue;
			}
			foreach ( $output_item['content'] as $content_item ) {
				if ( is_array( $content_item ) && isset( $content_item['text'] ) && is_string( $content_item['text'] ) ) {
					return $content_item['text'];
				}
			}
		}
	}

	// Fallback : certains formats renvoient "output_text"
	if ( isset( $data['output_text'] ) && is_string( $data['output_text'] ) ) {
		return $data['output_text'];
	}

	return '';
}

Schritt 6 – Zusammenfassung anzeigen (Shortcode)

Ein Shortcode ist ein kleines Codefragment in eckigen Klammern, das WordPress durch Inhalt ersetzt. Es ist die einfachste Methode zur Integration in:

  • Divi 5 : Modul „Code“ oder „Text“ (im Textmodus) mit [ai_summary].
  • Elementor : Widget „Shortcode“.
  • Avada : „Shortcode“-Element (Fusion Builder).
add_shortcode( 'ai_summary', 'bpcab_ai_summary_shortcode' );

/**
 * Shortcode: [ai_summary id="123"]
 * Sans id, affiche le résumé du post courant.
 */
function bpcab_ai_summary_shortcode( array $atts ): string {
	$atts = shortcode_atts(
		array(
			'id' => 0,
		),
		$atts,
		'ai_summary'
	);

	$post_id = (int) $atts['id'];
	if ( $post_id <= 0 ) {
		$post_id = get_the_ID();
	}

	if ( ! $post_id ) {
		return '';
	}

	$summary = get_post_meta( $post_id, BPCAB_AI_SUMMARY_META_KEY, true );
	if ( ! is_string( $summary ) || $summary === '' ) {
		return '';
	}

	// Affichage : on autorise uniquement du texte (pas de HTML)
	$summary = esc_html( $summary );

	return '<div class="ai-summary">' . $summary . '</div>';
}

Wenn Sie minimales HTML (z. B. Kursivschrift) benötigen, speichern Sie kein unkontrolliertes Roh-HTML. Verwenden Sie stattdessen wp_kses_post() zum Zeitpunkt der Anzeige und einer strikten Whitelist. Für Anfänger empfehle ich: einfachen Text.


Der vollständige kompilierte Code

Kopieren Sie diese gesamte Datei und fügen Sie sie ein in wp-content/mu-plugins/ai-auto-summary.phpFügen Sie als Nächstes die API-Schlüsselkonstante hinzu. wp-config.php.

<?php
/**
 * Plugin Name: AI Auto Summary (mu-plugin)
 * Description: Génère et stocke un résumé IA des articles WordPress.
 * Version: 1.0.0
 * Requires at least: 6.9
 * Requires PHP: 8.1
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

const BPCAB_AI_SUMMARY_META_KEY = '_ai_summary';
const BPCAB_AI_SUMMARY_MODEL    = 'gpt-4.1-mini';
const BPCAB_AI_TIMEOUT_SECONDS  = 20;
const BPCAB_AI_MAX_INPUT_CHARS  = 12000;

add_action( 'save_post', 'bpcab_ai_maybe_generate_summary', 20, 3 );
add_shortcode( 'ai_summary', 'bpcab_ai_summary_shortcode' );

/**
 * Déclenche la génération du résumé lors de l'enregistrement.
 *
 * @param int     $post_id ID du post.
 * @param WP_Post $post    Objet post.
 * @param bool    $update  True si mise à jour, false si création.
 */
function bpcab_ai_maybe_generate_summary( int $post_id, $post, bool $update ): void {
	if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
		return;
	}

	if ( ! ( $post instanceof WP_Post ) ) {
		return;
	}

	if ( $post->post_type !== 'post' ) {
		return;
	}

	$status = get_post_status( $post_id );
	if ( $status !== 'publish' ) {
		return;
	}

	$existing = get_post_meta( $post_id, BPCAB_AI_SUMMARY_META_KEY, true );
	if ( is_string( $existing ) && $existing !== '' ) {
		return;
	}

	if ( ! defined( 'BPCAB_OPENAI_API_KEY' ) || ! is_string( BPCAB_OPENAI_API_KEY ) || BPCAB_OPENAI_API_KEY === '' ) {
		error_log( '[AI Summary] Clé API absente. Ajoutez BPCAB_OPENAI_API_KEY dans wp-config.php.' );
		return;
	}

	$content = bpcab_ai_get_post_text_for_summary( $post_id );
	if ( $content === '' ) {
		return;
	}

	$lock_key = 'bpcab_ai_summary_lock_' . $post_id;
	if ( get_transient( $lock_key ) ) {
		return;
	}
	set_transient( $lock_key, 1, 10 * MINUTE_IN_SECONDS );

	$summary = bpcab_ai_generate_summary_via_openai( $content, get_the_title( $post_id ) );

	if ( $summary === '' ) {
		delete_transient( $lock_key );
		return;
	}

	$summary = sanitize_text_field( $summary );

	update_post_meta( $post_id, BPCAB_AI_SUMMARY_META_KEY, $summary );
}

/**
 * Récupère un texte propre à résumer (sans HTML/shortcodes).
 */
function bpcab_ai_get_post_text_for_summary( int $post_id ): string {
	$post = get_post( $post_id );
	if ( ! $post instanceof WP_Post ) {
		return '';
	}

	$text = (string) $post->post_content;

	$text = strip_shortcodes( $text );
	$text = wp_strip_all_tags( $text );
	$text = preg_replace( '/s+/', ' ', $text );
	$text = trim( (string) $text );

	if ( $text === '' ) {
		return '';
	}

	if ( mb_strlen( $text ) > BPCAB_AI_MAX_INPUT_CHARS ) {
		$text = mb_substr( $text, 0, BPCAB_AI_MAX_INPUT_CHARS );
	}

	return $text;
}

/**
 * Génère un résumé via l'API OpenAI.
 */
function bpcab_ai_generate_summary_via_openai( string $content, string $title = '' ): string {
	$hash      = hash( 'sha256', $title . '|' . $content );
	$cache_key = 'bpcab_ai_summary_' . substr( $hash, 0, 32 );

	$cached = get_transient( $cache_key );
	if ( is_string( $cached ) && $cached !== '' ) {
		return $cached;
	}

	$endpoint = 'https://api.openai.com/v1/responses';

	$instruction = "Vous êtes un assistant de rédaction. Résumez l'article en français en 2 à 3 phrases maximum. " .
		"Ton neutre. Pas de listes. Pas de titre. Pas de conclusion. " .
		"Ne mentionnez pas que vous êtes une IA.";

	$input = "Titre : " . $title . "nnTexte :n" . $content;

	$body = array(
		'model' => BPCAB_AI_SUMMARY_MODEL,
		'input' => array(
			array(
				'role'    => 'system',
				'content' => $instruction,
			),
			array(
				'role'    => 'user',
				'content' => $input,
			),
		),
		'max_output_tokens' => 160,
		'temperature'       => 0.2,
	);

	$args = array(
		'timeout' => BPCAB_AI_TIMEOUT_SECONDS,
		'headers' => array(
			'Authorization' => 'Bearer ' . BPCAB_OPENAI_API_KEY,
			'Content-Type'  => 'application/json',
		),
		'body'    => wp_json_encode( $body ),
	);

	$response = wp_remote_post( $endpoint, $args );

	if ( is_wp_error( $response ) ) {
		error_log( '[AI Summary] wp_remote_post error: ' . $response->get_error_message() );
		return '';
	}

	$code = (int) wp_remote_retrieve_response_code( $response );
	$raw  = (string) wp_remote_retrieve_body( $response );

	if ( $code < 200 || $code >= 300 ) {
		error_log( '[AI Summary] HTTP ' . $code . ' - Réponse: ' . $raw );
		return '';
	}

	$data = json_decode( $raw, true );
	if ( ! is_array( $data ) ) {
		error_log( '[AI Summary] JSON invalide. Body: ' . $raw );
		return '';
	}

	$summary = bpcab_ai_extract_text_from_openai_responses( $data );
	$summary = trim( (string) $summary );

	if ( $summary === '' ) {
		error_log( '[AI Summary] Résumé vide. Body: ' . $raw );
		return '';
	}

	$summary = wp_strip_all_tags( $summary );

	set_transient( $cache_key, $summary, 30 * DAY_IN_SECONDS );

	return $summary;
}

/**
 * Extrait le texte depuis la réponse OpenAI "Responses API".
 */
function bpcab_ai_extract_text_from_openai_responses( array $data ): string {
	if ( isset( $data['output'] ) && is_array( $data['output'] ) ) {
		foreach ( $data['output'] as $output_item ) {
			if ( ! is_array( $output_item ) ) {
				continue;
			}
			if ( ! isset( $output_item['content'] ) || ! is_array( $output_item['content'] ) ) {
				continue;
			}
			foreach ( $output_item['content'] as $content_item ) {
				if ( is_array( $content_item ) && isset( $content_item['text'] ) && is_string( $content_item['text'] ) ) {
					return $content_item['text'];
				}
			}
		}
	}

	if ( isset( $data['output_text'] ) && is_string( $data['output_text'] ) ) {
		return $data['output_text'];
	}

	return '';
}

/**
 * Shortcode: [ai_summary id="123"]
 */
function bpcab_ai_summary_shortcode( array $atts ): string {
	$atts = shortcode_atts(
		array(
			'id' => 0,
		),
		$atts,
		'ai_summary'
	);

	$post_id = (int) $atts['id'];
	if ( $post_id <= 0 ) {
		$post_id = get_the_ID();
	}

	if ( ! $post_id ) {
		return '';
	}

	$summary = get_post_meta( $post_id, BPCAB_AI_SUMMARY_META_KEY, true );
	if ( ! is_string( $summary ) || $summary === '' ) {
		return '';
	}

	return '<div class="ai-summary">' . esc_html( $summary ) . '</div>';
}

Code-Erklärung

Warum save_post (und nicht the_content)?

save_post Es wird ausgelöst, wenn WordPress Inhalte speichert. Dadurch haben Sie einen „natürlichen“ Zeitpunkt, die KI einmalig aufzurufen, das Ergebnis zu speichern und sie dann in Ruhe zu lassen.

Ich habe oft Tutorials gesehen, die „auf dem Display“ zusammenfassen. the_contentAuf einer Website mit deaktiviertem Cache oder einem Crawler-Bot zahlen Sie in einer Endlosschleife für die API. Und der Leser sieht möglicherweise bei jedem Seitenaufruf eine andere Zusammenfassung.

Die vorübergehende Sperre (Anti-Doppelanruf)

WordPress kann mehrfach speichern (automatisches Speichern, Updates, SEO-Plugins). bpcab_ai_summary_lock_{ID} Vermeidet es, die KI 3 Mal innerhalb von 30 Sekunden auszulösen.

Inhalts-Hash-Cache

Wir berechnen einen SHA-256-Hash aus Titel und Inhalt. Wird derselbe Text wiederholt (kopiert oder wiederhergestellt), verwenden wir den Hash des temporären Eintrags wieder. Dies führt zu einer direkten Kostenersparnis.

Desinfektion: Was wir schützen

  • sanitize_text_field() : Entfernt Zeichen und normalisiert einfachen Text.
  • wp_strip_all_tags() : entfernt alle möglicherweise generierten HTML-Tags.
  • esc_html() display: verhindert jegliche HTML/JS-Einschleusung.

Reales Risiko: Wenn Sie eine KI-Antwort anzeigen, ohne sie zu maskieren, öffnen Sie eine Tür für Einschleusung (selten, aber ich habe es schon einmal bei Inhalten gesehen, die die KI dazu "zwingen", HTML zu erzeugen).


API-Kosten und Optimierung

Die Preise ändern sich regelmäßig. Prüfen Sie daher vor dem Einsatz immer die offizielle Webseite: OpenAI-Preise.

Einfache Abschätzung (Größenordnung)

Eine Zusammenfassung von 2–3 Sätzen lautet oft:

  • Teilnahmegebühr: 1.000 bis 3.000 Token (abhängig von der Länge des Artikels),
  • Ausgabe: 80 bis 160 Token.

Beispiel einer monatlichen Berechnung:

  • 200 zusammengefasste Artikel pro Monat
  • Durchschnittlich 2.000 Einstiegs-Tokens + 120 Ausstiegs-Tokens

Man multipliziert mit dem Preis des gewählten Modells. Der entscheidende Punkt: Die Kosten liegen hauptsächlich im Einstiegspreis.Daher die Grenze BPCAB_AI_MAX_INPUT_CHARS.

Optimierungen, die tatsächlich funktionieren

  • Beschränken Sie den gesendeten Text 8.000–12.000 Zeichen sind oft ausreichend.
  • „Mini“-Modell Für Zusammenfassungen gilt: Es ist nicht nötig, für eine umständliche Vorlage für 3 Sätze zu bezahlen.
  • Nur einmal generieren und in Metadaten speichern.
  • Manuelle Regeneration (Erweiterte Option) anstatt bei jedem Update neu zu generieren.

Erweiterte Varianten und Anwendungsfälle

1) Die Zusammenfassung wird neu generiert, wenn der Artikel aktualisiert wird.

Standardmäßig ersetzt der Code keine bestehende Zusammenfassung. Wenn Sie diese bei jeder Aktualisierung neu erstellen möchten, löschen Sie diesen Block:

// 4) Si un résumé existe déjà, ne pas écraser
$existing = get_post_meta( $post_id, BPCAB_AI_SUMMARY_META_KEY, true );
if ( is_string( $existing ) && $existing !== '' ) {
	return;
}

Aber auf den meisten Seiten mache ich das nicht: Ein Autor kann 10 kleine Korrekturen vornehmen, und man zahlt das 10-fache.

2) Außerdem Seiten zusammenfassen (post_type = page)

Sie können dies zulassen page :

if ( ! in_array( $post->post_type, array( 'post', 'page' ), true ) ) {
	return;
}

3) Divi 5 / Elementor / Avada-Integration (praktisch)

  • Divi 5 Füge ein „Code“-Modul hinzu und platziere es [ai_summary]Wenn Sie eine Theme Builder-Vorlage verwenden, fügen Sie den Shortcode in das Artikel-Layout ein.
  • Elementor : Widget „Shortcode“ in der Vorlage für einzelne Beiträge, dann [ai_summary].
  • Avada : „Shortcode“-Element im Builder oder in einer globalen Vorlage.

Falls Ihr Builder den Shortcode „bereinigt“ (dies geschieht bei einigen Textmodulen), verwenden Sie das dafür vorgesehene Widget/Element „Shortcode“.


Sicherheit und bewährte Verfahren

Geben Sie den API-Schlüssel niemals clientseitig preis.

Führen Sie keine OpenAI-Aufrufe über JavaScript im Browser durch. Selbst wenn Sie den Schlüssel „verstecken“, wird er irgendwann sichtbar. Der Aufruf muss serverseitig (über PHP) erfolgen. wp_remote_post().

Begrenzen dessen, was die Generierung auslösen kann

Der Haken save_post Wird durch Bearbeitungsaktionen ausgelöst. Im Administrationsbereich ist dies bereits durch WordPress-Funktionen geschützt. Wenn Sie später eine Schaltfläche „Regenerieren“ hinzufügen, verwenden Sie Folgendes:

  • un Nuntius (Anti-CSRF-Token)
  • current_user_can( 'edit_post', $post_id ).

Reference: Nonces (developer.wordpress.org).

Gebührenbegrenzung (Missbrauchs-/Überraschungsgesetz)

Wenn mehrere Autoren gleichzeitig veröffentlichen, können Sie die Anzahl der Zusammenfassungen mithilfe einer globalen Variable auf X pro Stunde begrenzen. Speichern Sie beispielsweise einen Zähler und lehnen Sie Beiträge ab, die einen bestimmten Schwellenwert überschreiten. Das ist zwar einfach, verhindert aber das Szenario „Ich habe 2.000 Beiträge importiert und alle auf einmal zusammengefasst“.

DSGVO: Daten, die an Dritte übermittelt werden

Sie senden Inhalte an einen externen Anbieter. Je nach Ihrer Situation:

  • Aktualisieren Sie Ihre Datenschutzrichtlinie.
  • Vermeiden Sie die Übermittlung unnötiger personenbezogener Daten.
  • Inhalte, die sensible Informationen enthalten, sollten nicht automatisch zusammengefasst werden.

Wie man testet und Fehler behebt

1) WordPress-Protokolle aktivieren

Tanz wp-config.php (in einer Testumgebung) aktivieren:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );

Die Fehler werden darin enthalten sein wp-content/debug.log.

2) Minimaltest

  1. Erstelle einen neuen Artikel.
  2. Fügen Sie 2–3 Absätze Text hinzu (nicht nur eine Überschrift).
  3. Veröffentlichen.
  4. Überprüfen Sie die Metadaten. _ai_summary (mit einem Plugin wie „Query Monitor“ oder über den Editor, falls Sie ein Meta-Tool besitzen).
  5. hinzufügen [ai_summary] in Ihrer Vorlage oder im Inhalt und überprüfen Sie die Anzeige.

3) Überprüfen Sie die HTTP-Aufrufe

Falls es zu Zeitüberschreitungen kommt, erhöhen Sie zunächst vorübergehend das Timeout. BPCAB_AI_TIMEOUT_SECONDS um 30 Uhr. Überprüfen Sie dann, ob Ihr Hosting-Anbieter ausgehende Anfragen blockiert.

Offizielle Dokumentation zur WordPress HTTP-API: HTTP-API.


Wenn es nicht funktioniert

Diagnosetabelle

Symptom Mögliche Ursache Überprüfung Lösung
Die Zusammenfassung wird nie angezeigt Code an der falschen Stelle eingefügt / mu-Plugin nicht geladen Prüfen Sie, ob die Datei vorhanden ist. wp-content/mu-plugins/ Verschieben Sie die Datei, überprüfen Sie die Berechtigungen und laden Sie das Admin-Panel neu.
Fehler 500 Mitarbeiter in der Verwaltung zum Zeitpunkt der Veröffentlichung PHP-Fehler (fehlendes Semikolon, fehlende Klammer) Konsultieren wp-content/debug.log Korrigieren Sie die Syntax oder stellen Sie eine Sicherungskopie wieder her.
HTTP-401-Fehler in den Protokollen Ungültiger API-Schlüssel alle Veranstaltungen debug.log (API-Antwort) Schlüssel neu generieren, einlegen wp-config.php
HTTP-429-Fehler in den Protokollen Quote überschritten / Lieferantenpreislimit Lieferanten-Dashboard Warten, Anrufe reduzieren, Ratenbegrenzung und Cache hinzufügen
Zeitüberschreitung / Veröffentlichung „blockiert“ Timeout zu niedrig, langsames Hosting, ausgehende Firewall Erhöhen Sie das Timeout und testen Sie von einem anderen Server aus. Timeout 30 Sekunden, Hosting-Anbieter kontaktieren, Endpunkt autorisieren
Leere Zusammenfassung Inhalte, die zu kurz oder zu sehr nach Baukasten aussehen (Shortcodes) Überprüfen Sie den Text nach der Reinigung. Fügen Sie echten Text hinzu, passen Sie die Bereinigung an, fassen Sie einen Auszug zusammen
Sie bearbeiten den Artikel, aber die Zusammenfassung ändert sich nicht. Der Code wird nicht neu generiert, wenn Metadaten bereits vorhanden sind. Überprüfen Sie, ob _ai_summary Entfernen Sie das Meta-Tag oder führen Sie eine manuelle Neugenerierung durch.

Realistische Fehler, die ich häufig sehe (und wie man sie vermeidet)

  • Test im Produktivbetrieb ohne Backup Führen Sie einen Test in der Staging-Umgebung durch. Ein PHP-Syntaxfehler kann das gesamte Admin-Panel blockieren.
  • Verwechslung von Aktion und Filter : save_post ist eine Aktion. Versuchen Sie nicht, etwas "zurückzugeben", um einen Wert zu ändern.
  • Unangemessener Haken : vermeiden the_content für eine kostspielige Aufgabe.
  • Cache-Plugin Wenn Sie die Zusammenfassung über einen Shortcode anzeigen und einen aggressiven Cache verwenden, leeren Sie diesen nach der ersten Generierung.
  • PHP ist zu alt Wenn Ihr Hosting-Anbieter PHP 7.4 verwendet, aktualisieren Sie auf Version 8.1 oder höher. Referenz: Unterstützte PHP-Versionen (php.net).

Ressourcen


FAQ

1) Funktioniert es mit Elementor / Divi 5 / Avada?

Ja. Der Shortcode [ai_summary] Das ist die einfachste Methode. Verwenden Sie das Widget/Element „Shortcode“ in Ihrem Builder.

2) Warum ein MU-Plugin anstelle von functions.php?

Weil functions.php Das hängt vom Theme ab. Wenn Sie das Theme ändern, geht die Funktionalität verloren. Das mu-Plugin wird unabhängig vom Theme automatisch geladen.

3) Wird sich dadurch die Veröffentlichung des Artikels verzögern?

Ja, ein wenig: Man wartet während des Speichervorgangs auf die API-Antwort (bis zum Timeout). Auf sehr aktiven Websites stelle ich diese Verarbeitung oft auf einen asynchronen Task (WP-Cron oder Action Scheduler) um, aber das ist ein fortgeschrittenerer Ansatz.

4) Darf ich nur einen Auszug (die ersten beiden Absätze) zusammenfassen?

Ja. Bearbeiten bpcab_ai_get_post_text_for_summary() nur die ersten X Zeichen übernehmen oder den Text vor der Bereinigung in Absätze aufteilen.

5) Warum in den Post-Metadaten speichern, anstatt sie bei der Anzeige neu zu berechnen?

Um zu vermeiden, dass die API bei jedem Seitenaufruf belastet wird und um eine stabile Zusammenfassung zu gewährleisten, wird nur beim Anzeigen ein schneller Datenbankzugriff durchgeführt.

6) Kann ich die Zusammenfassung automatisch in den Archiven (Kategorien) anzeigen lassen?

Ja. Sie können den Shortcode in die Archivvorlage einfügen (oder über den Builder, falls Ihr Theme dies zulässt). Eine weitere Möglichkeit: Fügen Sie einen Filter hinzu. the_excerpt Ich ersetze den Auszug durch die Metadaten, aber nur, wenn die Website eine klare Strategie verfolgt (ansonsten überrascht es die Autoren).

7) Schreibt WordPress 6.9.4 eine bestimmte Vorgehensweise für HTTP-Anfragen vor?

Nein: Die empfohlene Vorgehensweise bleibt bestehen. wp_remote_post() über die HTTP-API. Es ist stabil und kompatibel mit Umgebungen, in denen cURL nicht direkt in PHP zugänglich ist.

8) Wie kann man eine erneute Zusammenfassung eines Artikels erzwingen?

Entferne die Meta-Datei _ai_summary (über ein Metatool oder ein kleines Admin-Skript) und anschließend den Artikel aktualisieren. Elegantere Alternative: einen „Neu generieren“-Button mit Nonce und Capability hinzufügen.

9) Kann KI Informationen in einer Zusammenfassung erfinden?

Ja, das ist möglich. Eine niedrigere Temperatur hilft, garantiert aber keine Fehlerfreiheit. Bei sensiblen Inhalten sollte eine menschliche Überprüfung beibehalten oder auf den Einsatz von KI verzichtet werden.

10) Warum sollte man BPCAB_AI_MAX_INPUT_CHARS begrenzen, anstatt die Token zu zählen?

Um Tokens präzise zu zählen, ist eine spezielle Bibliothek erforderlich. Für eine Anfängerseite ist eine Zeichenbegrenzung einfach, robust und reduziert die Kosten bereits erheblich.