Limites et Optimisation de la Vision API
Connaître les limites pour mieux les contourner
La Vision API de Mistral est un outil puissant, mais elle a des limites qu’il est essentiel de connaître pour construire des applications fiables. Dans cette leçon, vous découvrirez les contraintes techniques, les pièges courants et les stratégies d’optimisation.
Limites techniques
Taille et résolution des images
- Taille maximale : environ 20 Mo par image
- Résolution : les images sont redimensionnées en interne (généralement à 768x768 ou équivalent). Envoyer une image 4K n’améliore pas les résultats
- Formats : JPEG, PNG, GIF (première frame uniquement), WebP
- PDF : non supporté directement par la Vision API (utilisez Document AI pour les PDF)
Consommation de tokens
Chaque image consomme un nombre significatif de tokens :
# Estimation du coût d'un appel vision
def estimer_cout(nb_images=1, tokens_texte=100, tokens_reponse=500,
tokens_par_image=1000, prix_input=0.10, prix_output=0.30):
"""Estime le coût en dollars pour un appel vision (Small 3.2)."""
input_tokens = (nb_images * tokens_par_image) + tokens_texte
output_tokens = tokens_reponse
cout_input = (input_tokens / 1_000_000) * prix_input
cout_output = (output_tokens / 1_000_000) * prix_output
cout_total = cout_input + cout_output
print(f"Tokens input : {input_tokens} ({cout_input:.4f} $)")
print(f"Tokens output : {output_tokens} ({cout_output:.4f} $)")
print(f"Coût total : {cout_total:.4f} $")
return cout_total
# 1 image avec Small 3.2
estimer_cout(nb_images=1)
# 5 images avec Large 3
estimer_cout(nb_images=5, prix_input=2.00, prix_output=6.00)
Fenêtre de contexte
Le total des tokens (images + prompt + réponse) ne doit pas dépasser la fenêtre de contexte du modèle. Avec plusieurs images, vous pouvez atteindre cette limite rapidement.
Les hallucinations visuelles
Le modèle peut « inventer » des détails qui ne sont pas dans l’image. C’est le problème le plus critique en production.
Types d’hallucinations courantes
- Texte inventé : le modèle lit un mot qui n’existe pas dans l’image
- Chiffres approximatifs : les valeurs lues sur un graphique peuvent être fausses de 5-15 %
- Détails ajoutés : le modèle complète ce qu’il ne voit pas clairement avec des informations plausibles mais fausses
- Confusion d’éléments : deux objets proches sont confondus
Stratégies anti-hallucination
# 1. Demander un niveau de confiance
prompt_confiance = """Analysez cette image. Pour chaque information extraite, indiquez votre niveau de confiance :
- [CERTAIN] : clairement visible
- [PROBABLE] : partiellement visible ou déduit
- [INCERTAIN] : peu lisible, estimation
Si un élément est illisible, indiquez [ILLISIBLE] plutôt que de deviner."""
# 2. Demander une vérification croisée
prompt_verification = """Lisez le texte de ce document. Après la transcription, relisez l'image et vérifiez chaque chiffre et chaque nom propre. Corrigez les erreurs détectées."""
# 3. Double lecture avec deux modèles
def double_verification(image_uri, question):
"""Envoie la même image à deux modèles et compare les résultats."""
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
Optimiser les coûts
Redimensionner avant l’envoi
L’optimisation la plus impactante : réduire la taille des images avant l’envoi.
from PIL import Image
from io import BytesIO
import base64
def optimiser_image(chemin, taille_max=1024, qualite=80):
"""Redimensionne et compresse une image pour l'API Vision."""
img = Image.open(chemin)
taille_originale = img.size
# Redimensionner
if max(img.size) > taille_max:
img.thumbnail((taille_max, taille_max))
# Compresser en 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} | Optimisé : {img.size} | Taille : {taille_fichier/1024:.0f} Ko")
return f"data:image/jpeg;base64,{b64}"
Choisir le bon modèle
Utilisez un routage par complexité :
def choisir_modele(tache):
"""Sélectionne le modèle optimal selon la tâche."""
taches_simples = ["classification", "ocr_simple", "description", "triage"]
taches_complexes = ["extraction_json", "comparaison", "analyse_graphique", "raisonnement"]
if tache in taches_simples:
return "mistral-small-latest" # ~0.10 $/M tokens
else:
return "mistral-large-latest" # ~2.00 $/M tokens
Mettre en cache les résultats
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"):
"""Analyse une image avec cache disque."""
# Générer une clé de cache
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"
# Vérifier le cache
if cache_file.exists():
return json.loads(cache_file.read_text())
# Appel API
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
# Sauvegarder en cache
cache_file.write_text(json.dumps(resultat, ensure_ascii=False))
return resultat
Optimiser la latence
Pour les applications temps réel :
- Redimensionnez les images côté client (1024px max)
- Utilisez Small ou Ministral pour les réponses rapides
- Envoyez les images par URL si elles sont déjà hébergées (évite l’upload Base64)
- Activez le streaming pour afficher la réponse progressivement :
# Streaming pour une réponse progressive
stream = client.chat.stream(
model="mistral-small-latest",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "Décrivez cette image."},
{"type": "image_url", "image_url": url_image}
]
}]
)
for chunk in stream:
if chunk.data.choices[0].delta.content:
print(chunk.data.choices[0].delta.content, end="", flush=True)
Checklist de mise en production
Avant de déployer une application Vision en production :
- Redimensionnement automatique des images (max 1024px)
- Validation du format et de la taille avant envoi
- Gestion des erreurs API (retry, timeout, fallback)
- Cache des résultats pour les images identiques
- Routage intelligent (Small pour le simple, Large pour le complexe)
- Monitoring des coûts et de la latence
- Tests avec des images de mauvaise qualité (flou, basse résolution, rotation)
- Stratégie anti-hallucination (niveaux de confiance, double vérification)
Points clés à retenir
- Les images sont redimensionnées en interne — inutile d’envoyer de la très haute résolution
- Les hallucinations visuelles sont le risque principal — demandez des niveaux de confiance
- Le redimensionnement côté client est l’optimisation la plus rentable
- Le routage par complexité (Small vs Large) réduit les coûts de 90 % sur les tâches simples
- Le cache disque évite les appels API redondants sur les mêmes images