mcpField
B
bytedance-seedream

bytedance-seedream

github.com/calesthioクリエイティブ・メディア219106 回インストール15 週間前に更新
リポジトリ

bytedance-seedream について

ByteDance のファーストパーティ Ark 画像生成 API から Seedream を利用するアプリ向けのスキルです。モデルとリージョンの選択、リクエスト作成、参照画像による編集、複数画像生成、ストリーミング、応答検証を案内します。

主な機能

  1. 01リージョンと機能に応じたモデル選択
  2. 02テキストプロンプトからの画像生成
  3. 03参照画像の編集と合成
  4. 04複数画像生成とストリーミング
  5. 05リクエストと API 応答の検証

活用シーン

  1. 01一連のキャンペーン画像の作成
  2. 02複数の参照画像を使った編集
  3. 03生成結果のストリーミングと検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/calesthio/generative-media-skills --skill bytedance-seedream

インストール後、Claude Code で/bytedance-seedreamを入力して読み込まれたか確認します。

同梱ファイルEVAL.md

詳細

ByteDance Seedream

アプリケーションがファーストパーティの Ark 画像生成 API を通じて Seedream を直接呼び出す場合に、このスキルを使用してください。テキストからの画像生成、参照画像を使った生成、自然言語による編集、複数画像の融合、グループ出力、ストリーミングによるグループ出力を扱います。Seedance による動画生成、サードパーティのゲートウェイ、コンシューマー向けアプリ、モデルのトレーニング、UI の自動化は対象外です。

以下の変動する情報は、2026-07-09 にファーストパーティのドキュメントで確認しました。本番環境への導入前に、リンク先のモデルカタログ、提供状況、料金、提供終了に関するページを再確認してください。

根拠ラベル

  • ドキュメントで確認済み は、ByteDance Seed、Volcengine、または BytePlus の最新ページに記載されている動作を意味します。
  • プロバイダーの主張 は、ファーストパーティの製品ページまたは研究ページによる定性的な性能上の主張です。アプリケーションのデータでテストするまでは、マーケティング情報として扱ってください。
  • 運用上の推奨事項 は、ドキュメントに記載された仕様から導いた本番環境での実践事項であり、プロバイダーによる保証ではありません。
  • 不明 は、確認したファーストパーティのページで動作が明らかにされていないことを意味します。仕様を作り上げず、機能を検出するか、サポートに問い合わせてください。

まず利用する商用環境を選択する

モデル ID とホストは相互に置き換えられません。

デプロイ先API のベース URL2026-07-09 時点でドキュメントに記載されている現行の画像モデル ID重要な制約
Volcengine Ark、中国https://ark.cn-beijing.volces.com/api/v3doubao-seedream-5-0-pro-260628、doubao-seedream-5-0-260128(別名 doubao-seedream-5-0-lite-260128 としても記載)、doubao-seedream-4-5-251128、doubao-seedream-4-0-250828中国のホストと doubao-... ID のみ
BytePlus ModelArk、アジア太平洋https://ark.ap-southeast.bytepluses.com/api/v3seedream-5-0-260128(別名 seedream-5-0-lite-260128 としても記載)、seedream-4-5-251128、seedream-4-0-250828グローバルホストと doubao で始まらない ID のみ
BytePlus ModelArk、欧州https://ark.eu-west.bytepluses.com/api/v3Seedream 5.0 Lite は EU 向けに記載されています。それ以外のバージョンをデプロイする前に、最新のカタログを確認してください提供状況のページでは、Seedream 4.0 を含む制限対象モデルの EU/EU 市場での使用を禁止しています

3 つすべてで、次を使用します。

POST {API_BASE}/images/generations
Authorization: Bearer {ARK_API_KEY}
Content-Type: application/json

中国のトークンを BytePlus のホストに送ったり、BytePlus のトークンを中国のホストに送ったりしないでください。ホストの許可リストを厳密に設定し、キーを決してログに記録せず、シークレットマネージャーから実行時に注入してください。

ドキュメントで確認済みのリージョンに関する注意事項: BytePlus によると、AP へのリクエストは通常 AP で処理されますが、EU で処理される場合があります。また、必要に応じて EU へのリクエストが AP で処理される場合もあります。そのため、エンドポイントの選択はデータレジデンシーを保証するものではありません。データレジデンシーが要件である場合は、契約上の回答を得てください。

名前だけでなく、契約内容に基づいてモデルを選択する

機能China 5.0 Pro5.0 Lite4.54.0
テキスト生成および参照/編集入力はいはいはいはい
参照画像の最大数101414保守的には10。汎用 API では14と記載
グループ化出力(sequential_image_generation)いいえはいはいはい
SSE ストリーミング(stream)いいえはいはいはい
web_search ツールいいえはいいいえいいえ
出力形式の制御PNG/JPEGPNG/JPEGJPEG のみJPEG のみ
解像度ラベル1K/2K2K/3K/4K2K/4K1K/2K/4K
プロンプト最適化standard。fast は不可standard。fast は不可standard。fast は不可standard または fast

5.0 Pro の行は、上記で説明した China モデルに適用されます。BytePlus の現在のグローバル画像 API には、グローバルな 5.0 Pro ID は記載されていません。存在しない ID を作らないでください。

**ファーストパーティの 4.0 に関する矛盾:**現在の汎用画像 API では参照入力は2~14件と説明されていますが、Seedream 4.0 のモデル固有カードでは、複数画像から1枚、および複数画像から複数枚を生成するワークフローの入力数は2~10件と説明されています。4.0 の本番環境での検証では、デフォルトを10件にしてください。11~14件を使用するのは、認可されたライブカナリアでエンドポイントの正確な契約を確認した後に限り、レスポンスと取得日を記録してください。

グループ化出力、SSE、または任意のリアルタイムウェブ検索が必要な場合は、5.0 Lite を使用してください。ドキュメントに記載された高性能な単一画像の契約が必要で、グループ化出力やストリーミングが不要な場合は、China 5.0 Pro を使用してください。4.5 または 4.0 を使い続けるのは、互換性、品質、コスト上の理由を実測で確認できた場合に限り、移行テストも維持してください。

Seedream 5.0 Lite のファーストパーティリリースページでは、マルチモーダル推論、世界知識、任意のリアルタイム検索について説明されています。これらはプロバイダーの主張です。代表的なプロンプトを使用して、事実の正確性と視覚的品質を評価してください。

リクエストの契約

基本フィールド
  • model は必須で、ドキュメントに記載されたモデル ID または設定済みのエンドポイント ID を指定できます。
  • prompt は必須です。ガイドでは、中国語で約300文字以内、または英語で600語以内を推奨しています。これは推奨事項であり、サーバーのハードリミットとして記載されているものではありません。
  • image は任意で、単一の URL/データ URL、または配列を指定できます。データ URL は data:image/<lowercase-format>;base64,<payload> の形式である必要があります。
  • 入力形式:JPEG、PNG、WebP、BMP、TIFF、GIF、HEIC、HEIF。各辺の長さは14 pxを超えている必要があり、アスペクト比は [1/16, 16] の範囲内、ファイルサイズは最大30 MB、画像の総面積は最大36 MPです。
  • size には、モデルがサポートする解像度ラベルまたは明示的な WIDTHxHEIGHT 文字列を指定できます。送信前に、選択したモデルで有効か確認してください。
  • response_format は url または b64_json で、デフォルトは url です。返された URL は24時間後に有効期限が切れます。
  • watermark はデフォルトで true であり、視認可能な中国語の「AI 生成」マークを追加します。false にすると、その視認可能なマークは削除されますが、開示、来歴、または法的義務が免除されるわけではありません。
  • output_format は現在の 5.0 モデルでのみ png または jpeg を指定でき、デフォルトは JPEG です。変換を期待して 4.5/4.0 に送信しないでください。

現在の正式な 4.0~5.0 API ページでは、これらのモデルの seed または guidance_scale は記載されていません。BytePlus では、これらの制御をレガシー 3.0 モデルについてのみ説明しており、現行ファミリーでは guidance がサポートされないことも明示しています。管理対象の正確なエンドポイントの契約に明記されていない限り、これらを送信しないでください。似た出力が得られることと、決定論的な出力が得られることは同じではありません。

サイズ制約
モデルデフォルト明示的なピクセル数の範囲アスペクト比
China 5.0 Pro1024x1024921,600~4,624,220 ピクセル[1/16, 16]
5.0 Lite2048x20483,686,400~16,777,216 ピクセル[1/16, 16]
4.52048x20483,686,400~16,777,216 ピクセル[1/16, 16]
4.02048x2048921,600~16,777,216 ピクセル[1/16, 16]

プロンプトでは、ドキュメントに記載されたラベルとアスペクト比や用途の説明を組み合わせるか、ドキュメントで推奨されているサイズを選んでください。ドキュメントに記載された一般的な例として、5.0 Lite の 2K 正方形 2048x2048、横長 2848x1600、縦長 1600x2848、5.0 Lite の 4K 正方形 4096x4096、Pro の 1K 正方形 1024x1024 または 2K 正方形 2048x2048 があります。同じラベルでも、モデルによって対応するピクセル寸法が同じとは限りません。

自然言語による編集と参照画像

現在の統合 API には、個別のマスク/インペイント用ルートはありません。image 参照画像を 1 枚以上指定し、prompt で編集内容を説明してください。

  1. 順番を示して参照画像を指定します:「画像 1 の……に、画像 2 のライティングを使用します。」
  2. 対象となる操作を明記します:追加、削除、置き換え、色変更、ライティング変更、スタイル変更、または合成。
  3. 変更しない要素を明示します:「ポーズ、顔の比率、カメラ、背景、およびすべてのテキストは変更しないでください。」
  4. 対象箇所を特定しにくい場合は、元画像に矢印、枠、または落書きで印を付け、その印を参照します。この手法はプロンプトガイドに記載されています。
  5. 融合する場合は、モデルに役割を推測させるのではなく、各参照画像に単一の役割を割り当てます。

参照 URL はサービスが取得できる必要があります。短時間のみ有効で、最小権限の署名付き URL を推奨します。データ URL なら公開ホスティングは不要ですが、画像は引き続きプロバイダーに送信され、リクエスト本文が大きくなる場合があります。

グループ化された出力

5.0 Lite、4.5、または 4.0 の場合:

{
  "sequential_image_generation": "auto",
  "sequential_image_generation_options": {"max_images": 4}
}

max_images は 1~15 で、デフォルトは 15 です。入力参照画像数と生成画像数の合計は 15 を超えてはなりません。auto を指定すると、画像を生成するかどうかと生成枚数をモデルが決定します。指定した最大枚数が保証されるわけではありません。プロンプトには正確なセットを指定してください。たとえば「ストーリーボードのフレームをちょうど 4 枚作成してください。フレーム 1 は……、フレーム 2 は……」のように記述し、実際の枚数を検証します。これらのフィールドを China 5.0 Pro に送信しないでください。

オプションのウェブ検索

現在、ドキュメントに記載されているのは 5.0 Lite のみです:

{"tools": [{"type": "web_search"}]}

ウェブ検索を使用するかどうかはモデルが決定します。usage.tool_usage.web_search で使用状況を確認できます。検索によって遅延が増え、外部コンテンツに関するリスクが生じます。検索で得られた事実や視覚的な詳細は信頼できないものとして扱い、帰属と権利を確認してください。また、検索が実行されると約束しないでください。

レスポンスを慎重に解釈する

非ストリーミングリクエストでは、トップレベルに model、created、data、usage、および場合によっては error が含まれます。各 data 要素には、成功した画像(url または b64_json、size、および対応している場合は output_format)か、要素単位の error が含まれる場合があります。そのため、200 レスポンスでも一部が失敗していることがあります。

次の項目を検証してください:

  1. トップレベルのステータスとエラー。
  2. 各 data 要素を個別に確認します。
  3. 実際の成功数と要求した納品内容を照合します。
  4. 申告されたサイズ/形式とデコードしたバイトデータを照合します。
  5. コストと可観測性を確認するため、usage.generated_images、出力トークン、およびツールの使用状況を確認します。

ドキュメントによると、課金対象となるのは生成に成功した画像のみです。クライアント側でタイムアウトしたからといって、コストがゼロだと推測しないでください。ドキュメントに記載された冪等性キーがない場合、タイムアウトしたリクエストがサーバー側で完了している可能性があります。

stream: true の場合は、最後の完了イベントまでサーバー送信イベントを解析してください:

  • image_generation.partial_succeeded:image_index(0 始まり)に加え、url または b64_json、および size を含みます。
  • image_generation.partial_failed:image_index とエラーを含みます。
  • image_generation.completed:最終的な usage/ツールの概要です。
  • トップレベルの error:リクエストの失敗です。

ストリームガイドによると、コンテンツフィルターによる失敗の後に後続の画像が生成される場合がありますが、内部エラー 500 が発生すると以降の生成は停止します。最初の部分イベントを完了として扱わないでください。

本番ワークフロー

1. ポリシーの枠組みを定める

API を呼び出す前に、次の項目を記録します。

  • デプロイ先、アカウント、エンドポイントのリージョン、正確なモデル/エンドポイント ID、開始日
  • 許可する入力の分類、および個人データ、顔、商標、機密情報の利用が許可されるかどうか
  • ジョブごとの参照画像数、出力数、寸法、バイト数、レイテンシ、再試行回数、費用の上限
  • 目に見える透かし/開示のポリシー、および永続的な来歴管理のポリシー
  • 公開、商用、または影響の大きい用途におけるレビュー担当者と承認基準
2. 入力を正規化して検証する
  • ローカル入力をデコードして実際の形式を確認します。ファイル名やクライアントの MIME タイプだけを信用しないでください。
  • 形式が不正なもの、サイズが大きすぎるもの、圧縮爆弾、圧縮率が極端なもの、未対応の入力は、アップロード前に拒否します。
  • 意図的に保持する場合を除き、不要な EXIF/位置情報メタデータを削除します。
  • 各ソースのハッシュを計算し、ライセンス/同意記録を保存して、固定の参照番号を割り当てます。
  • プロンプトから監査記録まで、参照順序を固定します。
3. テスト可能なプロンプトを作成する

キーワードを並べるのではなく、自然な文章を使用します。

[deliverable/use]. [subject and action]. [environment].
[camera/composition]. [lighting/color/style].
[exact text in double quotes]. [reference roles]. [edit invariants].
[numbered outputs if a set].

文字を含める場合は、必要な文言を二重引用符で囲み、配置と優先順位を指定してから、OCR と人による校正を行います。生成で組版が保証されるわけではありません。

4. ジョブ台帳とともに送信する

クライアントのジョブ ID を作成し、リクエストハッシュ、秘匿化したパラメーター、モデル、ホスト、開始時刻、試行回数、状態を保存します。画像 API は同期型、またはリクエスト単位の SSE です。現行ドキュメントには、非同期画像ジョブ、ポーリング API、Webhook、冪等性キーの記載はありません。Seedance の動画タスクのセマンティクスを持ち込まないでください。

5. 範囲を絞って再試行する

ネットワーク障害、429 のスロットリング、一時的な 500 エラーは、上限付き指数バックオフ、ジッター、利用可能な場合は Retry-After を使用して再試行します。試行回数と同時実行数に上限を設けます。次のケースは再試行しないでください。

  • 認証、アカウント、残高、サービス未開通、モデルアクセスに関するエラー
  • 無効または不足しているパラメーター、不正な画像
  • コンテンツの安全性、著作権、プライバシーに関する拒否
  • 重複した課金/出力を許容できない場合の、タイムアウトの曖昧な状態に対する自動再試行

Ark のドキュメントには、アカウント/モデル/エンドポイント固有の IPM、RPM、TPM、バースト、クォータ、過負荷エラーが記載されています。BytePlus の現行 Seedream チュートリアルには、現在のグローバルモデルの上限として 1 分あたり 500 枚の画像が記載されていますが、アカウントやエンドポイントの制御により、上限がこれより低い場合があります。実際のヘッダー/エラーを測定し、確認済みの上限を下回るように受付制御を設定してください。

6. 成果物を安全に取得する

url レスポンスの場合、リンクの有効期間は 24 時間のため、すぐにダウンロードします。成果物の URL は、別途信頼できないフェッチ先として扱います。

  • HTTPS を必須とし、認証情報/フラグメントを拒否し、予期しないリダイレクトを拒否します。
  • Ark の Authorization ヘッダーを成果物のリクエストに付けないでください。
  • バッファーに格納する前に、ストリーミング時のバイト数に上限を設けます。
  • 画像のコンテンツタイプを許可リストで制限し、マジックバイトを検証し、寸法をデコードして、再スキャンします。
  • 一時ファイルに書き込み、必要に応じて fsync を実行してから、アトミックに昇格させます。
  • 最終バイト列のハッシュを計算し、寸法、MIME タイプ、モデル、プロンプトハッシュ、ソースハッシュ、レビュー状態、プロバイダーの使用量を保存します。

b64_json の場合は、厳密な Base64 デコードを行い、デコード後のバイト数、マジックバイト、寸法、スキャンに同じチェックを適用します。

7. リリース前に評価する

テキストから画像への生成、単一編集、複数参照画像での一貫性、文字組み、禁止コンテンツ、極端なアスペクト比、グループ数、部分的な失敗、スロットリング、タイムアウト、破損したダウンロードのケースを含む固定の回帰テストセットを使用します。プロンプトへの準拠度、編集範囲の局所性、参照画像との一貫性、視覚的な欠陥、OCR の精度、安全性、レイテンシ、コストを採点します。可能であれば、候補モデルと現行モデルをブラインドで比較します。

例:厳密な非ストリーミング生成

この完全な Python 標準ライブラリの例は、このスキルでは実行されません。BytePlus AP と 5.0 Lite を使用し、有効期限のあるアセット URL を避けるために base64 を要求し、API ベース URL を検証し、デコード後のサイズに上限を設け、PNG/JPEG のマジックナンバーを確認して、永続性のあるメタデータを書き込みます。ARK_API_KEY を設定してください。必要に応じて、互換性が確認されたドキュメント記載の組み合わせで ARK_API_BASE と SEEDREAM_MODEL を設定してください。

import base64
import binascii
import hashlib
import json
import os
import pathlib
import tempfile
import time
import urllib.error
import urllib.request

ALLOWED = {
    "https://ark.cn-beijing.volces.com/api/v3": {
        "doubao-seedream-5-0-pro-260628", "doubao-seedream-5-0-260128",
        "doubao-seedream-5-0-lite-260128", "doubao-seedream-4-5-251128",
        "doubao-seedream-4-0-250828",
    },
    "https://ark.ap-southeast.bytepluses.com/api/v3": {
        "seedream-5-0-260128", "seedream-5-0-lite-260128",
        "seedream-4-5-251128", "seedream-4-0-250828",
    },
    "https://ark.eu-west.bytepluses.com/api/v3": {
        "seedream-5-0-260128", "seedream-5-0-lite-260128",
    },
}
MAX_IMAGE_BYTES = 25 * 1024 * 1024
MAX_JSON_BYTES = ((MAX_IMAGE_BYTES + 2) // 3) * 4 + 1024 * 1024


def atomic_write(path, data):
    path.parent.mkdir(parents=True, exist_ok=True)
    handle, temporary = tempfile.mkstemp(dir=path.parent, prefix=".seedream-")
    try:
        with os.fdopen(handle, "wb") as output:
            output.write(data)
            output.flush()
            os.fsync(output.fileno())
        os.replace(temporary, path)
    except Exception:
        try:
            os.unlink(temporary)
        except FileNotFoundError:
            pass
        raise

api_base = os.getenv(
    "ARK_API_BASE", "https://ark.ap-southeast.bytepluses.com/api/v3"
).rstrip("/")
api_key = os.environ.get("ARK_API_KEY", "")
model = os.getenv("SEEDREAM_MODEL", "seedream-5-0-260128")
configured_endpoint = os.getenv("SEEDREAM_ALLOWED_ENDPOINT_ID")
configured_endpoint_base = os.getenv("SEEDREAM_ALLOWED_ENDPOINT_API_BASE")
configured_endpoint_model = os.getenv("SEEDREAM_ALLOWED_ENDPOINT_MODEL")
allowed_models = ALLOWED.get(api_base, set())
bound_endpoint = (
    model == configured_endpoint
    and api_base == configured_endpoint_base
    and configured_endpoint_model in allowed_models
)
if api_base not in ALLOWED or (model not in allowed_models and not bound_endpoint):
    raise SystemExit("Refusing an undocumented host/model namespace pairing")

payload = {
    "model": model,
    "prompt": (
        "Square editorial product photograph for a coffee brand. A matte black "
        "ceramic cup on pale limestone, soft window light from camera left, subtle "
        "steam, generous negative space, realistic materials. No logos or text."
    ),
    "size": "2K",
    "response_format": "b64_json",
    "output_format": "png",
    "watermark": True,
}
body = json.dumps(payload, separators=(",", ":")).encode("utf-8")
request = urllib.request.Request(
    api_base + "/images/generations",
    data=body,
    method="POST",
    headers={
        "Authorization": "Bearer " + api_key,
        "Content-Type": "application/json",
        "Accept": "application/json",
    },
)
print({
    "model": payload["model"], "size": payload["size"],
    "request_sha256": hashlib.sha256(body).hexdigest(),
    "watermark": payload["watermark"],
})
if os.environ.get("SEND_SEEDREAM_REQUEST") != "1":
    raise SystemExit("Dry run only; set SEND_SEEDREAM_REQUEST=1 after approving the paid call")
if not api_key:
    raise SystemExit("Set ARK_API_KEY before the paid call")
try:
    with urllib.request.urlopen(request, timeout=180) as response:
        raw = response.read(MAX_JSON_BYTES + 1)
except urllib.error.HTTPError as exc:
    # Do not print request headers or secrets.
    detail = exc.read(64 * 1024).decode("utf-8", "replace")
    raise SystemExit(f"Ark HTTP {exc.code}: {detail}") from exc

if len(raw) > MAX_JSON_BYTES:
    raise SystemExit("JSON response exceeded configured base64 envelope cap")
try:
    document = json.loads(raw)
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
    raise SystemExit("Response was not valid bounded JSON") from exc
if document.get("error"):
    raise SystemExit(f"Ark error: {document['error']}")
successful = [item for item in document.get("data", []) if item.get("b64_json")]
failed = [item.get("error") for item in document.get("data", []) if item.get("error")]
if len(successful) != 1 or failed:
    raise SystemExit(f"Expected one image; successes={len(successful)} failures={failed}")

encoded = successful[0]["b64_json"]
if len(encoded) > ((MAX_IMAGE_BYTES + 2) // 3) * 4 + 8:
    raise SystemExit("Encoded image exceeds the configured cap")
try:
    image = base64.b64decode(encoded, validate=True)
except (binascii.Error, ValueError) as exc:
    raise SystemExit("Image was not strict base64") from exc
if len(image) > MAX_IMAGE_BYTES:
    raise SystemExit("Decoded image exceeds the configured cap")
if image.startswith(b"\x89PNG\r\n\x1a\n"):
    suffix = ".png"
elif image.startswith(b"\xff\xd8\xff"):
    suffix = ".jpg"
else:
    raise SystemExit("Unexpected image signature")

out = pathlib.Path("seedream-output" + suffix)
atomic_write(out, image)
metadata = {
    "created_unix": int(time.time()),
    "model": document.get("model", model),
    "request_sha256": hashlib.sha256(body).hexdigest(),
    "artifact_sha256": hashlib.sha256(image).hexdigest(),
    "declared_size": successful[0].get("size"),
    "usage": document.get("usage"),
    "watermark_requested": payload["watermark"],
}
atomic_write(
    out.with_suffix(out.suffix + ".json"),
    json.dumps(metadata, indent=2, ensure_ascii=False).encode("utf-8"),
)
print(out.resolve())

本番コードではさらに、保守されている画像ライブラリでピクセルをデコードし、幅/高さ/面積を制限し、コンテンツをスキャンし、アトミックな書き込みを使用し、ログをマスキングして、上記の再試行ポリシーを組み込んでください。

例:複数の参照画像、グループ化された出力、安全な URL 取得

この完全な例は、このスキルでは実行されません。BytePlus から取得可能な 2 つの HTTPS 参照 URL と、プロバイダーの現在のアーティファクトドメインを基に管理者が保守する、カンマ区切りの SEEDREAM_ASSET_HOSTS 許可リストが必要です。グループ化モードが auto のため、モデルが 3 枚未満の画像を返す場合があります。このプログラムは各アイテムの失敗を記録し、API トークンを転送せずに成功した URL を安全にダウンロードします。

import hashlib
import ipaddress
import json
import os
import pathlib
import socket
import tempfile
import urllib.error
import urllib.parse
import urllib.request

API_BASE = "https://ark.ap-southeast.bytepluses.com/api/v3"
API_KEY = os.environ.get("ARK_API_KEY", "")
REF_1 = os.environ.get("SEEDREAM_REFERENCE_1", "https://example.invalid/reference-1")
REF_2 = os.environ.get("SEEDREAM_REFERENCE_2", "https://example.invalid/reference-2")
MAX_DOWNLOAD = 25 * 1024 * 1024
MAX_API_JSON = 8 * 1024 * 1024
ASSET_HOSTS = {
    host.strip().lower()
    for host in os.environ.get("SEEDREAM_ASSET_HOSTS", "").split(",")
    if host.strip()
}
if not ASSET_HOSTS:
    raise SystemExit("Set SEEDREAM_ASSET_HOSTS to approved provider artifact hostnames")


class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None


def validate_artifact_url(url):
    parsed = urllib.parse.urlsplit(url)
    if (
        parsed.scheme != "https" or parsed.hostname not in ASSET_HOSTS
        or parsed.username or parsed.password or parsed.fragment
    ):
        raise ValueError("Artifact URL is outside the approved HTTPS allowlist")
    addresses = {
        info[4][0] for info in socket.getaddrinfo(parsed.hostname, parsed.port or 443)
    }
    if not addresses or any(not ipaddress.ip_address(value).is_global for value in addresses):
        raise ValueError("Artifact host did not resolve exclusively to public addresses")


def atomic_write(path, data):
    path.parent.mkdir(parents=True, exist_ok=True)
    handle, temporary = tempfile.mkstemp(dir=path.parent, prefix=".seedream-meta-")
    try:
        with os.fdopen(handle, "wb") as output:
            output.write(data)
            output.flush()
            os.fsync(output.fileno())
        os.replace(temporary, path)
    except Exception:
        try:
            os.unlink(temporary)
        except FileNotFoundError:
            pass
        raise

for ref in (REF_1, REF_2):
    parsed = urllib.parse.urlsplit(ref)
    if parsed.scheme != "https" or parsed.username or parsed.password:
        raise SystemExit("References must be credential-free HTTPS URLs")

payload = {
    "model": "seedream-5-0-260128",
    "prompt": (
        "Create exactly three coordinated campaign images. Preserve the product "
        "shape and label from image 1. Use only the blue-and-amber palette and soft "
        "paper texture from image 2. Frame 1: square hero. Frame 2: vertical detail. "
        "Frame 3: wide environmental scene. Keep all label spelling unchanged."
    ),
    "image": [REF_1, REF_2],
    "size": "2K",
    "sequential_image_generation": "auto",
    "sequential_image_generation_options": {"max_images": 3},
    "response_format": "url",
    "output_format": "jpeg",
    "watermark": True,
}
request = urllib.request.Request(
    API_BASE + "/images/generations",
    data=json.dumps(payload).encode("utf-8"),
    method="POST",
    headers={
        "Authorization": "Bearer " + API_KEY,
        "Content-Type": "application/json",
        "Accept": "application/json",
    },
)
print({
    "model": payload["model"], "references": len(payload["image"]),
    "max_images": payload["sequential_image_generation_options"]["max_images"],
    "request_sha256": hashlib.sha256(json.dumps(payload, separators=(",", ":")).encode()).hexdigest(),
})
if os.environ.get("SEND_SEEDREAM_REQUEST") != "1":
    raise SystemExit("Dry run only; set SEND_SEEDREAM_REQUEST=1 after approving the paid call")
if not API_KEY or "example.invalid" in {urllib.parse.urlsplit(REF_1).hostname, urllib.parse.urlsplit(REF_2).hostname}:
    raise SystemExit("Set ARK_API_KEY and both SEEDREAM_REFERENCE environment variables")
try:
    with urllib.request.urlopen(request, timeout=240) as response:
        response_bytes = response.read(MAX_API_JSON + 1)
except urllib.error.HTTPError as exc:
    raise SystemExit(f"Ark HTTP {exc.code}") from exc
if len(response_bytes) > MAX_API_JSON:
    raise SystemExit("Grouped API response exceeded configured JSON cap")
try:
    document = json.loads(response_bytes)
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
    raise SystemExit("Grouped API response was not valid bounded JSON") from exc

def safe_download(url: str, destination: pathlib.Path) -> dict:
    opener = urllib.request.build_opener(NoRedirect())
    current = url
    response = None
    for _ in range(6):
        validate_artifact_url(current)
        # Deliberately no Authorization header here or on any redirect hop.
        req = urllib.request.Request(current, headers={"Accept": "image/png,image/jpeg"})
        try:
            response = opener.open(req, timeout=90)
            break
        except urllib.error.HTTPError as exc:
            if exc.code not in {301, 302, 303, 307, 308}:
                raise
            location = exc.headers.get("Location")
            exc.close()
            if not location:
                raise ValueError("Artifact redirect omitted Location")
            current = urllib.parse.urljoin(current, location)
    else:
        raise ValueError("Too many artifact redirects")

    hasher = hashlib.sha256()
    total = 0
    prefix = b""
    destination.parent.mkdir(parents=True, exist_ok=True)
    with response:
        content_type = response.headers.get_content_type()
        if content_type not in {"image/png", "image/jpeg"}:
            raise ValueError(f"Unexpected Content-Type: {content_type}")
        declared = response.headers.get("Content-Length")
        if declared and int(declared) > MAX_DOWNLOAD:
            raise ValueError("Artifact exceeds byte cap")
        fd, temporary = tempfile.mkstemp(dir=destination.parent, prefix=".seedream-")
        try:
            with os.fdopen(fd, "wb") as output:
                while True:
                    chunk = response.read(64 * 1024)
                    if not chunk:
                        break
                    total += len(chunk)
                    if total > MAX_DOWNLOAD:
                        raise ValueError("Artifact exceeded byte cap while streaming")
                    if len(prefix) < 12:
                        prefix += chunk[: 12 - len(prefix)]
                    hasher.update(chunk)
                    output.write(chunk)
                output.flush()
                os.fsync(output.fileno())
            is_png = prefix.startswith(b"\x89PNG\r\n\x1a\n")
            is_jpeg = prefix.startswith(b"\xff\xd8\xff")
            if not (is_png or is_jpeg):
                raise ValueError("Unexpected artifact signature")
            os.replace(temporary, destination)
        except Exception:
            try:
                os.unlink(temporary)
            except FileNotFoundError:
                pass
            raise
    return {"bytes": total, "sha256": hasher.hexdigest(), "content_type": content_type}

if document.get("error"):
    raise SystemExit(f"Ark error: {document['error']}")
manifest = {"model": document.get("model"), "usage": document.get("usage"), "items": []}
success_index = 0
for index, item in enumerate(document.get("data", [])):
    if item.get("error"):
        manifest["items"].append({"index": index, "error": item["error"]})
        continue
    if not item.get("url"):
        manifest["items"].append({"index": index, "error": "missing url"})
        continue
    success_index += 1
    path = pathlib.Path("seedream-set") / f"image-{success_index:02d}.jpg"
    audit = safe_download(item["url"], path)
    manifest["items"].append({"index": index, "path": str(path), **audit})

atomic_write(
    pathlib.Path("seedream-set/manifest.json"),
    json.dumps(manifest, indent=2, ensure_ascii=False).encode("utf-8"),
)
if success_index == 0:
    raise SystemExit("No successful images")
print(f"Saved {success_index} image(s); review count, labels, and visual consistency")

例:リクエスト単位の SSE

この curl の例は、このスキルでは実行されません。本番環境での解析方法ではなく、通信の形式を示しています。本番環境では実際の SSE パーサーを使用し、イベントの順序を維持して、一部の失敗に対処し、image_generation.completed を待ってください。

: "${ARK_API_KEY:?set ARK_API_KEY}"
: "${SEND_SEEDREAM_REQUEST:?set SEND_SEEDREAM_REQUEST=1 after approving the paid call}"
[ "${SEND_SEEDREAM_REQUEST}" = "1" ] || { echo 'Refusing paid call without SEND_SEEDREAM_REQUEST=1' >&2; exit 2; }
curl --fail-with-body --no-buffer \
  --connect-timeout 10 --max-time 300 \
  'https://ark.ap-southeast.bytepluses.com/api/v3/images/generations' \
  -H "Authorization: Bearer ${ARK_API_KEY}" \
  -H 'Content-Type: application/json' \
  -H 'Accept: text/event-stream' \
  --data-binary @- <<'JSON'
{
  "model": "seedream-5-0-260128",
  "prompt": "Create a three-frame horizontal storyboard of a paper airplane crossing a quiet library: establishing shot, close tracking shot, final landing. Consistent airplane and lighting in every frame.",
  "size": "2K",
  "sequential_image_generation": "auto",
  "sequential_image_generation_options": {"max_images": 3},
  "response_format": "url",
  "stream": true,
  "watermark": true
}
JSON

エラーマップと可観測性

分類代表的なドキュメント記載のエラー対処
400 リクエストパラメーターが無効/不足、画像/base64/URL が無効、入力/出力の安全性/著作権/プライバシーむやみに再試行しないでください。入力を修正するか、処理を中止してください
401/403認証、アクセス拒否、アカウントの支払い期限超過、サービス未開通、利用規約/アクセス認証情報またはアカウントの状態を修正してください。一時的なエラーとして再試行しないでください
404モデル/エンドポイントが見つからない、モデルへのアクセスが無効サーフェス、リージョン、モデル ID、エンドポイント、ライフサイクル、利用資格を確認してください
429モデル/アカウントの IPM、RPM、TPM、エンドポイント上限、バースト、クォータ、過負荷キューに入れ、同時実行数を減らし、再試行タイミングを守って、ジッター付きのバックオフを行ってください
500サービス内部エラー重複リスクが許容できる場合は、上限付きで再試行してください。それ以外の場合は手動で照合してください

試行回数、ステータス/エラーコード、スロットリング分類、キュー待ち時間、プロバイダーのレイテンシー、ダウンロードのレイテンシー、成功/失敗した画像の数、バイト数、解像度、モデル、リージョンのエンドポイント、ウェブ検索の使用状況、推定コストと請求コスト、モデレーション結果、レビュアーの判断について、メトリクスを出力してください。生のシークレットは記録しないでください。テレメトリーに含める生のプロンプトやソース URL は最小限に抑えてください。

コストとライフサイクルの管理

価格は変動します。2026-07-09 時点で中国の価格ページには、4.0 が成功した画像 1 枚あたり ¥0.20、4.5 が ¥0.25、5.0 Lite が ¥0.22、5.0 Pro が 2.36 MP 以下で ¥0.30、2.36 MP 超で ¥0.60 と掲載されていました。確認した BytePlus のファーストパーティページには、4.0 が画像 1 枚あたり US$0.03、4.5 が画像 1 枚あたり US$0.04 と表示されていました。5.0 Lite の現在のグローバル価格は確認できませんでした。リージョン、契約、税金、パック、プロモーションによって異なる場合があるため、アカウントコンソールと公式価格ページを再確認してください。

送信前に、グループの最大成功数と適用される解像度の料金区分を使って、上限コストを見積もってください。見積もりだけに頼らず、プロバイダーの usage と請求書を照合してください。モデレーションで失敗した出力は請求対象外と記載されていますが、一部失敗したグループ内の成功した出力は請求対象です。

日付付きのモデル ID を固定し、モデルの廃止通知を購読して、移行用のゴールデンスイートを維持してください。利用可否の判断基準は、最新のカタログと API ページです。製品名からライフサイクルを推測しないでください。エイリアスの動作は変わる可能性があります。従来の 3.0 の seed ワークフローは、4.0/5.0 にレガシーパラメーターを持ち込まず、移行プロジェクトとして扱ってください。

プライバシー、権利、安全性の確認

BytePlus の現行 Model Service 規約では、個別の同意がない限り、Input と Output はベースモデルのトレーニングに使用されず、BytePlus は Output の所有権を主張しないと定められています。また、出力は独自のものとは限らず、BytePlus/関連会社/第三者が保有する権利はそれぞれに帰属し、顧客は入力、出力、適用法、および権利処理について引き続き責任を負うとしています。これは、出力が著作権の対象となること、独占性があること、権利を侵害しないこと、正確であること、または安全であることを保証するものではありません。

BytePlus のデータ処理に関するページでは、マレーシア、インドネシア、および/または EU/EEA で処理される可能性があると記載されており、安全性フィルターに該当するコンテンツはマレーシアで 180 日間保持されるとしています。この保持期間は、生成画像 URL の 24 時間とは別のものです。実際の契約について、現行の DPA、サブプロセッサーの所在地、暗号化オプション、削除、およびサポートに関する約束を確認してください。

生成および公開の前に、以下を行ってください。

  • 元画像、商標、著作権で保護されたキャラクター、個人データ、顔、肖像、および生体情報に関わる用途について、ライセンスや同意を取得してください。
  • 性的搾取、未成年者への虐待、同意のない親密な画像、なりすましによる欺瞞、詐欺、違法な監視/特定、ヘイト/過激主義、自傷行為の助長、その他禁止されているコンテンツを禁止してください。
  • 安全システムを回避したり、必要な認証情報、透かし、メタデータ、または開示を削除・隠蔽したりしないでください。
  • 法律、プラットフォームのポリシー、または文脈上必要な場合は、技術的に watermark: false を使用している場合でも、AI 生成コンテンツであることを明示してください。
  • 事実に関する主張、タイポグラフィ、肖像、医療/法律/金融上の影響、選挙/公共政策、および重大な影響を伴う用途について、人間によるレビューを必須としてください。
  • 来歴の記録を保管してください。確認した画像 API 契約では、C2PA が常に保証されることは確認できませんでした。

適用される Model Service 規約で禁止されている場合、モデルまたはその出力を、別のモデルのトレーニングや改善に使用しないでください。管轄区域および契約に応じた助言については、弁護士に相談してください。

公式情報源

2026-07-09 に確認:

既知の不足点

確認済みのファーストパーティ画像 API ページでは、冪等性キー、非同期画像ジョブ/ポーリング/Webhook の契約、現行ファミリーにおける決定論的なシード、普遍的なデータ保存場所の保証、すべての出力への C2PA の適用、または 5.0 Lite の一般公開されている現行 BytePlus 価格は確認できません。これらの機能があると主張しないでください。必要な場合は、ドキュメントを再確認するか、プロバイダーから書面で回答を得てください。

コメント