GenvisoとBetterTokenで画像生成をテンプレート化する

ビジュアル探索とバックエンド実行を分離し、よいプロンプトをバージョン管理された本番テンプレートへ変える実践手順です。

よい画像が一枚できても、それだけでは本番運用の仕組みにはなりません。一枚なら、プロンプトを書き直して結果を見比べ、手作業で選べます。しかしSKUが数百件になると、その方法は高コストな試行の連続になります。光、アングル、素材を変えるたびに新しいリクエストが発生し、なぜ成功したのかは担当者の記憶にしか残りません。

作業を二つの循環に分けます。最初に、チームがビジュアルの方向性を試し、再利用できる規則を記録します。次に、バックエンドが承認済みテンプレートへ業務データを入れ、リクエスト送信、結果保存、エラー処理を行います。創造的な探索は本番キューの外に残り、構図を直すたびにサーバーコードを変更する必要がなくなります。

コード内でのプロンプト調整が破綻しやすい理由

関連する変数が多すぎる

画像モデルは、被写体、環境、照明、カメラ位置、素材、被写界深度、色調へ同時に反応します。美容液ボトルの写真でも、次の違いが結果を大きく変えます。

  • 正面構図と45度の俯瞰構図
  • 硬い指向性照明と柔らかな拡散光
  • 反射を強調したガラスとマットな表面
  • トラバーチン、金属、無地の紙による背景
  • 85mmマクロ風と広角の構図

複数の要素を同時に変えると、どの表現が効いたのか判断できません。一つずつ変えると、今度はリクエスト数が増えます。BetterTokenを使うバックエンドはImage APIでテンプレートを実行できますが、検証前にキューを動かすと、未確認の仮説を大量に複製するだけです。

用途ごとにビジュアル文法が異なる

商品カードには、読み取りやすい輪郭、制御された反射、レイアウト用の余白が必要です。3Dイラストでは形状と素材の条件が異なります。SNSポスターでは情報階層、コントラスト、安全領域が重要です。すべてを一つのプロンプトで扱うと、矛盾する形容が増えていきます。

用途別のテンプレート群を持つ方が実用的です。

skincare_product luxury_watch food_photography 3d_illustration social_poster

各テンプレート群に必須項目と受入基準を定義します。アプリケーションは商品カテゴリからテンプレートを選び、個別の商品やキャンペーンの値を入れます。

探索と本番運用では規則が違う

探索では多数の案と主観的な比較を許容できます。本番運用には、予測可能な契約、テンプレートの版、回数を制限した再試行、ジョブ識別子、明確な合否判定が必要です。

コード内のプロンプトを変更 → APIリクエストを送信 → 画像を開く → 再びコードを変更 → 次のリクエストを送信

この流れには、ビジュアル案を承認済みとする地点がありません。そのため、デザイン上の相談が毎回バックエンドとタスクキューに影響します。

ビジュアル仮説からCMSのファイルまで

ビジュアル探索では、チームがGenvisoのビジュアルプロンプトギャラリーで候補を比較し、構図、光、スタイルを検証して、うまくいった構造を保存します。サーバー実行では、アプリケーションがBetterTokenのOpenAI互換Base URLと利用者自身のAPI Keyを使い、値を挿入し、現在利用できるモデルを呼び出して結果を記録します。二つの工程で受け渡すものは、選んだ画像や口頭指示ではなく、版を持つPrompt Templateです。

キュー接続前にこの境界を試すには、自分のAPI Keyを作成し、承認済みテンプレートで確認リクエストを一回実行して、モデル、状態、実際の課金をDashboardですぐ照合します。これならビジュアル探索を本番リクエストの連続にせず、サーバー経路だけを確認できます。

ビジュアル探索 ↓ Prompt Templateの検証 ↓ 変数と制約の確定 ↓ PIM / CMS / SKUデータの挿入 ↓ バックエンドからImage APIを呼び出す ↓ ファイル、状態、メタデータを保存 ↓ ビジュアル受入判定

工程ごとに確認可能な成果物を用意します。

工程成果物次へ進む条件
ビジュアル探索採用案と不採用案影響する要素が特定できている
テンプレート検証名前付き変数を持つプロンプト代表的な複数商品で成立する
統合描画関数と入力スキーマAPI前に必須項目を検証できる
テスト保存ファイルとリクエスト記録ファイルが開き、モデルと状態が正しい
本番template_id、版、job_idを持つタスク再試行が有限で結果がSKUに結び付く

第1段階:ビジュアル案をテンプレートにする

化粧品のスタジオ撮影なら、次の構造から始められます。

Commercial studio product photography of {subject}. Environment: {environment} Visual style: {visual_style} Lighting: {lighting} Composition: {composition} Color palette: {color_palette} Crisp reflections, premium material texture, high-end commercial editorial photography.

説明順序とビジュアルの軸は固定し、subjectenvironmentvisual_stylelightingcompositioncolor_paletteの値だけを個別に変えます。

開発者へ渡す前に、さらに四点を決めます。

  1. 必須項目。 subjectcompositionがなければ、リクエストを送信しません。
  2. 許可する値。 アングルが三種類なら、CMSの自由入力ではなく列挙値にします。
  3. 禁止する組み合わせ。 透明な容器と鏡面背景には、別テンプレートが必要かもしれません。
  4. 受入基準。 商品の輪郭が読める、ロゴが変形しない、商品が切れない、背景が最終レイアウトに使えることを確認します。

プロンプトの隣に機械可読な契約を保存します。

{ "template_id": "skincare_product_v3", "required_variables": [ "subject", "environment", "visual_style", "lighting", "composition", "color_palette" ], "output_size": "1024x1024" }

template_idの版は再現性のために必要です。照明や構図が変わったら新しいタスクだけ次の版を使い、既存素材は以前の版との関係を維持します。

第2段階:テンプレートをバックエンドへ接続する

最初のテストには、公式Python SDKのopenai、自分のBetterToken API Key、現在のImage APIドキュメントで確認したModel IDを使います。キーとモデルは環境変数に置きます。

python -m pip install openai export BETTERTOKEN_API_KEY="your_api_key_here" export BETTERTOKEN_IMAGE_MODEL="current_image_model_id"

実際のキーをリポジトリ、プロンプト、スクリーンショット、ログへ入れてはいけません。本番ではシークレット管理を使い、アプリケーションや環境ごとにキーを分けます。

次の例はテンプレートを描画し、一回のリクエストを送り、b64_jsonからPNGを保存します。

import base64 import os from pathlib import Path from typing import Mapping from openai import OpenAI client = OpenAI( base_url="https://www.bettertoken.ai/v1", api_key=os.environ["BETTERTOKEN_API_KEY"], ) def render_product_prompt(variables: Mapping[str, str]) -> str: return f""" Commercial studio product photography of {variables['subject']}. Environment: {variables['environment']} Visual style: {variables['visual_style']} Lighting: {variables['lighting']} Composition: {variables['composition']} Color palette: {variables['color_palette']} Crisp reflections, premium material texture, high-end commercial editorial photography. """.strip() product = { "subject": "frosted amber glass serum bottle with a minimalist gold dropper", "environment": "organic travertine pedestal surrounded by subtle water ripples", "visual_style": "high-end botanical skincare editorial", "lighting": "warm directional morning rim light with soft diffused fill", "composition": "centered 85mm macro product photography with shallow depth of field", "color_palette": "earthy amber, warm beige and subtle gold", } response = client.images.generate( model=os.environ["BETTERTOKEN_IMAGE_MODEL"], prompt=render_product_prompt(product), size="1024x1024", n=1, ) image_base64 = response.data[0].b64_json if not image_base64: raise RuntimeError("Image API response does not contain b64_json") output_path = Path("serum-product.png") output_path.write_bytes(base64.b64decode(image_base64)) print(f"Saved: {output_path}")

client.images.generate(...)b64_jsonのデコードは、現在のSDK契約に沿っています。モデルはBETTERTOKEN_IMAGE_MODELから読むため、Prompt Templateや業務ロジックを書き換えずに変更できます。

最小の一括ジョブループ

次は統合層を明示する擬似コードです。save_jobgenerate_imageApiErrorはストレージとAPIクライアント用のアダプターを表し、SDKの追加メソッドではありません。

MAX_ATTEMPTS = 3 RETRYABLE_STATUS = {429, 500, 502, 503, 504} for sku in sku_rows: variables = validate_variables(sku) # API呼び出し前 prompt = render_product_prompt(variables) job_id = uuid4().hex prompt_hash = sha256(prompt.encode()).hexdigest() save_job(job_id=job_id, sku_id=sku["id"], template_id="skincare_product_v3", prompt_hash=prompt_hash, status="pending") for attempt in range(1, MAX_ATTEMPTS + 1): save_job(job_id=job_id, status="running", attempt=attempt) try: result = generate_image(prompt) except ApiError as error: if error.status_code in {400, 401}: save_job(job_id=job_id, status="failed", error_code=error.status_code) break if error.status_code not in RETRYABLE_STATUS or attempt == MAX_ATTEMPTS: save_job(job_id=job_id, status="failed", error_code=error.status_code) break sleep(min(2 ** attempt, 8)) continue except TimeoutError: save_job(job_id=job_id, status="unknown", error_code="timeout") break # 再送信前にDashboardと保存先を確認 if not result.b64_json: save_job(job_id=job_id, status="failed", error_code="empty_output") break output_path = persist_png(job_id, result.b64_json) save_job(job_id=job_id, status="succeeded", output_path=output_path, model=result.model, attempt=attempt) break

ローカルのjob_idはSKU、テンプレート、ファイルを結び付けますが、外部リクエストを冪等にはしません。タイムアウト後はunknownのまま、Dashboardを時刻で検索し、ストレージを確認してから一回だけ再送信するか判断します。

問題を確認する順序

症状最初に確認修正再確認
400必須変数、現在のModel ID、対応するsizeデータか引数を修正し、同じ要求を自動再送しない確認SKU一件を実行してPNGを開く
401Key変数の読み込み、所有者、プロトコルKeyをログへ出さず交換または再作成最小要求を送りDashboardで状態を探す
429同時実行数とタスク頻度新規投入を止め、同時実行を減らし有限backoffを使う一件を通してから負荷を徐々に戻す
5xx要求時刻と試行回数MAX_ATTEMPTSまでに限定し、時刻と状態を保存テンプレートを変えず、待機後に一件試す
タイムアウトDashboardとストレージunknownを維持し、失敗と決めつけない記録もファイルもなければ同じローカルjob_idで一回再送する
空のb64_jsonまたはデコード失敗現在の応答形式、モデル、引数Keyを含めずエラーを保存し、解析か設定を修正一件を再実行してPNGが開くか確認

一括生成の前に追加するもの

リクエスト前にデータを検証する

空のmaterialproduct_name内の想定外マークアップ、承認色ではない自由文はプロンプトを変えます。必須項目、長さ、許容値を確認し、最終プロンプトのハッシュ、template_id、SKU識別子をタスクと一緒に保存します。

再試行を制限する

タイムアウト後の再試行は、最初の応答を受け取れなかった場合でも別の画像を作る可能性があります。試行回数を有限にし、待ち時間を設け、各実行へjob_idを付けます。400、401、モデル設定のエラーを無限に再試行せず、データ、キー、設定を先に直します。

技術判定とビジュアル判定を分ける

HTTP 200と有効なPNGは技術的成功を示します。構図、商品の変形、ブランド適合性は別に評価します。自動処理はファイルとメタデータを保存し、次の工程でテンプレートのビジュアル基準を適用します。

利用記録と照合する

確認用の生成後、時刻を使ってDashboardのリクエストを探し、モデル、状態、課金を確認します。利用項目には入力、出力、キャッシュのTokenが表示されます。Dashboardは利用量と支出のメタデータを扱い、完全なプロンプトや応答を保存する場所ではありません。予算には最新のモデルと価格ページを使い、実際のテスト消費はリクエスト記録で確認します。

商品カタログの処理例

PIM / SKUデータベース ↓ 商品カテゴリ → template_id ↓ 名称 / 素材 / 色 / 背景 ↓ 必須項目の検証 ↓ Prompt Templateの描画 ↓ job_id付き生成タスク ↓ Image API ↓ オブジェクトストレージ ↓ ビジュアル受入判定 ↓ CMS / メディアライブラリ

プロンプトはバージョン管理された本番オブジェクトになります。どのテンプレートがファイルを作ったかを追跡し、版ごとの不採用率を比較し、統合全体を書き直さずに問題のある変更を戻せます。

開始前チェックリスト

  • 代表商品と難しい境界事例でテンプレートを試した。
  • template_id、必須変数、受入基準を定義した。
  • API Keyをソースコードやログに入れていない。
  • Model IDを環境変数または設定から読む。
  • 一回のテストで期待サイズの有効なファイルができる。
  • 400/401では再試行前にデータか設定を修正する。
  • 429/5xxの再試行回数が有限である。
  • 各タスクがSKU、job_id、テンプレート版、保存先に結び付く。
  • 技術検証とビジュアル受入を分けた。
  • モデル、状態、テスト消費をDashboardで照合した。

役割分担が共同作業を簡単にする

Genvisoは対話的な工程を担当し、ビジュアル方向の探索、プロンプト比較、開発者へ渡す前のテンプレート検証を行います。BetterTokenはサーバー工程を担当し、API Key、OpenAI互換接続、利用可能なモデルの呼び出し、利用記録を扱います。両チームが共有する契約は、変数、版、受入基準を持つPrompt Templateです。

承認済みテンプレートを動作するバックエンドへ移し、代表SKU一件で消費を確かめるには、自分のAPI Keyを作成し、Image APIリファレンスの最小リクエストを実行して、キュー接続前にDashboardでモデル、状態、課金を確認します。

LLM ワークフローを最適化しませんか?

単一 API でモデルを接続し、キーと AI コストを管理できます。