0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

記事のアイキャッチ(OGP画像)をCloudflare Workers AIで0円・全自動生成する(flux-1-schnell+SVGフォールバック)

0
Last updated at Posted at 2026-07-09

この記事の要点

  • 記事のアイキャッチ(OGP画像)を、Cloudflare Workers AIのflux-1-schnellで自動生成する構成を実装した。
  • 画像モデルが不安定・混雑でも公開を止めないため、決定論的なSVGフォールバックを必ず用意する。
  • Cloudflareの無料枠のみ(Workers/Pages/R2/Workers AIの無料分)で回しており、追加費用は0円。実運用のコードを交えて解説する。

なぜアイキャッチを自動生成するのか?

コンテンツを自動生成するパイプラインを組むと、最後に必ず「アイキャッチ画像をどうするか」が残ります。人手で毎回作るとそこが律速になり、「全自動」が崩れます。かといって有料の画像APIを常用すると、記事1本あたりのコストが積み上がります。

そこで、Cloudflare Workers AIの画像生成モデルを使い、記事タイトルからアイキャッチを自動生成する構成にしました。ポイントは「生成に失敗しても公開は止めない」ことです。

使うモデルと料金の考え方

Workers AIは無料枠(1日あたりのNeuron上限)内なら追加課金なしで利用できる。上限を超えた分だけ従量課金になる。

画像は @cf/black-forest-labs/flux-1-schnell を使います。schnell(=ドイツ語で「速い」)の名の通り数ステップで生成でき、レスポンスが速くバッチ向きです。生成結果はbase64のPNGで返るので、そのままR2に保存してOGPのURLにします。

実装:生成 → 失敗時フォールバック

肝は try/catchで画像生成を包み、失敗したら例外を投げずにSVGを返す ことです。公開パイプラインから安全に呼べます。

export interface OgpInput {
  title: string
  category?: string
  slug: string
}

// Workers AI で PNG を生成。失敗したら null を返す(例外を投げない)
async function generateFluxPng(env: Env, prompt: string): Promise<ArrayBuffer | null> {
  try {
    const res: any = await env.AI.run('@cf/black-forest-labs/flux-1-schnell', {
      prompt,
      steps: 4,
    })
    // flux は { image: base64 } を返す
    const b64 = res?.image
    if (!b64) return null
    return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)).buffer
  } catch (e) {
    console.warn('flux failed, will fall back to SVG', e)
    return null
  }
}

フォールバックのSVGは外部リソースに一切依存しない(フォントも色もインラインで完結する)ように書きます。これで「必ず何かは出る」を保証できます。

function fallbackSvg(d: OgpInput): string {
  const title = escapeXml(d.title).slice(0, 40)
  const cat = escapeXml(d.category || '宅建コラム')
  return `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630">
    <defs>
      <linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
        <stop offset="0" stop-color="#0b1e3f"/>
        <stop offset="1" stop-color="#123a6b"/>
      </linearGradient>
    </defs>
    <rect width="1200" height="630" fill="url(#g)"/>
    <text x="80" y="150" fill="#7fd4ff" font-size="34" font-family="sans-serif">${cat}</text>
    <text x="80" y="330" fill="#ffffff" font-size="64" font-weight="bold"
          font-family="sans-serif">${title}</text>
  </svg>`
}

function escapeXml(s: string): string {
  return s.replace(/[<>&"']/g, (c) =>
    ({ '<': '&lt;', '>': '&gt;', '&': '&amp;', '"': '&quot;', "'": '&#39;' }[c] as string),
  )
}

呼び出し側は「PNGが取れればPNG、ダメならSVG」を選ぶだけです。

export async function buildOgp(env: Env, d: OgpInput): Promise<{ body: BodyInit; contentType: string }> {
  const prompt = `A clean, professional cover image for an article titled "${d.title}". Minimal, blue tones, no text.`
  const png = await generateFluxPng(env, prompt)
  if (png) return { body: png, contentType: 'image/png' }
  return { body: fallbackSvg(d), contentType: 'image/svg+xml' }
}

よくある失敗

  • 画像生成の失敗で公開が止まる:モデルは混雑や一時エラーで落ちることがある。生成は「あれば嬉しい」程度に扱い、フォールバックを本線として設計する。
  • フォールバックが外部依存:Webフォントや外部画像を参照するSVGにすると、そこが落ちたときに二重で失敗する。色もフォントもインラインで完結させる。
  • base64のデコードミスatobの結果は文字列なので、Uint8Array.from(..., c => c.charCodeAt(0))でバイト列に戻す。ここを飛ばすと画像が壊れる。
  • プロンプトに文字を描かせる:画像モデルは日本語テキストの描画が苦手。**「no text」**を明示し、タイトル文字はSVG側で載せると破綻しない。

まとめ

  • アイキャッチ自動生成は「生成できたら使う」「失敗したら決定論的SVG」の二段構えにすると、全自動パイプラインが止まらない。
  • Cloudflare Workers AIの無料枠で回せば、記事量産のコストは実質0円に抑えられる。
  • 実運用では宅建学習アプリ「宅建BOOST」のコラム自動生成に組み込み、毎時の予約公開まで無人で回しています。

参考: 宅建BOOST コラム(この構成で生成したアイキャッチを使っています)

#Cloudflare #TypeScript #生成AI #OGP

公式サイト: https://my-agent.work/

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?