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:
Referenz für die Produktidentität: Silhouette, Proportionen, sichtbares Design und Verpackung.
Detailreferenz: Material, Beschläge, Bedienelemente oder Etikettengestaltung.
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.