WordPress 7.0より以前のバージョンでは、AI機能を提供するプラグインがAIサービス(以下AIプロバイダー)と連携するために、それぞれAPIキーの取得や管理を行う必要があります。

しかしこの方法には、コード量の増加や保守性の低下といった課題がありました。プラグインごとに容量の大きな専用SDKライブラリを組み込む必要があるうえ、AIプロバイダーごとに異なるcURLリクエストを実装しなければなりません。

WordPress 7.0では、標準のAI連携機能が導入されたことでこの課題が解消されています。現在では、複数のAIプロバイダーの認証情報を一元管理できるインターフェースと、新しいPHP APIが標準で提供されています。この新たなアーキテクチャでは、コマンドや指示、メディアファイル、データ構造を統一された方法で送信でき、実際のリクエスト処理はWordPressコアが担ってくれます。

このアーキテクチャには、サイト管理者とプラグイン開発者の双方に大きなメリットがあります。

サイト管理者は、プラグインごとにAIの設定を行う必要がありません。WordPress 7.0では、各AIプロバイダー用のコネクタープラグインをインストール・有効化し、共通の「コネクタ」画面でAPIキーを設定するだけで、その情報をサイト内のすべてのAI対応プラグインで利用できます。また、AIの利用状況を確認したり、利用するモデルを切り替えたり、特定のAIプロバイダーへのアクセスをワンクリックで無効化したりすることも可能です。

またプラグイン開発者にとってもメリットは大きく、データセキュリティやコードの移植性の向上に加え、技術的負債を大幅に削減できます。

この記事では、WordPressの新たなAIアーキテクチャについて詳しく解説していきます。

WordPressのAIアーキテクチャを構成する3つの層

現在のWordPressサイトには、AIを活用するための基盤が標準で備わっています。ただし、AI機能をそのまま利用できるという意味ではなく、AIを活用したウェブサイトを構築するための土台が用意されているということです。

新しいアーキテクチャは、以下の3つのレイヤーで構成されています。

AIコネクタ

WordPress 7.0より前は、サイトにAI機能を追加するプラグインごとに、APIキーを保存・管理するための独自の仕組みが必要でした。認証情報をプラグイン内に直接記述するか、専用の設定画面で管理しなければならず、どちらも機密情報を扱う方法として理想的ではありません。

WordPress管理画面の「コネクタ」ページ
WordPress管理画面の「コネクタ」ページ

統一されたインターフェースにより、利用するすべてのAIプロバイダーを一箇所で設定・管理できます。この仕組みによって、サイト運用は大きく効率化されます。

管理やセキュリティの面で特に大きなメリットは、一度「コネクタ」画面で設定してしまえば、その後はAIプロバイダーを利用するプラグインがいくつ増えても、個別に設定し直す必要がないことです。APIキーを変更する場合も、各プラグインをそれぞれ設定し直す必要はなく、「コネクタ」画面で一括更新できます。

認証情報を一元管理することで、コーディング品質や信頼性に問題のあるサードパーティ製プラグインを経由して機密情報が漏洩するリスクも軽減できます。

もうひとつの大きなメリットは、コードの移植性が向上することです。新しいアーキテクチャでは、認証処理とプラグインのロジックが分離されているため、プラグイン側で利用するAIプロバイダーを意識する必要がありません。そのため、利用するAIプロバイダーを変更しても、プラグインのコードを書き換えることなく、そのまま動作し続けます。

さらに、サイト管理者は複数のコネクターを同時に設定できます。プラグインからリクエストが送信されると、WordPressはプラグインの設定内容と利用可能なモデルの機能をもとに、最適なAIプロバイダーとモデルを自動的に選択して処理を実行します。

AIクライアント

AIプロバイダーの設定画面を提供するのが「コネクタ」であるのに対し、AIクライアントは、開発者がAIモデルとやり取りするための実行インターフェースです。

これは、グローバル関数wp_ai_client_prompt()と統一されたPHPメソッド群を通じて、AIモデルとのやり取りを標準化するWordPress標準のソフトウェアインターフェースです。

技術的負債の軽減

開発者にとって、新しいAIアーキテクチャの大きなメリットのひとつは、技術的負債や依存関係を減らせることです。WordPress 7.0より前は、AI機能を追加するにはコードの保守に多くの手間がかかりました。OpenAIAnthropicなどが提供する専用SDKをプラグインに組み込み、ライブラリのバージョン管理も継続的に行う必要があったためです。その結果、プラグインのコードベースは肥大化し、互換性の問題も発生しやすくなっていました。

さらに、ネットワーク通信やレスポンスの解析処理も、自分で数多くのコードを書いて実装しなければなりませんでした。しかも、その実装はAIプロバイダーごとに異なるため、別のAIプロバイダーへ切り替える場合は、通信処理の大部分を書き直す必要がありました。

AIクライアントは、こうした課題をWordPressコアに処理を委ねることで根本から解決します。プラグインのコードは特定のAIプロバイダーに依存しなくなり、開発者は指示を一度記述するだけで済みます。あとは、WordPressが利用するAIモデルに合わせて適切な形式へ変換してくれます。

インテリジェントなモデル選択

AIクライアントのFluent Interfaceには、利用するモデルを柔軟に指定できる機能があります。wp_ai_client_prompt()ビルダー関数から利用できるusing_model_preference()メソッドを使うことで、プラグインの用途に応じて優先的に使用したいモデルを順番に指定できます。

例えば、以下のコードでは、テキスト生成のために優先順位を付けた3つのモデルを指定しています。

$text_result = wp_ai_client_prompt( 'Convert the following transcript into a concise, blog-ready draft.' )
	->using_model_preference(
		'gemini-2.5-flash',
		'claude-3-5-sonnet',
		'gpt-4o'
	)
	->generate_text();

WordPressは、「コネクタ」画面で設定されているAIプロバイダーを確認し、プラグインで指定されたモデルの一覧と照合します。そして、サイトで利用可能なAIプロバイダーの中から、条件に一致する最初のモデルを自動的に選択します。

プラグインで指定したモデルが有効なAIプロバイダーに存在しない場合は、モデルの優先順位を無視し、サイトで利用可能な互換性のある最初のモデルが自動的に使用されます。

この仕組みにより、サイト管理者が利用するAIプロバイダーを変更しても、プラグインはそのまま動作し続けます。

AIプロバイダー

AIプロバイダーとは、私たちが日常的に利用している大規模言語モデル(LLM)を開発・学習・提供するサービスです。WordPress 7.0では、OpenAI、Google Gemini、Anthropicなどの主要なAIプロバイダーが専用のコネクターを通じてサポートされています。また、開発者が独自のAIプロバイダーを追加することも可能です。

プラグインからgenerate_text()メソッド(またはその他の生成メソッド)が呼び出されると、WordPressはコネクタで設定されたエンドポイントを通じて、AIプロバイダーへデータを送信します。バックグラウンドでは、次のような処理が実行されます。

  1. AIプロバイダーが、入力されたプロンプトと添付されたメディアファイルを受け取る
  2. システム指示を適用しながら、リクエストを処理
  3. 処理結果を、テキストまたはJSON形式で生成
  4. HTTPレスポンスとしてWordPressへ返す

AIプロバイダーごとに、性能や処理速度、料金体系の異なるさまざまなモデルが提供されています。テキスト処理に特化したモデルだけでなく、テキストに加えて音声・画像・動画などのメディアを入力として扱えるマルチモーダルモデルや、画像生成、音声の文字起こしといった特定の用途に特化したモデルも利用できます。

GoogleのGemini 3シリーズ
GoogleのGemini 3シリーズ

先にも触れましたが、WordPressサイトで利用できるモデルは、「コネクタ」画面で設定されているAIプロバイダーによって決まります。そのため、プラグイン側で特定のAIプロバイダーを指定する必要はありません。

wp_ai_client_prompt()関数を詳しく見る

wp_ai_client_prompt()関数を使うことで、プラグインからAIモデルと統一された方法でやり取りできるようになります。外部ライブラリを組み込んだり、AIプロバイダーごとの設定を実装したり、HTTPリクエストを個別に記述したりする必要はありません。

この関数は、開発者が一度コードを書くだけで済むように設計されたソフトウェアインターフェースです。複雑な処理はWordPressが担い、開発者が記述した指示を各AIモデルに適した形式へ自動的に変換します。

この関数は、WP_AI_Client_Prompt_Builderクラスのインスタンスを返します。このクラスは、メソッドチェーンを利用してプロンプトを柔軟に組み立てられるFluent Interfaceを提供します。

関数の定義は以下のとおりです。

/**
 * Fluent builder for constructing AI prompts, returning WP_Error on failure.
 *
 * This class provides a fluent interface for building prompts with various
 * content types and model configurations. It wraps the PHP AI Client SDK's
 * PromptBuilder and adds WordPress-specific behavior including WP_Error
 * handling instead of exceptions, snake_case method naming, and integration
 * with the Abilities API.
 *
 * Only the generating methods will return a WP_Error, to not break the fluent
 * interface. As soon as any exception is caught in a chain of method calls,
 * the returned instance will be in an error state, and all subsequent method
 * calls will be no-ops that just return the same error state instance. Only
 * when a generating method is called, the WP_Error will be returned.
 */

wp_ai_client_prompt()は、呼び出した時点ではHTTPリクエストを送信しません。代わりに、WP_AI_Client_Prompt_Builderオブジェクトを生成して返します。50種類以上の設定メソッドを使って、このオブジェクトの内部プロパティをFluent Interfaceで構成できるため、可読性が高く、記述しやすいコードを書くことができます。

以下は、頻繁に使用されるメソッドの一部です。

  • with_text():現在のメッセージにテキストを追加
  • with_file():現在のメッセージにファイルを追加
  • with_history():会話履歴を追加
  • using_model_preference():優先的に使用するモデルを順番に指定
  • using_system_instruction():システム指示を設定
  • using_max_tokens():生成する最大トークン数を設定
  • using_temperature():生成時のTemperatureを設定
  • using_top_p():生成時のTop-pを設定
  • using_top_k():生成時のTop-kを設定
  • as_output_file_type():出力ファイル形式を設定
  • as_json_response():レスポンスをJSON形式で出力
  • is_supported_for_text_generation():現在の設定でテキスト生成に対応しているかを確認
  • is_supported_for_image_generation():現在の設定で画像生成に対応しているかを確認
  • is_supported_for_text_to_speech_conversion():現在の設定でテキスト読み上げ(Text-to-Speech)に対応しているかを確認
  • is_supported_for_video_generation():現在の設定で動画生成に対応しているかを確認
  • generate_text():プロンプトに基づいてテキストを生成
  • generate_image():プロンプトに基づいて画像を生成
  • convert_text_to_speech():テキストを音声に変換
  • generate_speech():プロンプトに基づいて音声を生成
  • generate_video():プロンプトに基づいて動画を生成

すべてのメソッドについては、WordPressコアのソースコードをご覧ください。

例えば、音声ファイルの内容を正確に文字起こししたい場合は、以下のように記述できます。

$transcription_builder = wp_ai_client_prompt( $prompt )
	->with_file( $audio_base64, $mime_type )
	->using_temperature( 0.1 );

if ( ! $transcription_builder->is_supported_for_text_generation() ) {
	// No configured provider supports the current prompt configuration.
	return new WP_Error(
		'no_supported_model',
		'No configured AI model can process this request. Configure a provider in Settings > Connectors.',
		array( 'status' => 400 )
	);
}

$transcript = $transcription_builder->generate_text();

このコードの役割は以下のとおりです。

  • $prompt:主な指示を含む文字列
  • with_file():インターフェースのマルチモーダル機能を有効化。上記の例では、Base64エンコードした音声データと対応するMIMEタイプを指定
  • using_temperature():レスポンスの決定性を制御。低い値(0.1)では音声に忠実な結果を生成し、高い値ではより自由度の高い生成が可能。using_top_p()(Top-Probability)と組み合わせることで、出力をさらに細かく制御可能
  • is_supported_for_text_generation():ネットワークリクエストを送信する前に、有効なコネクターが現在のプロンプトの要件をサポートしているかを確認
  • generate_text():Fluent Interfaceの終端メソッド。現在有効なAIプロバイダーへ統一されたHTTPリクエストを送信し、成功時は生成されたテキスト、失敗時はWP_Errorオブジェクトを返す

要するに、このコードではAIプロバイダーにテキストプロンプトと音声ファイルを渡し、音声をできるだけ忠実に文字起こしできるようTemperatureを低く設定しています。その後、有効な設定でテキスト生成に対応していることを確認し、最後にAIの応答をもとに音声を文字起こしします。

別のプロンプトを使用すれば、生成された文字起こし結果をAIへ渡し、JSONオブジェクト形式のレスポンスを生成することもできます。

$structured_json = wp_ai_client_prompt(
	"Convert the following transcript into a concise, blog-ready draft in American English.n" .
	"Return JSON with a post title and body sections.n" .
	"Each section must have: heading, level (2 or 3), and paragraphs (array).n" .
	"If useful, include bullet_points (array) for short actionable lists.n" .
	"Use short, clear headings and readable paragraph text.nn" .
	"Transcript:n" . $transcript
)
	->using_system_instruction( "You are a professional WordPress content editor. Always write in American English (use appropriate spelling, grammar, and conventions for that language variant)." )
	->using_temperature( 0.4 )
	->as_json_response( $schema )
	->generate_text();

この2つ目のプロンプトでは、APIのその他の機能を活用するために、以下のメソッドを使用しています。

  • using_system_instruction():モデルの動作コンテキストと役割を定義し、「WordPressコンテンツの専門編集者」というペルソナをデータペイロードから明確に分離
  • using_temperature( 0.4 ):前の文字起こし処理よりも高いTemperatureを設定し、より豊かな語彙と自然な文章表現を可能に
  • as_json_response()$schema変数で定義した構造に一致するJSON文字列を返すようモデルに指定。Gutenberg向けのコンテンツを準備する際に便利

JSON形式で出力すれば、個々のフィールドを抽出し、WordPressコア関数を使ってGutenbergの標準ブロックにプログラムから割り当てることができます。

AIクライアントについてさらに詳しくは、開発者向けノート「Introducing the AI Client in WordPress 7.0」もあわせてご覧ください。

AIで音声ファイルをGutenberg用コンテンツに変換する

WordPressのAIアーキテクチャを構成する3つのレイヤーについて理解できたところで、実際のユースケースを見ながら、それらがどのように連携して動作するのかを確認してみましょう。このセクションでは、音声ファイルから完成度の高い投稿下書きを生成するAIプラグインの中核となるロジックを実装する方法をご紹介します。

この機能には、さまざまな活用方法があります。例えば、お気に入りのメッセージアプリで送信した短い音声メモから詳細なブログ記事を作成したり、スマートフォンで録音したインタビューを文字起こしして記事化したりできます。

この機能をWordPressサイトに実装するには、カスタムプラグインを作成する必要があります。本記事では、プラグインのひな形や登録処理については扱いません。プラグイン開発の基礎については、Kinstaブログの関連記事またはWordPress.org公式ドキュメントをご参照ください。

ここでは、プラグインがAIプロバイダーとやり取りするための中核となるロジックに焦点を当てます。なお、完成したプラグインはGitHubリポジトリで公開しており、自由に閲覧・ダウンロードできます。

このプラグインでできること

まずはGitHubのプロジェクトページからプラグインのZIPファイルをダウンロードします。次に、WordPress 7.0以降の管理画面にログインし、「プラグイン」>「プラグインを追加」>「プラグインのアップロード」からインストールしてください。インストールして有効化すれば、準備完了です。

AI Content Builderプラグインを有効化すると、ブロックエディターの右上にあるアイコンからアクセスできる専用の設定サイドバーが追加されます。このサイドバーには、音声ファイルをアップロードするためのインターフェースと、AIが現在の投稿タイトルを上書きできるようにする切り替えスイッチが用意されています。

AI Content Builderプラグインは、ブロックエディターに専用の設定サイドバーを追加
AI Content Builderプラグインは、ブロックエディターに専用の設定サイドバーを追加

Upload audio note(音声メモをアップロード)」をクリックすると、WordPressのメディアアップローダーが開き、新しい音声ファイルをアップロードしたり、メディアライブラリ内の既存の音声ファイルを選択したりできます。

WordPressのメディアライブラリで音声ファイルを選択またはアップロード
WordPressのメディアライブラリで音声ファイルを選択またはアップロード

音声ファイルを読み込んだら、「Generate Content(コンテンツを生成)」をクリックすると、有効になっているAIプロバイダーに対して非同期リクエストが送信されます。

「Generate Content」ボタンが有効になったプラグインのサイドバー
Generate Content」ボタンが有効になったプラグインのサイドバー

以下のスクリーンショットは、AIがわずか数秒で生成した投稿下書きです。

音声ファイルからAIが生成した、整った構成のブログ記事の下書き
音声ファイルからAIが生成した、整った構成のブログ記事の下書き

生成されたコンテンツは、段落や見出し、リストなどのネイティブなGutenbergブロックで構成されます。

WordPressのコードエディターで適切に構造化されたマークアップ
WordPressのコードエディターで適切に構造化されたマークアップ

それでは、プラグインのロジックを見ていきましょう。

このプラグインの開発に必要なもの

WordPressの新しいAIアーキテクチャを利用して投稿コンテンツを生成するプラグインを開発するには、以下が必要です。

  • WordPress 7.0以降
  • 音声をテキストへ変換できるモデルを提供するAIプロバイダーのAPIキー(本記事ではGoogle AIを使用)
  • お好みのIDE(本記事ではVS Codeを使用)
  • WordPressとGutenbergの開発、REST API、PHP、JavaScriptに関する基本的な知識

なお、このプラグインはWordPressコアのネイティブAPIのみを使用しているため、Node.jsやビルドプロセス、外部ライブラリの導入は不要です。その結果、軽量かつ安全で、技術的負債のないプラグインを実現できます。

プラグインの構成とWordPressの最低動作バージョン

AI Content Builderプラグインは、以下の2つのファイルだけで構成されています。

  • ai-content-builder.php
  • editor.js.

また、WordPress 7.0以降が必須となるため、プラグインヘッダーで最低動作バージョンを指定しておきます。

<?php
/**
 * Plugin Name: AI Content Builder
 * Description: Gutenberg sidebar plugin that uploads audio and generates post content using the WordPress 7.0 AI Client.
 * Version: 1.0.0
 * Requires at least: 7.0
 * Requires PHP: 8.0
 * Author: Your Name
 * License: GPL-2.0-or-later
 * Text Domain: ai-content-builder
 */

それでは、WordPress向けAIプラグインの構築手順を見ていきましょう。

プラグインのデータフロー

コードを詳しく見ていく前に、WordPressのバックエンド(ブロックエディター)からPHPサーバーとAIプロバイダーへデータが送られ、再び戻ってくるまでの流れを確認します。

  1. ブラウザからサーバーへ─非同期RESTリクエスト:ブラウザから、プラグインのカスタムRESTエンドポイントに音声ファイルのIDを送信します。処理中にUIが操作不能になるのを防ぐため、PHPサーバーへの非同期リクエストを受け付けるRESTエンドポイントを登録します。
  2. 1回目のAI処理:音声の文字起こし:サーバーがリクエストを受け取り、データベースから音声ファイルを取得します。その後、AIクライアントにリクエストを送り、音声から未加工の文字起こしテキストを抽出します。
  3. 2回目のAI処理:コンテンツの構造化:サーバーから文字起こしテキストを再びAIクライアントに送信し、構造化されたJSONオブジェクトとして整形するよう指示します。
  4. データの整形とブロックの生成:JavaScript(JS)が構造化されたJSONオブジェクトを受け取り、データを正規化して、各フィールドをGutenbergのネイティブブロックに割り当てます。

それでは、各段階の処理を詳しく見ていきます。

1. APIエンドポイントの登録

AI Content Builderプラグインの開発では、まずAPIエンドポイントを登録します。音声ファイルの処理とレスポンスの生成には、AIモデルによって数秒、場合によっては数分かかることがあるためです。RESTエンドポイントを登録することで、サイドバーの操作から非同期リクエストを送信できるようになり、処理中にUIが操作不能になるのを防げます

また、WordPress標準の認証システムを利用できるため、ログイン認証情報を公開することなく、安全にAIへリクエストを送信できます。

エンドポイントの登録と初期化を行うロジックは、プラグインのPHPファイル内にあるinit()register_rest_routes()の2つのクラスメソッドで処理します。

public static function init(): void {

	// Register the custom REST endpoint used by the sidebar JS.
	add_action( 'rest_api_init', array( __CLASS__, 'register_rest_routes' ) );

	...
}

rest_api_initフックに関数を登録することで、プラグインはREST APIリクエストが発生したときにのみエンドポイントを登録します。これにより、不要な処理を避けられるため、ページの読み込み速度への影響を抑えることができます。

ルートは、以下のようにregister_rest_routes()メソッドで設定します。

public static function register_rest_routes(): void {
	register_rest_route(
		self::REST_NAMESPACE,
		self::REST_ROUTE,
		array(
			'methods'             => WP_REST_Server::CREATABLE, // Alias for POST.
			'callback'            => array( __CLASS__, 'handle_generate_request' ),
			'permission_callback' => static function (): bool {
				// Reject unauthenticated or insufficiently privileged requests early.
				return current_user_can( 'edit_posts' );
			},
			'args'                => array(
				'audio_id' => array(
					'type'     => 'integer',
					'required' => true,
				),
			),
		)
	);
}

以下のメソッドでは、次の処理を行います。

  • register_rest_route()関数:ルートの名前空間、ルート本体、設定配列の3つの引数を受け取る
  • WP_REST_Server::CREATABLE:POSTメソッドのエイリアス。WordPressの内部アーキテクチャとの互換性を最大限に確保するために使用
  • permission_callback:自分の投稿を作成・編集する権限を持つ認証済みユーザーのみにアクセスを制限する匿名関数。不正なアクセスの場合、WordPressがリクエストを処理前にブロックし、401 Unauthorizedまたは403 Forbiddenエラーを自動的に返す
  • args:APIレスポンスの調整に使用できる追加パラメータの任意の配列。エンドポイントへのリクエストで必要となるデータを指定可能
  • audio_id:必要なデータ型を指定するパラメータ配列。返されたデータが整数でない、または値が存在しない場合、REST APIが処理を中止してエラーを返す

最後に、エディターのサイドバーからデータの送信先を認識できるようにします。これには、enqueue_editor_assets()メソッド内でwp_localize_script()関数を使用します。

public static function enqueue_editor_assets(): void {
	...

	wp_localize_script(
		'ai-content-builder-editor',
		'AICBData',
		array(
			'restPath' => '/' . self::REST_NAMESPACE . self::REST_ROUTE,
			'nonce'    => wp_create_nonce( 'wp_rest' ),
		)
	);
}
  • wp_localize_script()関数:本来は登録済みのスクリプトをローカライズし、JavaScript変数にデータを渡すための関数。ここでは、グローバルオブジェクトAICBDataを介してPHPからJavaScriptへデータを渡すために使用
  • restPath:エンドポイントのアドレス。ここで指定することで、JavaScriptファイルへのハードコーディングを回避
  • nonce不正アクセスを防ぐためのセキュリティトークンを設定

2. AIクライアントを使用して音声ファイルからテキストを抽出する

プラグインの中核となるのがhandle_generate_request()メソッドです。このメソッドは、一連の処理を実行し、セキュリティチェックから始まり、ブロックエディターへ挿入する投稿コンテンツの生成までを担います。

まず、AIクライアントが利用可能であることと、サイトでAI機能が無効化されていないことを確認します。

public static function handle_generate_request( WP_REST_Request $request ) {
	if ( ! function_exists( 'wp_ai_client_prompt' ) ) {
		return new WP_Error(
			'ai_client_unavailable',
			'WordPress AI Client is not available. This plugin requires WordPress 7.0 .',
			array( 'status' => 500 )
		);
	}

	if ( function_exists( 'wp_supports_ai' ) && ! wp_supports_ai() ) {
		return new WP_Error(
			'ai_disabled',
			'AI support is disabled on this site.',
			array( 'status' => 403 )
		);
	}
	...
}

いずれかのチェックに失敗した場合は、それぞれ500または403エラーを返します。

続くコードでは、以下の処理を行います。

  • リクエストから音声ファイルのIDを取得
  • 音声ファイルのIDが正の整数であることを確認
  • ファイル形式を確認
  • MIMEタイプを取得
  • ファイルの内容を抽出し、Base64形式にエンコード
$audio_id = absint( $request->get_param( 'audio_id' ) );
if ( ! $audio_id ) {
	return new WP_Error( 'invalid_audio_id', 'Invalid audio ID.', array( 'status' => 400 ) );
}

$attachment = get_post( $audio_id );
if ( ! $attachment || 'attachment' !== $attachment->post_type ) {
	return new WP_Error( 'audio_not_found', 'Audio attachment not found.', array( 'status' => 404 ) );
}

$mime_type = (string) get_post_mime_type( $audio_id );
if ( 0 !== strpos( $mime_type, 'audio/' ) ) {
	return new WP_Error( 'invalid_audio_type', 'Attachment must be an audio file.', array( 'status' => 400 ) );
}

$audio_path = get_attached_file( $audio_id );
if ( ! $audio_path || ! file_exists( $audio_path ) ) {
	return new WP_Error( 'audio_path_missing', 'Audio file path not found on server.', array( 'status' => 404 ) );
}

$audio_bytes = file_get_contents( $audio_path );
if ( false === $audio_bytes ) {
	return new WP_Error( 'audio_read_failed', 'Unable to read audio file.', array( 'status' => 500 ) );
}

$audio_base64 = base64_encode( $audio_bytes );
無効なファイル形式を処理しようとした場合に表示されるエラーメッセージ
無効なファイル形式を処理しようとした場合に表示されるエラーメッセージ

これで、バイナリファイルをAIクライアントに渡せる文字列へ変換できました。続いて、音声からテキストを抽出します。

$output_language = self::get_output_language();

$transcription_builder = wp_ai_client_prompt(
	"Transcribe this voice note accurately into $output_language. Return plain text only."
)
	->with_file( $audio_base64, $mime_type )
	->using_temperature( 0.1 );

if ( ! $transcription_builder->is_supported_for_text_generation() ) {
	return new WP_Error(
		'no_supported_model',
		'No configured AI model can process this request. Configure a provider in Settings > Connectors.',
		array( 'status' => 400 )
	);
}

$transcript = $transcription_builder->generate_text();

上記のコードでは、以下の処理を行います。

  • get_output_language():スクリプト内で定義された静的メソッド。サイトのロケールを取得(詳細はGitHubを参照
  • wp_ai_client_prompt():メインプロンプトを渡して、新しいAIリクエストを初期化
  • with_file():AIクライアントにマルチモーダルリクエストであることを伝え、Base64形式のファイルデータとMIMEタイプを渡す
  • using_temperature():レスポンスの決定性を指定。ここでは音声を忠実に文字起こしするため、低い値を設定
  • is_supported_for_text_generation():AIプロバイダーが渡されたデータからテキストを生成できるかを確認。マルチモーダル入力に対応していない場合は、処理を中断してエラーメッセージを返す
  • generate_text():HTTPリクエストを開始し、AIによるテキスト生成を実行

3. 音声の文字起こしを構造化されたコンテンツに変換する

文字起こしテキストを取得したら、構造化されたJSONレスポンスを生成するために、プラグインは再度AIプロバイダーと通信します。そのために、まずJSON形式の構造化配列を定義します。

$schema = array(
	'type'       => 'object',
	'properties' => array(
		'title'    => array( 'type' => 'string' ),
		'sections' => array(
			'type'  => 'array',
			'items' => array(
				'type'       => 'object',
				'properties' => array(
					'heading'    => array( 'type' => 'string' ),
					'level'      => array(
						'type' => 'integer',
					),
					'paragraphs' => array(
						'type'  => 'array',
						'items' => array( 'type' => 'string' ),
					),
					'bullet_points' => array(
						'type'  => 'array',
						'items' => array( 'type' => 'string' ),
					),
				),
				'required'   => array( 'heading', 'level', 'paragraphs' ),
			),
		),
	),
	'required'   => array( 'title', 'sections' ),
);

この構造を使用すると、AIはタイトルとセクションの配列を含むJSONオブジェクトを返します。各セクションには、見出し、見出しレベル、1つ以上の段落、箇条書きを含める必要があります。

スキーマを定義できたので、続いてAIクライアントへの2回目のリクエストを設定します。

$structured_json = wp_ai_client_prompt(
	"Convert the following transcript into a concise, blog-ready draft in $output_language.n" .
	"Return JSON with a post title and body sections.n" .
	"Each section must have: heading, level (2 or 3), and paragraphs (array).n" .
	"If useful, include bullet_points (array) for short actionable lists.n" .
	"Use short, clear headings and readable paragraph text.nn" .
	"Transcript:n" . $transcript
)
	->using_system_instruction( "You are a professional WordPress content editor. Always write in $output_language (use appropriate spelling, grammar, and conventions for that language variant)." )
	->using_temperature( 0.4 )
	->as_json_response( $schema )
	->generate_text();

if ( is_wp_error( $structured_json ) ) {
	return $structured_json;
}

このコードの主なポイントは以下のとおりです。

  • using_system_instruction():AIモデルの役割(ペルソナ)を定義
  • using_temperature( 0.4 ):Temperatureをやや高めに設定し、AIがより自然で表現力豊かな文章を生成できるようにする
  • as_json_response( $schema ):定義したスキーマに従った構造化JSON形式でレスポンスを返すよう指定
  • エラーが発生した場合は、プラグインが処理を中断

次に、返されたデータをデコードしてサニタイズします。

$structured = json_decode( (string) $structured_json, true );

if ( ! is_array( $structured ) ) {
	return new WP_Error(
		'invalid_ai_json',
		'Could not parse structured AI response.',
		array( 'status' => 500 )
	);
}

$normalized = self::normalize_structured_post( $structured );

if ( '' === $normalized['title'] && empty( $normalized['sections'] ) ) {
	return new WP_Error(
		'empty_structured_content',
		'The AI provider returned empty structured content.',
		array( 'status' => 500 )
	);
}

上記のコードでは、以下の処理を行います。

  • json_decode()関数:JSON文字列をPHPの連想配列に変換
  • JSONの形式が不正な場合:後続のチェックで、わかりやすいエラーメッセージとともに500ステータスコードを返す
  • normalize_structured_post()静的メソッド:AIプロバイダーの出力をJavaScriptに渡す前にサニタイズおよび正規化(関数の定義はGitHubを参照

最後に、生成された出力をJavaScriptに渡し、Gutenbergのネイティブブロックを生成します。

4. Gutenberg対応のコンテンツを生成する

次に、JSON形式の出力をGutenbergのネイティブブロックに割り当てます。この処理はクライアント側で行われ、editor.jsファイルが担います。

ユーザーが音声ファイルを選択すると、以下のコードが実行されます。

function AIContentBuilderSidebar() {

	const [ audioId, setAudioId ] = useState( 0 );
	...

	const onSelectAudio = function ( media ) {
		if ( ! media || ! media.id ) {
			return;
		}
		setAudioId( media.id );
		setAudioLabel( media.title || media.filename || ( 'Audio #'   media.id ) );
		setNotice( null );
	};
	...
}

このコードは、WordPressデータベースに保存されている音声ファイルのIDを取得します。

ユーザーが「Generate Content」ボタンをクリックすると、onGenerate() 関数apiFetch()を使用してAPIエンドポイントへPOSTリクエストを送信します。このリクエストには、音声ファイルのIDなどの情報が含まれます。

const onGenerate = async function () {

	...

	try {
		const response = await apiFetch( {
			path: AICBData.restPath,
			method: 'POST',
			headers: {
				'X-WP-Nonce': AICBData.nonce,
			},
			data: {
				audio_id: audioId,
			},
		} );
		...
	}
	...
}
  • await:AIからのレスポンスを待っている間、一時的に処理を停止します。これにより、ブラウザのUIがフリーズするのを防げます。
  • response変数:受信したJSONオブジェクトを一時的に格納

response変数には、以下のような構造のオブジェクトが格納されます。

{
	title: "Two Wheels to the Medina: ...",
	sections: [
		{
			heading: "A Well-Deserved Rest in the Red City",
			level: 2,
			paragraphs: [ "On April 24th, our group of seven motorcyclists..." ],
			bullet_points: []
		},
		{
			heading: "Diving into the Medina and Jemaa el-Fnaa",
			level: 2,
			paragraphs: [ "We spent the day exploring the historic Medina..." ],
			bullet_points: [ 
				"Navigating the vibrant, maze-like alleys of the ancient Medina", 
				"Tasting traditional Berber tagines and Moroccan mint tea",
				"Shopping for handmade leather goods, spices, and lanterns in the souks" 
			]
		},
		{...}
	],
	content: "Fallback plain text...",
	transcript: "Full transcript..."
}

このオブジェクトには、ブロックの生成に必要なデータが含まれています。ただし、スクリプトが受け取るデータは、JSON文字列の場合と、オブジェクト内にJSONが含まれている場合があります。そのため、ブロックを生成する前に、データを正規化する処理が必要になります。

const normalizedSections = normalizeSectionsPayload( response.sections );
  • normalizeSectionsPayload()関数:形式が一定でないsectionsデータを正規化し、配列に変換(コードはGitHubを参照

続いて、normalizedSections変数を引数として、各sectionsのデータをGutenbergのネイティブブロックへ変換する関数に渡します。

const structuredBlocks = buildBlocksFromSections( normalizedSections );

buildBlocksFromSections()関数のコードの一部を以下に示します。

function buildBlocksFromSections( sections ) {
	if ( ! Array.isArray( sections ) ) {
		return [];
	}

	const blocks = [];

	sections.forEach( function ( section ) {
		if ( ! section || 'object' !== typeof section ) {
				return;
			}

			const heading =
				( 'string' === typeof section.heading )
					? section.heading.trim()
					: '';

			const level = ( 3 === section.level ) ? 3 : 2;

			if ( heading ) {
				blocks.push(
					createBlock( 'core/heading', {
						content: heading,
						level: level,
					} )
				);
			}

			...

		} );
	return blocks;
}

上記のコードでは、forEach()ループでデータ配列を順番に処理し、section.headingsection.levelを取得して、WordPress標準のcreateBlock()関数に渡します。section.paragraphssection.bullet_pointsの割り当てについては、GitHubのソースコードをご覧ください。

広がるAI活用の可能性

WordPressにAIが統合されたことで、サイト管理者や開発者に新たな可能性が広がっています。これまでは、WordPressサイトにAI機能を組み込むために、必要なインフラや仕組みを一から構築しなければなりませんでした。しかし現在では、編集ワークフローの自動化やAIを活用した機能の追加を、これまでよりもはるかに簡単に実現できます。

本記事の後半でご紹介したサンプルプラグインは、数ある活用例の一つにすぎません。それでも、サイト管理やコンテンツ編集を大幅に効率化するツールを構築できることがおわかりいただけたのではないでしょうか。さらに、このアーキテクチャにより、これまで実装が複雑だった機能や、コスト面で導入が難しかった高度な機能も、現実的な選択肢になりつつあります。

とはいえ、これはまだ始まりにすぎません。AIは今なお進化を続けており、今後もWordPressの開発や運用に大きな変化をもたらしていくでしょう。

AIを活用したWordPressサイトを構築・運用するなら、その基盤となるサーバー選びも重要です。Kinstaでは、高性能なインフラとWordPressに特化した機能で、快適な開発・運用環境をご提供しています。ぜひKinstaをお試しいただくか、営業担当までお気軽にお問い合わせください。

Carlo Daniele Kinsta

ウェブデザインとフロントエンド開発をこよなく愛し、WordPress歴は10年以上。イタリアおよびヨーロッパの大学や教育機関とも共同研究を行う。WordPressに関する記事を何十件も執筆しており、イタリア国内外のウェブサイトや雑誌に掲載されている。詳しい仕事情報はXとLinkedInで公開中。