WordPress REST APIでAI生成記事を下書き登録する仕組みを作った。ただし自動公開はしない

はじめに

前回の記事では、AIが生成したブログ下書きをそのまま使わず、品質チェックする仕組みについて書いた。

筆者のブログ自動化ツールでは、承認済みテーマから記事構成案を作り、OpenAI APIで本文下書きを生成し、その後に品質チェックを行う。

ここまで来ると、次にやりたくなるのがWordPressへの登録である。

手元のMarkdownファイルとして下書きが作れても、毎回WordPress管理画面を開き、タイトルを入れ、本文を貼り付け、下書き保存するのは手間である。

そこで筆者は、WordPress REST APIを使って、品質チェック済みの記事だけをWordPressへ下書き登録する仕組みを作った。

ただし、ここでも重要なのは、自動公開はしないということである。

今回の仕組みでは、WordPressへ送信する場合でも、投稿ステータスは必ず draft にする。公開はWordPress管理画面で人間が最終確認してから行う。

記事下書き生成
↓
品質チェック
↓
wordpress_ready を確認
↓
WordPressへ draft 登録
↓
人間が管理画面で確認
↓
公開

本記事では、WordPress下書き登録機能をどのように作ったか、そしてなぜ自動公開しない設計にしたのかを紹介する。

WordPressに送る対象を絞る

最初に決めたのは、すべての下書きをWordPressへ送らないことである。

前工程の品質チェックでは、記事ごとに wordpress_ready を出力するようにしている。

wordpress_ready = yes
wordpress_ready = after_revision
wordpress_ready = no

WordPress下書き登録の対象にするのは、原則として yes または after_revision のものだけである。

yes
→ WordPress下書き登録候補

after_revision
→ 修正前提で下書き登録してもよい候補

no
→ 登録しない

この条件を入れないと、品質チェックで問題ありと判定された記事までWordPressに流れてしまう。

AI記事生成では、生成できたことと、投稿してよいことは別である。

そこで、WordPress投稿処理では outputs/draft_review_summary.csv を読み込み、wordpress_ready を見て対象記事を絞るようにした。

dry-runをデフォルトに

WordPress連携で特に重要なのは、いきなり投稿しないことである。

今回の仕組みでは、dry-runとexecuteを分けた。

python scripts/post_wordpress_drafts.py --limit 1 --dry-run

dry-runでは、WordPress APIには送信しない。
送信予定の記事タイトル、本文、対象ファイル、投稿ステータスなどを確認するだけである。

実際にWordPressへ送る場合は、明示的に --execute を付ける。

python scripts/post_wordpress_drafts.py --limit 1 --execute

この設計にした理由は、事故を防ぐためである。

自動化ツールでは、実行した瞬間に外部サービスへ反映される処理が一番怖い。
WordPress投稿もその一つである。

dry-runを挟むことで、送信前に確認できる。

投稿ステータスは必ずdraftにする

今回の仕組みでは、WordPressに投稿する場合でも、ステータスは必ず draft に固定した。

status = draft

publish は実装しない。

これはかなり重要である。

AIが生成した記事は、品質チェックを通っていても、まだ人間の最終確認が必要である。

確認すべき項目は多い。

  • タイトルは自然か
  • 本文に誤情報がないか
  • 見出し構成は読みやすいか
  • 公式確認が必要な箇所は残っていないか
  • 内部リンクは正しいか
  • アイキャッチ画像は適切か
  • カテゴリとタグは合っているか
  • MarkdownからHTMLへの変換が崩れていないか

これらを確認せずに公開するのは危険である。

そのため、今回の仕組みでは、WordPressへの登録はあくまで下書き作成までにした。

AIとPythonは下書き置き場まで運ぶ係である。
公開ボタンを押す係ではない。

WordPress Application Passwordを使う

WordPress REST APIで認証付きの操作を行うために、Application Passwordを使った。

.env には、以下のような設定を入れる。

WORDPRESS_BASE_URL=https://syanaise3wariup.com
WORDPRESS_USERNAME=your_wordpress_username
WORDPRESS_APPLICATION_PASSWORD="your_application_password"

ここで注意が必要なのは、WORDPRESS_APPLICATION_PASSWORD に入れるのはWordPressの通常ログインパスワードではないという点である。

WordPressのプロフィール画面で発行する、アプリケーションパスワードを使う。

また、これらの値は .env に入れ、GitHubには上げない。

.env.example にはダミー値だけを書く。

WORDPRESS_BASE_URL=https://example.com
WORDPRESS_USERNAME=your_wordpress_username
WORDPRESS_APPLICATION_PASSWORD=your_wordpress_application_password

WordPress下書き登録機能では、OpenAI APIキーやGoogle OAuth情報と同じく、秘密情報の扱いが重要になる。

動けばよいではなく、漏れないように動かす必要がある。

MarkdownをHTMLに変換して投稿する

本文下書きはMarkdownで保存している。

一方、WordPress REST APIへ投稿する本文は、HTMLとして送る方が扱いやすい。

そのため、投稿処理ではMarkdownをHTMLに変換してから送信するようにした。

流れは以下である。

outputs/article_drafts/*.md
↓
front matterを読み取る
↓
title / description / action を取得
↓
Markdown本文をHTMLに変換
↓
WordPress REST APIへPOST

front matterには、たとえば以下のような情報を入れている。

---
title: Teamsで添付ファイルが開けない時の原因と確認ポイント
description: Teamsで添付ファイルが開けない時の確認ポイントを社内SE向けに整理する。
action: create_new_article
review_status: draft
---

この情報を使って、WordPress投稿時のタイトルや本文を組み立てる。

タイトルがfront matterにない場合は、Markdown内の最初のH1を使うようにした。

このようにしておくと、本文生成からWordPress下書き登録までの流れがつながりやすい。

二重投稿を防ぐ

WordPress投稿機能で気を付けたのが、同じ記事を何度も投稿してしまう問題である。

テスト中は、同じコマンドを何度も実行することがある。
そのたびに同じタイトルの下書きが増えると、WordPressの投稿一覧が下書きの群生地になる。

そこで、投稿結果をCSVに記録するようにした。

outputs/wordpress_post_results.csv
outputs/wordpress_post_report.md

投稿済みのファイルやタイトルを記録し、重複投稿を避ける設計にした。

完全ではないが、少なくとも同じ下書きを何度も送る事故は減らせる。

自動化ツールでは、成功時の結果を記録しておくことが重要である。
あとから「何を送ったか」が分からなくなると、調査が面倒になる。

REST APIのURL形式で詰まった

WordPress REST API連携では、いくつかのエラーに遭遇した。

その一つが、REST APIのURL形式である。

最初は、以下の形式で投稿しようとしていた。

https://syanaise3wariup.com/wp-json/wp/v2/posts

しかし、環境によっては404が返ることがあった。

そこで、?rest_route= 形式にも対応させた。

https://syanaise3wariup.com/?rest_route=/wp/v2/posts

今回の環境では、最終的に rest_route 形式を使うことで通るようになった。

.env では、以下のように設定できるようにした。

WORDPRESS_REST_API_MODE=rest_route

また、自動判定用に auto も使える設計にした。

WORDPRESS_REST_API_MODE=auto

WordPress REST APIは、環境やパーマリンク設定、サーバー設定によって挙動が変わることがある。
そのため、URL形式を固定せず、設定で切り替えられるようにしておくと安心である。

Application Password認証でも詰まった

次に詰まったのが、Application Password認証である。

/wp/v2/users/me にアクセスしても、401が返る状態になった。

ユーザー名やApplication Passwordは確認しても合っているように見える。
このとき疑ったのが、AuthorizationヘッダーがWordPressまで届いていない問題である。

最終的には、WordPressのパーマリンク設定を見直すことで認証が通るようになった。

このあたりは、WordPress REST API連携でかなり詰まりやすいポイントである。

確認すべき項目は以下である。

  • WordPressのユーザー名が正しいか
  • 通常パスワードではなくApplication Passwordを使っているか
  • Application Passwordの空白や改行が混ざっていないか
  • パーマリンク設定が適切か
  • AuthorizationヘッダーがPHP/WordPressへ届いているか
  • セキュリティプラグインがREST APIを制限していないか

WordPress REST APIの認証エラーは、単純なパスワード間違いとは限らない。
サーバー設定やパーマリンク設定が絡むこともある。

実記事だけ403になる問題もあった

認証が通り、最小テスト投稿も成功したあと、実際の記事下書きを投稿すると403になる問題が起きた。

これはかなり悩ましい。

なぜなら、テスト投稿は通るからである。

つまり、REST APIも認証も成功している。
それなのに実記事だけ403になる。

この場合、原因として考えられるのは、本文の中身がWAFに引っかかっていることである。

技術記事には、次のような要素が含まれることがある。

  • PowerShellコマンド
  • Pythonコード
  • Windowsパス
  • JSON
  • YAML
  • HTMLタグ
  • URL
  • コードブロック

これらが、サーバー側のWAFに攻撃っぽく見えることがある。

実際、最小本文では投稿できるが、コードブロックを含む実記事では403になるという状況があった。

そこで、投稿方式や本文の安全化を設定で変えられるようにした。

form送信とWAF安全モードを追加した

WAF対策として、まず送信形式を切り替えられるようにした。

WORDPRESS_POST_BODY_MODE=form

JSON送信ではなく、フォーム送信に切り替えることで通る場合がある。

また、WAF安全モードも追加した。

WORDPRESS_WAF_SAFE_MODE=true

WAF安全モードでは、WordPressに送る本文に対して以下のような処理を行う。

  • コードブロックを一時的にプレースホルダー化する
  • 長いコマンド列をそのまま送らない
  • 危険視されやすいHTMLタグを除去する
  • 本文末尾に、詳細コマンドは管理画面で確認・追記する旨を入れる

元のMarkdownファイルは変更しない。
WordPressへ送るpayloadだけを安全化する。

これにより、WAFに弾かれやすい記事でも、下書き登録できる可能性が上がる。

もちろん、WAF安全モードを使った場合は、WordPress管理画面で本文を確認し、必要に応じてコードブロックを手動で整える必要がある。

自動化は便利だが、WAFと戦う場合は人間の確認も欠かせない。

Codexには何を作らせたか

このWordPress下書き登録機能も、Codexに段階的に作らせた。

最初から「WordPressに投稿する機能を作って」とだけ頼んだわけではない。

依頼した内容は、ざっくり言えば以下である。

draft_review_summary.csv を読み込む
wordpress_ready の記事だけ対象にする
Markdown下書きを読み込む
front matterからタイトルなどを取得する
MarkdownをHTMLへ変換する
WordPress REST APIへ status=draft で投稿する
dry-runとexecuteを分ける
投稿結果をCSVとMarkdownレポートに保存する
REST API URL形式を切り替えられるようにする
form送信とWAF安全モードに対応する

ポイントは、公開処理を作らせなかったことである。

Codexには、下書き登録までを明確に指示した。

自動公開はしない
status は必ず draft
publish は実装しない

この制約を最初から入れておくことで、危険な実装を避けやすくなる。

実行の流れ

現在の実行イメージは以下である。

まずdry-runで確認する。

python scripts/post_wordpress_drafts.py --limit 1 --dry-run

問題なければ、1件だけexecuteする。

python scripts/post_wordpress_drafts.py --limit 1 --execute

REST APIの形式やWAF対策を指定する場合は、.env で設定する。

WORDPRESS_REST_API_MODE=rest_route
WORDPRESS_POST_BODY_MODE=form
WORDPRESS_WAF_SAFE_MODE=true

最初から複数件を送らない。
まずは1件だけ送る。

WordPress側で下書きとして登録されたことを確認し、本文や表示を確認する。

この「1件だけ試す」運用はかなり重要である。

自動化では、大量処理できることより、少量で安全に確認できることの方が先である。

下書き登録後に確認すること

WordPressに下書き登録できたら、管理画面で以下を確認する。

  • 投稿ステータスが下書きになっているか
  • タイトルが意図通りか
  • 本文の見出し構成が崩れていないか
  • コードブロックが崩れていないか
  • 内部リンクが正しいか
  • アイキャッチ画像を設定するか
  • カテゴリとタグが適切か
  • 要公式確認の箇所が残っていないか

下書き登録はゴールではない。

WordPress管理画面で確認するための準備である。

ここを勘違いすると、AI自動化が危険なものになる。

実装して感じたこと

WordPress下書き登録を実装して感じたのは、外部サービス連携は最後の数メートルが一番ぬかるむということである。

Search Console API取得、記事構成案生成、本文生成、品質チェックまではローカル中心の処理である。

しかし、WordPress投稿は外部サービスに書き込む処理である。

そこには、認証、権限、パーマリンク、WAF、サーバー設定、REST APIの形式などが絡む。

実際、以下のような問題に遭遇した。

401
403
404
WAF
rest_route
Application Password
Authorizationヘッダー

このあたりは、単にコードを書くだけでは解決しにくい。
サーバー側の設定やWordPress側の仕様も関係する。

だからこそ、dry-run、status=draft固定、1件ずつ実行、ログ出力が重要である。

自動公開しない設計にしてよかった

実際にWordPress連携でエラーに遭遇して、あらためて自動公開しない設計にしてよかったと感じた。

もし最初から自動公開まで実装していたら、エラー対応だけでなく、公開事故のリスクも抱えることになっていた。

下書き登録までなら、失敗しても影響は限定的である。
成功しても、管理画面で確認してから公開できる。

AIとWordPressをつなぐ場合、最初に実装するべきなのは自動公開ではない。
安全な下書き登録である。

この順番はかなり大事である。

まとめ

筆者は、品質チェック済みのAI生成記事を、WordPress REST APIで下書き登録する仕組みを作った。

ただし、自動公開はしない。

WordPressへの投稿ステータスは必ず draft とし、公開は人間が管理画面で最終確認してから行う。

実装では、Application Password認証、dry-run、executeの分離、MarkdownからHTMLへの変換、投稿結果ログ、REST API URL形式の切り替え、WAF対策などを入れた。

特に重要なのは、WordPressへ送る対象を品質チェック済みに限定することである。

AIが生成した記事をそのまま公開するのではなく、下書きとして運び、人間が確認してから公開する。

この設計にすることで、AIによる効率化と安全なブログ運用のバランスを取りやすくなる。

次回は、WordPress REST API連携で実際に詰まった401、403、404エラーと、その対処について詳しく紹介する予定である。

コメント

タイトルとURLをコピーしました