
Die meisten Tutorials zur Bilderzeugung hören bei einem Bild auf. Sie zeigen einen curl-Befehl, eine hübsche Ausgabe und nennen es erledigt. Das reicht für „generiere ein Katzenbild". Es ist nutzlos, wenn du 500 Bilder für ein echtes Projekt brauchst.
Programmatische Bilderzeugung — Bilder in großem Umfang aus Code generieren, ohne menschliches Eingreifen — ist eine andere Fähigkeit. Dieser Leitfaden behandelt die gesamte Pipeline: Prompt Engineering im großen Maßstab, Batch-Verarbeitung, Fehlerbehandlung, asynchrone Verarbeitung, Ausgabeverwaltung und Integration in Produktionssysteme.
Die drei Ebenen einer Produktions-Bild-Pipeline
Jede Produktions-Bild-Pipeline hat drei Ebenen:
| Ebene | Aufgabe | Werkzeuge |
|---|---|---|
| Generation | Wandelt Prompts in Bilder um | AnyCap CLI, REST-APIs |
| Orchestration | Verwaltet Batches, Wiederholungen, Parallelität | Python-Skripte, Queue-Systeme |
| Integration | Verbindet mit App, CMS, Speicher | Webhooks, S3, CMS-APIs |
Die meisten Entwickler denken nur an Ebene 1. Aber die Ebenen 2 und 3 entscheiden über Erfolg oder Scheitern der Pipeline.
Ebene 1: Prompt Engineering im großen Maßstab
Wenn du ein Bild generierst, kannst du den perfekten Prompt liebevoll ausarbeiten. Wenn du 500 generierst, brauchst du ein Prompt-System.
Der Template-Ansatz
# prompts.py — Zentrale Prompt-Vorlagen
from dataclasses import dataclass
from typing import Optional
@dataclass
class ImageJob:
template: str
params: dict
output_path: str
model: str = "nano-banana-2"
PROMPT_TEMPLATES = {
"product_hero": "E-Commerce-Produktfoto: {product_name}, {color}, Studiolicht, weißer Hintergrund, 1024x1024, Werbefotografie",
"blog_hero": "Blog-Header-Illustration: {topic}, {style}-Stil, {mood}-Stimmung, 1200x630, redaktionell",
"social_post": "Social-Media-Visual: {subject}, {platform}-Format, {vibe}-Ästhetik, {dimensions}",
}
def build_prompt(template_key: str, **params) -> str:
return PROMPT_TEMPLATES[template_key].format(**params)
Das Scale-Up-Muster
# 100 Produktfotos aus einer CSV generieren
import csv, subprocess, json
from concurrent.futures import ThreadPoolExecutor, as_completed
def generate_single(job: ImageJob) -> dict:
prompt = build_prompt(job.template, **job.params)
result = subprocess.run([
"anycap", "image", "generate",
"--prompt", prompt,
"--model", job.model,
"--output-format", "json",
"-o", job.output_path
], capture_output=True, text=True)
return {
"output_path": job.output_path,
"success": result.returncode == 0,
"data": json.loads(result.stdout) if result.returncode == 0 else None,
"error": result.stderr if result.returncode != 0 else None
}
# Job-Liste aus Daten erstellen
jobs = []
with open("products.csv") as f:
for row in csv.DictReader(f):
jobs.append(ImageJob(
template="product_hero",
params={"product_name": row["name"], "color": row["color"]},
output_path=f"output/{row['sku']}.png"
))
# Mit Parallelitätskontrolle ausführen
with ThreadPoolExecutor(max_workers=4) as executor:
futures = {executor.submit(generate_single, job): job for job in jobs}
for future in as_completed(futures):
result = future.result()
status = "✅" if result["success"] else "❌"
print(f"{status} {result['output_path']}")
Ebene 2: Orchestration — Der Teil, den alle vergessen
Generieren ist einfach. Es im großen Maßstab zuverlässig zu machen, ist die eigentliche Ingenieursarbeit.
Muster 1: Asynchrone Batch-Verarbeitung
Für große Batches (100+ Bilder) den Async-Modus verwenden, um Blockierungen zu vermeiden:
# Batch-Job einreichen
anycap image generate \
--prompt "$(python build-prompts.py --csv products.csv)" \
--model nano-banana-2 \
--async \
--batch-size 20 \
--webhook "https://deine-app.com/webhooks/images" \
-o output/products/
Dein Webhook erhält Ergebnisse, sobald sie fertig sind. Kein Polling. Keine Timeout-Probleme.
Muster 2: Wiederholung mit exponentiellem Backoff
import time, random
def generate_with_retry(job: ImageJob, max_retries: int = 3) -> dict:
for attempt in range(max_retries):
result = generate_single(job)
if result["success"]:
return result
if attempt < max_retries - 1:
wait = (2 ** attempt) + random.uniform(0, 1)
print(f"Wiederholung {attempt + 1}/{max_retries} für {job.output_path} in {wait:.1f}s")
time.sleep(wait)
return result # Letzten Fehlschlag zurückgeben
Muster 3: Queue-basierte Architektur
Für Produktionssysteme eine richtige Queue verwenden:
# Einfache Redis-basierte Job-Queue
import redis, json
r = redis.Redis()
def enqueue_job(job: ImageJob):
r.lpush("image_jobs", json.dumps({
"template": job.template,
"params": job.params,
"output_path": job.output_path,
"model": job.model,
}))
def worker_loop():
while True:
_, job_data = r.brpop("image_jobs")
job = json.loads(job_data)
result = generate_single(ImageJob(**job))
if result["success"]:
r.lpush("image_results", json.dumps(result))
else:
r.lpush("image_failures", json.dumps(result))
Ebene 3: Integration — Bilder dorthin bringen, wo sie hingehören
Upload zu S3
import boto3
s3 = boto3.client("s3")
def upload_to_s3(local_path: str, bucket: str, key: str) -> str:
s3.upload_file(local_path, bucket, key, ExtraArgs={
"ContentType": "image/png",
"CacheControl": "public, max-age=31536000",
})
return f"https://{bucket}.s3.amazonaws.com/{key}"
Im CMS veröffentlichen
import requests
def update_cms_product_image(sku: str, image_url: str):
requests.patch(
f"https://cms.example.com/api/products/{sku}",
headers={"Authorization": "Bearer $CMS_TOKEN"},
json={"image_url": image_url}
)
Team benachrichtigen
def notify_slack(message: str):
requests.post(
"https://hooks.slack.com/services/YOUR/WEBHOOK/URL",
json={"text": message}
)
Das vollständige Pipeline-Skript
#!/usr/bin/env python3
"""production-pipeline.py — Vollständige Bilderzeugungs-Pipeline"""
import csv, subprocess, json, time, random, sys
from concurrent.futures import ThreadPoolExecutor, as_completed
from dataclasses import dataclass
import boto3, requests
# --- Konfiguration ---
S3_BUCKET = "my-assets"
SLACK_WEBHOOK = "https://hooks.slack.com/services/YOUR/WEBHOOK"
MAX_WORKERS = 4
MAX_RETRIES = 3
PROMPTS = {
"product": "E-Commerce-Foto: {name}, {color}, Studio, weißer Hintergrund, 1024x1024",
"lifestyle": "Lifestyle-Foto: {name}, {color}, {scene}, natürliches Licht, 1024x1024",
}
@dataclass
class Job:
template: str
params: dict
output: str
model: str = "nano-banana-2"
def generate(job: Job) -> dict:
prompt = PROMPTS[job.template].format(**job.params)
for attempt in range(MAX_RETRIES):
result = subprocess.run([
"anycap", "image", "generate",
"--prompt", prompt, "--model", job.model,
"--output-format", "json", "-o", job.output
], capture_output=True, text=True)
if result.returncode == 0:
data = json.loads(result.stdout)
return {"path": job.output, "url": data.get("image_url"), "success": True}
if attempt < MAX_RETRIES - 1:
time.sleep((2 ** attempt) + random.uniform(0, 1))
return {"path": job.output, "success": False, "error": result.stderr}
def upload(path: str) -> str:
key = path.replace("output/", "")
s3 = boto3.client("s3")
s3.upload_file(path, S3_BUCKET, key, ExtraArgs={"ContentType": "image/png"})
return f"https://{S3_BUCKET}.s3.amazonaws.com/{key}"
def notify(text: str):
requests.post(SLACK_WEBHOOK, json={"text": text})
def run_pipeline(csv_path: str):
jobs = []
with open(csv_path) as f:
for row in csv.DictReader(f):
jobs.append(Job("product", {"name": row["name"], "color": row["color"]}, f"output/{row['sku']}.png"))
notify(f"🚀 Pipeline gestartet: {len(jobs)} Bilder")
results = []
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
futures = {executor.submit(generate, job): job for job in jobs}
for future in as_completed(futures):
result = future.result()
if result["success"]:
result["s3_url"] = upload(result["path"])
results.append(result)
success = len(results)
failed = len(jobs) - success
notify(f"{'✅' if failed == 0 else '⚠️'} Pipeline abgeschlossen: {success}/{len(jobs)} Bilder. {failed} fehlgeschlagen.")
return results
if __name__ == "__main__":
run_pipeline(sys.argv[1])
Das richtige Modell für deine Pipeline wählen
| Pipeline-Typ | Modell | Warum |
|---|---|---|
| Hero-Bilder, finale Ausgabe | Seedream 5 | Beste Qualität im ersten Durchlauf |
| Massengenerierung, Varianten | Nano Banana 2 | Am schnellsten und günstigsten |
| Überarbeitungen, Verfeinerungen | Nano Banana Pro | Beste Image-to-Image-Bearbeitung |
| Prototyping, Iteration | Nano Banana 2 | Geschwindigkeit > Perfektion in frühen Phasen |
Kosten im großen Maßstab
| Volumen | Nano Banana 2 | Seedream 5 | Manuelles Design |
|---|---|---|---|
| 100 Bilder | ~$0.50 | ~$1.50 | $500–1.000 |
| 1.000 Bilder | ~$5 | ~$15 | $5.000–10.000 |
| 10.000 Bilder | ~$50 | ~$150 | $50.000+ |
| 100.000 Bilder | ~$500 | ~$1.500 | Nicht praktikabel |
Letzte Aktualisierung: Mai 2026