Если вы когда-либо в спешке вставляли отрывок из статьи в метаописание посреди ночи, вы знаете проблему: он повторяется, часто слишком длинный и иногда отклоняется от темы. Автоматические аннотации, с другой стороны, могут быть надежными… при условии, что они правильно связаны с… WordPress 6.9.4, с кэшированием, средствами защиты и без открытых ключей API.

Потребность / Вариант использования

сайт WordPress Главное здесь — содержание. Но читатель бегло просматривает текст. Краткое, лаконичное изложение (2-3 предложения) лучше:

  • Страницы категорий (однородный обзор статей).
  • информационные бюллетени (Краткие тизеры без необходимости ручного переписывания).
  • Страницы «Рекомендуемые» (положить (более читабельно)
  • Веб-сайты с несколькими авторами : тот же стиль резюме, та же длина.

В итоге у вас получится система, которая:

  • Создает краткое изложение статьи с помощью ИИ при ее сохранении.
  • хранить его в цель (в базе данных WordPress)
  • Оно кэширует данные (временные данные), чтобы избежать двойной оплаты.
  • Отображает его с помощью шорткода (совместим с Divi 5, Elementor, Avada).
  • Обрабатывает ошибки (тайм-аут, квота, некорректный JSON) без нарушения работы вашего сайта.

Краткое резюме

  • Вы храните ключ API в WP-config.php (никогда не задается жестко в коде).
  • Un мю-плагин генерирует сводку посредством wp_remote_post() к API OpenAI.
  • Сводка зафиксирована в пост мета (_ai_summary) и повторно использованы.
  • Un преходящий Это позволяет избежать повторных звонков и снизить затраты.
  • короткий код [ai_summary] позволяет отображать в Divi 5/Elementor/Avada.
  • У вас есть режим отладки, чистые журналы и диагностическая панель.

Когда следует использовать ИИ для этого?

Я активирую этот тип автоматического составления сводки, когда сайт соответствует хотя бы одному из следующих условий:

  • Много статей (обширные архивы, очень активные категории).
  • Несколько редакторов а также необходимость в едином формате.
  • Длинный контент (путеводители, файлы, исследования), где часто забывают о роли человека.
  • переработка : новостная рассылка, push-уведомления, страницы с «лучшими статьями».

Это также отлично работает для сайтов, использующих Divi 5, Elementor или Avada: мы отображаем краткое содержание с помощью шорткода, не затрагивая конструктор.

Когда НЕ следует использовать ИИ

Не стоит платить за API, если достаточно нативного решения. Типичные примеры:

  • У вас уже есть фрагмент кода WordPress. (поле «Извлечь») и ваша команда правильно его заполнит.
  • Ваши статьи короткие. (300–500 слов): может быть достаточно сокращенного отрывка.
  • У вас действуют строгие правовые ограничения. (Медицинский/юридический контент): резюме, созданное с помощью ИИ, может «интерпретировать» информацию и вносить в нее ошибки.
  • Вам нужен на 100% детерминированный сводный отчет? Искусственный интеллект различается даже при строгих параметрах.

Простая альтернатива (без ИИ): отобразить выдержку или сгенерировать «резюме», сократив содержимое (например, wp_trim_words()Это бесплатно, мгновенно и зачастую достаточно.

Предпосылки

Версии и среда

  • WordPress : 6.9.4 (апрель 2026 г.) или позже.
  • PHP Рекомендуется версия 8.1 и выше. При использовании версии 7.x в современном коде могут возникать фатальные ошибки.
  • HTTPS На стороне сайта (настоятельно рекомендуется) это делается во избежание передачи административных сессий в открытом текстовом виде.

API: простое определение (полезно перед написанием кода)

Один API (Интерфейс прикладного программирования) — это сервис, доступный по протоколу HTTP. На практике WordPress отправляет HTTP-запрос (часто в формате JSON) на URL-адрес, сервис отвечает JSON, а ваш код преобразует этот ответ в пригодный для использования текст.

В WordPress это корректно реализуется через HTTP API: wp_remote_post() et wp_remote_get()Официальный источник: HTTP API (developer.wordpress.org).

Ключ API и хранилище (wp-config.php)

Вам потребуется ключ API от поставщика решений в области ИИ. Здесь я показываю OpenAI, поскольку его API стабилен и хорошо документирован, но структура будет аналогичной для Anthropic/Mistral/Google.

Далее добавьте ключ к WP-config.php (выше «Вот и всё, прекратите редактировать!»). Никогда не используйте это в плагине, никогда в общем фрагменте кода, никогда в репозитории Git.

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

Обратите внимание на стоимость: Каждое сводное сообщение — это платная услуга. Вы сможете ограничить это за счет кэширования и создания сводных сообщений только при определенных событиях (публикация/обновление).

Куда вставить код

Для такого рода функций я избегаю functions.php (слишком легко сломать при обновлении темы). Два надежных варианта:

  • мю-плагин (рекомендуется): файл в wp-content/mu-plugins/Загружено автоматически.
  • пользовательские плагины : файл в wp-content/plugins/, можно включить/выключить.

Я уезжаю мю-плагинПотому что я часто видел, как сайты «теряют» свои сниппеты. после Изменение темы оформления или деактивация плагина сниппетов.


Архитектура решения

Поток (текстовая схема)

Редактор WordPress → сохранение статьи → хук save_post → Получение контента → Проверки (тип, статус, автосохранение) → Временный кэш (во избежание двойных вызовов) → wp_remote_post() → API для ИИ → Парсинг JSON → Санитарная обработка → Хранение метаданных постов _ai_summary → Отображение через шорткод.

Почему именно такой дизайн?

  • Хранить в метаданных : сводка является постоянной, индексируемой и многократно используемой без вызова API при каждом отображении.
  • преходящий : позволяет избежать многократных вызовов, если вы несколько раз нажмете кнопку «Обновить» или если плагин запустит несколько резервных копий.
  • Короткий код Совместимо с Divi 5 / Elementor / Avada без специальной разработки.

Полный код — шаг за шагом

Шаг 1 — Создайте mu-плагин

Создайте папку wp-content/mu-plugins/ Если файл не существует, добавьте его:

  • wp-content/mu-plugins/ai-auto-summary.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;
}

Шаг 2 — Определите константы и настройки.

Настройки централизованы: длина, модель, время ожидания. Вы можете изменить их, не меняя всю логику.

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

Шаг 3 — Добавьте хук генерации в нужный момент.

Un крючок является точкой продолжения. А действие Выполняет код в заданный момент времени (например, сохранение сообщения). фильтры изменяет значение (например, содержимое перед отображением). Здесь мы используем действие : save_post.

Классическая ловушка: используйте the_content Для генерации сводки ИИ запускается каждый раз при её отображении. Результат: плата за API и медленная работа сайта. Сводка генерируется в момент сохранения, а не при рендеринге.

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 );
}

Шаг 4 — Извлеките «чистый» текст из статьи.

Контент WordPress может содержать блоки, HTML-код и шорткоды. Отправка такого необработанного контента искусственному интеллекту часто приводит к странным сводкам (он «обобщает» фрагменты макета). Мы это исправляем:

  • удаление шорткодов
  • удаление HTML-тегов,
  • ограничение по размеру.
/**
 * 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;
}

Шаг 5 — Вызов API OpenAI с помощью функции wp_remote_post()

Мы отправляем HTTP POST-запрос к конечной точке "Responses" (современный API). Мы обрабатываем:

  • Тайм-аут (в противном случае ваш файл сохранения может оказаться "зависшим").
  • HTTP-коды (401, 429, 500…),
  • Недопустимый JSON,
  • кэш (временный, основанный на хеше содержимого).
/**
 * 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 '';
}

Шаг 6 — Отобразить сводку (короткий код)

Шорткод — это небольшой фрагмент кода, заключенный в квадратные скобки, который WordPress заменяет контентом. Это самый простой способ интеграции в:

  • Дива 5 : Модуль «Код» или «Текст» (в текстовом режиме) с [ai_summary].
  • Elementor : виджет «Шорткод».
  • Авада : Элемент «Шорткод» (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>';
}

Если вам нужен минимальный HTML-код (например, курсив), не храните необработанный, неконтролируемый HTML-код. Используйте wp_kses_post() на момент отображения и при строгом соблюдении белого списка. Для начинающих я рекомендую: простой текст.


Полный собранный код

Скопируйте и вставьте весь этот файл в wp-content/mu-plugins/ai-auto-summary.phpДалее добавьте константу ключа API в 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>';
}

Код Пояснение

Почему используется save_post (а не the_content)?

save_post Он срабатывает, когда WordPress сохраняет контент. Это дает вам «естественный» момент для однократного вызова ИИ, сохранения результата и прекращения его работы.

Я часто встречал обучающие материалы, в которых "на экране" кратко описывается следующее: the_contentНа сайте с отключенным кэшированием или с поисковым роботом вы платите за API по кругу. И читатель может видеть разное резюме при каждой загрузке.

Временная блокировка (защита от двойного вызова)

WordPress позволяет сохранять данные несколько раз (автосохранение, обновления, SEO-плагины). Временные данные bpcab_ai_summary_lock_{ID} предотвращает срабатывание ИИ 3 раза за 30 секунд.

кэш хешей содержимого

Мы вычисляем хеш SHA-256 заголовка и содержимого. Если один и тот же текст повторяется (скопирован, вставлен, восстановлен), мы повторно используем хеш из временного файла. Это приводит к прямой экономии средств.

Санитарная обработка: то, что мы защищаем

  • sanitize_text_field() : удаляет символы и нормализует простой текст.
  • wp_strip_all_tags() : удаляет все сгенерированные HTML-теги.
  • esc_html() display: предотвращает внедрение HTML/JS-кода.

Реальный риск: если вы отображаете ответ ИИ без возможности экранирования, вы открываете дверь для внедрения кода (редко, но я сталкивался с этим раньше, когда контент «заставлял» ИИ генерировать HTML).


Стоимость и оптимизация API

Цены регулярно меняются. Возьмите за правило проверять официальную страницу перед развертыванием: Цены OpenAI.

Простая оценка (порядок величины)

Краткое изложение в 2-3 предложениях часто выглядит так:

  • Вступительный взнос: от 1000 до 3000 токенов (в зависимости от длины статьи).
  • Выходные данные: от 80 до 160 токенов.

Пример ежемесячного расчета:

  • 200 статей в кратком изложении в месяц
  • В среднем 2000 входных токенов + 120 выходных токенов.

Вы умножаете на цену выбранной модели. Ключевой момент: Основная часть затрат приходится на вход.Следовательно, предел BPCAB_AI_MAX_INPUT_CHARS.

Оптимизации, которые действительно работают

  • Ограничьте количество отправляемого текста. Зачастую достаточно 8000–12000 символов.
  • «Мини» модель Что касается кратких изложений: нет необходимости платить за громоздкий шаблон для трех предложений.
  • Генерировать только один раз и хранить в метаданных.
  • Ручная регенерация (расширенная опция) вместо повторной генерации при каждом обновлении.

Расширенные варианты и сценарии использования

1) Сгенерируйте краткое содержание заново при обновлении статьи.

По умолчанию код не заменяет существующую сводку. Если вы хотите создавать её заново при каждом обновлении, удалите этот блок:

// 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;
}

Но я так не поступаю на большинстве сайтов: автор может внести 10 мелких исправлений, и вы платите 10 раз больше.

2) Также суммируйте страницы (post_type = page)

Вы можете разрешить page :

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

3) Интеграция Divi 5 / Elementor / Avada (практично)

  • Дива 5 : добавьте модуль «Код» и поместите [ai_summary]Если вы используете шаблон Theme Builder, вставьте шорткод в макет статьи.
  • Elementor : виджет «Шорткод» в шаблоне отдельной записи, затем [ai_summary].
  • Авада : Элемент «Шорткод» в конструкторе или в глобальном шаблоне.

Если ваш конструктор «очищает» шорткод (это происходит в некоторых текстовых модулях), используйте специальный виджет/элемент «Шорткод».


Безопасность и передовой опыт

Никогда не раскрывайте ключ API на стороне клиента.

Не выполняйте вызовы OpenAI из JavaScript в браузере. Даже если вы «скроете» ключ, он в конечном итоге станет видимым. Вызов должен оставаться на стороне сервера (PHP). wp_remote_post().

Ограничение факторов, способных инициировать генерацию

Крючок save_post Запускается при редактировании. В административной панели это уже защищено возможностями WordPress. Если вы позже добавите кнопку «Сгенерировать заново», используйте:

  • un нунций (токен защиты от CSRF-атак)
  • current_user_can( 'edit_post', $post_id ).

ссылка: Одноразовые номера (developer.wordpress.org).

Ограничение тарифов (закон о борьбе со злоупотреблениями / неожиданными платежами)

Если несколько авторов публикуют материалы одновременно, можно ограничить количество сводок до X в час, используя глобальный временный параметр. Например, можно сохранить счетчик и отклонять публикации, превышающие определенный порог. Это простое решение, но оно предотвращает ситуацию, когда «я импортировал 2000 постов и сделал сводку по всем сразу».

GDPR: данные, передаваемые третьей стороне.

Вы отправляете контент внешнему поставщику. В зависимости от вашей ситуации:

  • Обновите свою политику конфиденциальности.
  • Избегайте отправки ненужных персональных данных.
  • Не следует автоматически составлять краткое содержание контента, содержащего конфиденциальную информацию.

Как тестировать и отлаживать

1) Включите ведение журналов WordPress.

В wp-config.php (в тестовой среде) включите:

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

Ошибки будут в wp-content/debug.log.

2) Минимальный тест

  1. Создать новую статью.
  2. Добавьте 2–3 абзаца текста (а не только заголовок).
  3. Публиковать.
  4. Проверьте метаданные _ai_summary (с помощью плагина типа «Query Monitor» или через редактор, если у вас есть инструмент для работы с метаданными).
  5. добавлять [ai_summary] В вашем шаблоне или в содержимом проверьте отображение.

3) Проверьте HTTP-запросы

Если у вас возникают проблемы с таймаутом, начните с временного увеличения времени ожидания. BPCAB_AI_TIMEOUT_SECONDS В 30-м шаге проверьте, не блокирует ли ваш хостинг-провайдер исходящие запросы.

Официальная документация по HTTP API WordPress: HTTP API.


Если это не сработает

Диагностическая таблица

симптом Причина вероятна проверка Решение
Краткое содержание так и не появляется. Код вставлен не в то место / mu-плагин не загружен Убедитесь, что файл находится в wp-content/mu-plugins/ Переместите файл, проверьте права доступа, перезагрузите панель администратора.
ERREUR На момент публикации административные расходы составляли 500 долларов. Ошибка PHP (отсутствует точка с запятой, скобка) советоваться wp-content/debug.log Исправьте синтаксис или восстановите резервную копию.
В логах обнаружена ошибка HTTP 401. Недействительный ключ API увидеть debug.log (Ответ API) Восстановите ключ, вставьте его. wp-config.php
В логах обнаружена ошибка HTTP 429. Превышена квота / лимит тарифа поставщика Панель управления поставщика Подождите, сократите количество звонков, добавьте ограничение скорости и кэширование.
Истекло время ожидания / публикация «заблокирована» Слишком мало времени ожидания, медленный хостинг, исходящий брандмауэр Увеличьте время ожидания, протестируйте с другого сервера. Истекло время ожидания (30 секунд), свяжитесь с хостинг-провайдером, авторизуйте конечную точку.
Пустое резюме Контент, который слишком короткий или слишком "конструкторский" (шорткоды) Проверьте текст после очистки. Добавить реальный текст, отредактировать текст, резюмировать отрывок.
Вы редактируете статью, но аннотация не меняется. Код не перегенерируется, если метаданные уже существуют. Проверьте наличие _ai_summary Удалите метатег или выполните ручную перегенерацию.

Реалистичные ошибки, которые я часто вижу (и как их избежать)

  • Тестирование в производственной среде без резервного копирования. : Запустите тест на тестовой площадке. Синтаксическая ошибка PHP может заблокировать всю панель администратора.
  • Путаница между действием и фильтром. : save_post Это действие. Не пытайтесь "вернуть" что-либо, чтобы изменить значение.
  • Неподходящий зацеп : избегать the_content для дорогостоящей задачи.
  • плагин кэширования Если вы отображаете сводку с помощью шорткода и используете агрессивный кеш, очистите его после первой генерации.
  • PHP слишком устарел. Если ваш хостинг-провайдер использует PHP 7.4, обновитесь до версии 8.1 и выше. Ссылка: Поддерживаемые версии PHP (php.net).

Ресурсы


FAQ

1) Работает ли это с Elementor / Divi 5 / Avada?

Да. Короткий код [ai_summary] Это самый простой метод. Используйте виджет/элемент «Шорткод» в своем конструкторе.

2) Почему именно mu-плагин, а не functions.php?

Потому что functions.php Это зависит от темы оформления. При смене темы вы потеряете функциональность. Плагин mu загружается автоматически независимо от темы.

3) Замедлит ли это публикацию статьи?

Да, немного: вы ждете ответа от API во время процесса сохранения (до истечения таймаута). На очень активных сайтах я часто переключаю эту обработку на асинхронную задачу (WP-Cron или Action Scheduler), но это более продвинутый подход.

4) Могу ли я кратко изложить только отрывок (первые 2 абзаца)?

Да. Редактировать bpcab_ai_get_post_text_for_summary() Удалить только первые X символов или разбить на абзацы перед очисткой.

5) Почему данные сохраняются в пост-метатегах, а не пересчитываются при отображении?

Чтобы избежать оплаты API за каждый просмотр страницы и гарантировать стабильное сводное представление данных, вы выполняете быстрое считывание данных из базы данных только при отображении страницы.

6) Можно ли автоматически отображать сводку в архивах (категориях)?

Да. Вы можете добавить шорткод в шаблон архива (или через конструктор, если ваша тема это позволяет). Другой вариант: прикрепить фильтр к the_excerpt Заменить отрывок мета-тегом, но я делаю это только в том случае, если у сайта есть четкая стратегия (иначе это удивит авторов).

7) Навязывает ли WordPress 6.9.4 какой-либо особый способ выполнения HTTP-запросов?

Нет: рекомендуемый подход остается прежним. wp_remote_post() через HTTP API. Он стабилен и совместим со средами, где cURL недоступен напрямую в PHP.

8) Как "принудительно" перезапустить статью, которая уже была кратко изложена?

Удалить метатеги _ai_summary (с помощью мета-инструмента или небольшого административного скрипта), затем обновите статью. Более аккуратный вариант: добавьте кнопку «Перегенерировать» с одноразовым кодом и возможностью обновления.

9) Может ли ИИ искусственно создавать информацию в виде резюме?

Да, это возможно. Снижение температуры помогает, но не гарантирует полного отсутствия ошибок. Для конфиденциального контента рекомендуется проводить проверку вручную или избегать использования ИИ.

10) Почему ограничение BPCAB_AI_MAX_INPUT_CHARS применяется вместо подсчета токенов?

Для точного подсчета токенов требуется специальная библиотека. Для начинающего сайта ограничение количества символов — это простое, надежное решение, которое уже значительно снижает затраты.