Обновляем краткое содержания статей в блоге с помощью ИИ, денег, bash и wp-cli

Всем привет! Если вы не первый раз на моём сайте, то могли заметить, что краткое содержание заметок содержит первые n слов (вроде, 22) из заметки безо всякого осмысления. По крайней мере, так было до недавнего времени.

Поскольку сейчас наступил XXI век, то пора передать формирование краткого содержания заметок на откуп искусственному интеллекту.

Делать плагин мне откровенно не хочется, поэтому я обновил все заметки с помощью bash-скрипта, запрашивая краткое содержание статьи через curl у Yandex GPT через их API.

Алгоритм такой:

  • получаю ID всех статей блога с помощью wp-cli
  • для каждой статьи блога, отправляю её содержимое в Yandex GPT с помощью API
  • получаю краткую выжимку статьи и показываю пользователю
  • если он согласен с содержимым, то обновляю поле post_excerpt у статьи
  • вывожу post_excerpt в мета поле description при формировании статьи в разделе
  • ???
  • PROFIT

Для регистрации и работы с Yandex GPT нужно немного денег, я потратил 50 рублей. Регистрация и получение идентификаторов каталога (folder_id) и API ключа (api_key) найдёте в этой статье на «Хабре»: (Как подключить Yandex GPT к своему проекту на Python)[https://habr.com/ru/articles/780008/].

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

Перед запуском скрипта, не забудьте изменить folder_id, api_key и wordpress_dir на свои.

#!/usr/bin/env bash

# Конфигурация
FOLDER_ID="folder_id"
API_KEY="api_key"
API_URL="https://llm.api.cloud.yandex.net/foundationModels/v1/completion"

wrap_for_yandexgpt() {
  local text="$1"

  jq -n \
    --arg text "$text" \
    --arg folder "$FOLDER_ID" \
    '{
      "modelUri": "gpt://\($folder)/yandexgpt-lite",
      "completionOptions": {
        "stream": false,
        "temperature": 0.3,
        "maxTokens": "2000"
      },
    "messages": [
    {
      "role": "user",
      "text": $text
    }
  ]
}'
}

# Функция для отправки запроса
create_post_excerpt() {
  local content="$1"
  local prompt="Сделай краткое метаописание статьи (до 60 слов):\n\n$content"
  local json_payload
  json_payload=$(wrap_for_yandexgpt "$prompt")

  # Выполняем запрос и сохраняем ответ в переменную
  local response
  response=$(curl -s -w "\nHTTPSTATUS:%{http_code}" -X POST -H "Content-Type: application/json" -H "Authorization: Api-Key $API_KEY" -H "x-folder-id: $FOLDER_ID" -d "$json_payload" $API_URL)

  # Проверяем, что curl выполнился успешно
  if [ $? -ne 0 ]; then
    echo "Ошибка выполнения curl" >&2
    return 1
  fi

  # Извлекаем HTTP код и тело ответа
  local http_code
  local body
  http_code=$(echo "$response" | sed -n 's/HTTPSTATUS://p')
  body=$(echo "$response" | sed '$d')

  # Проверяем HTTP-код
  if [ "$http_code" -ne 200 ]; then
    echo "ошибка HTTP: $http_code" >&2
    return 1
  fi

  # Проверяем наличие ошибки в теле ответа
  if echo "$body" | jq -e '.error' >/dev/null 2>&1; then
    local error_msg
    error_msg=$(echo "$body" | jq -r '.error.message // .error')
    echo "ошибка API: $error_msg" >&2
    return 1
  fi  

  # Извлекаем текст
  local result
  result=$(echo "$body" | jq -r '.result.alternatives[0].message.text // empty')

  if [ -z "$result" ]; then
    echo "пустой ответ от API" >&2
    return 1
  fi

  local status
  status=$(echo "$body" | jq -r '.result.alternatives[0].status // empty')

  if [ "$status" == "ALTERNATIVE_STATUS_CONTENT_FILTER" ]; then
    echo "[cencored]"
    return 1
  fi

  echo "$result"
  return 0
}

update_post_excerpt() {
  local post_id=$1
  local excerpt="$2"
  wp --allow-root post update "$post_id" --post_excerpt="$excerpt"
}

get_post_excerpt() {
  local post_id=$1
  wp --allow-root post get "$post_id" --field=post_excerpt
}

get_post_title() {
  local posts_data="$1"
  local post_id="$2"

  echo "$posts_data" | jq ".[] | select(.ID==$post_id) | .post_title"
}

get_post_content() {
  local posts_data="$1"
  local post_id="$2"

  echo "$posts_data" | jq ".[] | select(.ID==$post_id) | .post_content"
}


cd wordpress_dir || exit

posts_data=$(wp post list --allow-root --post_type=post --fields=ID,post_title,post_content,post_excerpt --format=json --post_status=publish | jq 'map(select(.post_excerpt=="" or .post_excerpt==null))')

post_ids=$(echo "$posts_data" | jq .[].ID)

for post_id in $post_ids; do
  title=$(get_post_title "$posts_data" "$post_id")
  echo "ЗАГОЛОВОК: $title"
  content=$(get_post_content "$posts_data" "$post_id")
  post_excerpt=$(create_post_excerpt "$content")
  exit_code=$?

  if [ $exit_code -ne 0 ]; then
    echo "ОШИБКА ПОЛУЧЕНИЯ ЦИТАТЫ: $post_excerpt"
    echo
    continue
  fi

  echo "ЦИТАТА: \"$post_excerpt\""

  while true; do
    read -r -n1 -p "Обновить цитату? (y - да, n - пропустить, q - выход) " choice
    choice=$(echo "$choice" | tr '[:upper:]' '[:lower:]')
    echo

    case $choice in
      y)
        update_post_excerpt "$post_id" "$post_excerpt"
        break
        ;;
      n)
        break
        ;;
      q)
        exit 0
        ;;
      *)
        continue
        ;;
    esac
  done

  echo
done

cd - || exit

Всё, что делает этот скрипт он делает только под вашу личную ответственность. Я никаких гарантий не даю и ответственности за результат не несу. Примерно половину скрипта написал DeepSeek, претензии к нему.

Комментариев нет. Будьте первым!
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Блог Евгения Жирнова