← ブログ

Z-Image-Base API 完全ガイド:CFG+ネガティブプロンプト実践活用

Z-Image-Base API 完全チュートリアル:CFGパラメータのチューニング、negative_promptの使い方、参照画像ガイダンス、強度コントロール。Python/cURLコード例付き。

3 min read
Z-Image-Base API 完全ガイド:CFG+ネガティブプロンプト実践活用

こんにちは、Doraです。小さな不満が私をZ-Image-Base APIへと駆り立てたのは不思議な話です。手元には参照画像のバッチがあり、一貫した見た目が必要なコピーがあり、プロンプトを逐一調整する時間はありませんでした。人々が設定を手動で微調整して一発ネタの成果を投稿しているのをよく見かけました。デモならそれでもいい。でも私の仕事には、週2回実行しても驚きのないものが必要でした。そこで2026年1月〜2月の数回のセッションを通じて、Z-Image-Baseを組み込み、どこで抵抗されるかに注意を払い、実際に信頼できると感じさせた要素をメモしました。

これはすべてのパラメータを網羅したガイドではありません。私が繰り返し触れた部分と、精神的な労力を減らした小さな調整についての話です。AIツールに囲まれていて、ただ素直に動く一つを求めているなら、参考になるかもしれません。

APIの基本設定

WaveSpeed APIキーの取得

WaveSpeedダッシュボードでサインアップし、APIキーを取得しました。特に驚くことはありませんでした。一点だけ:キーの権限はプロジェクト単位でスコープされていました。共有ワークフローでは便利ですが、プロジェクト名を最初から明確にしておく必要があります。ログやクォータが本番環境と混ざらないよう、画像実験専用のプロジェクトを作成しました。

キーをローテーションする場合(私は月次でやっています)、Z-Image-Baseはクリーンに対応しました。環境変数のシークレットを差し替えてスクリプトを再実行したところ、認証状態が残留することもありませんでした。本来そうあるべきですが、一発で動いたときは少しほっとしました。

エンドポイントURLの構造

ベースはモデル切り替え付きの標準RESTセットアップのような形でした:

  • ベース:https://api.wavespeed.ai
  • バージョン付きパス:/v1/images
  • モデルセレクタ:model: "Z-Image-Base"

実際には、プロンプトのみの実行には/v1/images/generationsへPOSTリクエストを送り、参照画像を渡す場合は/v1/images/editsを使用しました。クエリ文字列に詰め込むより明確なパスの方が好みで、これは私の思考様式と合っていました。ルーティングが異なる場合でも、モデル名は重要です:Z-Image-Baseは派手なモデルよりもやや保守的に見えましたが、それが気に入りました。

認証ヘッダーの設定

特に難しいことはありませんが、正確なスペルが重要です:

  • Authorization: Bearer YOUR_WAVESPEED_API_KEY
  • プロンプトのみの場合:Content-Type: application/json
  • 画像をアップロードする場合:Content-Type: multipart/form-data

キーは.envに保存し、実行時に読み込んでいます。地味なやり方ですが、誤ったコミットを防げます。また、CIからスクリプトを実行する場合は、キーをマスクされたシークレットとして設定し、完全なリクエストのログを避けてください。当然のことですが、言っておく価値があります。一行のログが後々悩みの種になることがあります。WaveSpeedのセキュリティベストプラクティスによると、APIキーは環境変数に保存し、フロントエンドのコードに公開せず、定期的にローテーションすべきとされています。

コアパラメータの詳細説明

prompt - 順方向プロンプトの書き方

プロンプトのスタイルについては深く考えすぎませんでした。Z-Image-Baseでは、凝った文体より単純な順方向プロンプトの方が効果的でした。意図を先に書き、制約を後に書くようにしました:

  • 「リネンのテーブルにセラミックマグを置いたエディトリアル調のプロダクト写真、柔らかな朝の光。」
  • そして具体的な指定:「35mmの質感、ニュートラルなカラーグレーディング、テキストなし、すっきりした背景。」

動きが必要でない限り、動詞は入れませんでした。素材、光、レンズの感触、背景のタイプといった具体的な内容にするとモデルは安定しました。雰囲気を加えると迷走しました。「居心地の良いミニマリズム」と書くとキャンドルだらけになりました。「柔らかい光、拡散した一つの光源、キャンドルなし」と書くと従ってくれました。

negative_prompt - 不要な要素の除外

最初はネガティブプロンプトに抵抗がありましたが、安全柵として欠かせない存在になりました。再利用する短いベースラインを用意しました:

  • 「テキストなし、透かしなし、余分な手なし、余分な手足なし、ロゴなし、署名なし、ボーダーなし」

これを追加することでクリーンアップ時間が削減されました。すべてのエッジケースを解決できたわけではありません(マグカップにリングが紛れ込むことはまだありました)が、少量のバッチでは再レンダリングが必要なほどの奇妙な結果の発生率が下がりました。厳密なプロダクトアングルが必要な場合は「ドラマチックなパースなし、傾きなし」を追加したところ、よりレベルなショットが得られました。

guidance_scale(CFG)- プロンプト準拠度の制御

これは想定以上に重要でした。私のメモ:

  • 5〜6:緩め、テクスチャと即興性が少し増す
  • 7〜8:信頼できる:ほとんどの実行で使用
  • 9以上:文字通りの準拠、ただし照明が脆く色が平坦になることがある

良い参照画像がある場合はCFGを6〜7に下げると、マイクロコントラストが良くなりました。参照なしの場合は7.5が中央値でした。Z-Imageの技術ドキュメントによると、このモデルはフルClassifier-Free Guidance(CFG)をサポートしており、複雑なプロンプトエンジニアリングに精度をもたらします。9以上は慎重な友人にさらに慎重になれと言うようなもので、間違いではないですが、少し生気が失われる感じでした。

image + strength - 参照画像ガイダンス

このペアが一番活躍しました。単一の参照JPGを添付し、strengthを調整してモデルがどの程度従うかを制御しました:

  • 0.25〜0.35:軽いタッチ:パレットと雰囲気を維持
  • 0.45〜0.6:しっかりした準拠:構図が近い状態を保つ
  • 0.7以上:ほぼコピー:小さなスタイル変更のバリエーションに有用

0.5がスイートスポットになるとは思いませんでしたが、そうでした。レイアウトを維持するのに十分な構造と、照明調整のための十分な自由度がありました。強度を上げるとモデルが頑固になりました:ソースに影が間違った位置にある場合、モデルはそれに固執しました。代わりにプロンプトに「カメラ左の影」と加えてstrengthを下げるようにしました。そうすることで再試行が減りました。

実用的なメモ:参照画像はアップロード前に長辺1024にリサイズしました。寸法を統一することで、クロップと速度の予測可能性が上がりました。多数のイテレーションにわたる帯域幅の節約にもなりました。

コード実装

cURLによるクイックテスト

新しいエンドポイントを組み込む際は、レスポンスの形状とエラーコードを確認するためにまずcURLから始めます。

curl -X POST \
  https://api.wavespeed.ai/v1/images/generations \
  -H "Authorization: Bearer $WAVESPEED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Z-Image-Base",
    "prompt": "Editorial-style product photo of a ceramic mug on a linen table, soft morning light. 35mm look, neutral color grading, no text, uncluttered background.",
    "negative_prompt": "no text, no watermark, no extra hands, no extra limbs, no logo, no signature, no border",
    "guidance_scale": 7.5,
    "size": "1024x1024"
  }'

画像の配列(オプションに応じてbase64またはURL)を含むJSONペイロードが返ってきました。直接URLが好みなら、APIがサポートしている場合は出力先またはストレージフラグを設定してください。テスト中はbase64のままローカルでデコードしました。

Pythonの完全な例

これは火曜日の朝のバッチジョブで実行したものに近いコードです。シンプルで、リトライに十分な構造を持っています。

import os
import base64
import requests

API_KEY = os.getenv("WAVESPEED_API_KEY")
BASE_URL = "https://api.wavespeed.ai/v1"

headers = {
    "Authorization": f"Bearer {API_KEY}",
}


def generate_image(prompt, negative_prompt, guidance_scale=7.5, size="1024x1024"):
    url = f"{BASE_URL}/images/generations"
    
    payload = {
        "model": "Z-Image-Base",
        "prompt": prompt,
        "negative_prompt": negative_prompt,
        "guidance_scale": guidance_scale,
        "size": size,
    }
    
    response = requests.post(
        url,
        headers={**headers, "Content-Type": "application/json"},
        json=payload,
        timeout=60
    )
    response.raise_for_status()
    
    data = response.json()
    b64_string = data["data"][0]["b64_json"]
    return base64.b64decode(b64_string)


if __name__ == "__main__":
    prompt = (
        "Editorial-style product photo of a ceramic mug on a linen table, soft morning light. "
        "35mm look, neutral color grading, no text, uncluttered background."
    )
    negative_prompt = (
        "no text, no watermark, no extra hands, no extra limbs, "
        "no logo, no signature, no border"
    )

    try:
        image_bytes = generate_image(prompt, negative_prompt)
        with open("output.png", "wb") as f:
            f.write(image_bytes)
        print("Saved output.png")
    except requests.HTTPError as e:
        print("HTTP error:", e.response.text if e.response else str(e))
    except Exception as e:
        print("Error:", str(e))

不安定なWi‑Fiでジョブが止まった後、60秒のタイムアウトを追加しました。例外ブロックは簡潔ですが、本番ではステータスコード(429、503)をバックオフにマッピングしています。

JavaScript/Node.jsの例

Nodeではfetchと小さなヘルパーを使いました。同じ考え方:認証をシンプルに保ち、エラーを明確に表示する。

import fs from 'node:fs/promises';
import fetch from 'node-fetch';

const API_KEY = process.env.WAVESPEED_API_KEY;
const BASE_URL = 'https://api.wavespeed.ai/v1';

async function generateImage({
  prompt,
  negativePrompt,
  guidanceScale = 7.5,
  size = '1024x1024',
}) {
  const response = await fetch(`${BASE_URL}/images/generations`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: 'Z-Image-Base',
      prompt,
      negative_prompt: negativePrompt,
      guidance_scale: guidanceScale,
      size,
    }),
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`HTTP ${response.status}: ${errorText}`);
  }

  const json = await response.json();
  const base64String = json.data[0].b64_json;
  return Buffer.from(base64String, 'base64');
}

(async () => {
  try {
    const prompt = 
      'Editorial-style product photo of a ceramic mug on a linen table, soft morning light. ' +
      '35mm look, neutral color grading, no text, uncluttered background.';

    const negativePrompt = 
      'no text, no watermark, no extra hands, no extra limbs, ' +
      'no logo, no signature, no border';

    const imageBuffer = await generateImage({ prompt, negativePrompt });
    
    await fs.writeFile('node-output.png', imageBuffer);
    console.log('Saved node-output.png');
  } catch (err) {
    console.error('Error:', err.message);
  }
})();

これが動くようになったら、配管のことを考えなくなりました。それが目的でした。Z-Image-Base APIは邪魔をしませんでした。

非同期モードと同期モード

両方のモードを試しました。同期は単一画像や素早い確認に問題ありませんでした。1024正方形の出力では、レイテンシは平均して数秒程度でした。8〜12個のレンダリングをキューに入れる場合は、スクリプトが各ジョブでブロックしないよう非同期に切り替えました。

非同期パターンは予測可能でした:ジョブを送信し、idを取得し、ステータスエンドポイントをポーリングし、state: "succeeded"になったら結果を取得する。2〜3秒のポーリング間隔と2分の上限を設定しました。不安定なネットワークでは、ジョブを再送信せずにポーリングを再開できるため、非同期でタイムアウトを回避できました。小さな違いですが、移動中のWi‑Fiでは大きな差になります。

ワークフローがインタラクティブ(調整→確認→調整)なら同期の方が使いやすいです。バッチ処理やスケジュール実行なら、非同期の方がパイプラインをスムーズに保ち、エラー時の復帰も洗練されています。

エラーハンドリング

エラーを退屈なものに保つために役立った2つのパターンがあります。

  • レート制限(429):ジッター付きの指数バックオフを使いました。約500msから始め、約20sで上限を設け、5〜6回試行後に停止。非同期ジョブでは、APIがジョブの消失を示した場合を除き、レンダリングを再キューするのではなくステータス呼び出しを再試行しました。
  • バリデーションエラー(400/422):主にコンテンツタイプの不一致やサポートされていないサイズが原因でした。画像編集で422が出た場合は、ほぼ必ずフォームデータのフィールド名が間違っていました(imagereference_imageの混同)。これを防ぐために、リクエスト前に小さなスキーマチェックを追加しました。

もう一点:modelguidance_scalesize、プロンプトのハッシュをログに記録しました。何かおかしなものが通り抜けたとき、正確に再現できました。首をかしげて終わるか修正できるかの違いです。

Z-Image-Baseは他のモデルより失敗が少ないでしょうか?一概には言えません。私が気づいたのは、失敗したとき、メッセージが十分に具体的で推測に時間を取られなかったことです。それは良い人間工学です。