thats-me/documentation/bildgenerierung-gemini-api.md
Kevin Adametz 522deffa1a Hintergründe: 15 kuratierte Bilder statt Testfotos
Elf universelle Hintergründe von grafisch bis fotografisch (Tinte, Goldstaub,
Horizont, Kohle, Leinen, Nebel, Dämmerung, Nachthimmel, Tusche, Papier,
Aquarell) plus vier v2-Fassungen zur Abstimmung, alle 2048² JPEG mit
320er Vorschauen (7 MB statt 48 MB). Auswahl, Reihenfolge und Helligkeit
stehen in src/config/backgrounds.js; der Picker zeigt Name und aktuelle
Wahl, das Logo folgt der Bildhelligkeit, Presets zeigen auf die neuen
Bilder, alte Pfade werden beim Laden auf den Nachfolger umgeschrieben.
Der Service Worker cacht nur die Vorschauen vor.

Werkzeuge zum Neu-Rendern (SVG-Builder, Gemini-Prompts) liegen in
dev/tools/backgrounds, die Sichtungs-Renders bleiben lokal (gitignore).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-25 09:31:09 +00:00

6.6 KiB
Raw Blame History

Google-Gemini-Bild-API: Zugang

Stand: 25.09.2026

Überblick

Die Key-Images der Demo-Lifelines entstehen über die Google Gemini API, Modell gemini-3.1-flash-image (Produktname „Nano Banana 2"). Der Zugang läuft ausschließlich über REST mit einem API-Key aus dem Google AI Studio – kein SDK, kein OAuth, kein Google-Cloud-Projekt-Setup nötig.

Genutzt wird der Zugang nur von den Entwickler-Werkzeugen unter dev/tools/lifeline-images/:

Datei Aufgabe
gemini.py Einziger API-Client: liest den Key, baut den Request, speichert das Bild
anchors.py Erzeugt die Altersanker je Figur (Porträt-Kette)
events.py Erzeugt ein Bild pro Lifeline-Event mit Ankern als Referenz
contact_sheet.py, montage.py Reine Sichtungs-Werkzeuge, ohne API-Zugriff

Die Laravel-Anwendung selbst spricht nie mit der API. Sie importiert nur fertige Dateien per php artisan lifelines:import-images <handle>.

Zugangsdaten

Der Key steht als GOOGLE_API_KEY in backend/.env – sonst nirgends. .env ist in backend/.gitignore ausgeschlossen, der Key liegt also nicht im Repository.

GOOGLE_API_KEY=<key aus dem AI Studio>

gemini.py liest die Datei direkt zeilenweise und schneidet Anführungszeichen ab; fehlt der Eintrag, bricht das Skript mit GOOGLE_API_KEY missing in backend/.env ab. Der Key wird nur als HTTP-Header x-goog-api-key gesendet, nie in eine URL, einen Log oder eine Ausgabe geschrieben.

Regeln für den Umgang:

  • Der Key gehört nur in die lokale Entwicklungsumgebung. Auf Produktion wird er nicht benötigt – dort landen ausschließlich die fertigen PNG-Dateien und der Import-Befehl.
  • Generierte Bilder und Anker liegen unter backend/storage/app/private/lifeline-images/ und sind ebenfalls vom Repository ausgeschlossen.
  • Der Key ist ein Zahlungsmittel: Wer ihn hat, generiert auf Kosten des hinterlegten Guthabens. Nicht in Tickets, Chats oder Screenshots weitergeben.
  • Bei Verdacht auf Weitergabe: im AI Studio löschen und neu erzeugen (siehe letzter Abschnitt).

Endpunkt und Request

Ein Aufruf ist ein POST auf generateContent des Bildmodells:

POST https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-flash-image:generateContent
Content-Type: application/json
x-goog-api-key: <GOOGLE_API_KEY>

Der Body enthält einen einzigen Nutzer-Turn. Referenzbilder stehen als inline_data vor dem Text; ihre Reihenfolge ist bedeutsam, weil der Prompt sie als „Reference photo 1/2/3" anspricht:

{
  "contents": [{ "role": "user", "parts": [
    { "inline_data": { "mime_type": "image/jpeg", "data": "<base64>" } },
    { "text": "<Prompt>" }
  ]}],
  "generationConfig": {
    "responseModalities": ["IMAGE"],
    "imageConfig": { "aspectRatio": "4:3", "imageSize": "1K" }
  }
}

Parameter, die wir setzen:

Feld Werte Bedeutung
responseModalities ["IMAGE"] Nur Bild zurück, kein Begleittext
imageConfig.aspectRatio 4:3 bzw. 3:4 Querformat; 3:4 für Selfies und Porträt-Anker
imageConfig.imageSize 1K Auflösungsstufe
Modell-ID Umgebungsvariable LIFELINE_IMAGE_MODEL Überschreibt die Vorgabe gemini-3.1-flash-image

Einen seed verwenden wir nicht mehr; falls doch gesetzt, akzeptiert die API nur Werte bis 1.000.000.

Antwort verarbeiten

Das Bild steckt base64-kodiert im ersten Kandidaten:

candidates[0].content.parts[*].inlineData.data

Zwei Eigenheiten, die im Client berücksichtigt sind:

  • Das Feld heißt je nach Antwort inlineData oder inline_data – beide Schreibweisen prüfen.
  • Die gelieferten Daten sind faktisch JPEG, obwohl wir die Dateien mit der Endung .png ablegen. Beim Hochladen als Referenz wird der MIME-Typ deshalb aus den Magic Bytes bestimmt (\x89PNG → image/png, sonst image/jpeg) und nicht aus der Dateiendung.

Kommt kein Bild zurück, steht der Grund in candidates[0].finishReason oder in promptFeedback. Der Client wiederholt den Aufruf bis zu dreimal (5 s Pause; bei HTTP 429/500/503 gestaffelt 10/20 s) und wirft danach einen Fehler mit dem Grund im Text.

Kontingente, Abrechnung, Fehlerbilder

Guthaben und Tageslimit sind zwei getrennte Grenzen. Beide haben wir beim Aufbau der Bildsets erreicht, sie sehen im Log sehr ähnlich aus:

HTTP Meldung Ursache Abhilfe
402 Your prepayment credits are depleted Prepaid-Guthaben aufgebraucht Im AI Studio unter Billing aufladen; wirkt sofort
429 Quota exceeded for metric: generate_requests_per_model_per_day, limit: 100 Tageslimit von 100 Anfragen pro Modell erreicht Warten bis Mitternacht Pacific (ca. 9 Uhr MESZ) oder höhere Stufe beantragen
200 mit finishReason: IMAGE_OTHER The model could not generate the image based on the prompt provided Inhaltsfilter des Modells, deterministisch pro Prompt Prompt umformulieren – Wiederholen hilft nicht

Zum Tageslimit: Es zählt Anfragen, nicht erfolgreiche Bilder. Fehlversuche und Wiederholungen zählen mit. Ein vollständiger Durchlauf für eine große Lifeline (Jonas, 252 Bilder) sprengt das Limit deshalb und muss über mehrere Tage laufen. Der aktuelle Verbrauch ist unter ai.dev/rate-limit einsehbar.

Zu IMAGE_OTHER: Der Filter reagiert auf Formulierungen, nicht auf Zufall. In unseren Sets waren die Auslöser unter anderem Rollenangaben wie „Singer of the band" (Prominenten-Filter), oberkörperfreie Referenzbilder und Kombinationen aus erschöpften Gesichtsbeschreibungen mit Bettszenen. Vorgehen: Prompt halbieren und die Hälften einzeln testen, bis die auslösende Passage feststeht.

Key anlegen oder rotieren

  1. aistudio.google.com mit dem Google-Konto öffnen, das die Rechnung trägt.

  2. Unter Get API key einen Key für das gewünschte Projekt erzeugen. Der Wert wird nur einmal vollständig angezeigt.

  3. Für das Bildmodell muss im Projekt Billing aktiv sein und Guthaben vorhanden – unter ai.studio/projects zu prüfen. Ohne Guthaben antwortet die API mit HTTP 402.

  4. Key in backend/.env als GOOGLE_API_KEY= eintragen. Kein php artisan config:clear nötig, da nur die Python-Werkzeuge ihn lesen.

  5. Prüfen mit einem einzelnen Bild, zum Beispiel:

    cd dev/tools/lifeline-images
    python3 gemini.py "Ein roter Apfel auf einem weißen Tisch, Foto." --out /tmp/test.png
    

    Erfolg gibt den Pfad aus; bei Problemen erscheint die HTTP-Fehlermeldung im Klartext.

  6. Alten Key nach der Umstellung im AI Studio löschen.

Das Tageslimit hängt an Projekt und Abrechnungsstufe, nicht am einzelnen Key. Ein neuer Key im selben Projekt umgeht das Limit also nicht.