すべての記事に戻る

私たちのブログ制作をエンドツーエンドで自動化している方法

Mingです。vm0ではプロダクトデザインに加えて、私たちのコンテンツ運営も自分のロールの一部です。そして始めてみると、想像よりずっと多くの作業が含まれていました: ワークフローの設計、イラスト制作、画像のプロダクション、CMSまわり、翻訳。それを少しずつ片付けていくために組み上げてきた仕組みについて書きます。


コンテンツも私の仕事の一部です

vm0での1日のほとんどはプロダクトデザインかNext.jsのコードベースで過ごしています。ただ、私たちのコンテンツ運営も私の担当に含まれていて、真剣に取り組んでみると、ちゃんと機能するブログにはどれだけの作業が乗っているかが見えてきました。安定したアウトプット、まともなキーワードリサーチ、ブランドに沿ったビジュアル、ちゃんとしたメタディスクリプション、そして私たちがサポートしている4言語への翻訳。

きちんと運用し始めた頃、1記事あたりほぼ1日かかっていました。実際の執筆は、その中のごく一部です。残りの時間は執筆のまわりの作業に消えていました: 適切なキーワードを探し、カバー画像を作り、CMSのフィールドを正しく埋め、ロケールごとにそれを繰り返す。

なので、そのパイプラインのうち渡せる部分を少しずつLucasに移してきました。LucasはZero上で自分の日常業務を題材に組み上げたAI marketing agentで、ロールは「Marketing Manager」。スコープはマーケティング関連全般で、ブログ制作が現時点で一番作り込まれている部分です。スキルとコネクタを足すごとに彼は成長していきます。

今のところLucasはStrapiヘッドレスcms、Ahrefsアカウント、チームSlack、ウェブチャット、ブランド一貫性のあるイラストを生成する内製スキル draw、ライティングのメカニクスを担当する標準の copywriting スキルにアクセスできます。

これからこのAI blog automationが実務でどう動いているかを順に説明します: Lucasが曖昧なプロンプトをどう受け取り、テーマとキーワードに分解し、記事を書き、カバーを描き、自然な翻訳を回し、4ロケール分のdraftをヘッドレスcmsに着地させるか。最後に、ここに辿り着くまでの過程と、意図的に人間(主に私自身)の仕事として残していることについても触れます。


Lucasの使い方: ウェブアプリとSlack

一体のAIエージェントに集まる複数のチャットバブル

私はLucasと主にvm0のウェブチャットで作業しています。そっちがディープワーク向けのインターフェースで、アウトライン、ドラフト、カバーのイテレーション、編集を、彼と一緒に座って詰めていく場所です。長めのスレッド、長めのコンテキスト、Slackチャネルでやるには扱いづらい種類のやり取り。

Lucasはチーム共通のSlackにも繋がっていて、こちらはチーム共有の入り口です。スマホから短いプロンプトを投げるのもSlack、他のメンバーが彼を呼び出すのもSlackです。ブログの担当週に当たった人は、私と同じように一行投げてポストを着手できます。同じエージェント、両方のインターフェースをまたいでコンテキストを保ちます。


典型的なプロンプトの形

私がLucasに投げるプロンプトはたいてい短いです。最大で3文。代表的な1つ:

Lucas, write a tutorial on running effective weekly meetings: cover meeting structure, common pitfalls, and how to keep remote teams engaged. Tutorial style, SEO-friendly, end with a CTA to use Zero. Draft link only, don't publish.

ブリーフはこれだけ。たった1メッセージ。アウトラインも、キーワードリストも、デザインブリーフも、文字数指定もなし。あとはLucasが受け取って動きます。

この形に落ち着くまで少し時間がかかりました。最初はもっと長いブリーフを書いていました。エージェントには全部書き出さないと伝わらないと思い込んでいたからです。でも実際には、コンテキストの大半は「毎回のプロンプト」ではなく「常設指示とスキル」に置くべきものでした。プロンプトはトリガーで、設定の側が重みを担っています。


Lucasがテーマとキーワードをどう分析するか

多くのキーワードタグが1つの選ばれたキーワードに絞られていくファネル

Lucasがまず行うのは、プロンプトから構造を抽出することです。上のような依頼に対して、彼の作業メモはこう展開されます:

次にキーワードを選びます。コンテンツマーケティングの中で、私が始めた頃に一番直感が効かなかった部分なので、Lucasにキーワード作業を頼むときに毎回通すルーチンを渡しています。自律的に動くスキルではなく、私が信頼している手順という形です:

  1. Ahrefsから候補を引く。 トピックフレーズに対してAhrefs APIをコールし、月間検索ボリューム、Keyword Difficulty(KD)、parent topic、SERP intent付きでロングテールバリエーションを取得。
  2. Difficultyでフィルタする。 ハードルールを与えてあります: KD < 30。vm0は新しめのドメインで権威性が限定的なので、KDが高いキーワードを今追っても意味がありません。最初はロングテールが効きます。
  3. Strapiインデックスと突き合わせる。 Strapi REST APIから公開済みslugを全件引き、すでにカバーしているキーワードを除外。共食い(cannibalization)を防ぎます。
  4. IntentをプロダクトFitと合わせる。 キーワードのintentは私たちのユースケースライブラリと両立しなければなりません。「How to run effective weekly meetings」 → operations / team management → 合致。「Best ramen in Tokyo」 → ボリュームに関係なく不適合。
  5. 1つ選ぶ。 フィルター後のリストから最もボリュームの大きいオプションを選び、記事の背骨にします。例えば remote weekly meeting best practices

オンページSEOには単純なヒューリスティックを適用します: タイトル、最低1つのH2、冒頭100語にターゲットキーワードを入れること。同義語と関連フレーズを自然に織り込み、当てはまる場所には関連記事への内部リンクを2〜3本。あとは普通に書き、キーワードスタッフィングはしない。

構造の残り(H2を6〜7個、各ステップの下に「ヒント」コールアウト、終盤にCTA)は、過去の記事を例として積み上げてきたチュートリアルテンプレートに沿っています。


カバーがどう描かれるか

カバーはブログ制作の中で私が一番気にしている部分です。半分は自分がデザイナーだからで、もう半分は読者が一文字も読まないうちに目にするものだからです。XやLinkedInで共有されるOpen Graph画像でもあり、ブログインデックスを「ストックフォトの壁」ではなく「1つの出版物」に見せる要素でもあります。

私が望んでいるルックではストックフォトは選択肢に入らず、毎回1枚ずつイラストを発注すると記事あたりの制作コストが破綻します。なので draw というZeroスキルを作りました。200行のPythonスクリプトが5つのことをやっているだけです:

  1. インプットを2つだけ受け取る: メタファー(何を描くか)と(色のフレンドリーネームとhexアンカー)。
  2. それを固定プロンプトテンプレートに差し込む。テンプレートが構図の残り全部を固定する: ウォームグレーの背景 #eeeeee、選んだ色で描かれた水彩ブロブをキャンバス中央に、上から手描きのインクスケッチ、左上 (25, 25) にvm0ロゴ。
  3. 完成したプロンプトをfal.aiの nano-banana-pro モデルに16:9で投げる。
  4. PNGをダウンロード。背景がズレないよう、ニアホワイトのピクセルを正確に #eeeeee に正規化。1600×900にアップスケール。透過のvm0ロゴをアルファコンポジットで重ねる。
  5. 結果をvm0のCDNにアップロードしてURLを出力する。

典型的な呼び出しはこんな具合です:

python draw.py \
  --metaphor "a tilted hourglass with sand draining into a small open notebook, a sparkle near the rim" \
  --color-name "dusty rose" \
  --color-hex "#e08b96" \
  --upload

入力は2つだけ。残りのブランド要素は記事ごとに一貫したまま保たれます。色は記事のテーマに合わせて選んでいます。チュートリアル系の温かい記事はコーラルやセージ、テクニカル寄りの記事はブルーやラベンダー、アナウンス系はマスタード。繰り返し使う色が積み重なると、ブログインデックスが少しずつ認識可能なパレットに育っていきます。

苦労して学んだのは、レバレッジはモデルではなくテンプレートにあるということです。構図、パレット、レイアウトをモデルに自由に選ばせると、毎回違うブランドのカバーになってしまう。そこを固定して、メタファーだけをモデルに任せると、記事をまたいだルックの一貫性が保たれます。

兄弟スキルとして illustration も作っています。記事内のスポットアート用です(この記事のセクション間に出ている小さな絵がそれ)。考え方は同じで、スタイルを固定して、メタファーと色だけを変える、というものです。16:9のカバーではなく1024×1024の正方形アート向けにチューニングされています。

画像SEOについて少しだけ: 同じカバー画像が記事のOpen GraphとTwitter Cardのアセットとして設定されているので、ソーシャルシェアがきれいに拾います。Strapiはアップロード時に4サイズのバリアント(thumbnail、small、medium、large)を自動生成しますが、記事本文の画像は今のところ srcset を使わずplainな <img> タグでロードしているので、ここは小さなフロントエンドのTODOです。インラインのイラストは、Lucasがマークダウンに埋め込むときに記述的なalt textを書いています。Strapi側のカバー本体の alternativeText フィールドはまだ埋めていないので、これも追々入れていく予定です。


記事本体がどう書かれるか

左にアウトライン、右に展開された段落ブロック、横にヒントの電球

テーマ、キーワード、カバーが固まれば、ドラフティングはほぼ機械的です。Lucasは:

  1. 先にアウトラインを引く。 H2を6〜7個、各H2に明確な役割(問題提起、ステップ手順、「ヒント」コールアウト、まとめ、CTA)を持たせる。
  2. 各H2を copywriting スキルで本文に展開する。 ボイス(短文、具体例、似合うところでは二人称、たまの乾いたユーモア)はLucasの常設指示に書かれたルールから来ていて、スキル自体に入っているわけではありません。
  3. 各ステップの下に実用的な「ヒント:」コールアウトを置く。 チュートリアルでは便利です。多くの読者はスキミングするので、コールアウトが長文を貫通するパスになります。
  4. 記事の description フィールドを書く。 Strapiが80文字に制限しているので短くまとまり、人が書いたように読めます。記事のサブタイトルとしても、OG/Twitterカードのディスクリプションとしても使われます。
  5. プロダクト名を出して無料ワークスペースへ誘導するCTAで締める。

最初のドラフトは、プロンプトの2〜3分後に返ってきます。私は読み、ボイスがズレている部分にトーン調整をリクエストし、Lucasがハマるまでイテレーションを回します。最初のドラフトはほとんどshipには出せません。おおむね合っているけれど少しズレている、というのが普通で、私の作業時間の大半はそこに注いでいます。


DraftがStrapiにどう着地するか

「draft」タグ付きの梱包をCMSのポストに入れる図、上にポーズアイコン

私たちはStrapiをヘッドレスcmsとして使っています。articles コンテンツタイプには cover(メディア)、authorcategory(リレーション)、locale(文字列)、本文markdownを保持する blocks 動的ゾーンがあります。

LucasのCMS同期フロー:

  1. カバーPNGを /api/upload にPOST。返ってきた documentId を控える。
  2. /api/articles?status=draft にPOST。title、description(Strapiが≤80文字で強制してくる)、slug、locale、cover/author/categoryリレーション(数値ID)、本文markdownを単一の shared.rich-text ブロックで送る。
  3. 必須: publishedAt を含めない。 Strapiはnullをdraft状態として扱います。
  4. プレビューURLをvm0のdraftパターンで返す: https://www.vm0.ai/{locale}/blog/posts/{slug}?status=draft

Strapi v5の注意点: パブリックREST APIが特定のコンテンツタイプで publishedAt を上書きしてしまうことがあり、API経由で作った「draft」が黙って公開される可能性があります。直し方は、draft書き込み時にそのフィールドを取り去る小さなDB migrationを足すこと。

slug戦略もSEOに効きます。私たちは4ロケール全部に共通の正規slugを使い、翻訳しません。/de/blog/posts/remote-weekly-meeting-best-practices の読者は /en の読者と同じURL構造を見ますし、被リンクプロファイルが翻訳されたURL間で分散しません。これらのlocale relationsからhreflang linkタグを出力するのは、フロントエンド側の小さなTODOで、まだ実装していません。


i18n翻訳の仕組み

中央のドキュメントから4つの小さなコピーがEN、DE、JA、ESのコードに分かれて広がる

vm0は4ロケールで運用されています: 英語、ドイツ語、日本語、スペイン語。記事をGoogle Translateにかけたりはしません。安易な道はvoiceを殺すし、正しい道は慣用句を適応させることです。

LucasはまずENのdraftを作り、返ってきた documentId を保持してから、PUT /api/articles/{documentId}?locale=de(さらに ?locale=ja?locale=es)を3回叩きます。それぞれのpayloadは自然な翻訳に置き換えてあります。Strapiはそれらを同じcanonical documentのlocaleバリアントとして扱うので、4バージョンが管理画面でもフロントエンドのlocale switcherでもひも付いたままになります。

私が彼に渡している翻訳ルール、ほとんどは間違ったアウトプットに気づいて修正していった結果です:

multilingual content publishingは以前ワークフローの中で最も時間を食う部分でした。今は同じエージェントランの最終ステップに過ぎません。


自動化されないもの

短いけれど大事なセクション。このワークフローのすべてが自動化されているわけではなく、それは意図的です:

要点は「私をループから消すこと」ではありません。「私を本当に必要としないループの部分から、私を消すこと」です。


どうやってこのワークフローに辿り着いたか

ある週末にこれを設計したわけではありません。記事を書きながら、毎回どのステップが時間を食っているかに注意を払い、徐々に積み上がってきたものです。

気づいたパターンはこうです: 自動化する価値があるのはクリエイティブな部分ではなく、接続の部分でした: キーワードのpull、カバーのrendering、uploadコール、ロケールへのfan-out、プレビューURLのフォーマット。正直に計測してみると、執筆そのものより段取りに時間を使っていました。

なので段取りを少しずつエージェントに渡していきました。モデルが書く。エージェントが「モノを移動させる」作業を引き受ける。私は依然として「何を書くか」「どんな響きにするか」を決めていますが、ピースをつなぎ合わせるのは私の役目ではなくなりました。

私にとってのコンテンツオートメーションは、結局「AIがブログを書く」ではなく「AIが書く以外の全部をやってくれて、私は実際の文章を読む時間が取れるようになる」だったわけです。


似たようなことを試してみたい人へ

似た状況にいるなら、自分の専門以外の帽子もいくつか被っていて、その中にコンテンツがある状態なら、ここに書いた内容のほとんどは再現可能です:

無料でZeroワークスペースを始める →

先にエージェントワークフローを見たいなら、ユースケースライブラリ にエンジニアリング・プロダクト・マーケティング・オペレーションを横断する19個のすぐコピーできるオートメーションが並んでいます。

Lucasを具体的にクローンしたければ: エージェントを作り、ロールに Marketing Manager を設定、Strapi(または使っているCMS)+ Ahrefs + 画像生成ツールをコネクト、私たちの draw を参考にしたスキルと、自分のブランドボイスを内蔵した copywriting スキルを追加してください。あとは最初のプロンプトから組み立ててくれます。

関連記事

最新情報をキャッチ

// エージェントネイティブ開発の最新インサイトを入手。

購読するDiscordに参加