В предыдущей статье — «Как сделать AI-чат для сайта: MODX, Groq, Cloud Run, email и Telegram» — я подробно разобрал создание AI-ассистента для сайта на MODX.
Мы сделали чат, который:
- общается с посетителем;
- уточняет детали проекта;
- запоминает контекст разговора;
- определяет, когда информации уже достаточно;
- формирует краткое AI-резюме;
- отправляет лид на email;
- дополнительно уведомляет владельца сайта в Telegram.
При этом запросы к AI и Telegram проходят через небольшой gateway в Google Cloud Run.
После того как MODX-версия заработала, возник очевидный вопрос:
Можно ли использовать тот же Cloud Run для WordPress?
Конечно можно — и практически без изменений инфраструктуры.
Нам вообще не потребуется создавать второй AI backend. Достаточно написать небольшой WordPress adapter.
Исходный код MODX- и WordPress-версий находится в одном репозитории:
https://github.com/web86/modx-ai-lead-assistant
Что мы не будем делать заново
В первой статье уже была построена такая часть системы:
Google Cloud Run
│
├── /chat
│ ↓
│ Groq
│ ↓
│ GPT-OSS 120B
│
└── /telegram
↓
Telegram Bot APICloud Run уже умеет:
- принимать запросы от сайта;
- проверять
X-Gateway-Secret; - обращаться к Groq;
- возвращать structured AI response;
- отправлять уведомления через Telegram;
- хранить API-ключи в Google Secret Manager.
И всё это совершенно не зависит от MODX.
Поэтому для WordPress эту часть менять не нужно.
Новая архитектура
Теперь один Cloud Run обслуживает сразу две CMS:
┌── MODX 2
│ chat.php
Visitor → Chat Widget ───┤
│
└── WordPress
REST API
│
▼
Cloud Run
/chat /telegram
│ │
▼ ▼
Groq TelegramРазличается только небольшой слой между frontend и Cloud Run.
Для MODX это:
/assets/components/assistant/api/chat.phpДля WordPress:
/wp-json/web86-ai-lead/v1/chatПолучается довольно удобная архитектура: AI-инфраструктура остаётся общей, а каждая CMS получает только собственный adapter.
1. Делаем из WordPress adapter небольшой плагин
Вместо отдельного PHP-файла, как в MODX, для WordPress логичнее сделать обычный plugin.
Структура получается совсем простой:
wp-content/
└── plugins/
└── web86-ai-lead-assistant/
└── web86-ai-lead-assistant.phpВ репозитории готовый файл находится здесь:
wordpress/
└── web86-ai-lead-assistant.phpПосле загрузки файла активируем:
Plugins
→ Web86 AI Lead AssistantПлагин добавляет собственную страницу:
Settings
→ AI Lead Assistant2. Настройки WordPress
В настройках можно указать:
Enabled
Owner name
Model
Lead email
History messages
Cloud Run chat URL
Gateway secret
Telegram enabled
Cloud Run Telegram URLНапример:
Model:
openai/gpt-oss-120b
Cloud Run chat URL:
https://YOUR-SERVICE.run.app/chat
Cloud Run Telegram URL:
https://YOUR-SERVICE.run.app/telegramCode language: PHP (php)То есть это те же самые endpoints, которые уже использовала MODX-версия.
Новый Cloud Run создавать не нужно.
3. Где хранить Gateway Secret
Технически secret можно хранить в настройках WordPress.
Но для production я предпочитаю вынести его из базы данных в:
wp-config.phpCode language: CSS (css)Например:
define(
'WEB86_AI_GATEWAY_SECRET',
'YOUR_LONG_RANDOM_SECRET'
);Code language: JavaScript (javascript)Плагин сначала проверяет эту константу.
Если она определена, значение из базы WordPress игнорируется.
Получается:
Browser
↓
WordPress
↓
X-Gateway-Secret
↓
Cloud RunПри этом браузер никогда не видит secret.
4. Создаём WordPress REST endpoint
В MODX мы обращались к обычному PHP endpoint. В WordPress естественнее использовать REST API.
Плагин регистрирует:
POST /wp-json/web86-ai-lead/v1/chatУпрощённо это выглядит так:
add_action(
'rest_api_init',
static function (): void {
register_rest_route(
'web86-ai-lead/v1',
'/chat',
[
'methods' =>
WP_REST_Server::CREATABLE,
'callback' =>
'web86_ai_lead_rest_chat',
'permission_callback' =>
'__return_true',
]
);
}
);Code language: PHP (php)На первый взгляд __return_true может выглядеть странно.
Но endpoint предназначен именно для обычных посетителей сайта, которые не авторизованы в WordPress.
Поэтому аутентификация пользователя здесь нам не подходит.
Вместо неё adapter отдельно выполняет:
- same-origin check;
- проверку email;
- ограничение размера сообщения;
- rate limiting;
- проверку
conversation_id; - серверную авторизацию Cloud Run через gateway secret.
Если на сайте используется Clearfy и включена функция блокировки REST API / JSON API, публичный endpoint ассистента
/wp-json/web86-ai-lead/v1/chatможет перенаправляться на главную страницу с HTTP 301. В этом случае необходимо разрешить WordPress REST API в настройках Clearfy. После отключения блокировки endpoint начинает работать без каких-либо изменений Cloud Run или AI-кода.
5. Frontend практически не меняется
Самое приятное — HTML, CSS и почти весь JavaScript остались общими для MODX и WordPress.
Для MODX было:
<div
class="fw-assistant"
id="fwAssistant"
data-endpoint="/assets/components/assistant/api/chat.php"
>Code language: JavaScript (javascript)Для WordPress достаточно заменить endpoint:
<div
class="fw-assistant"
id="fwAssistant"
data-endpoint="/wp-json/web86-ai-lead/v1/chat"
>Code language: JavaScript (javascript)Остальной widget может остаться тем же.
6. Почему появился conversation_id
Здесь между MODX и WordPress есть одно важное отличие.
В MODX первая версия хранила историю через:
PHP sessionДля WordPress я решил не запускать PHP sessions вообще.
Вместо этого frontend создаёт случайный:
conversation_idНапример:
{
"email": "john@example.com",
"message": "Мне нужен сайт на WordPress",
"page_url": "https://example.com/",
"conversation_id": "a71ca84258904c58b596d818309cb9730a88"
}Code language: JSON / JSON with Comments (json)ID создаётся в браузере с помощью:
crypto.getRandomValues()Code language: CSS (css)и сохраняется в:
sessionStorage7. WordPress Transients вместо PHP session
Получив conversation_id, WordPress не использует его напрямую как ключ.
Сначала из него создаётся HMAC:
function web86_ai_lead_conversation_key(
string $conversationId
): string {
return 'web86_ai_conv_' . substr(
hash_hmac(
'sha256',
$conversationId,
wp_salt('auth')
),
0,
40
);
}Code language: PHP (php)После этого история сохраняется через WordPress Transients.
Условно:
set_transient(
$conversationKey,
$conversation,
12 * HOUR_IN_SECONDS
);Code language: PHP (php)То есть архитектура такая:
Browser
│
│ conversation_id
▼
WordPress
│
│ HMAC
▼
Transient
│
└── email
history
page_url
handoff stateИстория хранится максимум 12 часов.
Для небольшого lead-assistant этого более чем достаточно.
8. Почему Transients удобнее PHP session в WordPress
WordPress-сайты часто используют:
- page cache;
- object cache;
- Redis;
- reverse proxy;
- CDN;
- различные optimization plugins.
Добавлять поверх этого собственную PHP session без необходимости не очень хочется.
Transient API гораздо естественнее вписывается в WordPress.
Кроме того, если позже появится persistent object cache, WordPress сможет использовать его автоматически.
9. Ограничиваем историю диалога
Как и в MODX-версии, нет смысла бесконечно отправлять AI всю историю.
Плагин ограничивает её настройкой: History messages – например 10
Код примерно такой:
if (count($history) > $limit) {
$history =
array_slice(
$history,
-$limit
);
}Code language: PHP (php)Это уменьшает:
- token usage;
- размер запросов;
- задержку;
- вероятность упереться в rate limits.
10. Отправляем запрос в Cloud Run через WordPress HTTP API
В MODX мы использовали обычный cURL.
В WordPress лучше воспользоваться встроенным HTTP API:
wp_remote_post()Например:
$response = wp_remote_post(
$url,
[
'timeout' => 40,
'headers' => [
'X-Gateway-Secret' =>
$gatewaySecret,
'Content-Type' =>
'application/json',
'Accept' =>
'application/json',
],
'body' =>
wp_json_encode(
$payload
),
]
);Code language: PHP (php)Это лучше интегрируется с самим WordPress и различными конфигурациями хостинга.
Ошибку можно проверить стандартным способом:
if (is_wp_error($response)) {
// connection error
}Code language: PHP (php)HTTP status:
$code =
wp_remote_retrieve_response_code(
$response
);Code language: PHP (php)А тело ответа:
$body =
wp_remote_retrieve_body(
$response
);Code language: PHP (php)11. Сам AI prompt менять не пришлось
Это ещё одно преимущество нашей архитектуры.
AI не знает, работает сайт на:
MODX
WordPress
Laravel
обычном PHPЕму всё равно.
WordPress adapter отправляет в тот же /chat:
{
"model": "openai/gpt-oss-120b",
"instructions": "...",
"input": [...],
"max_output_tokens": 400
}Code language: JSON / JSON with Comments (json)И получает тот же structured response:
{
"reply": "...",
"ready_to_handoff": true,
"handoff_message": "...",
"lead": {
"name": "Иван",
"website": "example.com",
"request": "...",
"summary": "..."
}
}Code language: JSON / JSON with Comments (json)То есть вся AI-логика осталась общей.
12. Handoff работает точно так же
AI сам не отправляет лид.
Он только сообщает серверу:
"ready_to_handoff": trueCode language: JavaScript (javascript)После этого WordPress уже решает, удалось ли действительно доставить сообщение.
Логика остаётся:
AI
↓
ready_to_handoff
↓
WordPress
↓
Email / Telegram
↓
успешно?
↓
только тогда:
"Ваш запрос передан"Code language: JavaScript (javascript)Это важно, потому что модель не должна утверждать пользователю, что сообщение отправлено, если реальной доставки не произошло.
13. Email через wp_mail()
Для WordPress больше не нужен MODX modPHPMailer.
Используем штатную функцию:
wp_mail()Например:
$headers = [
'Content-Type: text/html; charset=UTF-8',
'Reply-To: ' . $visitorEmail,
];
$sent = wp_mail(
$emailTo,
'[AI Lead] ' . $subject,
$html,
$headers
);Code language: PHP (php)В письмо попадают:
Email
Name
Website
Source page
Request
AI Summary
ConversationЕсли на сайте уже установлен SMTP plugin, например для обычных WordPress-писем, наш assistant автоматически использует ту же почтовую инфраструктуру.
Важно только помнить: wp_mail() с результатом true означает, что WordPress передал письмо почтовому transport без немедленной ошибки.
Это ещё не является SMTP delivery receipt.
14. Telegram вообще не пришлось менять
Это, пожалуй, лучший пример пользы Cloud Run.
WordPress не обращается напрямую к:
api.telegram.orgCode language: CSS (css)Он просто вызывает уже существующий:
Cloud Run /telegramСхема:
WordPress
↓
Cloud Run /telegram
↓
Telegram Bot API
↓
TelegramTelegram Bot Token при этом хранится в Google Secret Manager. WordPress его вообще не знает.
15. Один Cloud Run — несколько сайтов
Теперь появляется ещё одна интересная возможность.
Один gateway технически может использоваться несколькими сайтами:
MODX site
\
\
WordPress site
\
→ Cloud Run → Groq
/
another siteКаждому новому CMS adapter не требуется заново реализовывать интеграцию с Groq или Telegram.
Нужно только научить CMS безопасно общаться с gateway.
16. Ошибка, на которую легко наткнуться при переносе с MODX
При первом тестировании WordPress-версии я получил в чате:
Не удалось получить ответ. Попробуйте ещё раз.
В Network при этом отправлялся запрос на:
/assets/components/assistant/api/chat.phpТо есть frontend всё ещё использовал MODX endpoint.
WordPress закономерно возвращал обычную HTML-страницу 404.
JavaScript ожидал JSON:
await response.json();Code language: JavaScript (javascript)но получал:
<!DOCTYPE html>
<html>
...
404 Not FoundCode language: HTML, XML (xml)Отсюда и ошибка.
Для WordPress endpoint должен быть:
/wp-json/web86-ai-lead/v1/chatПоэтому важно поменять:
data-endpoint="/wp-json/web86-ai-lead/v1/chat"Code language: JavaScript (javascript)Если используется Autoptimize, WP Rocket или другой optimizer/cache plugin, после замены JavaScript также стоит очистить его cache.
17. Как проверить WordPress adapter вручную
До подключения frontend удобно проверить REST API напрямую.
Например:
curl -i \
-X POST \
"https://example.com/wp-json/web86-ai-lead/v1/chat" \
-H "Content-Type: application/json" \
-H "Origin: https://example.com" \
-d '{
"email": "test@example.com",
"message": "Мне нужен новый сайт на WordPress",
"page_url": "https://example.com/",
"conversation_id": "a71ca84258904c58b596d818309cb9730a88"
}'Code language: PHP (php)Нормальный ответ:
{
"ok": true,
"message": "Конечно. Расскажите немного подробнее...",
"handoff_sent": false,
"handoff_channels": {
"email": false,
"telegram": false
}
}Code language: JSON / JSON with Comments (json)Если задача уже достаточно понятна:
{
"ok": true,
"message": "Спасибо! Ваш запрос передан...",
"handoff_sent": true,
"handoff_channels": {
"email": true,
"telegram": true
}
}Code language: JSON / JSON with Comments (json)18. Что в итоге пришлось изменить?
Удивительно мало:
Cloud Run: без изменений
Groq: без изменений
Telegram: без изменений
Prompt: практически без изменений
Frontend:
+ conversation_id
+ другой endpointBackend:
MODX adapter
↓
WordPress REST adapterИ вместо:
MODX mail
PHP session
cURLмы получили:
wp_mail()
Transients
wp_remote_post()Итоговая архитектура
Теперь проект выглядит так:
┌─────────────────────────────┐
│ Website Visitor │
└──────────────┬──────────────┘
│
▼
┌─────────────────────────────┐
│ Vanilla JS Assistant │
└──────────────┬──────────────┘
│
┌───────┴────────┐
│ │
▼ ▼
┌─────────────┐ ┌──────────────┐
│ MODX │ │ WordPress │
│ chat.php │ │ REST API │
└──────┬──────┘ └──────┬───────┘
│ │
└────────┬────────┘
│
▼
┌─────────────────────────────┐
│ Google Cloud Run │
│ │
│ /chat /telegram │
└──────┬──────────────┬───────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Groq │ │ Telegram API │
│ GPT-OSS 120B │ └──────────────┘
└──────────────┘Главный вывод
Когда мы делали первую MODX-версию, Cloud Run мог показаться просто обходным решением для доступа к внешним API.
Но после подключения WordPress стало видно другое его преимущество. Мы фактически отделили CMS от AI infrastructure.
Теперь CMS отвечает за:
пользователя
валидацию
историю
email
настройкиа Cloud Run:
Groq
Telegram
API secrets
external networkingИменно поэтому перенос ассистента с MODX на WordPress оказался довольно небольшим. Вместо создания второго AI-бота мы просто написали ещё один adapter.
А значит, точно таким же способом в будущем можно подключить:
Laravel
Drupal
OctoberCMS
чистый PHP
Node.js
любую другую CMSCode language: CSS (css)к тому же самому AI gateway.
Исходный код
Полный проект находится на GitHub:
https://github.com/web86/modx-ai-lead-assistant
WordPress adapter:
wordpress/
├── web86-ai-lead-assistant.php
└── README.mdОбщий frontend:
frontend/
├── assistant.html
├── assistant.css
└── assistant.jsCloud Run gateway:
cloud-run/
├── index.js
├── package.json
└── .env.exampleЕсли Cloud Run уже был настроен по предыдущей статье, для подключения WordPress повторно разворачивать его не нужно.
