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

Les equipes juridiques consacrent des heures a la revue des NDA entrants au regard de leur politique interne — verifier que la responsabilite est correctement plafonnee, que les exceptions de communication existent, que la clause de restitution des informations contient les bonnes exceptions. Ce cookbook montre comment construire un pipeline qui charge un NDA dans Paradigm, execute un ensemble configurable de controles juridiques, et pour chaque controle en echec propose une reformulation issue d’un NDA deja signe (ou, a defaut, d’un modele par defaut). Le pattern se generalise a tout workflow de revue contractuelle : MSA, contrats fournisseurs, DPA, contrats de travail. Partout ou un controle de politique et une proposition de correction feraient gagner du temps au relecteur.
Cet exemple est inspire d’un workflow de production utilise par un gestionnaire d’actifs francais pour la revue des NDA issus de processus M&A. Les quatre controles presentes ici correspondent aux regles de conformite reelles du client — vous pouvez les remplacer par les votres en editant une simple liste Python.

Demo

Decouvrez le pipeline revisant un NDA d’exemple, echouant sur deux controles sur quatre, et proposant des reformulations extraites d’une reference precedemment signee :

Fonctionnement

  1. L’utilisateur charge un NDA entrant, plus zero a plusieurs NDA de reference deja signes.
  2. Chaque document est charge dans Paradigm et indexe via embedding.
  3. Le co-contractant (Target Company) est extrait de chaque document et sert a apparier le NDA entrant avec la reference la plus proche.
  4. Quatre controles de conformite sont executes sur le NDA entrant. Chaque controle est un petit arbre de questions juridiques oui/non — chaque question est resolue en combinant une Document Search agentique (pour localiser la clause) et une Chat Completion structuree (pour classifier la clause et renvoyer la citation exacte).
  5. Pour chaque controle en echec, le meme flux d’extraction est applique au NDA de reference apparie pour proposer une reformulation. Si aucune reference n’a ete appariee, un modele par defaut est utilise a la place.
  6. Les resultats sont compiles dans un rapport JSON avec, pour chaque controle, le statut, les citations extraites et la reformulation proposee.
Pipeline de revue de conformite NDA — diagramme d'architecture montrant le chargement et embedding, l'identification du co-contractant, l'evaluation des quatre controles, la reformulation et le rapport JSON

Prerequis

  • Une cle API Paradigm (obtenir une cle)
  • Python 3.10+
  • Au moins un NDA a revoir (des NDA d’exemple sont fournis dans le depot GitHub)
  • Optionnel : des NDA precedemment signes a utiliser comme sources de reformulation

Endpoints API utilises

EndpointRole dans ce pipeline
POST /api/v2/filesCharger un NDA (PDF ou DOCX) dans Paradigm
GET /api/v2/files/{id}Attendre que le document termine son embedding
POST /api/v3/threads/turnsRecherche documentaire agentique (RAG) pour localiser les clauses
POST /api/v2/chat/completionsClassification JSON structuree via un response format base sur un JSON schema

Implementation etape par etape

Etape 1 : Charger et attendre l’embedding

Contrairement aux workflows bases sur les upload sessions, les NDA sont charges en tant que fichiers uniques via POST /api/v2/files. Parce que Document Search requiert que le document soit pleinement embed, on interroge GET /api/v2/files/{id} en boucle jusqu’a ce que son statut passe a embedded.
def upload_and_embed(self, file_path: str) -> str:
    """Upload a single file and block until it is embedded."""
    with open(file_path, "rb") as fh:
        resp = requests.post(
            f"{self.base_url}/api/v2/files",
            headers={"Authorization": f"Bearer {self.api_key}"},
            files={"file": fh},
            timeout=120,
        )
    file_id = resp.json()["id"]

    deadline = time.time() + 180
    while time.time() < deadline:
        status = self.get_file(file_id).get("status")
        if status == "embedded":
            return str(file_id)
        if status == "fail":
            raise ParadigmError(f"Embedding failed for file {file_id}")
        time.sleep(2.0)
    raise ParadigmError(f"Timeout waiting for file {file_id} to embed")

Etape 2 : Poser une question juridique oui/non

Chaque verification de conformite se reduit au meme pattern en deux etapes : d’abord une Document Search fait remonter la clause pertinente, puis une Chat Completion structuree la classifie et renvoie la citation exacte. Le JSON schema garantit un format de reponse toujours previsible.
def _ask_yes_no(client, file_id: str, question: str) -> QueryResult:
    # 1. Find the passage that discusses this topic.
    search_query = f"Dans ce document, dans quelle section se trouve la clause qui parle de : {question}"
    passage = client.ask_question(file_id, search_query)

    # 2. Classify the passage against the question and return the exact quote.
    classify_query = (
        f"Voici les informations récupérées du document NDA :\n{passage}\n\n"
        f"Extrais la phrase exacte du document qui correspond à la requête suivante "
        f"et indique si la clause est présente et affirmative. Requête : {question}"
    )
    schema = {
        "type": "object",
        "properties": {
            "extracted_text": {"type": "string"},
            "is_true": {"type": "boolean"},
        },
        "required": ["extracted_text", "is_true"],
        "additionalProperties": False,
    }
    answer = client.structured_completion(classify_query, schema=schema)
    return QueryResult(
        question=question,
        is_true=bool(answer["is_true"]),
        extracted_text=answer["extracted_text"].strip(),
    )
Les requetes sont formulees en francais car les NDA du client sont revus par des juristes francophones qui les ont redigees ainsi a l’origine. Paradigm gere le francais nativement — traduisez-les dans votre propre langue si cela fait plus de sens pour votre equipe.

Etape 3 : Utiliser force_tool et response_format pour la structure

Deux fonctionnalites Paradigm font le gros du travail. Premierement, force_tool: "document_search" sur l’endpoint V3 threads garantit que le modele effectue un lookup RAG sur le fichier specifie plutot que de repondre depuis sa connaissance generale :
def ask_question(self, file_id: str, question: str) -> str:
    payload = {
        "ml_model": self.model,
        "query": question,
        "force_tool": "document_search",
        "file_ids": [int(file_id)],
    }
    resp = requests.post(
        f"{self.base_url}/api/v3/threads/turns",
        headers=self._json_headers(),
        json=payload,
        timeout=180,
    )
    return _extract_v3_answer(resp.json())
Deuxiemement, response_format avec un JSON schema sur chat/completions garantit que la reponse de classification est un objet JSON valide et parseable — fini le parsing par regex de reponses en texte libre :
payload = {
    "model": self.model,
    "messages": [
        {"role": "system", "content": self._JSON_SYSTEM_PROMPT},
        {"role": "user", "content": query},
    ],
    "temperature": 0.1,
    "max_tokens": 800,
    "response_format": {
        "type": "json_schema",
        "json_schema": {"name": "response", "schema": schema, "strict": True},
    },
}

Etape 4 : Modeliser les controles sous forme d’arbres de requetes

Chaque controle de conformite est un arbre de questions oui/non avec des regles de branchement. Garder les controles sous forme de donnees — pas de code — les rend faciles a auditer, a ajuster, ou a deleguer a une personne non-technique.
CONTROLS = [
    {
        "number": "1",
        "name": "Liability of the receiving party",
        "queries": [
            {"id": "q1", "text": "Is there a liability clause for unauthorised disclosure?"},
            {"id": "q2", "text": "Is liability capped to direct damages only?"},
            {"id": "q3", "text": "Is the NDA governed by French law?"},
        ],
        "default_templates": {
            "q2": "to indemnify the Counterparty ... against all direct claims ...",
            "q3": "This Agreement is subject to French law ...",
        },
    },
    # ... three more controls
]
La logique de branchement vit dans _run_control. Le controle 1, par exemple, se lit ainsi : “s’il existe une clause de responsabilite, alors elle doit etre plafonnee aux dommages directs ; sinon, le NDA doit etre gouverne par le droit francais.”
if control["number"] == "1":
    q1 = ask("q1")
    if q1.is_true:
        q2 = ask("q2")
        if not q2.is_true:
            status, failed_qid = "FAIL", "q2"
    else:
        q3 = ask("q3")
        if not q3.is_true:
            status, failed_qid = "FAIL", "q3"

Etape 5 : Extraire le co-contractant pour l’appariement

Avant d’executer les controles, on identifie la Target Company dans le NDA. Dans un contexte M&A, jusqu’a trois parties peuvent apparaitre — la Receiving Party (l’acquereur), un Financial Advisor intermediaire, et la Target Company dont les informations sont partagees. On veut la Target Company, pas les deux autres. Un prompt soigneusement redige plus un JSON schema a un seul champ suffisent a garder la sortie propre :
schema = {
    "type": "object",
    "properties": {
        "target_company": {
            "type": "string",
            "description": (
                "Nom commercial de la société cible (Target Company). "
                "Exclure la Receiving Party et le Financial Advisor intermédiaire."
            ),
        },
    },
    "required": ["target_company"],
    "additionalProperties": False,
}
Une fois le co-contractant de chaque document connu, on apparie le NDA entrant avec la reference la plus proche via difflib.SequenceMatcher (un substitut leger a la similarite trigram PostgreSQL).

Etape 6 : Reformuler les clauses en echec

Lorsqu’un controle echoue, on propose un correctif. La chaine de fallback est : “essayer d’extraire la clause equivalente du NDA de reference apparie ; si rien d’utilisable ne revient, utiliser un modele pre-redige.”
def _reformulate(client, question, default_template, reference_file_id):
    if reference_file_id:
        result = _ask_yes_no(client, reference_file_id, question)
        if result.extracted_text:
            return result.extracted_text, "reference"
    return default_template.strip(), "template"
Cela signifie que la reformulation proposee est en general tiree d’une formulation deja acceptee par le co-contractant — beaucoup plus facile a faire accepter qu’une clause modele generique.

Etape 7 : Compiler le rapport

Le rapport final regroupe le co-contractant, la reference appariee, et un ControlResult par controle (statut, requetes avec citations, reformulation proposee). Un sommaire de haut niveau rend trivial le branchement de la revue dans un check CI ou un tableau de bord.
{
  "nda_filename": "NDA - Project Kairos.docx",
  "counterparty": "Nexora Group",
  "reference": {
    "filename": "NDA - Project Kairos - Livana (Meridia AM signed).pdf",
    "counterparty": "Nexora Group"
  },
  "summary": {
    "total_controls": 4, "passed": 2, "failed": 2,
    "pass_rate": 50.0, "status": "FAIL"
  },
  "controls": [
    {
      "number": "1",
      "name": "Liability of the receiving party",
      "status": "FAIL",
      "queries": [
        {"id": "q1", "is_true": true,  "extracted_text": "You shall be responsible ..."},
        {"id": "q2", "is_true": false, "extracted_text": ""}
      ],
      "reformulation": "You shall be responsible ... for any direct damages ...",
      "reformulation_source": "reference"
    }
  ]
}

Code complet

Code source complet

Clonez le depot pour executer le pipeline complet avec les NDA d’exemple.

Reference API

Documentation complete de l’API Paradigm.

Personnalisation

ParametreDescriptionValeur par defautA ajuster si…
CONTROLS (dans src/pipeline.py)Liste des controles de conformite, chacun avec un arbre de requetes et des modeles par defaut4 controles M&A NDAVous avez des exigences de politique differentes
match_reference_nda(threshold=...)Seuil de similarite du co-contractant0.5Votre bibliotheque de references contient beaucoup de quasi-doublons (augmenter) ou tres peu d’entrees (reduire)
model (dans ParadigmClient)Modele Paradigm utilise pour la recherche et la completionalfred-ft5Vous avez besoin d’arbitrages vitesse/qualite differents
temperatureDeterminisme de la completion0.1Vous voulez des reformulations plus creatives depuis le fallback modele
Prompt systeme _JSON_SYSTEM_PROMPTInstructions de langue / format de sortieFrancais, JSON uniquementVos documents ou votre equipe travaillent dans une autre langue

Ajouter votre propre controle

Chaque controle est une entree de la liste CONTROLS. Etapes pour en ajouter un :
  1. Definir les requetes — une question juridique oui/non par noeud de votre arbre de decision.
  2. Ajouter la logique de branchement dans _run_control — sous quelle combinaison de reponses le controle est PASS ou FAIL, et quel template de requete est utilise pour la reformulation.
  3. Rediger un modele par defaut — la formulation fallback utilisee quand aucun NDA de reference n’est apparie.
{
    "number": "5",
    "name": "Term and termination",
    "queries": [
        {"id": "q1", "text": "Does the NDA specify a fixed duration of confidentiality?"},
    ],
    "default_templates": {
        "q1": "The obligations under this Agreement shall survive for a period of five (5) years ...",
    },
}

Bonnes pratiques

  1. Gardez les controles sous forme de donnees, pas de code — mettre le texte des requetes et les modeles dans une liste (pas disperses dans des fonctions) fait de la revue de conformite une tache que votre equipe juridique peut auditer directement. Les petits gains pour les juristes sont de gros gains pour le pipeline.
  2. Utilisez toujours force_tool: "document_search" pour les recherches de clauses — cela force Paradigm a citer le document reel plutot qu’a s’appuyer sur sa connaissance generale, ce qui compte enormement pour la revue juridique.
  3. Utilisez response_format avec un JSON schema pour la classification — le parsing de texte libre pour “oui/non plus citation” est fragile ; le JSON impose par schema est infaillible.
  4. Privilegiez les reformulations issues de reference plutot que les modeles — une formulation deja acceptee par le co-contractant est bien plus facile a faire signer qu’une clause modele generique. Le fallback modele n’intervient que si aucune reference n’a ete appariee.
  5. Appariez les co-contractants, pas les noms de fichiers — extraire la Target Company avant l’appariement permet d’utiliser un inventaire de centaines de NDA signes sans se soucier de leur nommage. Un seuil de similarite souple (0.5) pardonne les variations de nommage.
  6. Validez le co-contractant extrait — les NDA M&A impliquent souvent trois parties ; un extracteur bon marche soutenu par un schema, avec des regles explicites “exclure ces roles” dans le champ description, evite des re-executions couteuses.