Aller au contenu principal

Cas d'usage des annotations

Mis à jour le 29 juillet 2026

Des annotations pour chaque métier

Les annotations Document AI transforment des documents non structurés en données exploitables. Le mécanisme est toujours le même — un schéma, un prompt, un appel — mais ce que vous en tirez change radicalement selon le métier. Cette leçon parcourt quatre implémentations concrètes : la classification en amont d’un pipeline, l’analyse de clauses contractuelles, le contrôle de conformité réglementaire et l’enrichissement d’un index RAG.

Trier avant de traiter

Dans un flux réel, les documents arrivent mélangés : une facture, un bon de commande, une pièce d’identité, un courrier. Avant d’appliquer le schéma d’extraction adapté, il faut savoir à quoi l’on a affaire. L’annotation sert alors de premier étage d’aiguillage. L’enum TypeDocument fige les catégories admissibles, le score de confiance vous permet d’envoyer en revue humaine tout ce qui passe sous un seuil, et le résumé d’une phrase donne aux opérateurs de quoi trancher sans ouvrir le fichier.

import os
from enum import Enum
from pydantic import BaseModel, Field
from mistralai import Mistral
from mistralai.extra import response_format_from_pydantic_model

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

class TypeDocument(str, Enum):
    FACTURE = "facture"
    BON_COMMANDE = "bon_commande"
    CONTRAT = "contrat"
    RAPPORT = "rapport"
    COURRIER = "courrier"
    FORMULAIRE = "formulaire"
    PIECE_IDENTITE = "piece_identite"
    AUTRE = "autre"

class Classification(BaseModel):
    type_document: TypeDocument = Field(
        ..., description="Type du document identifié"
    )
    confiance: float = Field(
        ..., description="Score de confiance entre 0 et 1"
    )
    langue: str = Field(
        ..., description="Langue principale (code ISO 639-1)"
    )
    resume: str = Field(
        ..., description="Résumé en une phrase du contenu"
    )

def classifier_document(document_base64: str) -> Classification:
    """Classifie automatiquement un document."""
    response = client.ocr.process(
        model="mistral-ocr-latest",
        document={
            "type": "document_base64",
            "document_base64": document_base64
        },
        document_annotation_format=response_format_from_pydantic_model(
            Classification
        ),
        document_annotation_prompt=(
            "Identifie le type de ce document et fournis "
            "un résumé de son contenu."
        )
    )

    for page in response.pages:
        if page.document_annotation:
            return Classification.model_validate_json(
                page.document_annotation
            )

    raise ValueError("Aucune annotation retournée")

Lire un contrat comme un juriste

L’analyse de contrats est un cas d’usage majeur en juridique et en finance, et c’est celui où les schémas imbriqués prennent tout leur sens. Chaque Clause porte son titre, un résumé de son contenu, sa catégorie — confidentialité, résiliation, non-concurrence, responsabilité, paiement — et une appréciation du risque sur trois niveaux. Le contrat lui-même agrège ces clauses avec les parties, la date de signature, la durée, le montant et la juridiction applicable. Sur un portefeuille de deux cents contrats fournisseurs, vous obtenez en quelques heures la cartographie que personne n’avait le temps de produire à la main.

Le champ risque mérite une mise en garde : c’est une appréciation produite par un modèle, utile pour prioriser la relecture d’un juriste, jamais pour la remplacer.

from pydantic import BaseModel, Field
from typing import Optional

class Clause(BaseModel):
    titre: str = Field(..., description="Titre ou numéro de la clause")
    contenu: str = Field(..., description="Résumé du contenu de la clause")
    type_clause: str = Field(
        ..., description="Type: confidentialite, resiliation, "
        "non-concurrence, responsabilite, paiement, autre"
    )
    risque: str = Field(
        ..., description="Niveau de risque: faible, moyen, eleve"
    )

class AnalyseContrat(BaseModel):
    parties: list[str] = Field(
        ..., description="Noms des parties contractantes"
    )
    date_signature: Optional[str] = Field(
        None, description="Date de signature (YYYY-MM-DD)"
    )
    duree: Optional[str] = Field(
        None, description="Durée du contrat"
    )
    clauses_cles: list[Clause] = Field(
        ..., description="Clauses principales identifiées"
    )
    montant: Optional[float] = Field(
        None, description="Montant total si mentionné"
    )
    juridiction: Optional[str] = Field(
        None, description="Juridiction applicable"
    )

def analyser_contrat(document_base64: str) -> AnalyseContrat:
    """Analyse un contrat et extrait les clauses principales."""
    response = client.ocr.process(
        model="mistral-ocr-latest",
        document={
            "type": "document_base64",
            "document_base64": document_base64
        },
        document_annotation_format=response_format_from_pydantic_model(
            AnalyseContrat
        ),
        document_annotation_prompt=(
            "Analyse ce contrat commercial. Identifie les parties, "
            "les clauses principales et évalue le niveau de risque "
            "de chaque clause."
        )
    )

    for page in response.pages:
        if page.document_annotation:
            return AnalyseContrat.model_validate_json(
                page.document_annotation
            )

    raise ValueError("Aucune annotation retournée")

Vérifier la conformité automatiquement

Le contrôle réglementaire se prête particulièrement bien à l’annotation, parce que les critères sont écrits noir sur blanc dans la loi. Pour une facture française, le prompt énumère les obligations à vérifier — mentions obligatoires (SIRET, TVA intracommunautaire, conditions de paiement, pénalités de retard), format des montants, date d’émission — et le schéma impose, pour chaque critère, un verdict booléen accompagné de sa justification. Vous récupérez un score global et des recommandations d’amélioration, ce qui suffit à bloquer une facture non conforme avant qu’elle n’entre dans le circuit de paiement.

from pydantic import BaseModel, Field

class PointConformite(BaseModel):
    critere: str = Field(..., description="Critère de conformité vérifié")
    conforme: bool = Field(..., description="True si conforme, False sinon")
    justification: str = Field(
        ..., description="Explication de la conformité ou non-conformité"
    )

class AuditConformite(BaseModel):
    type_document: str = Field(..., description="Type de document audité")
    points_conformite: list[PointConformite] = Field(
        ..., description="Points de conformité vérifiés"
    )
    score_global: float = Field(
        ..., description="Score de conformité global (0-100)"
    )
    recommandations: list[str] = Field(
        ..., description="Recommandations d'amélioration"
    )

def audit_conformite_facture(document_base64: str) -> AuditConformite:
    """Vérifie la conformité d'une facture selon les règles françaises."""
    response = client.ocr.process(
        model="mistral-ocr-latest",
        document={
            "type": "document_base64",
            "document_base64": document_base64
        },
        document_annotation_format=response_format_from_pydantic_model(
            AuditConformite
        ),
        document_annotation_prompt=(
            "Vérifie la conformité de cette facture selon les "
            "obligations légales françaises : mentions obligatoires "
            "(SIRET, TVA intracommunautaire, conditions de paiement, "
            "pénalités de retard), format des montants, date d'émission."
        )
    )

    for page in response.pages:
        if page.document_annotation:
            return AuditConformite.model_validate_json(
                page.document_annotation
            )

    raise ValueError("Aucune annotation retournée")

Ne rien perdre avant l’indexation

Dernier usage, plus discret mais décisif : préparer le contenu pour un index RAG. Un index construit sur le seul Markdown perd tout ce qui était visuel, et vos recherches ne retrouveront jamais le graphique qui portait le chiffre clé. La fonction ci-dessous recompose, page après page, le texte, la description de chaque image et les métadonnées extraites, dans un bloc unique prêt à être découpé en chunks.

def enrichir_pour_rag(ocr_response) -> str:
    """Enrichit le contenu OCR avec les annotations pour le RAG."""
    parties = []

    for page in ocr_response.pages:
        # Contenu textuel
        parties.append(page.markdown)

        # Annotations des images (remplace les images par leur description)
        for image in page.images:
            if image.annotation:
                parties.append(
                    f"[Description de {image.id}]: {image.annotation}"
                )

        # Annotation du document
        if page.document_annotation:
            parties.append(
                f"[Métadonnées extraites]: {page.document_annotation}"
            )

    return "\n\n".join(parties)

En ajoutant les descriptions d’images et les métadonnées extraites au contenu indexé, vous ne perdez aucune information du document original. Vous remarquerez que ces quatre scénarios partagent la même ossature : ce qui les distingue tient au schéma et au prompt, pas au code d’appel.

Points clés à retenir

  • La classification automatique oriente les documents vers le bon pipeline de traitement
  • L’extraction de clauses contractuelles identifie les risques et les points clés
  • La vérification de conformité automatise les audits réglementaires
  • L’enrichissement par annotations améliore la qualité de l’indexation RAG
  • Chaque cas d’usage se résume à un schéma Pydantic bien conçu et un prompt adapté