Amazon BedrockでWordPress記事のアイキャッチ画像を自動生成する

本記事は 夏休みクラウド自由研究2025 8/31付の記事です

こんにちは、SCSKの木澤です。

夏休みクラウド自由研究2025も最後の記事ですね。
アクセス頂いた皆様、記事投稿にご協力いただいた皆様、ありがとうございました。

今年の投稿を総括すると、特にAWSエンジニアからAmazon Q Developer CLIやKiroを用いてVibe Codingにチャレンジしてみた系の記事発信が多かったですね。

Kiroは仕様(Spec)駆動開発と、弊社のようなSIerのシステム開発手法に馴染みある開発手法に近いので私も期待しています。
自社のシステム開発(アプリケーション開発)に直接適用するには、まだハードルはあると感じますが、インフラ系の部署に所属する属するメンバーが多いTechHarmonyへの寄稿者(登録者)からの発信が多いことを踏まえると、今後の変革に期待できるなと思いました。

さて、今日は本サイトのようなWordPressサイトの管理業務を少しだけ改善する話をしたいと思います。

はじめに

本サイトはCMSとしてWordPressを利用しているのですが、WordPressでの記事の寄稿においては記事のタイトルと本文以外にも設定が必要な項目が多数あります。主なものとしては

  • カテゴリ設定
  • タグの付与
  • メタディスクリプション(記事概要文)の入力
  • アイキャッチ画像の設定

特にアイキャッチ画像については作成が多少面倒なところもあり、寄稿者からの設定が漏れていることが多いかと思います。
本サイトでも一旦差し戻して設定をお願いしていますが、アイキャッチ画像は記事の本質的な部分でも無いので

寄稿者の手間を煩わすのもなぁ…

というのが、当サイトのレビュー担当者としての感覚でした。
そこで今回は生成AIを利用してアイキャッチ画像の自動生成にチャレンジしてみたいと思います。

 

アーキテクチャ

WordPress投稿時に自動的にCloudFrontのキャッシュクリアを行う方法について投稿について以前に記事化していますが、基本の仕組みとしては同じです。

今回はアイキャッチ画像が設定されていない際に、以下の処理を行い要件を満たすことにします。

  1. 記事本文から要約文を作成
  2. 要約文からアイキャッチ画像を作成
  3. 作成された画像をWordPressサイトのメディアライブラリにアップロード
  4. 当該記事にアイキャッチ画像を付与

なお今回、Amazon Bedrock上のLLMモデルにはAmazon Novaを利用しました

  • 要約:Amazon Nova Pro
  • 画像生成:Amazon Nova Canvas

要約する際に渡すプロンプトは以下のようにしました。

以下のブログ記事を要約して、200文字程度の紹介文形式にしてください
(本文)

画像生成の際のプロンプトは以下のようにしました。

あなたは優秀なWebデザイナーです。
技術ブログの記事「(タイトル)」のアイキャッチ画像を作成してください。

記事の概要文は以下の通りです。
(概要)
今回、時間の関係でプロンプトをチューニングする時間が無かったので、精度は追って高めたいと思います。

 

設定方法

今回の手順の概要は以下の通りです。

  1. WordPressサイトのアプリケーションパスワードを取得
  2. Amazon Bedrock上でモデルアクセスを有効化
  3. IAMロールを作成
  4. Systems Managerパラメーターストアにプロンプトを保存
  5. RequestsとBeattifulSoupを含んだLambdaレイヤーを作成
  6. Lambda関数を作成し、レイヤーを割り当て、環境変数を設定
  7. API Gatewayを設定しLambdaと紐付け
  8. WP Webhooksから呼び出す設定

WordPressアプリケーションパスワード取得

メディアライブラリへのアップロード及び記事の更新にWordPressのREST APIを利用します。
ユーザプロフィール内のアプリケーションパスワードでパスワードを発行しておきます。

Amazon Bedrockのモデルアクセス有効化

前述の通り、今回は Nova ProとNova Canvasを用います。
東京リージョンのNova Proはオンデマンド呼び出しに対応していないようなので、今回は別々のリージョンにしました。

  • Nova Pro : 北バージニアリージョン(us-east-1)
  • Nova Canvas:東京リージョン(ap-northeast-1)

 

IAMロール作成

最小権限の法則に則りIAMロールを作成します。
今回の仕組みの場合、IAMロールに与えるポリシーは以下となります。

  • AWS Systems Manager(Parameter Store)からの値の取得
    • ssm:GetParameterHistory
    • ssm:GetParametersByPath
    • ssm:GetParameters
    • ssm:GetParameter
  • Amazon Bedrockでの推論実行
    • bedrock:InvokeModel
  • CloudWatch Logsへのログ送信
    • logs:CreateLogGroup
    • logs:CreateLogStream
    • logs:PutLogEvent

Systems Managerパラメーターストアにプロンプトを保存

今回は2回LLMを呼び出しますので、2つのストアを作成します。

要約用プロンプト

新規にtextデータ形式のパラメータストアを作成し、以下の文書を入力します。

以下のブログ記事を要約して、200文字程度の紹介文形式にしてください
(本文)
画像生成用プロンプト

同様に以下の文書を入力します。

あなたは優秀なWebデザイナーです。
技術ブログの記事「(タイトル)」のアイキャッチ画像を作成してください。

記事の概要文は以下の通りです。
(概要)

Lambdaレイヤーを作成

後述のLambda関数では、Pythonプログラムの外部ライブラリとしてRequestsとBeattifulSoupを利用していますので、Lambdaレイヤーを作成します。

CloudShellにログインし、以下のようにコマンドを実行します。

$ mkdir ~/python
$ cd python
$ pip install beautifulsoup4 requests -t .
$ cd
$ zip -r lambda-layer.zip ./python

CloudShellのアクションボタンからファイルのダウンロードができますので
~/lambda-layer.zip と指定しダウンロードします。

続いてLambdaのAWSコンソールよりレイヤーを作成します。
互換設定はもう少しシビアに見た方が良いと思いますが。

Lambda関数の作成

いよいよLambda関数を作成します。

Python 3.13で新規関数を作成し、上述で設定したIAMロールを割り当てます。
ソースコードは以下のようにしました。

import os
import boto3
import json
import base64
import random
import requests
from datetime import datetime
from bs4 import BeautifulSoup

# boto3の初期設定
bedrock = boto3.client('bedrock-runtime', 'us-east-1')
ssm = boto3.client('ssm', 'ap-northeast-1')

def lambda_handler(event, context):
    # メタディスクリプションが未設定の場合に実行
    if event['post_thumbnail'] == 0:
        result = create_eyecatch(event)
    else:
        result = {"message":"Eyecatch image was exists."}

    print(result)

    return {
        'statusCode': 200,
        'body': result
    }

def create_eyecatch(event):
    # ブログ本文から文字列抽出
    soup = BeautifulSoup(event['post']['post_content'], 'html.parser')
    title = event['post']['post_title']
    texts = soup.get_text()

    # WordPressの記事IDを取得
    postid = event['post_id']

    # Bedrockで要約を実施
    summary = get_summary(texts)

    # 画像を生成
    image = create_image(title,summary)

    # WordPressのメディアライブラリにアップロード
    imageid = upload_media(postid, image)

    # 記事を更新
    result = update_post(postid, imageid)

    return imageid

def get_summary(texts):
    # AWS Systems Managerパラメータストアの読み込み
    pstore = os.environ['PARAMETER_STORE1']
    prompt = ssm.get_parameters(Names=[pstore])['Parameters'][0]['Value'].replace('(本文)',texts)

    # Amazon Bedrockで要約する
    modelId = 'amazon.nova-pro-v1:0'

    inferenceConfig = {
        "temperature": 0.1,
        "topP": 0.9,
        "maxTokens": 500,
        "stopSequences":[]
    }

    message = [
        {
            "role": "user",
            "content": [{"text":prompt}]
        }
    ]

    response = bedrock.converse(
        modelId = modelId,
        messages = message,
        inferenceConfig = inferenceConfig
    )

    # 要約を取得
    summary = response['output']['message']['content'][0]['text']
    return summary

def create_image(title,summary):
    # AWS Systems Managerパラメータストアの読み込み
    pstore = os.environ['PARAMETER_STORE2']
    prompt = ssm.get_parameters(Names=[pstore])['Parameters'][0]['Value']
    prompt = prompt.replace('(タイトル)',title).replace('(概要)',summary)

    # Amazon Bedrockでイメージ生成を行う
    modelId = 'amazon.nova-canvas-v1:0'
    seed = random.randint(0, 858993460)

    request_body = {
        "taskType": "TEXT_IMAGE",
        "textToImageParams": {
            "text": prompt
        },
        "imageGenerationConfig": {
            "numberOfImages": 1,
            "width": 1280,
            "height": 640,
            "cfgScale": 6.5,
            "seed": seed,
            "quality": "standard"
        }
    }

    response = bedrock.invoke_model(
        body=json.dumps(request_body),
        contentType='application/json',
        accept='application/json',
        modelId=modelId
    )

    response_body = json.loads(response['body'].read())
    image_data = base64.b64decode(response_body['images'][0])
    return image_data

def upload_media(postid, image):
    # WordPressのメディアライブラリにアップロード
    # 環境変数の読み込み
    url = os.environ['WP_URL'] + "wp-json/wp/v2/media"
    username = os.environ['WP_USER']
    password = os.environ['WP_PASSWORD']
    filename = str(postid) + ".png"

    headers = {
        'Content-Type': 'image/png',
        'Content-Disposition': 'attachment; filename=' + filename
        }

    # メディアのアップロード
    result = requests.post(url, headers=headers, data=image, auth=(username, password))

    # メディアのIDを取得
    res_dict = result.json()
    imageid = res_dict['id']
    return imageid

def update_post(postid,imageid):
    # WordPressに書き込み
    # 環境変数の読み込み
    url = os.environ['WP_URL'] + "wp-json/wp/v2/posts/" + str(postid)
    username = os.environ['WP_USER']
    password = os.environ['WP_PASSWORD']

    headers = {'Content-Type': 'application/json'}

    payload = {
        'featured_media': imageid
    }

    result = requests.post(url, headers=headers, json=payload, auth=(username, password))
    return result

Novaの呼び出し方法には本サイトの以下記事を参考にしました。

Amazon Nova を触ってみた (テキスト生成編)
昨年のre:Invent 2024にて、Amazon Bedrockでのみ利用可能なモデルとしてAmazon Novaが発表されました。本記事では、Amazon Novaシリーズの中でテキスト生成が可能な3つのモデル(Micro、Lite、Pro)についてまとめました。Amazon BedrockマネジメントコンソールとConverse APIから実行する方法をご紹介します。
Amazon Bedrock を利用して、画像生成アプリケーションを開発してみた !
生成系 AI アプリケーションの開発を支援する Amazon Bedrock の初学者や興味がある方を対象に、Amazon Bedrock を用いた画像生成アプリケーション開発の流れを解説します。

なお、本Lambda関数の実行時間は10秒を超えることがあるので、タイムアウトは20秒以上に設定することを推奨します。

続いて環境変数に必要なパラメータを指定します。

  • PARAMETER_STORE1:要約用プロンプト
  • PARAMETER_STORE2:画像生成用プロンプト
  • WP_URL:WordPressサイトのURL(トップ)
  • WP_USER:WordPressユーザーID
  • WP_PASSWORD:WordPressアプリケーションパスワード

API Gatewayを設定しLambdaと紐付け

本手順は過去の記事と同じですが、画面イメージが変更されているので改めて記載します。

WordPress記事更新時にCloudFrontのキャッシュを自動的にクリアする方法
WordPress記事の投稿・更新・削除、画像の差し替え時にCloudFrontのキャッシュをクリアする仕組みを開発しましたのでご紹介します。 これにより直ぐに更新を反映することができます。またキャッシュ時間を延ばしヒット率を上げることも期待できます。

APIの作成から名前を指定しREST APIを作成します。

メソッドの作成からPOSTを選択し、Lambda関数を指定します。
メソッドリクエストの設定から、APIキーを有効にしてください。


任意のステージでデプロイ後、左側のメニュー「APIキー」からAPIキーを作成します。

使用量プランを作成し、APIキーと紐付けます。

WP Webhooksからの呼び出し設定

最後にWordPressのプラグイン、WP Webhooksからの呼び出し設定を行います。

プラグインの設定画面を開き「Send Data」⇒「Post Updated」に、API GatewayのURLを追加します。

続いて「Authentication」⇒「Create Template」からAPIキーの箱を作成します。

API Gatewayで発行されたAPIキーを入力します。

最後に、先程作成したAPI設定を開き「Add authentication template」から作成したAPIキーを割り当てます。

 

結果・今後に向けて

以上の設定で、記事更新時にアイキャッチ画像が付与されていない際に、Amazon Nova自動設定されるようになりました。

試しに本記事の内容を掛けてみたところ、こんな画像が生成されました。
WordPressっぽい画像を生成しようとしてますね・・・w

但し、技術ブログのアイキャッチ画像としては品質的にまだまだ厳しい感じで。

プロンプトエンジニアリングの話もありますが、Amazon Nova Canvasではファインチューニングもできるようになっているので、オリジナルの画像生成にチャレンジしてみたいと思います。
コストも気になりますが・・・

Amazon Nova Canvas がファインチューニングのサポートを開始 - AWS
AWS の新機能についてさらに詳しく知るには、 Amazon Nova Canvas がファインチューニングのサポートを開始
著者について
木澤 朋隆

SCSKにてクラウドのアーキテクトやマーケティング/プロモーション、社内の開発者支援等を担当しています。

資格
 AWS認定15冠
 情報処理安全確保支援士・テクニカルスペシャリスト(NW)等
表彰
 AWS Ambassador (2021~)
 Japan AWS Top Engineer (2022~)
 Japan AWS All Certifications Engineer (2022~)
 AWS Community Builder (2023~)

木澤 朋隆をフォローする

クラウドに強いによるエンジニアブログです。

SCSKクラウドサービス(AWS)は、企業価値の向上につながるAWS 導入を全面支援するオールインワンサービスです。AWS最上位パートナーとして、多種多様な業界のシステム構築実績を持つSCSKが、お客様のDX推進を強力にサポートします。

AI・MLAWS
シェアする
タイトルとURLをコピーしました