cd ../back to blog
$Guide//June 4, 2026//7 min read

Vision et compréhension de documents : une seule API pour Claude et Gemini

Attache une image (URL ou base64) à ta requête de chat habituelle et lis-la avec Claude ou Gemini via Brievio : OCR, graphiques, reçus, formulaires, scans multi-pages, facturés honnêtement en tokens d’entrée.

Une part étonnante de ce qu’on appelle « IA documentaire » se résume à envoyer une image à un modèle de chat et à lire ce qu’il te répond. Un reçu, une capture de tableau de bord, une page de PDF scanné, la photo d’un tableau blanc — les modèles modernes Claude et Gemini lisent tout cela nativement, sans moteur OCR séparé. Le hic, c’est généralement la tuyauterie : chaque fournisseur a sa propre façon d’attacher une image, et porter du code de l’un à l’autre est pénible.

Avec Brievio, tu utilises une seule forme de requête — le tableau content des Chat Completions OpenAI avec une partie image_url — et elle fonctionne à l’identique contre Claude Opus 4.7, Sonnet 4.6, Haiku 4.5, et Gemini 2.5 Pro / Flash. Ce sont les véritables modèles first-party avec la vision native respectée : le même JPEG que Claude lit bien, c’est Claude qui le lit vraiment. Cet article couvre l’entrée image (la compréhension), pas la génération d’images : les URL, le base64, les prompts multi-images, et les schémas OCR / graphiques / documents scannés qui reviennent dans le travail réel.

Le cas le plus simple : une image par URL

Si ton image se trouve déjà à une URL HTTPS publique, attache-la comme une partie image_url à côté de ton texte. Remplace claude-sonnet-4-6 par gemini-2.5-pro et le corps de la requête ne change pas — cette portabilité est tout l’enjeu :

image_url.py
# Envoie une image par URL. Même forme de requête OpenAI, contre le modèle authentique.
from openai import OpenAI

client = OpenAI(
    api_key="sk-brievio-...",
    base_url="https://api.brievio.com/v1",
)

resp = client.chat.completions.create(
    model="claude-sonnet-4-6",     # ou gemini-2.5-pro — même forme de requête
    max_tokens=500,
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "What does this chart show? Give the trend in one sentence."},
                {
                    "type": "image_url",
                    "image_url": {"url": "https://example.com/q3-revenue.png"},
                },
            ],
        }
    ],
)

print(resp.choices[0].message.content)
# Les images sont facturées comme tokens d'ENTRÉE — lis resp.usage.prompt_tokens pour voir le coût.

Une chose à intégrer dès le départ : les images coûtent des tokens d’entrée. Un modèle ne voit pas les pixels gratuitement — il découpe l’image en tuiles et chaque tuile se facture comme du texte. Brievio rapporte des décomptes de tokens honnêtes, donc le coût de l’image apparaît dans resp.usage.prompt_tokens exactement comme le fournisseur en amont le facture. Une capture plein écran peut représenter de quelques centaines à un ou deux milliers de tokens d’entrée selon la résolution. Budgète-la comme tu budgéterais un paragraphe de contexte, pas comme un bonus gratuit.

Base64 : le cas que tu utiliseras vraiment

En production, l’image est rarement une URL publique — c’est un fichier qu’un utilisateur vient de téléverser, un buffer issu d’un scanner, un objet S3 privé. Pour ceux-là, intègre les octets en data URL data: base64. Le modèle ne voit pas la différence ; tes octets n’ont jamais à être accessibles publiquement :

base64_upload.py
# La plupart des images en production ne sont pas des URL publiques. Intègre-les en data URL base64.
import base64
from openai import OpenAI

client = OpenAI(api_key="sk-brievio-...", base_url="https://api.brievio.com/v1")

def data_url(path: str, media_type: str = "image/png") -> str:
    with open(path, "rb") as f:
        b64 = base64.standard_b64encode(f.read()).decode("utf-8")
    return f"data:{media_type};base64,{b64}"

resp = client.chat.completions.create(
    model="gemini-2.5-flash",      # économique + rapide pour l'OCR / les reçus
    max_tokens=800,
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "Extract every line item and total as JSON. Keys: items[], total."},
                {"type": "image_url", "image_url": {"url": data_url("receipt.jpg", "image/jpeg")}},
            ],
        }
    ],
)

print(resp.choices[0].message.content)
# Astuce : les data URL gonflent le corps de la requête d'environ 33 % par rapport au fichier brut.
# Garde des images de taille raisonnable — une capture de 2-3 Mpx suffit largement pour du texte ; tu as rarement besoin de 12 Mpx.

Deux mises en garde pratiques. D’abord, le base64 ajoute environ 33 % de surcharge au corps de ta requête, et il y a des limites de taille par image (Anthropic plafonne une image seule autour de 5 Mo sur l’API ; Gemini a son propre plafond). Si un gros scan renvoie un 413, réduis sa résolution — le texte reste lisible à une résolution bien plus basse que tu ne le crois. Ensuite, envoie le bon media_type (image/png, image/jpeg, image/webp) ; un type qui ne correspond pas est une cause fréquente d’échec de décodage silencieux. Quand une requête échoue bel et bien avec un 4xx ou un 5xx sur Brievio, elle ne t’est pas facturée — les appels échoués sont gratuits, donc tu peux réessayer avec une image réduite sans payer deux fois.

Prompts multi-images et documents scannés

Le tableau content accepte autant de parties image_url que tu veux, entrelacées avec du texte. Cela débloque les workflows réellement utiles : comparer une capture avant/après, lire un document scanné multi-pages, ou enchaîner une série de graphiques et demander le fil conducteur. L’astuce qui paie sur les deux familles de modèles, c’est d’étiqueter chaque image avec un petit point d’ancrage textuel pour que le modèle puisse la citer :

multi_image.py
# Plusieurs images dans un seul prompt — compare deux captures, ou lis un scan de 4 pages.
content = [
    {"type": "text", "text": "These are pages 1-3 of a scanned contract. Summarize the parties, term, and termination clause. Cite the page number for each."},
]
for i, path in enumerate(["page1.png", "page2.png", "page3.png"], start=1):
    content.append({"type": "text", "text": f"--- Page {i} ---"})
    content.append({"type": "image_url", "image_url": {"url": data_url(path)}})

resp = client.chat.completions.create(
    model="claude-opus-4-7",       # le raisonnement le plus solide sur des documents denses
    max_tokens=1200,
    messages=[{"role": "user", "content": content}],
)

print(resp.choices[0].message.content)
# Insérer une étiquette de texte avant chaque image ("--- Page 2 ---") donne au modèle
# un point d'ancrage à citer, et améliore nettement l'ancrage multi-image sur les deux familles.

Pour les longs documents, il y a un arbitrage sur le choix du modèle. Gemini 2.5 Flash est économique et rapide, et c’est un excellent choix par défaut pour l’OCR à fort volume, les reçus et l’extraction de formulaires. Claude Opus 4.7 raisonne plus dur sur de la matière dense et multi-pages — les contrats, les états financiers, tout ce où tu as besoin qu’il garde plusieurs pages en vue et les recoupe. Sonnet 4.6 et Gemini 2.5 Pro se situent entre les deux. Tu peux router par tâche sans changer une ligne de code à part la chaîne model ; vois la liste à jour sur /models.

Ce que ces modèles font bien (et moins bien)

La vision native gère bien tout un éventail de tâches réelles :

  • OCR et transcription — texte imprimé, et une écriture manuscrite étonnamment correcte. Aucun pipeline Tesseract à maintenir.
  • Graphiques et tableaux de bord — lire les valeurs sur des graphiques en barres/lignes, résumer une tendance, vérifier la cohérence d’une métrique dans une capture.
  • Extraction structurée — reçus, factures, formulaires, pièces d’identité vers du JSON. Associe-le à un schéma strict dans ton prompt pour une sortie propre.
  • Compréhension d’UI et de schémas — décrire un écran, lire une boîte de dialogue d’erreur, expliquer un diagramme d’architecture.

Et quelques limites assumées. Les modèles se trompent encore parfois sur un chiffre isolé ou une cellule de tableau dense, donc pour tout ce où un nombre erroné coûte cher, valide contre un schéma ou une somme de contrôle (par exemple, les lignes d’un reçu devraient additionner au total indiqué). Le texte minuscule dans une image basse résolution est l’échec le plus courant — donne-lui un recadrage en plus haute résolution. Et le comportement diffère vraiment d’un modèle à l’autre : une mise en page qu’un modèle gère sans broncher, un autre peut trébucher dessus, ce qui est précisément pourquoi pouvoir changer de modèle derrière une seule API et les A/B-tester sur tes propres documents vaut plus que n’importe quel benchmark unique.

Aller plus loin : la vision plus les outils

La vision se compose avec le reste de l’API. Tu peux donner au modèle une image et un ensemble d’outils, de sorte qu’il lise une capture puis appelle une fonction avec ce qu’il a extrait — « lis cette facture, puis appelle create_expense(amount, vendor, date) ». Cette couche d’appel d’outils est elle aussi uniforme entre Claude et Gemini via Brievio ; le guide d’appel d’outils couvre la forme partagée. Combinée à la vision, c’est l’essentiel d’un pipeline de traitement documentaire en une seule requête.

À retenir

Pour la compréhension d’images, tu n’as pas besoin d’un service OCR séparé ni de SDK propres à chaque fournisseur. Attache une partie image_url — URL ou data URL base64 — à ta requête de chat habituelle, étiquette les images multiples pour que le modèle puisse les citer, et route vers le modèle qui convient à la tâche : Gemini Flash pour les lectures économiques à fort volume, Claude Opus pour les documents difficiles. Souviens-toi que les images se facturent comme tokens d’entrée (affichés honnêtement dans usage), garde une résolution raisonnable, valide les nombres extraits, et appuie-toi sur les nouvelles tentatives gratuites quand un gros scan rebondit. La référence complète des requêtes et les limites par modèle sont dans la documentation — commence par là et tu auras des documents qui circulent à travers Claude ou Gemini en une après-midi.