ブログを始めて1ヶ月で11記事書いたが、OGP画像はデフォルトの1枚だけだった。 SNSでシェアされてもどの記事も同じ画像が出る。記事の内容が画像から伝わらない。

AIエージェントに記事ごとのOGP画像を作ってもらった。生成からHugoの実装まで全部任せた。 人間がやったのは、できたものを見て2回指摘を返すことだけだった。

渡した指示

最初に渡した指示はこれだけだった。

記事ごとのOGP画像を生成してほしい。 全体のWebデザインの雰囲気を壊さないようにしつつ、記事の内容やテーマに合った画像を作成してください。

デザインのトークンも、サイズも、ファイルの置き場所も指定していない。 AIエージェントが自分でサイトのCSSと既存デザインを読んで、色とフォントとレイアウトのトーンを把握すると期待した。

あがってきたもの

AIエージェントは11記事分のOGP画像を一度に作ってきた。

各記事のSVGソースと、それを1200x630のPNGにラスタライズしたペア。 デザインは先日Hallmarkで刷新したサイトのトーンに合わせてあった。 ペアイエローの背景に、ブルーと赤のアクセント、太いボーダーとオフセットシャドウ。 記事の内容を図解する要素がそれぞれに入っている。

たとえばHallmarkでブログを刷新した記事のOGPは、階段状のパスとノードで「3回作り直した」過程を図示している。 Homebrewの自動更新記事なら、ターミナルとパッケージのアイコンが配置されている。

同時に、全記事のfront matterに image: フィールドが追加され、HugoのビルドでOGPメタデータが出力されるようになっていた。

レビュー1回目: 文字の角度が壊れていた

できたPNGを確認すると、文字の位置と角度がおかしかった。 SVGでは正しく配置されているのに、PNGに変換したときにずれていた。

「SVG内の文字の位置や角度が正しくPNG画像に反映されていない」と伝えた。

AIエージェントはここから自力で診断を始めた。 SVGをラスタライズした結果とコミット済みのPNGをピクセル単位で比較し、差分を可視化した。 原因は、SVGの transform 属性の解釈がラスタライザとPNG出力で一致していなかったことだった。

修正後、SVGとPNGのピクセルが完全に一致するようになった。 確認のために scripts/check-ogp-parity.sh という検証スクリプトまで作ってくれた。 SVGを再ラスタライズして、コミット済みのPNGとピクセル差分を比較するスクリプットだ。

レビュー2回目: ファイルの場所とURL

画像ができたが、置き場所が違っていた。 AIエージェントは static/images/ogp/<slug>.png にまとめて置いていた。 このブログの規約では、記事の画像は static/images/<slug>/ に置くことにしていた。

「作成したOGP画像は static/images ディレクトリ配下の記事名と同じディレクトリに ogp.png としてください」

と伝えた。

ここで気づいたことがもう一つあった。 layouts/partials/extend_head.html の手書きのOGPメタタグが、画像URLを相対パスのまま出力していた。 SNSのクローラーは相対パスを解決できない。絶対URLにしないとOGP画像が表示されない。

AIエージェントが extend_head.html の手書きタグを削除し、テーマの opengraph.htmltwitter_cards.html パーシャルを上書きして、独自の image フィールドを絶対URLで出力するようにした。

完成したもの

最終的に、11記事すべてに記事専用のOGP画像が設定された。

各記事の static/images/<slug>/ogp.png に1200x630のPNGがあり、SVGソースも隣に置いてある。 front matterの image: フィールドがそれを指していて、Hugoのビルドで絶対URLのOGPメタデータが出力される。

ビルドして、全記事のHTMLに正しい og:imagetwitter:image が出力されていることを確認した。

任せきるために必要だったもの

最初の指示は2行だった。デザインの指定も、サイズも、ファイルの置き場所も書いていない。

それで機能したのは、AIエージェントがサイトの既存デザインを自分で読んで、トーンを合わせる判断をしたからだ。 Hallmarkで刷新した直後だったので、CSSのトークンが揃っていて、参照すべきデザインが明確だった。

人間がやったのは2回のレビューだ。 1回目はPNGの文字が壊れているのを見つけて、1文で伝えた。 2回目はファイルの置き場所が規約と違うのを見つけて、直し方を指示した。

任せきるために必要だったのは、詳細な指示ではなく、できたものを確認する判断と、直すべき点を1文で伝えることだった。

追記(2026-07-23): もっとシンプルな方法があった

公開後、「画像が表示されないのはそもそもPaperModの仕様では」と聞かれた。 テーマの opengraph.htmltwitter_cards.html を読み直すと、cover.imageimages(配列)しか見ていなかった。 このブログが使っていた image:(単数)は、テーマにとってはただの未知のフィールドだった。 だから独自のテンプレート上書きが必要になっていた。

image:images: の配列に書き換え、opengraph.htmltwitter_cards.html にあった独自の image フィールド分岐を削除した。 テーマ標準の処理に乗せただけで、絶対URL化も含めて解決した。 独自の上書きコードより、テーマが最初から用意している仕組みを先に確認すべきだった。