Appearance
EP-022 paid_article_ai_generate_all_considerations(有料記事 AI 考察一括生成)
← EP-一覧 · CPT-002 有料記事 · EP-021 欄ごと生成
概要
有料記事の編集画面から、画面上の全考察欄の下書きを 1 回の LLM 呼び出しで生成する。ハンドラは target_field => 本文 のマップを JSON で返す。post meta への自動保存はしない。欄ごとの生成は EP-021 を残す。
POST パラメータ
| フィールド | 必須 | 型・制約 | 説明 |
|---|---|---|---|
action | ○ | 文字列 | paid_article_ai_generate_all_considerations |
_wpnonce | ○ | 文字列 | paid_article_ai_generate_consideration で発行した nonce。nonce でも受け付ける |
post_id | ○ | 正整数 | 対象の paid_article 投稿 ID |
target_fields | ○ | 許可リスト文字列の配列 | 生成対象。編集画面上の考察欄(target_fields[]) |
target_field 許可リストは EP-021 と同じ。空配列・許可リスト外・31 件超は失敗。重複は除去する。
成功時 data
| 論理名 | 物理名 | 型 | 説明 |
|---|---|---|---|
| 本文群 | contents | object | キーが target_field、値が生成した考察 HTML |
LLM が返さなかった欄はマップに含めない。フロントは返った欄だけエディタへ入れる。返った欄がリクエストより少ないときは部分成功の案内を出す。生成中は欄ごとボタンと一括ボタンの両方を無効化する。
出力トークンは欄数に応じて増やす(6 欄未満は 8192、6 欄以上は 16384 起算で最大 32768)。Gemini へ渡す maxOutputTokens はモデル上限でクランプする。モデル名に gemini-2.5 を含む場合は 65536、それ以外は 8192 が上限である。欄ごと生成(EP-021)の既定 4096 はこの上限より低い。Claude の stop_reason === max_tokens と Gemini の finishReason === MAX_TOKENS は失敗とし、途中切れの JSON を成功扱いにしない。
Gemini の generationConfig.thinkingConfig(thinkingBudget: 0)はモデル名に gemini-2.5 を含むときだけ送る。未対応モデルへ送ると HTTP 400 になる。ツール定義の parameters.properties は JSON オブジェクトにする(空でも {}。PHP の空配列は [] になり HTTP 400 になる)。HTTP 非 200 時の error.message は画面に出さず、error_log の要約(summary=)で追う(Issue #3866)。
システムプロンプトの「HTML のみ」指示は一括時に JSON 契約へ置き換える。トップレベルが配列なら items とみなす。
ツール呼び出しループの経過秒数上限は 150 秒(欄ごと生成は 90 秒)。1 回の LLM HTTP 待ち秒数の上限は 90 秒(欄ごと生成は 30 秒)。次の HTTP は残り予算(最大 90 秒)で始めるので、ツール往復が 60 秒を超えても最終回答に進める。PHP 実行上限は 270 秒。
失敗・ブロック(success: false の data に含み得るキー)
| 条件 | message の内容 |
|---|---|
| AJAX コンテキスト外・nonce 不正 | Messages::AUTH_FAILED |
post_id が不正、または投稿タイプが paid_article でない | PaidArticleAiGenerationAjaxCopy::INVALID_POST |
edit_post 権限なし | Messages::PERMISSION_DENIED |
target_fields が空 | PaidArticleAiGenerationServiceCopy::TARGET_FIELDS_EMPTY |
いずれかの target_field が許可リスト外 | PaidArticleAiGenerationServiceCopy::TARGET_FIELD_INVALID |
| 件数が上限超過 | PaidArticleAiGenerationServiceCopy::TARGET_FIELDS_TOO_MANY |
| 結果 JSON が不正・有効な本文が 0 件 | PaidArticleAiGenerationServiceCopy::BULK_RESPONSE_INVALID |
| 出力が max_tokens で途中切れ | PaidArticleAiGenerationServiceCopy::OUTPUT_TRUNCATED_BULK |
| API キー未設定・対象月未設定・指針未設定・応答切れ・形式不正・ループ上限 | 対応する PaidArticleAiGenerationServiceCopy の固定文言 |
| HTTP 429 / quota(Service 例外) | Messages::AI_LLM_CLAUDE_RATE_LIMITED / AI_LLM_GEMINI_RATE_LIMITED(応答から取れた待ち秒が 1〜120 なら「(約 N 秒後)」を付与。生レスポンスは出さない) |
| HTTP 401 / 403(Service 例外) | Messages::AI_LLM_CLAUDE_AUTH_FAILED / AI_LLM_GEMINI_AUTH_FAILED |
| HTTP タイムアウト・経過上限(Service 例外) | Messages::AI_LLM_CLAUDE_TIMEOUT / AI_LLM_GEMINI_TIMEOUT |
| 接続失敗(WP_Error、タイムアウト以外)(Service 例外) | Messages::AI_LLM_CLAUDE_CONNECTION_FAILED / AI_LLM_GEMINI_CONNECTION_FAILED |
| その他の HTTP 非 200(Service 例外) | Messages::AI_LLM_CLAUDE_HTTP_ERROR / AI_LLM_GEMINI_HTTP_ERROR(ステータスコードのみ。例: …(HTTP 500)。) |
| 上記以外の Service 例外 | PaidArticleAiGenerationAjaxCopy::NOTICE_GENERIC_ERROR(詳細は error_log) |
上流 API のレスポンス本文や WP_Error の生メッセージは画面・JSON に載せない(Issue #3866)。ArticleAiLlmClient は ArticleAiLlmHttpErrorMode::ClientSafe で固定文言だけを例外に入れ、PaidArticleAiGenerationAjaxHandler は ArticleAiLlmClientErrorFilter で許可した文言以外を汎用文言に置き換える。上流の詳細は ExternalApiErrorLogFormatter の要約(HTTP ステータス・短い要約・ハッシュ)で error_log に残す。
権限
- ログイン必須(
wp_ajax_のみ。noprivは付けない) - capability:
edit_post(対象post_id)
生成契約
- 編集画面では「基本情報」の直下(ブロックエディタ本文の下、メタボックス先頭)に置く。ブロックエディタの右サイドバーには出ない
- 1 リクエストにつき LLM 1 系列(ツール呼び出しループは欄ごと生成と同じ上限)
- プロンプトで「記事全体で調子を一貫させる」「同じ事実を欄ごとに繰り返さない」「欄の趣旨に合わせる」を指示する
- 既存本文は保存済み post meta のみ。未保存のエディタ内容は見えない
set_post_context()のcurrent_fieldは空(一括のため生成中欄を区別しない)