Как подключить AI-ассистента к WordPress, используя тот же Cloud Run и Groq (часть 2)

В предыдущей статье — «Как сделать 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 API

Cloud 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 Assistant

2. Настройки 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)

и сохраняется в:

sessionStorage

7. 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
   ↓
Telegram

Telegram 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
+ другой endpoint

Backend:

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.js

Cloud Run gateway:

cloud-run/
├── index.js
├── package.json
└── .env.example

Если Cloud Run уже был настроен по предыдущей статье, для подключения WordPress повторно разворачивать его не нужно.

Понравилась статья? Поделиться с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!:

Спросить моего помощника