Zum Hauptinhalt springen
Lektion 11 von 12 · Bildanalyse und visuelle Erkennung über die API

Grenzen der Vision API und Optimierung

Lektion 11 von 12 Aktualisiert am 29. Juli 2026 Entwickler
Auf dieser Seite
  1. Grenzen kennen, um sie zu umgehen
  2. Technische Grenzen
  3. Visuelle Halluzinationen
  4. Kosten optimieren
  5. Latenz optimieren
  6. Produktions-Checkliste
  7. Kernaussagen

Grenzen kennen, um sie zu umgehen

Die vorherigen zehn Lektionen haben beleuchtet, was die Vision API gut kann. Diese Lektion widmet sich den Schwachstellen. Dies ist kein Akt der Demut: Der Unterschied zwischen einem Demo-Prototyp, der beeindruckt, und einer Anwendung, die in der Produktion standhält, hängt genau davon ab. Hier erfahren Sie über technische Einschränkungen, häufige Fallstricke – wobei visuelle Halluzinationen vor allem gemeint sind – und Strategien zur Optimierung von Kosten und Latenz.

Technische Grenzen

Wir haben Formatbeschränkungen bereits angerissen, aber lassen Sie uns diese hier zusammenfassen. Jedes Bild ist auf etwa 20 MB begrenzt und wird intern ohnehin verkleinert, typischerweise auf 768x768 oder ein Äquivalent: Ein 4K-Bild verbessert die Ergebnisse nicht. Akzeptierte Formate sind JPEG, PNG, GIF (nur der erste Frame) und WebP. Die Vision API unterstützt PDF nicht direkt – dafür müssen Sie Document AI aufrufen, wie in Lektion 8 besprochen.

Die wichtigste strukturelle Einschränkung bleibt der Token-Verbrauch. Jedes Bild verbraucht eine signifikante Anzahl an Tokens, und diese Funktion übersetzt diese Schätzung in Dollar statt in abstrakte Einheiten:

# Schätzung der Kosten eines Vision-Aufrufs
def estimate_cost(num_images=1, text_tokens=100, response_tokens=500,
                 tokens_per_image=1000, input_price=0.10, output_price=0.30):
    """Schätzt die Kosten in Dollar für einen Vision-Aufruf (Small 3.2)."""
    input_tokens = (num_images * tokens_per_image) + text_tokens
    output_tokens = response_tokens

    input_cost = (input_tokens / 1_000_000) * input_price
    output_cost = (output_tokens / 1_000_000) * output_price
    total_cost = input_cost + output_cost

    print(f"Eingabetokens: {input_tokens} (${input_cost:.4f})")
    print(f"Ausgabetokens: {output_tokens} (${output_cost:.4f})")
    print(f"Gesamtkosten: ${total_cost:.4f}")
    return total_cost

# 1 Bild mit Small 3.2
estimate_cost(num_images=1)

# 5 Bilder mit Large 3
estimate_cost(num_images=5, input_price=2.00, output_price=6.00)

Vergleichen Sie die beiden Aufrufe am Ende des Skripts: Der zweite kostet um ein Vielfaches mehr als der erste. Multiplizieren Sie dies mit Ihrem täglichen Volumen, bevor Sie eine Entscheidung treffen. Darüber hinaus gibt es die Kontextfenster-Beschränkung, die das Gesamtsumme aus Bildern + Prompt + Antwort nicht überschreiten darf – eine Obergrenze, die durch mehrere Bilder viel schneller erreicht wird als durch einen langen Text.

Visuelle Halluzinationen

Das Modell kann Details „erfinden”, die im Bild nicht vorhanden sind. Dies ist das kritischste Problem in der Produktion, da es stumm abläuft: Die Antwort ist gut strukturiert, plausibel und falsch.

Vier Muster treten wiederholt auf. Erfundener Text, bei dem das Modell ein Wort liest, das im Bild nicht existiert. Ungefähre Zahlen, bei denen Werte aus einem Diagramm um 5–15 % von der Realität abweichen können. Hinzugefügte Details, bei denen das Modell Lücken füllt, die es nicht klar sieht, mit plausiblen Informationen – eine verschwommene SIRET-Nummer wird zu einer perfekt formatierten, aber falschen SIRET-Nummer. Schließlich Elementverwechslung, bei der zwei nahe beieinander liegende Objekte zu einem verschmelzen.

Es gibt drei Gegenmaßnahmen; kombinieren Sie diese basierend auf der Kritikalität. Zwingen Sie das Modell zunächst, seine eigene Sicherheit zu qualifizieren. Lassen Sie es zweitens das Bild nach der Transkription erneut lesen. Drittens setzen Sie zwei Modelle derselben Frage gegenüber und behalten Sie nur das bei, worin sie übereinstimmen.

# 1. Bitte um ein Vertrauensniveau
prompt_confiance = """Analysieren Sie dieses Bild. Für jede extrahierte Information geben Sie Ihr Vertrauensniveau an:
- [SICHER]: deutlich sichtbar
- [WAHRSCHEINLICH]: teilweise sichtbar oder abgeleitet
- [UN SICHER]: kaum lesbar, geschätzt

Wenn ein Element unlesbar ist, markieren Sie es als [UNLESBAR] statt zu raten."""

# 2. Bitte um Kreuzverifizierung
prompt_verification = """Lesen Sie den Text in diesem Dokument. Nach der Transkription lesen Sie das Bild erneut und überprüfen Sie jede Zahl und jeden Eigennamen. Korrigieren Sie alle erkannten Fehler."""

# 3. Doppeltes Lesen mit zwei Modellen
def double_verification(image_uri, question):
    """Sendet dasselbe Bild an zwei Modelle und vergleicht die Ergebnisse."""
    resultats = {}
    for modele in ["mistral-small-latest", "mistral-large-latest"]:
        response = client.chat.complete(
            model=modele,
            messages=[{
                "role": "user",
                "content": [
                    {"type": "text", "text": question},
                    {"type": "image_url", "image_url": image_uri}
                ]
            }]
        )
        resultats[modele] = response.choices[0].message.content

    return resultats

Doppeltes Lesen verdoppelt die Kosten: Reservieren Sie dies für Beträge, Identifikatoren und alles, was eine irreversible Aktion auslöst.

Kosten optimieren

Die kosteneffektivste Optimierung wurde in Lektion 5 behandelt und ist die einfachste: Bildgröße vor dem Senden reduzieren. Ein 6 MB großes Foto, das auf 1024 Pixel verkleinert wird, verliert für Analyse-Zwecke nichts und entlastet die gesamte Kette.

from PIL import Image
from io import BytesIO
import base64

def optimiser_image(chemin, taille_max=1024, qualite=80):
    """Verkleinert und komprimiert ein Bild für die Vision API."""
    img = Image.open(chemin)
    taille_originale = img.size

    # Verkleinern
    if max(img.size) > taille_max:
        img.thumbnail((taille_max, taille_max))

    # Komprimieren als JPEG
    buffer = BytesIO()
    img.convert("RGB").save(buffer, format="JPEG", quality=qualite)
    taille_fichier = buffer.tell()

    b64 = base64.standard_b64encode(buffer.getvalue()).decode("utf-8")

    print(f"Original: {taille_originale} | Optimiert: {img.size} | Größe: {taille_fichier/1024:.0f} KB")
    return f"data:image/jpeg;base64,{b64}"

Als Nächstes kommt die Routierung nach Komplexität: Nicht jede Aufgabe verdient das teuerste Modell. Eine dreizeilige Funktion reicht aus, um zu entscheiden, sofern Sie Ihre Aufgaben upstream klassifiziert haben.

def choose_model(task):
    """Wählt das optimale Modell basierend auf der Aufgabe."""
    simple_tasks = ["classification", "ocr_simple", "description", "triage"]
    complex_tasks = ["extraction_json", "comparaison", "analyse_graphique", "raisonnement"]

    if task in simple_tasks:
        return "mistral-small-latest"  # ~$0.10/M tokens
    else:
        return "mistral-large-latest"  # ~$2.00/M tokens

Der dritte Hebel wird am häufigsten vergessen: Bezahlen Sie nicht zweimal für dieselbe Analyse. In der Produktion wird dasselbe Bild häufiger zurückgegeben, als man denkt – ein Prozess wird erneut ausgeführt, es gibt doppelte Sends oder Wiederholungen nach einem Vorfall. Ein Disk-Cache, der nach Datei-Hash und Prompt indiziert ist, eliminiert diese redundanten Aufrufe.

import hashlib
import json
from pathlib import Path

CACHE_DIR = Path("cache_vision/")
CACHE_DIR.mkdir(exist_ok=True)

def analyser_avec_cache(image_path, prompt, modele="mistral-small-latest"):
    """Analysiert ein Bild mit Disk-Caching."""
    # Generiere einen Cache-Schlüssel
    with open(image_path, "rb") as f:
        image_hash = hashlib.md5(f.read()).hexdigest()
    cache_key = hashlib.md5(f"{image_hash}{prompt}{modele}".encode()).hexdigest()
    cache_file = CACHE_DIR / f"{cache_key}.json"

    # Cache prüfen
    if cache_file.exists():
        return json.loads(cache_file.read_text())

    # API-Aufruf
    response = client.chat.complete(
        model=modele,
        messages=[{
            "role": "user",
            "content": [
                {"type": "text", "text": prompt},
                {"type": "image_url", "image_url": encoder_image(image_path)}
            ]
        }]
    )

    resultat = response.choices[0].message.content

    # Speichern im Cache
    cache_file.write_text(json.dumps(resultat, ensure_ascii=False))
    return resultat

Der Schlüssel enthält den Prompt und das Modell, nicht nur das Bild: Eine Änderung der Frage auf demselben Foto sollte tatsächlich einen neuen Aufruf auslösen.

Latenz optimieren

Für eine interaktive Anwendung beeinflussen vier Einstellungen die wahrgenommene Antwortzeit. Verkleinern Sie Bilder auf der Client-Seite auf maximal 1024 Pixel, was die Upload-Zeit verkürzt. Wählen Sie Small oder Ministral, da ihre Generierung deutlich schneller ist. Senden Sie Bilder per URL, wenn diese bereits gehostet sind, um einen großen Base64-Anfragekörper zu vermeiden. Schalten Sie schließlich Streaming ein, was die Gesamtzeit nicht reduziert, aber die Antwort anzeigt, während sie aufgebaut wird:

# Streaming für eine schrittweise Antwort
stream = client.chat.stream(
    model="mistral-small-latest",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": "Beschreiben Sie dieses Bild."},
            {"type": "image_url", "image_url": image_url}
        ]
    }]
)

for chunk in stream:
    if chunk.data.choices[0].delta.content:
        print(chunk.data.choices[0].delta.content, end="", flush=True)

Es ist chat.stream, das chat.complete ersetzt; der Rest der Nachricht ist exakt derselbe.

Produktions-Checkliste

Bevor Sie eine Vision-Anwendung in die Produktion bereitstellen, überprüfen Sie jeden dieser Punkte:

  • Automatische Bildverkleinerung (max. 1024 px)
  • Format- und Größenvalidierung vor dem Senden
  • Fehlerbehandlung der API (Wiederholung, Timeout, Fallback)
  • Caching von Ergebnissen für identische Bilder
  • Intelligente Routierung (Small für einfache, Large für komplexe Aufgaben)
  • Überwachung von Kosten und Latenz
  • Testen mit Bildern schlechter Qualität (verschwommen, niedrige Auflösung, gedreht)
  • Strategie gegen Halluzinationen (Vertrauensniveaus, doppelte Verifizierung)

Der vorletzte Punkt ist der, der am häufigsten übersprungen wird, und der am meisten Schaden anrichtet: Erstellen Sie einen Datensatz absichtlich schlechter Bilder und führen Sie diesen vor jeder Bereitstellung durch.

Kernaussagen

  • Bilder werden intern verkleinert – es besteht kein Bedarf, sehr hohe Auflösungen zu senden
  • Visuelle Halluzinationen sind das primäre Risiko – bitten Sie um Vertrauensniveaus
  • Client-seitige Verkleinerung ist die kosteneffektivste Optimierung
  • Routierung nach Komplexität (Small vs. Large) senkt die Kosten bei einfachen Aufgaben um 90 %
  • Disk-Caching vermeidet redundante API-Aufrufe bei denselben Bildern