Passer au contenu principal

Documentation Index

Fetch the complete documentation index at: https://docs.lighton.ai/llms.txt

Use this file to discover all available pages before exploring further.

Derniere mise a jour : Avril 2026 — L’API Paradigm evolue rapidement. Consultez toujours la derniere reference API et privilegiez les cookbooks les plus recents.

Presentation

Verifier la coherence des informations entre plusieurs documents lies — formulaires de marches publics, contrats, releves bancaires, declarations d’identite — est un travail fastidieux, source d’erreurs et couteux lorsqu’il est realise manuellement. Ce cookbook montre comment construire un pipeline de verification automatise qui charge des documents dans Paradigm, extrait des champs specifiques via Document Search, et les recoupe grace a Chat Completions avec des prompts structures. Ce pattern s’applique a tout workflow de verification multi-documents : audits de conformite, traitement de sinistres d’assurance, demandes de prets, integration de fournisseurs, et plus encore.
Cet exemple est base sur un cas d’usage reel en production pour la verification de formulaires de marches publics francais (DC4). Le pattern se generalise a tout scenario ou vous devez verifier la coherence entre plusieurs documents.

Demo

Decouvrez le pipeline en action — chargement de documents, execution des controles automatiques et generation d’un rapport de verification :

Fonctionnement

  1. L’utilisateur charge un ensemble de documents lies (ex. : un formulaire, un contrat, un RIB, une declaration).
  2. Les documents sont ingeres dans Paradigm via l’API Upload Sessions.
  3. Pour chaque controle, des champs specifiques sont extraits des documents concernes via Document Search.
  4. Les champs extraits sont compares via Chat Completions avec un prompt systeme structure qui gere la correspondance approximative (fautes, differences de format, abreviations).
  5. Chaque controle retourne un resultat structure : is_correct, les valeurs comparees, et une explication de la decision.
  6. Tous les resultats sont compiles dans un rapport de verification.
Pipeline de verification de documents — diagramme d'architecture montrant le chargement, l'extraction, le recoupement et la generation de rapport

Prerequis

  • Une cle API Paradigm (obtenir une cle)
  • Python 3.10+
  • Documents a verifier (des documents d’exemple sont fournis dans le depot GitHub)

Endpoints API utilises

EndpointRole dans ce pipeline
POST /v2/upload-sessionsCreer une session pour charger des documents
POST /v2/upload-sessions/{id}/filesCharger des fichiers individuels dans la session
POST /v2/chat/document-searchExtraire des champs specifiques des documents charges
POST /v2/chat/completionsRecouper les champs extraits avec correspondance approximative

Implementation etape par etape

Etape 1 : Configurer le client Paradigm

Creez un wrapper autour de l’API Paradigm. Ce client gere l’authentification, le chargement de documents, l’extraction de champs et le recoupement.
import requests
from typing import Optional

class ParadigmClient:
    """Client pour interagir avec l'API Paradigm."""

    def __init__(self, api_key: str, base_url: str = "https://paradigm.lighton.ai"):
        self.api_key = api_key
        self.base_url = base_url
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }

Etape 2 : Charger les documents

Les documents doivent etre charges dans Paradigm avant de pouvoir etre interroges. L’API Upload Sessions gere le pipeline d’ingestion — vous creez une session, chargez les fichiers, puis fermez la session pour declencher l’embedding.
def create_upload_session(self) -> dict:
    """Cree une nouvelle session de chargement pour l'ingestion de documents."""
    response = requests.post(
        f"{self.base_url}/api/v2/upload-sessions",
        headers=self.headers,
        json={"pipeline": "v2.2.1"}
    )
    response.raise_for_status()
    return response.json()

def upload_file(self, session_id: str, file_path: str) -> dict:
    """Charge un fichier dans une session existante."""
    with open(file_path, "rb") as f:
        response = requests.post(
            f"{self.base_url}/api/v2/upload-sessions/{session_id}/files",
            headers={"Authorization": f"Bearer {self.api_key}"},
            files={"file": f}
        )
    response.raise_for_status()
    return response.json()

def close_upload_session(self, session_id: str) -> dict:
    """Ferme la session pour declencher l'embedding des documents."""
    response = requests.post(
        f"{self.base_url}/api/v2/upload-sessions/{session_id}/close",
        headers=self.headers
    )
    response.raise_for_status()
    return response.json()
Les documents doivent etre entierement indexes avant de pouvoir etre interroges. Le temps d’indexation depend de la taille et de la complexite du document — en general quelques secondes a quelques minutes.
Une fois les documents indexes, utilisez Document Search pour extraire des champs specifiques. Le parametre query est une question en langage naturel — Paradigm recherche dans le document et retourne le contenu pertinent.
def search_document(
    self,
    file_ids: list[str],
    query: str,
    tool: str = "DocumentSearch"
) -> dict:
    """Extrait des informations specifiques des documents charges.

    Args:
        file_ids: identifiants Paradigm des fichiers a interroger.
        query: question en langage naturel decrivant ce qu'il faut extraire.
        tool: "DocumentSearch" pour le texte, "VisionDocumentSearch" pour les docs scannes.
    """
    payload = {
        "model": "alfred-4.2",
        "query": query,
        "file_ids": file_ids,
        "tool": tool
    }
    response = requests.post(
        f"{self.base_url}/api/v2/chat/document-search",
        headers=self.headers,
        json=payload,
        timeout=150
    )
    response.raise_for_status()
    return response.json()
Exemples de requetes d’extraction :
# Extraire le nom de l'acheteur d'un formulaire
result = client.search_document(
    file_ids=[form_file_id],
    query="Quel est le nom du pouvoir adjudicateur ?"
)

# Extraire le numero de reference du marche d'un avis de marche
result = client.search_document(
    file_ids=[tender_notice_id],
    query="Quel est le numero de reference du marche ?"
)

# Extraire les coordonnees bancaires d'un document scanne
result = client.search_document(
    file_ids=[bank_doc_id],
    query="Quel est le numero IBAN ?",
    tool="VisionDocumentSearch"  # Utiliser la vision pour les documents scannes
)

Etape 4 : Recouper les champs avec Chat Completions

C’est le coeur du pipeline de verification. Apres avoir extrait le meme champ de deux documents differents, utilisez Chat Completions avec un prompt systeme structure pour les comparer. Le prompt gere les imperfections du monde reel : fautes de frappe, differences de formatage, abreviations, accents manquants.
def cross_reference(self, query: str) -> dict:
    """Compare les valeurs extraites via correspondance approximative par LLM.

    Retourne une reponse JSON structuree avec :
        - is_correct: bool — si les valeurs correspondent
        - compare_values: dict — les valeurs comparees
        - details: str — explication du resultat de la comparaison
    """
    system_prompt = """You are a document verification assistant. Your role is to compare
data extracted from different documents and determine if they match.

Rules for comparison:
- Names: ignore case, accents, and minor spelling variations.
  "JEAN-PIERRE DUPONT" matches "Jean-Pierre Dupont" matches "Jean Pierre Dupont".
- Addresses: compare street, postal code, and city separately.
  Minor differences in formatting are acceptable.
- Phone numbers: ignore spaces, dots, and country prefixes.
  "01 23 45 67 89" matches "+33 1 23 45 67 89" matches "0123456789".
- Emails: case-insensitive comparison.

Always respond in valid JSON with this exact structure:
{
    "is_correct": true/false,
    "compare_values": {"document_1": "...", "document_2": "..."},
    "details": "Explanation of why the values match or don't match."
}"""

    payload = {
        "model": "alfred-4.2",
        "messages": [
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": query}
        ],
        "max_tokens": 500,
        "temperature": 0.1
    }
    response = requests.post(
        f"{self.base_url}/api/v2/chat/completions",
        headers=self.headers,
        json=payload,
        timeout=150
    )
    response.raise_for_status()
    data = response.json()
    return data["choices"][0]["message"]["content"]
Le prompt systeme ci-dessus est essentiel pour gerer les donnees reelles. Adaptez les regles de correspondance approximative a votre domaine. Par exemple, pour la verification de documents financiers, vous pourriez vouloir une correspondance stricte sur les montants mais approximative sur les noms d’entreprise.

Etape 5 : Definir les controles de verification

Chaque controle est une fonction qui extrait un champ de deux documents et les compare. Voici le pattern — repetez-le pour chaque champ a verifier.
import json

def verify_buyer_name(client: ParadigmClient, form_id: str, tender_id: str) -> dict:
    """Verifie que le nom de l'acheteur correspond entre le formulaire et l'avis de marche."""
    # Etape A : extraction du document 1
    form_result = client.search_document(
        file_ids=[form_id],
        query="What is the full name of the public buyer?"
    )

    # Etape B : extraction du document 2
    tender_result = client.search_document(
        file_ids=[tender_id],
        query="What is the full name of the public buyer?"
    )

    # Etape C : recoupement
    comparison = client.cross_reference(
        f"Compare these buyer names:\n"
        f"Document 1 (form): {form_result['answer']}\n"
        f"Document 2 (tender notice): {tender_result['answer']}"
    )

    return {
        "check": "buyer_name",
        "result": json.loads(comparison)
    }

Etape 6 : Orchestrer tous les controles

Executez tous les controles en parallele pour gagner en rapidite, puis compilez les resultats dans un rapport.
import concurrent.futures

def run_verification(client: ParadigmClient, document_ids: dict) -> list[dict]:
    """Execute tous les controles de verification en parallele.

    Args:
        document_ids: correspondance entre type de document et identifiant Paradigm.
            Exemple: {"form": "abc123", "tender_notice": "def456", "bank_details": "ghi789"}
    """
    checks = [
        lambda: verify_buyer_name(client, document_ids["form"], document_ids["tender_notice"]),
        lambda: verify_buyer_address(client, document_ids["form"], document_ids["tender_notice"]),
        lambda: verify_buyer_email(client, document_ids["form"], document_ids["tender_notice"]),
        lambda: verify_contract_ref(client, document_ids["form"], document_ids["tender_notice"]),
        lambda: verify_candidate_name(client, document_ids["form"], document_ids["contract"]),
        lambda: verify_iban(client, document_ids["form"], document_ids["bank_details"]),
        # ... ajoutez d'autres controles selon vos besoins
    ]

    results = []
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
        futures = [executor.submit(check) for check in checks]
        for future in concurrent.futures.as_completed(futures):
            results.append(future.result())

    return results
Exemple de sortie :
[
  {
    "check": "buyer_name",
    "result": {
      "is_correct": true,
      "compare_values": {
        "document_1": "Ministere de l'Interieur",
        "document_2": "MINISTÈRE DE L'INTÉRIEUR"
      },
      "details": "Les noms correspondent — les differences portent uniquement sur la casse et les accents."
    }
  },
  {
    "check": "iban",
    "result": {
      "is_correct": false,
      "compare_values": {
        "document_1": "FR76 3000 6000 0112 3456 7890 189",
        "document_2": "FR76 3000 6000 0112 3456 7890 199"
      },
      "details": "Divergence d'IBAN — les deux derniers chiffres different (189 vs 199)."
    }
  }
]

Etape 7 : Generer un rapport de verification

Compilez tous les resultats dans un rapport structure. L’exemple ci-dessous genere un resume simple — en production, vous pourriez generer un PDF ou ecrire en base de donnees.
def generate_report(results: list[dict]) -> dict:
    """Compile les resultats de verification dans un rapport de synthese."""
    passed = [r for r in results if r["result"]["is_correct"]]
    failed = [r for r in results if not r["result"]["is_correct"]]

    report = {
        "total_checks": len(results),
        "passed": len(passed),
        "failed": len(failed),
        "status": "VALID" if len(failed) == 0 else "INVALID",
        "details": {
            "passed_checks": [r["check"] for r in passed],
            "failed_checks": [
                {
                    "check": r["check"],
                    "reason": r["result"]["details"],
                    "values": r["result"]["compare_values"]
                }
                for r in failed
            ]
        }
    }
    return report

Code complet

Code source complet

Clonez le depot pour executer le pipeline complet avec des documents d’exemple.

Reference API

Documentation complete de l’API Paradigm.

Personnalisation

Adaptez ce pipeline a vos propres besoins de verification :
ParametreDescriptionDefautQuand l’ajuster…
modelModele LLM pour l’extraction et la comparaisonalfred-4.2Vous avez besoin d’un compromis vitesse/qualite different
temperatureDeterminisme de la comparaison0.1Vous voulez une correspondance plus stricte (baisser) ou plus souple (augmenter)
toolOutil de recherche documentaireDocumentSearchUtilisez VisionDocumentSearch pour les documents scannes ou les images
max_workersThreads paralleles pour les controles5Augmenter pour plus de controles, reduire si vous atteignez les limites de debit
Regles du prompt systemeComportement de correspondance approximativeVoir etape 4Votre domaine a des exigences de correspondance differentes (montants, dates, identifiants)

Ajouter vos propres controles

Pour ajouter un nouveau controle de verification, suivez ce pattern en trois etapes :
  1. Extraire le champ du document A avec search_document() et une requete en langage naturel claire
  2. Extraire le meme champ du document B
  3. Comparer avec cross_reference() — le prompt systeme gere la correspondance approximative
def verify_custom_field(client, doc_a_id, doc_b_id):
    a = client.search_document([doc_a_id], "Votre requete d'extraction pour le document A")
    b = client.search_document([doc_b_id], "Votre requete d'extraction pour le document B")
    comparison = client.cross_reference(
        f"Compare: Document A says '{a['answer']}', Document B says '{b['answer']}'"
    )
    return {"check": "custom_field", "result": json.loads(comparison)}

Bonnes pratiques

  1. Utilisez VisionDocumentSearch pour les documents scannes — le DocumentSearch standard fonctionne pour les PDF natifs, mais les documents scannes et les images necessitent l’outil vision pour une extraction fiable.
  2. Gardez les requetes d’extraction specifiques — “Quel est l’IBAN ?” fonctionne mieux que “Extraire toutes les informations bancaires.” Un champ par requete donne des resultats plus fiables.
  3. Adaptez le prompt systeme a votre domaine — les regles de correspondance approximative doivent refleter vos exigences metier. Les donnees financieres peuvent necessiter une correspondance exacte ; les noms et adresses necessitent generalement une correspondance approximative.
  4. Executez les controles en parallele — chaque controle est independant, utilisez le threading pour les traiter simultanement. Ajoutez un leger delai entre les lots si vous atteignez les limites de debit.
  5. Journalisez les resultats intermediaires — quand un controle echoue, avoir les valeurs brutes extraites des deux documents facilite grandement le debogage.