Генератор контента на WordPress с использованием OpenAI
На сегодняшний день по всему миру набирает популярность искусственный интеллект разработанный компанией OpenAI который открывает множество возможностей для разработки различных чат ботов и генераторов уникального текста, в данной статье мы решили поделиться личным опытом создания плагина генерации контента с использованием OpenAI.
Оглавление
Структура
Основная идея работы с разрабатываемым сервисом заключалась в создании промежуточного сервиса в котором пользователи смогут зарегистрировать аккаунт и настраивать доступ для своего WordPress сайта.
WordPress плагин обращается к Middleware с запросом на генерацию контента, если у сайта есть доступ — то Middleware регистрирует запрос в кабинете пользователя и формирует запрос к OpenAI API, ответ от API возвращается в том же порядке и отображается для возможности вставить в выбранный редактор.
Middleware
Middleware был реализован на Laravel, при регистрации аккаунта пользователь получает следующие возможности:
- Отслеживать статистику запросов на генерацию контента
- Видеть общее количество сгенерированного текста за все время или за выбранный период
- Пополнять баланс аккаунта (в том числе оформить подписку)
- Отслеживать историю пополнений баланса
- Скачать последнюю версию плагина
- Выписать доступ для доменов на которых будет работать плагин и получить токен доступа
Скрипт который подключается плагином на WordPress сайт и отвечает за большую часть работы генератора, реализован в виде CDN и подключается с помощью ключа доступа полученного в кабинете пользователя
Плагин
На WordPress сайте была произведена интеграция с большинством популярных редакторов — TinyMCE, Elementor, Gutenberg, YOOtheme Editor. Реализовано в виде иконки в редакторе при нажатии на которую появляется всплывающее окно с функционалом генерации контента
В данном окне есть возможность:
- Выбрать количество символов для генерации
- Выбрать количество предлагаемых сгенерированных вариантов
- Ввести тему на которую необходимо сгенерировать контент
- Выбрать шаблон генерации
- Редактировать сгенерированный контент
- Передать контент в редактор из которого было вызвано всплывающее окно
- Выбор языка генерируемого контента
Весь функционал этого всплывающего окна реализован при помощи подключенного скрипта через CDN, это позволяет централизованно выпускать обновления и правки.
OpenAI интеграция
Изначально для интеграции с OpenAI планировалось использовать OpenAI PHP for Laravel, но в процессе разработки нашлось еще одно решение как по мне более удобное и подходящее под наши задачи OpenAI PHP Client. Возможно для более глубокой работы с Laravel первое решение будет более подходящим, но на данном проекте обычный клиент с более внятным описанием работы и более гибкими требованиями мне показался идеальным решением (плюс ко всему большим рейтингом на GitHub).
Для начала работы с OpenAI API необходимо зарегистрироваться на их сайте и получить ключ доступа.
Далее создаем Laravel API маршрут который будет использоваться для генерации контента, для этого в файл routes/api.php мы добавили строку:
Route::middleware('auth:api')
->post('/generateText', [App\Http\Controllers\OpenAIController::class, 'generateText']);
Данный маршрут обращается к OpenAIController и вызывает метод generateText при этом делает проверку токена авторизации который пользователь получил в кабинете и добавил в плагин.
Для генерации контента в основном нужны такие параметры как модель и шаблон, первый отвечает непосредственно за качество результата и влияет на цену использования (подробнее про доступные модели тут), второй отвечает за формулировку того что именно вы хотите чтобы ИИ вам сгенерировал — к примеру вот один из наших шаблонов для генерации текста:
Create a blog article in {lang} on the following topic: {theme}/n/n consider the {local} spelling and grammar
В данном шаблоне мы объясняем ИИ что хотим получить, а так же используем переменные которые подставляются в зависимости от выбора сделанного пользователем во всплывающем окне.
В качестве модели мы выбрали text-davinci-003 так как его описание наиболее подходило под наши задачи:
Может решать любые языковые задачи с лучшим качеством, большей продолжительностью и последовательным выполнением инструкций, чем модели Кюри, Бэббиджа или Ада. Также поддерживает некоторые дополнительные возможности, например, вставку текста.
К примеру модель gpt-4 наиболее подходит для более сложных задач и живого общения в чате, а модель code-davinci-002 подходит для написания кода.
Основные параметры запроса и их описание можно найти тут вместе с их описанием, ниже будет представлена наша реализация функции генерации текста:
public function generateText( Request $input ) {
if ( $input->title == null ) { //Проверка передана ли в запросе тема для генерации контента
return response()->json( [ 'status' => 'error', 'message' => 'Not isset text or type' ] );
}
if ( empty( $input->header()['request-domain'] ) || empty( $input->header()['request-domain'][0] ) ) { //Проверка передан ли домен в запросе
return response()->json( [ 'status' => 'error', 'message' => 'Not isset domain' ] );
}
$domain = $input->header()['request-domain'][0];
$domain = $this->get_domain( $domain ); //Ищем домен в списке добавленных доменов данного пользователя
if ( ! $domain ) { // Если домена нет возвращаем ошибку
return response()->json( [ 'status' => 'error', 'message' => 'This domain not exist' ] );
}
$title = $input->title;
$suggestions = ! empty( $input->n ) ? intval( $input->n ) : 1; //Количество вариантов сгенерированного текста выбранное во всплывающем окне
$words = ! empty( $input->words ) ? intval( $input->words ) : 100; //Количество слов сгенерированного текста выбранное во всплывающем окне
$tokens = round( $words * 1.25 ); //Преобразуем количество слов в примерное количество токенов для генерации (используется формула с сайта OpenAI)
$text_length = $words * $suggestions; //Получаем полное количество слов использованных в запросе для регистрации в статистике и списания баланса
if ( Auth::user()->words_balance < $text_length ) { //Если на счету баланса недостаточно
if ( Auth::user()->auto_renew ) { //Проверяем включена ли опция автоматического продления
if ( empty( Auth::user()->preAuthorizationId ) ) { //Проверяем привязана ли карта в аккаунте клиента
return response()->json(
[
'status' => 'error',
'message' => 'You must add payment method for refill the balance',
]
);
}
$charge = BillsController::charge( false ); //Делаем попытке пополнения баланса
if ( ! $charge ) { //Если попытка не удалась делаем запись в базу и возвращаем ошибку
requests::create(
[
'user_id' => Auth::id(),
'words_count' => $text_length,
'type' => $input->type,
'status' => requests::STATUS_FAILED,
'domain_id' => $domain->id,
]
);
return response()->json( [ 'status' => 'error', 'message' => 'Not enough balance for this text' ] );
}
} else { //Если нет авто-пополнения, то возвращаем ошибку баланса
requests::create(
[
'user_id' => Auth::id(),
'words_count' => $text_length,
'type' => $input->type,
'status' => requests::STATUS_FAILED,
'domain_id' => $domain->id,
]
);
return response()->json( [ 'status' => 'error', 'message' => 'Not enough balance for this text' ] );
}
}
try {
$client = OpenAI::client( env( 'OPENAI_API_KEY' ) ); //Получаем ключ записанный в .env файле и создаем клиент
$lang = requests::$languages[ $input->lang ]['name']; //Получаем название языка
$local = requests::$languages[ $input->lang ]['local']; //Получаем код языка
$prompt = requests::$prompts[ $input->type ]['prompt']; //Получаем шаблон для генерации текста
$prompt = str_replace( '{lang}', $lang, $prompt ); //Подставляем название языка в шаблоне
$prompt = str_replace( '{theme}', $title, $prompt ); //Подставляем тему в шаблоне
$prompt = str_replace( '{local}', $local, $prompt ); //Подставляем код языка в шаблоне
$result = $client->completions()->create(
[ // Делаем запрос на генерацию текста
'model' => 'text-davinci-003', // Модель
'temperature' => 0.2, //Данный параметр отвечает за случайность текста
'n' => $suggestions, //Количество вариантов
'max_tokens' => $tokens, //Количество используемых токенов (символов)
'prompt' => $prompt, //Шаблон
]
);
} catch ( \Exception $ex ) {
return response()->json( [ 'status' => 'error', 'message' => $ex->getMessage() ] );
}
if ( empty( $result['choices'] ) ) {
return response()->json( [ 'status' => 'error', 'message' => 'Choices is empty' ] );
}
$text_length = $this->getTextLength( $result['choices'] ); // Считаем количество сгенерированных символов
requests::create(
[ //Записываем запрос в статистику
'user_id' => Auth::id(),
'words_count' => $text_length,
'type' => $input->type,
'status' => requests::STATUS_SUCCESS,
'domain_id' => $domain->id,
]
);
Auth::user()->set_balance( Auth::user()->words_balance - $text_length ); //Списываем с баланса пользователя символы
return response()->json(
[ //Возвращаем сгенерированный текст
'status' => 'success',
'body' => array(
'texts' => $result['choices'],
),
]
);
}
Заключение
Переда тем как начать работать с OpenAI API мне казалось, что интеграция с таким новым и довольно сложным сервисом будет непростым занятием, но в итоге в процессе разработки пришло понимание что это чуть ли не самая простая часть разработки данного сервиса. Наличие SDK клиента очень сильно упрощает процесс разработки а интуитивно понятная структура клиента и большое количество документации с примерами превращают процесс разработки решения с использованием OpenAI в легкую прогулку для разработчика даже начального уровня.
Комментирование этой и других статей доступно в нашем Телеграм канале