Preise+7% Bonus

Produktbilder mit der Seedream 5.0 Pro API in großen Mengen generieren

Erstellen Sie einen CSV-gesteuerten Workflow mit der Seedream 5.0 Pro API – mit Eingaben aus mehreren Referenzen, kontrollierter Parallelverarbeitung, Wiederholungsversuchen, Kostenschätzungen und menschlicher Prüfung.

Produktbilder mit der Seedream 5.0 Pro API in großen Mengen generieren

Ein Katalog mit 100 SKUs lässt sich nicht mit einer einzigen Bildanfrage abdecken. Für jedes Produkt werden eigene Referenzbilder, Prompt-Variablen, ein Dateiname, ein Wiederholungsprotokoll und ein Freigabestatus benötigt. Prompts in ein Playground zu kopieren, kann zwar Bilder erzeugen, sagt Ihnen aber nicht, welche Version zu MUG-001 gehört oder ob ein fehlgeschlagenes Taschenbild erneut generiert werden sollte.

Die praktische Lösung ist eine Seedream-Anfrage pro SKU, die über einen kleinen Worker-Pool ausgeführt wird. Das ist hier mit „Bulk“ gemeint. BytePlus führt Text-zu-mehreren-Bildern für Seedream 5.0 Pro derzeit als nicht unterstützt auf. Ein einzelner Prompt liefert also keinen vollständigen Katalog. Stattdessen stellt eine CSV-Datei die Produktdaten bereit, Python sendet mehrere unabhängige Anfragen mit kontrollierter Frequenz, und ein Manifest protokolliert jedes Ergebnis.

Der fertige Workflow verwendet die Seedream 5.0 Pro API auf GPTProto und erstellt:

  • products.csv, mit einer Zeile pro Produktbild;

  • bulk_seedream_products.py, das die Jobs erstellt und ausführt;

  • generated/, mit Dateinamen, die den SKUs zugeordnet sind; und

  • manifest.json, mit Versuchen, Fehlern, Speicherorten der Ausgaben und Prüfstatus.

Inhaltsverzeichnis

Was „Seedream 5.0 Pro API Batch“ wirklich bedeutet

Drei Begriffe werden oft synonym verwendet. Das sind sie nicht.

Begriff Bedeutung Aktuelles Verhalten
Eingabe mit mehreren Referenzen Mehrere Quellbilder dienen als Vorlage für eine Bearbeitung Der GPT Proto-Endpunkt zur Bildbearbeitung akzeptiert bis zu 10 Referenzen
Mehrere Ausgaben Ein Prompt liefert mehrere separate Bilder Für Seedream 5.0 Pro nicht verfügbar
Bulk-Generierung Viele SKU-spezifische Anfragen werden über eine Warteschlange verarbeitet Der unten beschriebene Workflow

Die Eingabe mit mehreren Referenzen verbessert ein einzelnes Ergebnis. Sie erzeugt keine zehn Ergebnisse. Eine Vorderansicht des Produkts, eine Detailaufnahme des Materials und eine Beleuchtungsvorlage können gemeinsam ein finales Bild beeinflussen. Um Bilder für 100 Produkte zu erstellen, sendet der Client weiterhin 100 Anfragen.

Diese Unterscheidung wirkt sich auf alles Weitere aus. Bei einer SKU pro Anfrage hat jede Ausgabe eine eigene Zuordnung, einen Dateinamen, einen Kosteneintrag und einen Wiederholungsverlauf. Gleichzeitige Ausführung beschleunigt die Warteschlange; sie macht das Modell nicht zu einem Endpunkt mit mehreren Ausgaben. Das zugrunde liegende Modellverhalten ist in BytePlus’ Matrix zur Bildgenerierung dokumentiert. GPT Proto stellt das Modell über eigene Routen unter /api/v3/ bereit.

Was Sie erstellen werden

Die Daten durchlaufen fünf Schritte:

CSV-Katalog → Prompt und Referenzen → kontrollierter Anfragepool → heruntergeladene Dateien → manuelle Prüfung

Das Skript verwendet synchrone Anfragen mit enable_sync_mode: true. Diese Entscheidung ist bewusst getroffen. Auf GPT Protos Live-Modellseite ist ein asynchroner Workflow mit result_id dokumentiert, aber das vollständige Schema für Status- und Ausgabedaten wird dort nicht veröffentlicht. Ein Tutorial für den Produktionseinsatz sollte diese Felder nicht erfinden.

Auch mit dem synchronen Modus ist eine Bulk-Generierung möglich. Jeder Worker bearbeitet ein Produkt von der Anfrage bis zum Download. Mit ThreadPoolExecutor können eine kleine Anzahl von Workern gleichzeitig arbeiten. Der Hauptthread schreibt das Manifest nach Abschluss jedes Workers. So können bei unterbrochenen Durchläufen bereits vorhandene Dateien übersprungen werden.

Für eine spätere Version mit höherem Durchsatz setzen Sie enable_sync_mode erst dann auf false, wenn Sie eine Live-Antwort erfasst und die aktuellen Felder für Aufgaben-ID, Status, Ausgabe und Fehler bestätigt haben. Speichern Sie dann die zurückgegebene result_id, bevor Sie /api/v3/predictions/{result_id}/result abfragen.

Was Sie vor dem Start benötigen

Installieren Sie Python und die einzige Abhängigkeit:

python --version
python -m pip install requests

Erstellen Sie einen GPT Proto-API-Schlüssel und speichern Sie ihn in einer Umgebungsvariablen:

export GPTPROTO_API_KEY="your-key-here"

Die aktuellen Live-API-Beispiele verwenden dieses Header-Format:

Authorization: Bearer YOUR_API_KEY

Außerdem benötigen Sie Produktreferenzen unter öffentlich zugänglichen HTTPS-URLs. Ein Pfad wie /Users/me/photos/mug.jpg ist nur auf Ihrem Computer vorhanden. Die Remote-API kann nicht darauf zugreifen. Laden Sie die Dateien in ein CDN, einen Objektspeicher-Bucket oder an einen anderen HTTPS-Speicherort hoch, auf den die API ohne Anmelde-Cookie zugreifen kann.

Beginnen Sie mit einem kleinen Ausschnitt des Katalogs. Wählen Sie Produkte mit unterschiedlichen Formen, Materialien und komplexen Etiketten. Ein einfacher Becher kann funktionieren, während eine Tasche mit mehreren Riemen Probleme bereitet. Ein erfolgreicher einfacher Test reicht daher nicht aus, um einen gemeinsamen Prompt freizugeben.

Schätzen Sie die Batch-Kosten vor dem Start

GPT Protos aktuelle Seedream-5.0-Pro-Preise wurden am 17. September 2026 überprüft. Die Modellseite enthält die Preise pro Bild; Informationen zu den Preisen auf Kontoebene finden Sie auf der GPT Proto-Preisseite.

Ausgabe Grundpreis einschließlich der ersten Referenz Jede weitere Referenz
1K $0.0405 pro Bild +$0.0027
2K $0.0810 pro Bild +$0.0027

Die aktuelle Seedream 5.0 Pro Image Edit API akzeptiert bis zu 10 Referenzbilder. Das erste ist im Grundpreis enthalten. Für die Referenzen zwei bis zehn werden jeweils $0.0027 berechnet.

Daraus ergeben sich folgende Planungswerte, ohne Wiederholungen oder abgelehnte Generierungen:

Batch Geschätzte API-Kosten
100 Bilder mit einer Referenz bei 1K $4.05
100 Bilder mit einer Referenz bei 2K $8.10
1.000 Bilder mit einer Referenz bei 1K $40.50
1.000 Bilder mit einer Referenz bei 2K $81.00
100 Bilder mit drei Referenzen bei 1K $4.59
100 Bilder mit drei Referenzen bei 2K $8.64

Die Kosten für die reine Generierung lassen sich leicht schätzen. Die Kosten pro genehmigtem Bild dagegen nicht. Wenn ein Produkt mehrere Versuche erfordert, zählt jedes generierte Bild. Verwenden Sie für ein realistischeres Budget diese Formel:

geschätzte Katalogkosten =
  Anzahl der SKUs
  × durchschnittliche Anzahl der Versuche pro genehmigtem Bild
  × (Grundpreis der Ausgabe + Kosten für zusätzliche Referenzbilder)

Verwenden Sie 1K, um das Prompt-System mit repräsentativen Produkten zu testen. Betrachten Sie das Ergebnis nicht als garantierte Vorschau auf einen späteren Aufruf mit 2K. Die 2K-Anfrage erstellt ein neues Bild, daher können sich Komposition und Details ändern. Prüfen Sie das Ergebnis erneut in der endgültigen Auflösung.

Schritt 1: Erstellen Sie eine Produkt-CSV-Datei

Speichern Sie die Datei unter dem Namen products.csv:

sku,name,color,material,angle,background,product_image_url,detail_image_url,style_reference_url,avoid
MUG-001,Stackable coffee mug,matte navy,stoneware,three-quarter,soft warm gray,https://cdn.example.com/mug-front.jpg,https://cdn.example.com/mug-glaze.jpg,,steam or hands
BAG-014,Compact crossbody bag,forest green,pebbled leather,front three-quarter,light beige,https://cdn.example.com/bag-front.jpg,https://cdn.example.com/bag-texture.jpg,https://cdn.example.com/style-neutral.jpg,model or extra straps

Ersetzen Sie vor der Ausführung des Skripts jede cdn.example.com-URL. Die CSV-Felder erfüllen unterschiedliche Aufgaben:

  • sku ist der dauerhafte Produktschlüssel. Er wird auch Teil des Ausgabedateinamens.

  • name, color, material, angle und background enthalten produktspezifische Angaben.

  • product_image_url ist die Referenz für die Produktidentität und erforderlich.

  • detail_image_url kann Material, Bedienelemente, Beschläge oder ein Etikett in Nahaufnahme zeigen.

  • style_reference_url bestimmt Beleuchtung und Hintergrundgestaltung, nicht die Produktidentität.

  • avoid führt einige sichtbare Fehler auf, die bei dieser SKU vermieden werden sollen.

Halten Sie die Angaben kurz und sachlich. Packen Sie kein vollständiges Kreativ-Briefing in eine einzelne Zelle. Speichern Sie den API-Schlüssel weder in der CSV-Datei noch im Quellcode oder Manifest.

Das folgende Skript weist doppelte SKUs, fehlende Produkt-URLs, Referenzen ohne HTTPS und mehr als zehn Referenzen zurück, bevor eine kostenpflichtige Anfrage gesendet wird.

Schritt 2: Weisen Sie jedem Referenzbild eine Aufgabe zu

Eine Seedream-5.0-Pro-API-Anfrage mit mehreren Bildreferenzen funktioniert besser, wenn der Prompt erklärt, wozu jedes Bild dient. Verwenden Sie eine feste Reihenfolge:

  1. Referenz für die Produktidentität: Silhouette, Proportionen, sichtbares Design und Verpackung.

  2. Detailreferenz: Material, Beschläge, Bedienelemente oder Etikettengestaltung.

  3. Stilreferenz: nur Beleuchtung, Oberfläche, Farbpalette und Hintergrund.

Der generierte Prompt enthält dann Anweisungen wie diese:

Bild 1 ist die Referenz für die Produktidentität. Bewahre Silhouette, Proportionen, sichtbares Design und Verpackung. Bild 2 ist die Detailreferenz. Verwende sie für Details zu Material und Beschlägen. Bild 3 dient ausschließlich als Referenz für Beleuchtung und Hintergrund. Übernimm keine Gegenstände daraus.

Beginnen Sie mit einer bis drei nützlichen Referenzen. Zehn nicht beschriftete Eingaben kosten mehr und können sich widersprechen. Mehr Bilder bedeuten nicht automatisch höhere Genauigkeit.

Ein Beitrag zum ComfyUI-Launch beschreibt konsistente Produktmerkmale bei unterschiedlichen Szenen als Anwendungsfall für die Produktion. Betrachten Sie das als Aussage zum Workflow, nicht als unabhängigen Benchmark. Die Produktidentität muss weiterhin im direkten Vergleich mit der Quelle geprüft werden.

Schritt 3: Erstellen Sie einen wiederverwendbaren Produkt-Prompt

Halten Sie katalogweite Regeln im Code und SKU-spezifische Angaben in der CSV-Datei fest. Eine sinnvolle Reihenfolge ist:

Asset-Typ
→ Produktidentität und geschützte Details
→ gewünschter Hintergrund
→ Kamera und Bildkomposition
→ Beleuchtung und Schatten
→ Materialeigenschaften
→ Ausschlüsse

Für die Zeile mit dem Becher erstellt das Skript Anweisungen ähnlich diesen:

Create one square e-commerce hero photograph of the Stackable coffee mug.
The product is matte navy stoneware. Preserve the product identity, silhouette,
proportions, visible design, color family, material, and packaging details from
the references. Show a three-quarter view on a soft warm gray background.
Use a large diffused key light from the upper left and a natural contact shadow.
Keep the whole product inside the frame with crop-safe space. Return one product
image, not a collage or contact sheet. Do not add steam or hands. Do not invent
logos, labels, accessories, duplicate products, handles, straps, or controls.

Nennen Sie beobachtbare Details. „Premiumqualität“ vermittelt dem Modell nur wenige physische Informationen. „Weicher Kontaktschatten, der nach hinten rechts fällt“ dagegen schon. Halten Sie Ausschlüsse kurz, da die aktuelle GPT Proto-Anfrage kein separates Feld negative_prompt bereitstellt.

Weitere Prompt-Muster finden Sie im Seedream-5.0-Pro-Prompt-Leitfaden.

Schritt 4: Testen Sie ein Produkt, bevor Sie die Warteschlange starten

Führen Sie zuerst eine einzelne synchrone Bildbearbeitungsanfrage aus:

curl --request POST \
  --url "https://gptproto.com/api/v3/doubao/dola-seedream-5-0-pro-260628/image-edit" \
  --header "Authorization: Bearer $GPTPROTO_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "images": [
      "https://your-cdn.example.com/mug-front.jpg",
      "https://your-cdn.example.com/mug-glaze.jpg"
    ],
    "prompt": "Create one square e-commerce hero photograph of the referenced mug. Image 1 is the product identity reference. Preserve its silhouette, proportions, handle, color, and visible design. Image 2 is the glaze detail reference. Show a three-quarter view on a soft warm gray background with a natural contact shadow. Keep the entire product inside the frame. No hands, steam, extra text, duplicate products, or collage.",
    "size": "1024x1024",
    "enable_base64_output": false,
    "enable_sync_mode": true,
    "output_format": "png"
  }'

Der Endpunkt und der aktuelle Bearer-Header entsprechen dem Live-Beispiel von GPT Proto. Geben Sie die erste erfolgreiche JSON-Antwort aus und speichern Sie sie, bevor Sie weitere Automatisierung hinzufügen. Prüfen Sie, ob sie eine Ausgabe enthält, die Ihr Client herunterladen kann.

Fahren Sie nicht allein deshalb fort, weil das Bild attraktiv aussieht. Vergleichen Sie es mit den Referenzen:

  • Ist die Silhouette wiederzuerkennen?

  • Sind Farbe und Material hinreichend ähnlich?

  • Hat das Modell einen Griff, Riemen, Knopf, Deckel oder ein Etikett hinzugefügt oder entfernt?

  • Ist der erforderliche Text Zeichen für Zeichen korrekt?

  • Berührt das Produkt die Oberfläche tatsächlich?

  • Ist genügend Platz für einen sicheren Zuschnitt bei der vorgesehenen Platzierung auf dem Marktplatz vorhanden?

Testen Sie ein repräsentatives und ein anspruchsvolles Produkt. Beide müssen überzeugen, bevor die vollständige Warteschlange startet.

Schritt 5: Senden Sie eine Anfrage pro SKU

Das Batch-Skript sendet dieselbe Bildbearbeitungs-Payload wie im cURL-Test. Die Variablen stammen aus der jeweiligen CSV-Zeile. MAX_WORKERS legt fest, wie viele unabhängige Anfragen gleichzeitig aktiv sein können.

Beginnen Sie mit einem kleinen Wert wie 2 oder 3. In der BytePlus-Dokumentation veröffentlichte Limits für direkte BytePlus-Konten gelten nicht automatisch auch für Ihr GPT Proto-Konto. Gibt die API 429 zurück, verringern Sie die Anzahl der Worker und befolgen Sie die Wiederholungshinweise in der Antwort.

Das Skript verwendet deterministische Dateinamen:

{sku}_hero_{attempt:02d}.{extension}

Beispiele sind MUG-001_hero_01.png und BAG-014_hero_02.png. Der Versuch bleibt nachvollziehbar, sodass Prüfende die ausgewählte Datei dem Manifest zuordnen können.

Schritt 6: Fügen Sie Wiederholungsversuche und Unterstützung zum Fortsetzen hinzu

Nicht jeder Fehler sollte eine weitere kostenpflichtige Anfrage auslösen.

Das Skript wiederholt Anfragen bei ausdrücklichen Antworten mit 429 und 5xx mit exponentiellem Backoff und Jitter. Wenn vorhanden, berücksichtigt es außerdem einen numerischen Retry-After-Header. Authentifizierungs-, Parameter-, Kontostands- und Moderationsfehler werden erfasst, ohne die Anfrage unverändert erneut zu senden.

Zeitüberschreitungen beim Lesen und getrennte Verbindungen sind anders zu behandeln. Der Server kann die Anfrage bereits angenommen haben, obwohl der Client keine Antwort erhalten hat. Ohne dokumentierten Idempotenzschlüssel könnte eine automatische erneute Übermittlung ein zweites Bild und eine zweite Gebühr verursachen. Das Skript kennzeichnet solche Fälle stattdessen mit manual_check. Das ist langsamer als blindes Wiederholen, aber für ein Produktionsbudget sicherer.

Nach Abschluss jedes Workers aktualisiert der Hauptthread manifest.json durch einen atomaren Dateiaustausch. Bei einem Neustart gilt:

  • Ein Eintrag mit dem Status downloaded, approved oder manual_review und einer vorhandenen lokalen Datei wird übersprungen;

  • Ein Eintrag mit dem Status failed_retryable wird nur dann fortgesetzt, wenn noch Versuche übrig sind;

  • Ein Eintrag mit dem Status manual_check wird nicht automatisch erneut übermittelt; und

  • Ein Eintrag mit dem Status failed behält seinen Fehler und wird übersprungen, bis eine zuständige Person die Ursache behebt und anschließend den SKU-Eintrag aus dem Manifest entfernt oder seinen Status auf failed_retryable setzt.

Dieses synchrone Design kann keine asynchrone Aufgaben-ID speichern. Wenn Sie später den dokumentierten Hintergrund-Workflow übernehmen, ergänzen Sie task_id im selben Manifest und speichern Sie die aktuelle result_id direkt nach der Übermittlung.

Vollständiges Python-Skript für die Bulk-Erstellung von Seedream-Produktbildern

Speichern Sie den folgenden Code als bulk_seedream_products.py neben products.csv.

from __future__ import annotations

import base64
import csv
import json
import os
import random
import re
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
from urllib.parse import urlparse

import requests

API_URL = (
    "https://gptproto.com/api/v3/doubao/"
    "dola-seedream-5-0-pro-260628/image-edit"
)
CSV_PATH = Path(os.getenv("PRODUCTS_CSV", "products.csv"))
OUTPUT_DIR = Path(os.getenv("OUTPUT_DIR", "generated"))
MANIFEST_PATH = Path(os.getenv("MANIFEST_PATH", "manifest.json"))

SIZE = os.getenv("SEEDREAM_SIZE", "1024x1024")
OUTPUT_FORMAT = os.getenv("SEEDREAM_OUTPUT_FORMAT", "png")
MAX_WORKERS = int(os.getenv("MAX_WORKERS", "3"))
MAX_ATTEMPTS = int(os.getenv("MAX_ATTEMPTS", "4"))
REQUEST_TIMEOUT = int(os.getenv("REQUEST_TIMEOUT", "300"))
PROMPT_VERSION = os.getenv("PROMPT_VERSION", "product-hero-v1")

REFERENCE_FIELDS = (
    (
        "product_image_url",
        "the product identity reference. Preserve its silhouette, proportions, "
        "visible design, color family, material, and packaging",
    ),
    (
        "detail_image_url",
        "the detail reference. Use it for material, hardware, controls, and label details",
    ),
    (
        "style_reference_url",
        "the lighting and background reference only. Do not copy products or props from it",
    ),
)

def now_utc() -> str:
    return datetime.now(timezone.utc).isoformat()

def is_https_url(value: str) -> bool:
    parsed = urlparse(value)
    return parsed.scheme == "https" and bool(parsed.netloc)

def clean_filename(value: str) -> str:
    cleaned = re.sub(r"[^A-Za-z0-9._-]+", "-", value.strip())
    return cleaned.strip("-._") or "product"

def reference_data(row: dict[str, str]) -> tuple[list[str], list[str]]:
    urls: list[str] = []
    role_lines: list[str] = []
    for field, role in REFERENCE_FIELDS:
        url = row.get(field, "").strip()
        if not url:
            continue
        urls.append(url)
        role_lines.append(f"Image {len(urls)} is {role}.")
    return urls, role_lines

def validate_and_load_rows(path: Path) -> list[dict[str, str]]:
    required = {
        "sku",
        "name",
        "color",
        "material",
        "angle",
        "background",
        "product_image_url",
        "detail_image_url",
        "style_reference_url",
        "avoid",
    }

    with path.open("r", encoding="utf-8-sig", newline="") as handle:
        reader = csv.DictReader(handle)
        fields = set(reader.fieldnames or [])
        missing = required - fields
        if missing:
            raise ValueError(f"CSV is missing columns: {sorted(missing)}")

        rows: list[dict[str, str]] = []
        seen_skus: set[str] = set()

        for line_number, raw_row in enumerate(reader, start=2):
            row = {key: (value or "").strip() for key, value in raw_row.items()}
            sku = row["sku"]
            if not sku:
                raise ValueError(f"Line {line_number}: sku is required")
            if sku in seen_skus:
                raise ValueError(f"Line {line_number}: duplicate sku {sku!r}")
            seen_skus.add(sku)

            urls, _ = reference_data(row)
            if not row["product_image_url"]:
                raise ValueError(
                    f"Line {line_number}: product_image_url is required"
                )
            if len(urls) > 10:
                raise ValueError(
                    f"Line {line_number}: {sku} has more than 10 references"
                )
            invalid_urls = [url for url in urls if not is_https_url(url)]
            if invalid_urls:
                raise ValueError(
                    f"Line {line_number}: references must be public HTTPS URLs: "
                    f"{invalid_urls}"
                )
            rows.append(row)

    if not rows:
        raise ValueError("The CSV contains no product rows")
    return rows

def build_prompt(row: dict[str, str], role_lines: list[str]) -> str:
    avoid = row.get("avoid") or "extra products or unrelated props"
    references = " ".join(role_lines)
    return (
        f"Create one square e-commerce hero photograph of the {row['name']}. "
        f"{references} "
        f"The product is {row['color']} {row['material']}. "
        "Preserve the product identity, silhouette, proportions, visible design, "
        "color family, material, and packaging details from the references. "
        f"Show a {row['angle']} view on a {row['background']} background. "
        "Use a large diffused key light from the upper left and a natural contact "
        "shadow falling back-right. Keep the whole product inside the frame with "
        "crop-safe space. Return one product image, not a collage or contact sheet. "
        f"Do not add {avoid}. Do not invent logos, labels, accessories, duplicate "
        "products, handles, straps, buttons, caps, or controls."
    )

def load_manifest(path: Path) -> dict[str, Any]:
    if not path.exists():
        return {"prompt_version": PROMPT_VERSION, "items": {}}
    with path.open("r", encoding="utf-8") as handle:
        data = json.load(handle)
    if not isinstance(data.get("items"), dict):
        raise ValueError("manifest.json must contain an object named 'items'")
    return data

def save_manifest(path: Path, manifest: dict[str, Any]) -> None:
    temporary = path.with_suffix(path.suffix + ".tmp")
    with temporary.open("w", encoding="utf-8") as handle:
        json.dump(manifest, handle, indent=2, ensure_ascii=False)
        handle.write("\n")
    os.replace(temporary, path)

def output_items(payload: dict[str, Any]) -> list[Any]:
    data = payload.get("data")
    containers = [data, payload] if isinstance(data, dict) else [payload]
    for container in containers:
        for key in ("outputs", "output", "images", "image"):
            value = container.get(key)
            if value:
                return value if isinstance(value, list) else [value]
    raise ValueError(
        "No output field found. Inspect the saved response and update output_items() "
        "to match the current API response."
    )

def output_value(item: Any) -> str:
    if isinstance(item, str):
        return item
    if isinstance(item, dict):
        for key in ("url", "image_url", "b64_json", "base64"):
            value = item.get(key)
            if isinstance(value, str) and value:
                return value
    raise ValueError(f"Unsupported output item: {item!r}")

def extension_from_content_type(content_type: str) -> str:
    mime = content_type.split(";", 1)[0].strip().lower()
    return {
        "image/png": ".png",
        "image/jpeg": ".jpg",
        "image/webp": ".webp",
    }.get(mime, f".{OUTPUT_FORMAT}")

def save_output(value: str, file_stem: Path) -> tuple[Path, str | None]:
    if value.startswith("data:image/"):
        header, encoded = value.split(",", 1)
        mime = header.split(";", 1)[0].removeprefix("data:")
        destination = file_stem.with_suffix(extension_from_content_type(mime))
        destination.write_bytes(base64.b64decode(encoded))
        return destination, None

    if value.startswith("https://") or value.startswith("http://"):
        response = requests.get(value, timeout=REQUEST_TIMEOUT)
        response.raise_for_status()
        extension = extension_from_content_type(
            response.headers.get("Content-Type", "")
        )
        destination = file_stem.with_suffix(extension)
        destination.write_bytes(response.content)
        return destination, value

# Some responses return raw Base64 without a data URI.
    try:
        decoded = base64.b64decode(value, validate=True)
    except ValueError as exc:
        raise ValueError("Output is neither a URL nor valid Base64") from exc
    destination = file_stem.with_suffix(f".{OUTPUT_FORMAT}")
    destination.write_bytes(decoded)
    return destination, None

def backoff_seconds(attempt: int, response: requests.Response | None) -> float:
    if response is not None:
        retry_after = response.headers.get("Retry-After")
        if retry_after:
            try:
                return max(0.0, float(retry_after))
            except ValueError:
                pass
    return min(60.0, (2 ** (attempt - 1)) + random.uniform(0.0, 1.0))

def result_record(
    row: dict[str, str],
    references: list[str],
    status: str,
    attempt: int,
    *,
    output_url: str | None = None,
    local_path: str | None = None,
    error: str | None = None,
    created_at: str | None = None,
) -> dict[str, Any]:
    timestamp = now_utc()
    return {
        "sku": row["sku"],
        "asset_type": "hero",
        "status": status,
        "attempt": attempt,
        "prompt_version": PROMPT_VERSION,
        "reference_urls": references,
        "output_url": output_url,
        "local_path": local_path,
        "error": error,
        "requester": "",
        "reviewer": "",
        "approval_status": "pending" if status == "downloaded" else "",
        "created_at": created_at or timestamp,
        "updated_at": timestamp,
    }

def process_product(
    row: dict[str, str], previous: dict[str, Any] | None
) -> dict[str, Any]:
    api_key = os.environ["GPTPROTO_API_KEY"]
    references, role_lines = reference_data(row)
    prompt = build_prompt(row, role_lines)
    created_at = (previous or {}).get("created_at") or now_utc()
    first_attempt = int((previous or {}).get("attempt", 0)) + 1

    if first_attempt > MAX_ATTEMPTS:
        return result_record(
            row,
            references,
            "failed",
            first_attempt - 1,
            error="Maximum attempts already reached",
            created_at=created_at,
        )

    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
    }
    body = {
        "images": references,
        "prompt": prompt,
        "size": SIZE,
        "enable_base64_output": False,
        "enable_sync_mode": True,
        "output_format": OUTPUT_FORMAT,
    }

    for attempt in range(first_attempt, MAX_ATTEMPTS + 1):
        response: requests.Response | None = None
        try:
            response = requests.post(
                API_URL,
                headers=headers,
                json=body,
                timeout=REQUEST_TIMEOUT,
            )

            if response.status_code == 429 or response.status_code >= 500:
                message = f"HTTP {response.status_code}: {response.text[:500]}"
                if attempt == MAX_ATTEMPTS:
                    return result_record(
                        row,
                        references,
                        "failed_retryable",
                        attempt,
                        error=message,
                        created_at=created_at,
                    )
                time.sleep(backoff_seconds(attempt, response))
                continue

            response.raise_for_status()
            payload = response.json()
            item = output_items(payload)[0]
            value = output_value(item)
            file_stem = OUTPUT_DIR / (
                f"{clean_filename(row['sku'])}_hero_{attempt:02d}"
            )
            local_file, output_url = save_output(value, file_stem)
            return result_record(
                row,
                references,
                "downloaded",
                attempt,
                output_url=output_url,
                local_path=str(local_file),
                created_at=created_at,
            )

        except requests.exceptions.ConnectTimeout as exc:

# No connection was established; a bounded retry is reasonable.
            if attempt == MAX_ATTEMPTS:
                return result_record(
                    row,
                    references,
                    "failed_retryable",
                    attempt,
                    error=str(exc),
                    created_at=created_at,
                )
            time.sleep(backoff_seconds(attempt, response))

        except (
            requests.exceptions.ReadTimeout,
            requests.exceptions.ConnectionError,
        ) as exc:

# The server may have accepted the request. Avoid a blind resubmission.
            return result_record(
                row,
                references,
                "manual_check",
                attempt,
                error=f"Ambiguous network failure; verify account history: {exc}",
                created_at=created_at,
            )

        except requests.exceptions.HTTPError as exc:
            status_code = response.status_code if response is not None else "unknown"
            details = response.text[:500] if response is not None else str(exc)
            return result_record(
                row,
                references,
                "failed",
                attempt,
                error=f"HTTP {status_code}: {details}",
                created_at=created_at,
            )

        except (ValueError, KeyError, json.JSONDecodeError) as exc:
            return result_record(
                row,
                references,
                "manual_check",
                attempt,
                error=str(exc),
                created_at=created_at,
            )

    return result_record(
        row,
        references,
        "failed",
        MAX_ATTEMPTS,
        error="Request ended without a result",
        created_at=created_at,
    )

def should_skip(entry: dict[str, Any] | None) -> bool:
    if not entry:
        return False
    if entry.get("status") in {"manual_check", "failed"}:
        return True
    if entry.get("status") not in {"downloaded", "manual_review", "approved"}:
        return False
    local_path = entry.get("local_path")
    return bool(local_path and Path(local_path).exists())

def main() -> None:
    if not os.getenv("GPTPROTO_API_KEY"):
        raise SystemExit("Set GPTPROTO_API_KEY before running the script")
    if MAX_WORKERS < 1:
        raise SystemExit("MAX_WORKERS must be at least 1")

    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
    rows = validate_and_load_rows(CSV_PATH)
    manifest = load_manifest(MANIFEST_PATH)
    items: dict[str, Any] = manifest["items"]

    pending: list[dict[str, str]] = []
    for row in rows:
        entry = items.get(row["sku"])
        if should_skip(entry):
            print(f"SKIP {row['sku']}: {entry['status']}")
        else:
            pending.append(row)

    if not pending:
        print("Nothing to submit")
        return

    with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
        future_to_row = {
            executor.submit(process_product, row, items.get(row["sku"])): row
            for row in pending
        }
        for future in as_completed(future_to_row):
            row = future_to_row[future]
            try:
                record = future.result()
            except Exception as exc:  # Preserve an unexpected worker failure.
                references, _ = reference_data(row)
                record = result_record(
                    row,
                    references,
                    "failed",
                    int(items.get(row["sku"], {}).get("attempt", 0)),
                    error=f"Unexpected worker error: {exc}",
                )
            items[row["sku"]] = record
            manifest["prompt_version"] = PROMPT_VERSION
            manifest["updated_at"] = now_utc()
            save_manifest(MANIFEST_PATH, manifest)
            print(f"{record['status'].upper()} {row['sku']}: {record.get('local_path') or record.get('error')}")

if __name__ == "__main__":
    main()

Führen Sie es folgendermaßen aus:

python bulk_seedream_products.py

Sie können die Anzahl der Worker und die Ausgabegröße ändern, ohne die Datei zu bearbeiten:

MAX_WORKERS=2 SEEDREAM_SIZE=2048x2048 python bulk_seedream_products.py

Die Konsole meldet den Abschluss jeder SKU. Öffnen Sie manifest.json, um zu sehen, welche Dateien heruntergeladen wurden, fehlgeschlagen sind oder manuell geprüft werden müssen. Wenn die Live-Antwort ein nicht von output_items() abgedecktes Feldformat verwendet, protokolliert das Skript die Abweichung, statt den Auftrag stillschweigend als abgeschlossen zu behandeln. Aktualisieren Sie den kleinen Parser anhand der JSON-Daten, die Sie beim Test eines einzelnen Produkts erfasst haben.

Prüfen Sie jedes Bild, bevor es in den Shop gelangt

Ein heruntergeladenes Bild ist nicht automatisch genehmigt. Trennen Sie den Generierungsstatus vom Freigabestatus.

Prüfpunkt Häufiger Fehler
Geometrie Veränderte Kappe, Riemen, Knopf, Griff oder Proportionen
Farbe Falscher Farbton, falsche Sättigung oder Oberflächenbeschaffenheit
Material Kunststoff statt Metall; Lederstruktur wirkt flach
Verpackung Erfundenes Logo, falsch geschriebenes Etikett oder verändertes Volumen
Szene Schwebender Gegenstand oder unplausibler Kontaktschatten
Zuschnitt Produkt wird in Marktplatz-Miniaturansichten abgeschnitten
Duplikate Ein zusätzliches Produkt oder Zubehörteil erscheint

Ändern Sie den Status eines Eintrags erst dann von pending zu approved, rejected oder needs_edit, wenn eine Person die Ausgabe mit den Quellbildern verglichen hat. Muss ein Etikettentext rechtlich exakt sein, übernehmen Sie das Quellettikett in der Nachbearbeitung, statt ohne Prüfung auf generierten Text zu vertrauen.

Führen Sie als eigenen Qualitätsbenchmark vor der Skalierung einen kleinen Testmatrix-Durchlauf aus:

  • eine etikettierte Flasche vor einem Kataloghintergrund, in einer Lifestyle-Szene und in einem Kampagnenlayout;

  • eine Ledertasche in denselben drei Szenentypen; und

  • eine Lampe mit sichtbarer Geometrie und Bedienelementen.

Erfassen Sie die Freigabequote im ersten Durchlauf, die Anzahl der Versuche pro freigegebenem Bild, Fehler bei der Produktidentität, Etikettenfehler und die API-Kosten pro freigegebenem Bild. Diese Messwerte beschreiben Ihren Katalog besser als eine allgemeine Modelldemo.

Machen Sie den Workflow teamtauglich

Das Manifest ist die Übergabegrundlage zwischen ausführender Person, Design und Prüfung. Behalten Sie diese Felder bei, auch wenn derzeit eine Person alle Aufgaben übernimmt:

Feld Warum es wichtig ist
sku Dauerhafter Primärschlüssel für das Produkt
prompt_version Kennzeichnet die gemeinsamen Regeln für die Ausgabe
reference_urls Ermöglicht die Rekonstruktion der exakten Quellenauswahl
attempt Unterscheidet erneute Durchläufe und unterstützt die Kostenanalyse
requester und reviewer Weist Verantwortlichkeiten zu
approval_status Trennt generierte von veröffentlichungsfähigen Assets
output_url und local_path Verknüpft das API-Ergebnis mit der gespeicherten Datei
error Hält fest, warum ein Datensatz nicht weiterverarbeitet wurde
Zeitstempel Zeigt, wann der Datensatz geändert wurde

Behandeln Sie die SKU als unveränderlich. Wenn sich der gemeinsame Prompt ändert, erhöhen Sie PROMPT_VERSION, statt den bisherigen Verlauf zu überschreiben. Führen Sie abgelehnte Zeilen erneut aus, nicht den gesamten Katalog. Speichern Sie freigegebene Assets getrennt von Entwürfen und committen Sie niemals einen API-Schlüssel im selben Repository.

Das ist der Unterschied zwischen einem Batch-Skript und einem Team-Workflow. Das Skript erstellt Dateien. Die Datensätze erklären, wie diese Dateien entstanden sind und ob sie veröffentlicht werden dürfen.

Häufige Probleme und Lösungen

Problem Wahrscheinliche Ursache Lösung
401 oder 403 Schlüssel, Header, Kontozugriff oder Kontostand Prüfen Sie den aktuellen Bearer-Header auf der Live-Modellseite und kontrollieren Sie das Konto
Referenz wird nicht geladen Private, lokale oder abgelaufene URL oder URL ohne HTTPS Verwenden Sie eine stabile, öffentlich zugängliche HTTPS-URL und testen Sie sie außerhalb Ihres angemeldeten Browsers
429 Zu viele gleichzeitige Anfragen Verringern Sie MAX_WORKERS und beachten Sie die dokumentierte Wiederholungsantwort
Anfrage liefert eine Aufgabe, aber kein Bild Asynchroner Modus ist aktiv Verwenden Sie für dieses Skript enable_sync_mode: true oder implementieren Sie eine Abfrage auf Grundlage einer verifizierten Live-Antwort
Mögliches Duplikat nach Zeitüberschreitung Client hat nach der Übermittlung die Antwort verloren Nicht blind erneut versuchen; prüfen Sie den Kontoverlauf und markieren Sie die Zeile manuell
Produkt verändert sich zu stark Rollen der Referenzen oder zu bewahrende Details sind unklar Benennen Sie die Referenz für die Produktidentität und führen Sie die sichtbaren Merkmale auf, die erhalten bleiben müssen
Zusätzliche Objekte oder Collage Das gewünschte Ergebnis ist nicht genau genug beschrieben Fordern Sie ein einzelnes Produktbild an und schließen Sie Collagen sowie Duplikate ausdrücklich aus
Etikettentext ist falsch Generative Darstellung ist nicht exakt Prüfen Sie jedes Zeichen; übernehmen Sie exakt vorgeschriebene Texte in der Nachbearbeitung
Ausgabe-Parser findet kein Bild Aktuelles JSON-Format weicht ab Prüfen Sie die Antwort des Einzeltests und aktualisieren Sie output_items()

Fügen Sie keine Wiederholungsversuche hinzu, bevor Sie wissen, ob der Fehler aufgetreten ist, bevor oder nachdem der Server die Anfrage angenommen hat. Zuverlässigkeit bedeutet nicht, alles erneut zu senden.

Wann Seedream 5.0 Pro die richtige Wahl ist

Seedream 5.0 Pro eignet sich für Lifestyle-Szenen mit Produkten, Kampagnenlayouts, lokalisierte Kreativinhalte und anhand von Referenzbildern gesteuerte Katalogvarianten. Dank der festen Preise pro Bild lässt sich auch das reine Generierungsbudget einfach berechnen.

Es ersetzt weder Produktfotografie noch Nachbearbeitung, wenn die Ausgabe pixelgenaue Geometrie, rechtlich exakte Verpackungen oder vorgeschriebene Etikettentexte bewahren muss. Es passt auch nicht zu einem System, das jedes generierte Bild ungeprüft veröffentlicht, oder zu einer Anforderung, bei der eine Anfrage viele separate Produktdateien liefern soll.

Nutzen Sie es für skalierbare Kreativproduktion. Zwischen Generierung und Veröffentlichung sollte immer eine Person prüfen.

Beginnen Sie mit einem repräsentativen Katalogausschnitt

Wählen Sie eine kleine Anzahl von SKUs, die unterschiedliche Formen, Materialien, Etiketten und Zubehör abdecken. Legen Sie CSV-Schema und Prompt-Version fest. Messen Sie Versuche, Ablehnungsgründe und Kosten pro freigegebenem Bild. Erweitern Sie den Umfang erst, wenn das Fortsetzen und der Prüfprozess funktionieren.

Prüfen Sie die aktuellen Parameter und Preise auf der Seedream-5.0-Pro-API-Seite und testen Sie ein Produkt, bevor Sie einen vollständigen Katalog-Batch finanzieren. Für Produktreferenzen statt reiner Textgenerierung öffnen Sie die Seedream 5.0 Pro Image Edit API.

FAQ

Wie erstelle ich mit der Seedream 5.0 Pro API mehrere Produktbilder?

Erstellen Sie eine Anfrage pro SKU. Lesen Sie Produktvariablen und Referenz-URLs aus einer CSV-Datei ein, führen Sie eine kleine Anzahl von Anfragen parallel aus, verwenden Sie Dateinamen mit SKU-Bezug und halten Sie jedes Ergebnis in einem Manifest fest. Das obige Skript setzt dieses Muster um.

Kann Seedream 5.0 Pro mehrere Bilder gleichzeitig generieren?

Nicht mit einer einzigen Anfrage, die einen Katalog separater Bilder zurückgibt. Das aktuelle Pro-Modell bietet keine Funktion, mit der sich mehrere Bilder aus einem Text generieren lassen. Sie können in Seedream 5 Pro dennoch mehrere Bilder generieren, indem Sie unabhängige Anfragen über eine kontrollierte Warteschlange parallel ausführen.

Wie viele Referenzbilder kann ich mit Seedream 5.0 Pro auf GPTProto verwenden?

Der aktuelle Endpunkt für die Bildbearbeitung akzeptiert bis zu 10 Referenzen. Die erste ist im Grundpreis enthalten, jede weitere Referenz kostet zusätzlich 0,0027 $. Weisen Sie jeder Referenz im Prompt eine eindeutige Rolle zu.

Wie viel kostet ein Seedream 5.0 Pro API-Batch?

Zum Preisstand vom 17. September 2026 kostet ein 1K-Bild 0,0405 $ und ein 2K-Bild 0,081 $. Rechnen Sie für jede Referenz nach der ersten 0,0027 $ hinzu und multiplizieren Sie den Betrag anschließend mit der durchschnittlichen Anzahl der Versuche, die für ein freigegebenes Ergebnis erforderlich sind. Prüfen Sie vor der Budgetplanung für einen Produktionslauf die aktuelle Modellseite.

Eignet sich Seedream 5.0 Pro für geschäftliche Abläufe oder Team-Workflows?

Ja, wenn die Generierung mit einer stabilen SKU-Zuordnung, Prompt-Versionen, einem fortsetzbaren Manifest, geschützten API-Schlüsseln, benannten Prüfern und Freigabestatus verknüpft ist. Die API erstellt Entwurfs-Assets. Ihr Workflow entscheidet, welche Assets veröffentlicht werden können.

Sollte ich Produktbilder in 1K oder 2K generieren?

Verwenden Sie 1K, um den gemeinsamen Prompt mit repräsentativen SKUs zu testen. Sobald der Prompt stabil ist, verwenden Sie die für die endgültige Platzierung erforderliche Auflösung. Behandeln Sie jede 2K-Anfrage als neue Generierung und prüfen Sie sie erneut, statt davon auszugehen, dass sie der 1K-Komposition entspricht.

Verwandte Artikel

Weitere Blogbeiträge
Die 5 besten erschwinglichen KI-Video-APIs 2026: Preise, E-Commerce und Kurzdramen

Die 5 besten erschwinglichen KI-Video-APIs 2026: Preise, E-Commerce und Kurzdramen

Vidu Q3 Turbo ist 2026 für die meisten Entwickler die beste erschwingliche KI-Video-API. Ein fünfsekündiger Clip in 720p kostet etwa $0.24, während 1080p $0.056 pro generierter Sekunde kostet. Seedance 2.0 Mini eignet sich besser für günstige Entwürfe, Hailuo 2.3 Standard für Actionclips mit fester Länge von sechs Sekunden, Kling 3.0 Standard für Dialoge und Wan 3.0 für längere Geschichten mit mehreren Einstellungen. Diese Empfehlungen ändern sich, wenn man Auflösung, Mindestclipdauer, Audio und fehlgeschlagene Versuche berücksichtigt. Dieser Vergleich geht über den niedrigsten beworbenen Preis hinaus und schätzt, was jede API für eine tatsächlich verwendbare Aufnahme kostet. Preishinweis: Die GPTProto-Preise in diesem Leitfaden wurden am 15. September 2026 überprüft. Die Preise der Video-APIs und die verfügbaren Einstellungen können sich ändern. Prüfen Sie daher die aktuelle Modellseite, bevor Sie einen Produktionslauf budgetieren. Vidu Q3 Turbo testen

Tiffany Layne | 2026-09-16

6 erschwingliche LLM-APIs für KI-Agenten im Jahr 2026

6 erschwingliche LLM-APIs für KI-Agenten im Jahr 2026

Eine erschwingliche LLM-API für einen KI-Agenten ist nicht unbedingt das Modell mit dem niedrigsten Preis pro Eingabe-Token. Ein Agent kann ein Tool auswählen, Argumente erstellen, das Ergebnis lesen, seinen Plan überarbeiten und ein weiteres Tool aufrufen, bevor er eine brauchbare Antwort liefert. Ein günstiges Modell, das ungültige Aufrufe tätigt oder mehrere Wiederholungsversuche benötigt, kann daher mehr kosten als ein etwas teureres Modell, das die Aufgabe auf Anhieb erledigt. Dieser Leitfaden vergleicht sechs agententaugliche Modelle, die über GPTProto verfügbar sind. Die Rangliste berücksichtigt API-Preise, Tool-Nutzung, unabhängige Leistungsnachweise, Geschwindigkeit, Kontextlimits sowie das praktische Risiko, für unnötige Agent-Schleifen zu bezahlen. Es handelt sich um einen Vergleich öffentlicher Benchmarks und Preise – nicht um die Behauptung, dass wir einen privaten direkten Vergleichstest durchgeführt haben. Ein Schlüssel für Ihr Team Kurz gesagt: GLM-5.3 Flash ist für die meisten kostenbewussten Agenten die beste Standardwahl. DeepSeek Flash ist die schnellere Alternative mit offenen Gewichten, während GPT-5.6 Luna für leichte Aufgaben mit hohem Volumen vielversprechend ist, sobald der Preis für die Live-Route bestätigt ist. MiniMax M3 eignet sich für lange Dokumentensitzungen, Gemini 3.8 Flash ist bei der multimodalen Geschwindigkeit führend, und Grok 4.6 sollte eher als Eskalationsmodell für schwierigere Aufgaben betrachtet werden.

Michael Johnson | 2026-09-15

So erstellen Sie ein KI-Live-Wallpaper mit Midjourney und Seedance 2.5

So erstellen Sie ein KI-Live-Wallpaper mit Midjourney und Seedance 2.5

Du kannst denselben Ablauf auch ohne Programmierkenntnisse durchführen: Erstelle oder wähle ein Bild aus, bitte ein Chatmodell, das Bilder verarbeiten kann, um einen Bewegungs-Prompt, animiere das Bild und lade das Ergebnis herunter. Der letzte Schritt hängt von deinem Gerät ab. Unter Windows und Android kannst du Video-Wallpaper-Apps verwenden; für den animierten Sperrbildschirm benötigt das iPhone ein kompatibles Live Photo.

Tiffany Layne | 2026-09-09

Mehrere Bilder gleichzeitig in ChatGPT generieren

Mehrere Bilder gleichzeitig in ChatGPT generieren

TL;DR Um zu meistern, wie man in ChatGPT mehrere Bilder auf einmal generiert, braucht es eine Kombination aus strukturierten Prompts in der Chat-Oberfläche und der Verwendung bestimmter Parameter oder Schleifen über die OpenAI API. Während die Standardoberfläche standardmäßig nur einzelne Bilder ausgibt, können Sie diesen Engpass mit Rasterlayouts und Batch-Befehlen umgehen. Die meisten Nutzer haben Schwierigkeiten, weil die Weboberfläche auf einfache Gespräche und nicht auf die Produktion großer Mengen ausgelegt ist. Wenn Sie die zugrunde liegende Funktionsweise von DALL-E 3 verstehen, können Sie das Tool wie eine kreative Produktionsstätte statt wie einen einfachen Chatbot nutzen. Dieser Leitfaden zeigt den Übergang von manuellen Einzelanfragen zu automatisierten oder strukturierten Batch-Workflows. So müssen Sie nie wieder warten, bis ein einzelnes Bild gerendert ist, bevor Sie mit dem nächsten kreativen Konzept beginnen können.

Schuyler Stacy | 2026-08-31