← Blog

Z-Image-Base API Umfassender Leitfaden: CFG + Negative Prompt Praktische Anwendung

Z-Image-Base API Umfassendes Tutorial: CFG-Parameter-Abstimmung, Verwendung von negative_prompt, Referenzbild-Führung, Stärkesteuerung. Enthält Python/cURL-Codebeispiele.

9 min read
Z-Image-Base API Umfassender Leitfaden: CFG + Negative Prompt Praktische Anwendung

Hallo, ich bin Dora. Es ist erstaunlich, wie mich eine kleine Unannehmlichkeit zur Z-Image-Base API geführt hat. Ich hatte einen Stapel Referenzbilder, Texte, die ein einheitliches Erscheinungsbild benötigten, und nicht genug Zeit, um Prompts ständig zu überwachen. Ich sah immer wieder Leute, die Einstellungen manuell anpassten und einmalige Ergebnisse posteten. Das ist für Demos in Ordnung. Meine Arbeit brauchte etwas, das ich zweimal pro Woche ohne Überraschungen ausführen konnte. Also verdrahtete ich über ein paar Sitzungen im Januar–Februar 2026 Z-Image-Base, achtete darauf, wo es mir Widerstand leistete, und machte mir Notizen darüber, was es tatsächlich zuverlässig erscheinen ließ.

Das ist keine Tour durch jeden Parameter. Es sind die Teile, die ich immer wieder angefasst habe, plus kleine Anpassungen, die den mentalen Aufwand reduzierten. Wenn du von KI-Tools umgeben bist und einfach eines willst, das sich ordentlich verhält, könnte dies helfen.

Grundlegende API-Konfiguration

Abrufen des WaveSpeed API-Schlüssels

Ich meldete mich an und holte einen API-Schlüssel aus dem WaveSpeed-Dashboard. Nichts Überraschendes dabei. Eine kleine Anmerkung: Die Schlüsselberechtigungen waren projektbezogen. Hilfreich in gemeinsamen Workflows, aber das bedeutet, dass du Projekte von Anfang an klar benennen solltest. Ich erstellte ein Projekt nur für Bildexperimente, damit Protokolle und Kontingente sich nicht mit der Produktion vermischten.

Wenn du Schlüssel rotierst (ich tue es, monatlich), handhabte Z-Image-Base das sauber. Ich tauschte das Secret in meinen Umgebungsvariablen aus, führte das Skript erneut aus und sah keinen verbleibenden Auth-Zustand. So sollte es sein, aber ich spürte trotzdem eine gewisse Erleichterung, als es beim ersten Versuch funktionierte.

Struktur der Endpunkt-URL

Die Basis sah wie ein Standard-REST-Setup mit einem Modell-Schalter aus:

  • Basis: https://api.wavespeed.ai
  • Versionierter Pfad: /v1/images
  • Modell-Selektor: model: "Z-Image-Base"

In der Praxis sendete ich POST-Anfragen an /v1/images/generations für reine Prompt-Läufe und an /v1/images/edits, wenn ich ein Referenzbild übergab. Ich bevorzuge klare Pfade gegenüber gepackten Query-Strings, und das passte zu meiner Denkweise. Wenn dein Routing abweicht, spielt der Modellname trotzdem eine Rolle: Z-Image-Base schien etwas konservativer als auffälligere Modelle, was mir gefiel.

Einstellungen des Authentifizierungs-Headers

Hier gibt es nichts Kniffliges, aber die genaue Schreibweise zählt:

  • Authorization: Bearer YOUR_WAVESPEED_API_KEY
  • Content-Type: application/json für reine Prompts
  • Content-Type: multipart/form-data beim Hochladen eines Bildes

Ich halte Schlüssel in .env und lade sie zur Laufzeit. Es ist langweilig, und es verhindert versehentliche Commits. Außerdem: Wenn du aus CI skriptest, setze den Schlüssel als maskiertes Secret und vermeide das Protokollieren vollständiger Anfragen. Offensichtlich, aber es lohnt sich, es zu sagen. Eine verirrte Protokollzeile kann dich heimsuchen. Gemäß den Best Practices für Sicherheit von WaveSpeed sollten API-Schlüssel in Umgebungsvariablen gespeichert, niemals im Frontend-Code offengelegt und regelmäßig rotiert werden.

Detaillierte Erklärung der Kernparameter

prompt - Methode zum Schreiben von Vorwärts-Prompts

Ich machte mir keine Gedanken über den Prompt-Stil. Mit Z-Image-Base funktionierten einfache Vorwärts-Prompts besser als aufwendige Prosa. Ich schrieb Absicht zuerst, Einschränkungen danach:

  • „Redaktionelles Produktfoto einer Keramiktasse auf einem Leinentisch, weiches Morgenlicht.”
  • Dann Spezifika: „35-mm-Look, neutrale Farbabstufung, kein Text, aufgeräumter Hintergrund.”

Ich ließ Verben weg, es sei denn, ich brauchte Bewegung. Das Modell war beständig, wenn ich konkret blieb: Materialien, Licht, Linsencharakter, Hintergrundtyp. Es driftete, wenn ich Stimmungen hinzufügte. Wenn ich „gemütlicher Minimalismus” schrieb, bekam ich Kerzen-Chaos. Wenn ich „weiches Licht, eine diffuse Quelle, keine Kerzen” schrieb, wurde es befolgt.

negative_prompt - Unerwünschte Elemente ausschließen

Ich widersetzte mich zunächst negativen Prompts, aber sie wurden meine Sicherheitsschiene. Ich behielt eine kurze Baseline, die ich wiederverwendete:

  • „kein Text, kein Wasserzeichen, keine extra Hände, keine extra Gliedmaßen, kein Logo, keine Unterschrift, kein Rand”

Das Hinzufügen davon verkürzte die Bereinigungszeit. Es behebt nicht jeden Grenzfall (Ringe schleichen sich bei Tassen manchmal immer noch ein), aber es senkte die Rate der Seltsamkeiten genug, dass ich für kleine Stapel aufhörte, neu zu rendern. Wenn ich strenge Produktwinkel brauchte, fügte ich „keine dramatische Perspektive, keine Neigung” hinzu und bekam ausgerichtetere Aufnahmen.

guidance_scale (CFG) - Den Grad der Prompt-Compliance kontrollieren

Dieser Parameter war wichtiger, als ich erwartet hatte. Meine Notizen:

  • 5–6: entspannt, etwas mehr Textur und Improvisation
  • 7–8: verlässlich: was ich für die meisten Läufe verwendete
  • 9+: wörtliche Compliance, aber manchmal spröde Beleuchtung und flachere Farbe

Wenn ich ein gutes Referenzbild hatte, senkte ich CFG auf 6–7 und bekam schöneren Mikrokontrast. Ohne Referenz war 7,5 mein Median. Gemäß der technischen Z-Image-Dokumentation unterstützt das Modell vollständiges Classifier-Free Guidance (CFG) und bietet Präzision für komplexes Prompt-Engineering. Über 9 fühlte es sich an wie einem sorgfältigen Freund zu sagen, noch vorsichtiger zu sein – nicht falsch, nur etwas leblos.

image + strength - Führung durch Referenzbilder

Dieses Paar erledigte die schwere Arbeit. Ich hängte ein einzelnes Referenz-JPG an und stimmte strength ab, um zu kontrollieren, wie stark das Modell folgte:

  • 0,25–0,35: leichte Berührung: behält Palette und Stimmung
  • 0,45–0,6: solide Einhaltung: Komposition bleibt nah
  • 0,7+: Fast-Kopie: nützlich für Variationen mit kleinen Stiländerungen

Ich erwartete nicht, dass 0,5 der Sweet Spot sein würde, aber es war so. Genug Struktur, um das Layout beizubehalten, genug Freiheit für Beleuchtungsanpassungen. Höhere Stärken machten es stur: Wenn die Quelle einen Schatten an der falschen Stelle hatte, klammerte sich das Modell daran. Ich begann, den Prompt mit „Schatten auf Kamera-links” zu nudgen und stattdessen die Stärke zu reduzieren. Weniger Wiederholungen auf diese Weise.

Eine praktische Anmerkung: Ich skalierte Referenzeingaben auf 1024 auf der langen Seite vor dem Hochladen. Konsistente Abmessungen gaben mir vorhersehbarere Zuschnitte und Geschwindigkeiten. Es sparte auch ein bisschen Bandbreite über viele Iterationen.

Code-Implementierung

Schnelltest mit cURL

Wenn ich einen neuen Endpunkt verdrahte, beginne ich mit cURL, nur um die Form der Antwort und Fehlercodes zu sehen.

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

Ich erhielt eine JSON-Nutzlast mit einem Array von Bildern (base64 oder URLs, je nach Optionen). Wenn du direkte URLs bevorzugst, setze ein Ausgabeziel oder ein Speicherflag, wenn die API es unterstützt. Ich blieb beim Testen bei base64 und dekodierte lokal.

Vollständiges Python-Beispiel

Das ist nah an dem, was ich an einem Dienstagmorgen als Batch-Job laufen ließ. Es ist einfach, mit gerade genug Struktur für Wiederholungen.

import os
import base64
import requests

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

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


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


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

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

Ich fügte ein 60s-Timeout hinzu, nachdem ein Job bei instabilem WLAN ins Stocken geriet. Der Exception-Block ist direkt, aber in der Produktion mappe ich Statuscodes (429, 503) auf Backoff.

JavaScript/Node.js-Beispiel

Für Node verwendete ich fetch mit einem kleinen Helfer. Gleiche Idee: Auth einfach halten, Fehler klar sichtbar machen.

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

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

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

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

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

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

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

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

Sobald das funktionierte, hörte ich auf, über die Infrastruktur nachzudenken, was der Sinn der Sache ist. Die Z-Image-Base API war kein Hindernis.

Asynchroner vs. Synchroner Modus

Ich probierte beide Modi aus. Synchron war in Ordnung für einzelne Bilder oder schnelle Checks. Die Latenz lag im Durchschnitt im niedrigen Sekundenbereich für 1024-Quadrat-Ausgaben. Wenn ich 8–12 Renders in die Warteschlange stellte, wechselte ich zu asynchron, damit mein Skript nicht bei jedem blockierte.

Das asynchrone Muster war vorhersehbar: Job senden, eine id erhalten, einen Status-Endpunkt abfragen, dann das Ergebnis abrufen, wenn state: "succeeded". Ich setzte ein Abfrageintervall von 2–3 Sekunden und eine 2-Minuten-Obergrenze. Bei instabilen Netzwerken rettete mich async vor Timeouts, weil ich das Abfragen fortsetzen konnte, ohne den Job erneut einzureichen. Kleines Detail, großer Unterschied bei Reise-WLAN.

Wenn dein Workflow interaktiv ist (anpassen, ansehen, anpassen), ist sync angenehmer. Wenn es stapelweise oder geplant ist, hält async die Pipeline reibungsloser und scheitert eleganter.

Fehlerbehandlung

Zwei Muster halfen mir, Fehler unspektakulär zu halten.

  • Rate-Limits (429): Ich verwendete exponentiellen Backoff mit Jitter. Starte bei ~500ms, begrenze auf ~20s und höre nach 5–6 Versuchen auf. Bei asynchronen Jobs wiederholte ich einfach den Status-Aufruf, anstatt den Render erneut einzureihen, es sei denn, die API teilte mit, dass der Job fehlte.
  • Validierungsfehler (400/422): Diese kamen normalerweise von nicht übereinstimmenden Content-Types oder nicht unterstützten Größen. Wenn ich einen 422-Fehler bei Bildbearbeitungen sah, war es fast immer mein falscher Form-Daten-Feldname (image vs. reference_image). Ich fügte eine kleine Schema-Prüfung vor Anfragen hinzu, um dies zu verhindern.

Noch eine Anmerkung: Ich protokollierte model, guidance_scale, size und einen Hash des Prompts. Wenn etwas Merkwürdiges durchschlüpfte, konnte ich es exakt reproduzieren. Das ist der Unterschied zwischen Achselzucken und Beheben.

Versagt Z-Image-Base weniger als andere Modelle? Schwer zu sagen. Was ich bemerkte, war, dass die Fehlermeldungen spezifisch genug waren, dass ich keine Zeit mit Raten verlor, wenn es scheiterte. Das ist gute Ergonomie.