NLP pentru analiza contractelor: de la OCR la înțelegere
În fiecare an, companiile din întreaga lume gestionează miliarde de contracte. Contracte de aprovizionare, clauze confidențialitate, condiții de serviciu, contracte de muncă: un ocean de text legal care tradițional necesită sute de ore de revizuire manuală de către avocați și parajuriști. În 2026, piața global de LegalTech a depășit 35 de miliarde de dolari, determinat de cerere automatizare în creștere în managementul documentelor juridice.
Adevărata revoluție nu este doar în digitizarea documentelor, ci în intelege-le. Transformați un PDF scanat al unui contract de 80 de pagini în date structurate, extrageți automat părți contractuale, obligații, termene și clauze de risc: acesta este teritoriul Procesarea limbajului natural (NLP) aplicate domeniului juridic.
În acest articol inaugurăm seria LegalTech și AI explorarea conductei complete care transformă un document pe hârtie în cunoștințe acționabile: dinOCR (Caracter optic Recunoaștere) al NER (Recunoașterea entității numite), din clasificare a clauze la înțelegere semantică. Vom vedea codul Python real, comparați principalele motoare OCR și modele NLP și vom analiza platformele LegalTech pe care le redefinesc sectorul.
Ce veți învăța în acest articol
- Canalul complet de analiză a contractului: document fizic → OCR → NLP → perspective structurate
- Comparația motoarelor OCR: Tesseract, AWS Texttract, Azure Document Intelligence, Google Document AI
- Preprocesarea documentelor: analiza aspectului, extragerea tabelelor, segmentarea paginilor
- Named Entity Recognition (NER) pentru contracte: părți, date, sume, clauze
- Clasificarea clauzelor cu modele Transformer (LegalBERT, DeBERTa)
- Implementarea completă a Python cu spaCy, HuggingFace Transformers și Tesseract
- Platforme reale LegalTech: Kira Systems, Luminance, Ironclad
- Măsuri de evaluare și cele mai bune practici pentru proiectele Contract AI
Prezentare generală a serii LegalTech și AI
| # | Articol | Concentrează-te |
|---|---|---|
| 1 | Sunteți aici — NLP pentru analiza contractelor | De la OCR la înțelegere |
| 2 | Cercetare jurisprudențială cu AI | Căutare semantică și grafic de cunoaștere |
| 3 | Contracte inteligente și Blockchain | Automatizarea contractelor pe blockchain |
| 4 | Rezumatul actelor juridice | Rezumate automate cu LLM |
| 5 | Motor de conformitate cu AI | Conformitate automată cu reglementările |
| 6 | e-Discovery și criminalistică | Analiza documentelor pentru litigii |
| 7 | Semnătura electronică și identitate digitală | eIDAS 2.0 și SPID |
| 8 | GDPR și confidențialitate prin design | Respectarea confidențialității cu AI |
| 9 | Copilot legal | Asistent AI pentru firme de avocatură |
| 10 | Integrarea datelor juridice | Interoperabilitate și standarde |
Conducta de analiză a contractelor
Analiza automată a unui contract nu este un singur algoritm, ci unul conductă în mai multe etape unde fiecare componentă o alimentează pe următoarea. Înțelegeți mai întâi arhitectura generală și fundamentală pentru a te cufunda în detaliile tehnice ale fiecărei faze.
Cele 6 faze ale conductei
- Achiziţie: Documentul intră în sistem (scanare, încărcare, e-mail, API DMS)
- Preprocesare și OCR: Analiza layout-ului, de-skew, binarizare, extragere text
- Structurare: Segmentarea în secțiuni, paragrafe, clauze, tabele
- Extragerea entității (NER): Identificarea pieselor, a datelor, a sumelor, a referințelor de reglementare
- Clasificare și analiză: Tip de clauză, nivel de risc, obligații, condiții
- Ieșire și integrare: JSON structurat, tablou de bord, alertă, integrare cu CLM/DMS
Fiecare fază introduce provocări specifice. O eroare în cascadele OCR făcând NER inutil mai sofisticat. Segmentarea incorectă poate determina clasificarea unei clauze de declinare a răspunderii ca o simplă definiție. Din acest motiv, se măsoară calitatea conductei cap la cap, nu component cu component.
| Fază | Intrare | Ieșiri | Tehnologii cheie |
|---|---|---|---|
| Achiziţie | PDF, TIFF, DOCX, imagini | Fișier normalizat | Apache Tika, python-docx, PyPDF2 |
| OCR | Imagine scanată/PDF | Text brut + coordonate | Tesseract, Text, Document AI |
| Structurarea | Text brut | Secțiuni, clauze, tabele | Analizator de aspect, regex, motor de reguli |
| NER | Text structurat | Entități adnotate | spaCy, Legal-NER, John Snow Labs |
| Clasificare | Propoziții izolate | Etichetă + scor de încredere | LegalBERT, DeBERTa, GPT-4 |
| Ieșiri | Date structurate | JSON, tablou de bord, alertă | API REST, webhooks, SDK CLM |
OCR pentru documente juridice: compararea tehnologiilor
OCR este primul blocaj din conductă. Un contract poate ajunge ca PDF nativ (cu text selectabil) sau ca scanare a unui document pe hârtie. În al doilea caz, OCR trebuie reconstruit text din pixeli, gestionarea calității variabile a scanării, fonturi legale dense, layout cu două coloane, tabele și adnotări scrise de mână.
În 2025-2026, peisajul OCR este dominat de patru platforme de întreprindere și un proiect open-source care rămâne o referinţă esenţială. Să vedem diferențele.
Tesseract OCR (sursă deschisă)
Teseract, întreținut de Google și acum la versiunea 5.x, și motorul OCR open-source cel mai răspândit în lume. Utilizează o rețea LSTM pentru recunoașterea caracterelor și suportă dincolo 100 de limbi. Puterea și flexibilitatea sa: poate fi integrat în orice conductă Python fără costurile de licențiere și cu control deplin asupra preprocesării.
Cu toate acestea, Tesseract are limitări semnificative privind documentele juridice complexe. Nu se descurcă nativ extragerea tabelului nu recunoaște structura aspectului (anteturi, subsol, coloane) și necesită o preprocesare semnificativă (binarizare, de-skew, eliminarea zgomotului) pentru a realiza Rezultate acceptabile la scanări de calitate medie.
AWS Texttract
Amazon Texttract depășește OCR tradițional, oferind extragerea de mesele, formular (perechi cheie-valoare) e semnături ca caracteristici native. Integrarea cu ecosistemul AWS (S3, Lambda, Step Functions) îl face ideal pentru conducte de mare volum fără server. Procesarea asincronă vă permite să procesați documente de sute de pagini fără expirări.
Azure AI Document Intelligence
Azure AI Document Intelligence (fostul Form Recognizer) oferă modele pre-antrenate pentru facturi, chitanțe și acte de identitate, precum și un puternic șablon de aspect că extrage paragrafe, tabele, semne de selecție și coduri de bare. Puterea și posibilitatea a antrena modele personalizate pe documente specifice domeniului dvs., fundamentale pentru contractele cu machete proprietare.
Google Document AI
Google Document AI combină OCR extrem de de înaltă precizie cu procesoare specializate pentru anumite tipuri de documente. The Procesor OCR de documente atinge precizie 98% pe text, în timp ce procesoarele personalizate pot fi antrenate să extragă câmpuri specifice din contracte standardizate. Integrarea cu Vertex AI vă permite să construiți conducte ML end-to-end.
Comparația motoarelor OCR pentru documente juridice
| Caracteristică | Teseract 5 | AWS Texttract | Azure Doc Intelligence | Google Document AI |
|---|---|---|---|---|
| Precizia textului | 92-95% | 96-98% | 97-99% | 98%+ |
| Extragerea mesei | Nici un nativ | Da (nativ) | Da (nativ) | Da (nativ) |
| Analiza aspectului | De bază | Bun | Excelent | Excelent |
| Modele personalizate | Instruire OCR | Interogări personalizate | Modele personalizate | Procesoare personalizate |
| Cost | Gratuit (OSS) | 1,50 USD/1000 pagini | 1,50 USD/1000 pagini | 1,50 USD/1000 pagini |
| On-premise | Si | No | Da (container) | No |
| Limbi acceptate | 100+ | ~20 | ~300 | ~200 |
| Scrisul de mână | Limitat | Bun | Bun | Bun |
| Ideal pentru | Prototipări, buget redus | Conductă fără server AWS | EnterpriseMicrosoft | Precizie ridicată, GCP |
Când NU AI nevoie de OCR
Multe contracte moderne sunt generate digital (Word, PDF nativ). În aceste cazuri, OCR e de prisos și potențial dăunătoare: extragerea directă a textului din PDF-uri native cu biblioteci precum PyMuPDF (fitz) sau pdf instalator și mai rapid, mai precis și fără costuri. Prima operațiune în conductă ar trebui să fie întotdeauna triajul: determina dacă documentul necesită OCR sau dacă textul este deja disponibil.
Implementare: OCR Pipeline în Python
Să vedem cum să construim o conductă OCR robustă în Python, care să gestioneze atât PDF-urile native, cât și scanările, cu preprocesare automată și alternativă între diferite motoare.
Triajul și preprocesarea documentelor
Primul pas este să determinați dacă un PDF conține text selectabil sau dacă este o imagine scanată. Această decizie conduce întreaga conductă ulterioară.
import fitz # PyMuPDF
import pytesseract
from PIL import Image, ImageFilter, ImageEnhance
from pathlib import Path
from dataclasses import dataclass
from typing import Optional
import io
@dataclass(frozen=True)
class OcrResult:
"""Risultato immutabile dell'estrazione testo."""
text: str
source: str # 'native' | 'tesseract' | 'textract'
confidence: float # 0.0 - 1.0
page_count: int
tables: list # tabelle estratte
def is_native_pdf(pdf_path: str, threshold: float = 0.1) -> bool:
"""Determina se un PDF contiene testo selezionabile.
Args:
pdf_path: Percorso al file PDF
threshold: Rapporto minimo caratteri/pagina per considerarlo nativo
Returns:
True se il PDF ha testo estraibile direttamente
"""
doc = fitz.open(pdf_path)
total_chars = sum(len(page.get_text()) for page in doc)
avg_chars_per_page = total_chars / len(doc) if len(doc) > 0 else 0
doc.close()
# Un contratto tipico ha 2000-5000 caratteri per pagina
return avg_chars_per_page > 100
def extract_native_text(pdf_path: str) -> OcrResult:
"""Estrae testo da PDF nativo senza OCR."""
doc = fitz.open(pdf_path)
pages_text = []
for page in doc:
pages_text.append(page.get_text("text"))
full_text = "\n\n".join(pages_text)
page_count = len(doc)
doc.close()
return OcrResult(
text=full_text,
source="native",
confidence=0.99,
page_count=page_count,
tables=[]
)
def preprocess_image(image: Image.Image) -> Image.Image:
"""Preprocessing per migliorare la qualità OCR.
Applica: conversione grayscale, contrasto, nitidezza,
binarizzazione adattiva.
"""
# Conversione in scala di grigi
gray = image.convert("L")
# Aumento contrasto
enhancer = ImageEnhance.Contrast(gray)
enhanced = enhancer.enhance(2.0)
# Aumento nitidezza
sharpened = enhanced.filter(ImageFilter.SHARPEN)
# Binarizzazione con soglia adattiva
threshold_value = 128
binary = sharpened.point(
lambda x: 255 if x > threshold_value else 0, "1"
)
return binary
def ocr_with_tesseract(
pdf_path: str,
lang: str = "ita+eng"
) -> OcrResult:
"""OCR con Tesseract e preprocessing avanzato."""
doc = fitz.open(pdf_path)
pages_text = []
confidences = []
for page_num in range(len(doc)):
page = doc[page_num]
# Renderizza pagina come immagine ad alta risoluzione
mat = fitz.Matrix(300 / 72, 300 / 72) # 300 DPI
pix = page.get_pixmap(matrix=mat)
img_bytes = pix.tobytes("png")
image = Image.open(io.BytesIO(img_bytes))
# Preprocessing
processed = preprocess_image(image)
# OCR con dati di confidenza
ocr_data = pytesseract.image_to_data(
processed,
lang=lang,
output_type=pytesseract.Output.DICT,
config="--oem 3 --psm 6"
)
# Estrai testo e calcola confidenza media
page_text = pytesseract.image_to_string(
processed, lang=lang,
config="--oem 3 --psm 6"
)
pages_text.append(page_text)
# Confidenza media (escludi valori -1)
valid_confs = [
int(c) for c in ocr_data["conf"] if int(c) > 0
]
if valid_confs:
confidences.append(sum(valid_confs) / len(valid_confs))
doc.close()
avg_confidence = (
sum(confidences) / len(confidences) / 100
if confidences else 0.0
)
return OcrResult(
text="\n\n".join(pages_text),
source="tesseract",
confidence=avg_confidence,
page_count=len(pages_text),
tables=[]
)
def process_contract(pdf_path: str) -> OcrResult:
"""Pipeline principale: triage + estrazione."""
if is_native_pdf(pdf_path):
return extract_native_text(pdf_path)
return ocr_with_tesseract(pdf_path)
Cele mai bune practici: DPI și preprocesare
Pentru documentele legale cu fonturi mici și machete dense, utilizați întotdeauna un DPI de cel puțin 300 pentru rasterizare. Preprocesare (binarizare, declinare, îndepărtare a marginilor) poate îmbunătăți acuratețea OCR cu 10-15% la scanările de calitate medie. Cu toate acestea, la scanări cutie de preprocesare agresivă de înaltă calitate se înrăutăţeşte rezultatele: testați întotdeauna pe a eşantion reprezentativ.
NLP pentru text legal: provocări specifice
Limbajul juridic nu este un limbaj natural obișnuit. Are caracteristici care fac prelucrarea automată este deosebit de dificilă, chiar și pentru cele mai avansate modele.
Complexitatea limbajului juridic
- Propoziții lungi și complexe: O singură clauză contractuală se poate prelungi pt 200-500 de cuvinte cu mai multe subordonate, gravuri și referințe încrucișate. Modele de transformatoare cu o fereastră de context limitată (512 jetoane pentru BERT) pot pierde sensul global.
- Terminologie de specialitate: Termeni precum „clauză de reziliere expresă”, „penal contractual”, „exceptio inadimpleti contractus” au semnificații precise care diferă de uz comune ale acelorași cuvinte.
- Ambiguitate intenționată: Unele clauze sunt în mod deliberat vagi pentru a permite interpretări flexibile. PNL trebuie să recunoască și să raporteze această ambiguitate, nu să o rezolve în mod arbitrar.
- Referințe încrucișate: „În conformitate cu articolul 5, alin.3, litera b)” cere rezolvarea referințelor interne și externe la document.
- Negative și condiții multiple: „Cu excepția cazurilor prevăzute în paragraful anterior, partea nu va fi obligată să...” inversează sensul blocurilor întregi de text.
- Multilingvism: Contractele internaționale pot conține clauze în mai multe limbi, cu clauze de prevalență lingvistică care adaugă un nivel suplimentar de complexitate.
Modele NLP pentru domeniul juridic
Modelele de limbaj generic (BERT, Roberta) au performanțe suboptime pe textul legal deoarece pregătirea lor prealabilă se bazează pe Wikipedia, cărți și pagini web generice. Acesta este motivul pentru care s-au născut modele specifice domeniului instruit pe corpuri juridice.
| Model | De bază | Corpus de formare | Limbi | Utilizare principală |
|---|---|---|---|---|
| Legal-BERT | BERT-de bază | 12 GB texte legale (UE, Marea Britanie, SUA) | engleză | NER, clasificarea clauzelor |
| Jurisprudența-BERT | BERT-de bază | Jurisprudența americană (Harvard) | engleză | Cercetare jurisprudențială |
| Italian-Legal-BERT | BERT-de bază | Legislația și jurisprudența IT | italian | NER persoane juridice italiene |
| LegalPro-BERT | BERT-de bază | Prevederi legale de specialitate | engleză | Clasificarea prevederilor |
| DeBERTa-v3-legal | DeBERTa-v3 | Contracte + legislatie | engleză | Răspuns la întrebare juridică |
| John Snow Labs Legal NLP | Spark NLP | Peste 600 de șabloane specifice juridice | Multilingv | Conductă completă a întreprinderii |
Reglarea fină LegalBERT pe seturi de date specifice, cum ar fi CUAD (Contract Understanding Atticus Dataset) atinge un scor F1 de 83-88% la clasificarea a 41 de tipuri de clauze contractuale, un Îmbunătățire cu 12-15% față de BERT generic pentru aceeași sarcină. Alegerea modelului de bază și la fel de important ca și calitatea datelor de reglare fină.
Recunoașterea entității denumite pentru contracte
Il Recunoașterea entității denumite (NER) și faza în care sistemul identifică și clasifică entitățile cheie din textul contractual. Spre deosebire de NER generic care recunoaște oameni, locuri și organizații, NER legal trebuie să extragă entități specifice domeniului.
Entități juridice cheie
| Entitate | Descriere | Exemplu |
|---|---|---|
| PARTE | Părți contractuale | "Acme S.p.A." (Furnizor), "Beta S.r.l." (Client) |
| DATE | Date relevante | Data semnăturii, data intrării în vigoare, expirarea, reînnoirea |
| CANTITATE | Sume monetare | „150.000,00 EUR pe an”, „50.000 USD” |
| DURATĂ | Perioade de timp | „36 de luni”, „3 ani de la data semnării” |
| CLAUSE_REF | Referiri la clauze | „Art. 5, paragraful 3”, „Secțiunea 12.1” |
| LAW_REF | Referințe normative | "Decretul legislativ 50/2016", "Art. 1341 c.c." |
| OBLIGAŢIE | Obligatii contractuale | "Furnizorul se obligă să livreze..." |
| JURISDICȚIA | Tribunal competent | „Tribunalul din Milano” |
| GOVERNING_LAW | Legea aplicabila | „Legea italiană”, „Legile Angliei” |
Implementarea NER cu spaCy
spaCy oferă un cadru flexibil pentru construirea de conducte NER personalizate. Să vedem cum să creăm un model NER specific pentru contractele italiene, care combină reguli deterministe cu învățarea automată.
import spacy
from spacy.tokens import Span
from spacy.language import Language
from spacy.matcher import Matcher, PhraseMatcher
from dataclasses import dataclass
from typing import Tuple
@dataclass(frozen=True)
class LegalEntity:
"""Entità legale estratta dal contratto."""
text: str
label: str
start_char: int
end_char: int
confidence: float
@Language.component("legal_entity_ruler")
def legal_entity_ruler(doc):
"""Componente spaCy per entità legali con regole."""
new_ents = list(doc.ents)
# Pattern per riferimenti normativi italiani
law_patterns = [
# D.Lgs. 50/2016, D.L. 18/2020
r"D\.(?:Lgs|L|P\.R|M)\.\s*\d+/\d{4}",
# Art. 1341 c.c., Art. 2043 c.c.
r"[Aa]rt(?:icolo|\.)\s*\d+(?:\s*(?:comma|co\.)\s*\d+)?(?:\s*c\.c\.|"
r"\s*c\.p\.c\.|c\.p\.)?",
# Legge n. 241/1990
r"[Ll]egge\s*(?:n\.\s*)?\d+/\d{4}",
]
# Pattern per importi monetari
amount_patterns = [
# EUR 150.000,00 / euro 150.000
r"(?:EUR|euro|Euro|€)\s*[\d.,]+",
# 150.000,00 euro
r"[\d.]+,\d{2}\s*(?:EUR|euro|Euro|€)",
# USD 50,000.00
r"(?:USD|GBP|CHF|\$|£)\s*[\d,]+(?:\.\d{2})?",
]
# Pattern per date italiane
date_patterns = [
# 15 marzo 2026, 1 gennaio 2025
r"\d{1,2}\s+(?:gennaio|febbraio|marzo|aprile|maggio|giugno|"
r"luglio|agosto|settembre|ottobre|novembre|dicembre)\s+\d{4}",
# 15/03/2026, 01.01.2025
r"\d{2}[/.\-]\d{2}[/.\-]\d{4}",
]
import re
for pattern in law_patterns:
for match in re.finditer(pattern, doc.text):
span = doc.char_span(
match.start(), match.end(), label="LAW_REF"
)
if span and not any(
ent.start <= span.start < ent.end
for ent in new_ents
):
new_ents.append(span)
for pattern in amount_patterns:
for match in re.finditer(pattern, doc.text):
span = doc.char_span(
match.start(), match.end(), label="AMOUNT"
)
if span and not any(
ent.start <= span.start < ent.end
for ent in new_ents
):
new_ents.append(span)
for pattern in date_patterns:
for match in re.finditer(pattern, doc.text):
span = doc.char_span(
match.start(), match.end(), label="DATE"
)
if span and not any(
ent.start <= span.start < ent.end
for ent in new_ents
):
new_ents.append(span)
doc.ents = sorted(new_ents, key=lambda e: e.start)
return doc
def create_legal_nlp() -> spacy.Language:
"""Crea pipeline spaCy per NER legale."""
# Carica modello italiano base
nlp = spacy.load("it_core_news_lg")
# Aggiungi componente custom dopo il NER standard
nlp.add_pipe("legal_entity_ruler", after="ner")
return nlp
def extract_entities(
text: str,
nlp: spacy.Language
) -> list:
"""Estrai entità legali dal testo contrattuale."""
doc = nlp(text)
return [
LegalEntity(
text=ent.text,
label=ent.label_,
start_char=ent.start_char,
end_char=ent.end_char,
confidence=0.85 # spaCy rule-based = high confidence
)
for ent in doc.ents
if ent.label_ in (
"PER", "ORG", "LOC", "DATE",
"AMOUNT", "LAW_REF", "PARTY"
)
]
# Esempio di utilizzo
nlp = create_legal_nlp()
sample = """
Il presente contratto e stipulato tra Acme S.p.A., con sede
in Via Roma 15, Milano (di seguito "Fornitore") e Beta S.r.l.,
con sede in Via Napoli 42, Roma (di seguito "Cliente").
Il corrispettivo annuo e pari a EUR 150.000,00 ai sensi
dell'Art. 1655 c.c. La durata e di 36 mesi a decorrere
dal 1 marzo 2026.
"""
entities = extract_entities(sample, nlp)
for entity in entities:
print(f"{entity.label:<12} {entity.text}")
Rezultat așteptat
ORG Acme S.p.A.
LOC Via Roma 15, Milano
ORG Beta S.r.l.
LOC Via Napoli 42, Roma
AMOUNT EUR 150.000,00
LAW_REF Art. 1655 c.c.
DATE 1 marzo 2026
Clasificarea clauzelor cu transformator
Clasificarea clauzelor este inima analizei automate a contractelor. Fiecare clauză este atribuit unei categorii (răspundere, confidențialitate, reziliere, penalități etc.) e evaluat opțional pentru nivelul de risc. În mod tradițional, acest proces durează ore de muncă legală: un model Transformer instruit o face în câteva secunde.
Setul de date CUAD
Il Contract Understanding Atticus Dataset (CUAD) și reperul de referință pentru clasificarea contractuala. Creat de The Atticus Project cu contribuții de la zeci de avocați, conţine mai mult 13.000 de adnotări pe 510 contracte de afaceri reale, acoperind 41 de tipuri de clauze relevante pentru tranzacțiile de fuziuni și achiziții și due diligence.
Cele 41 de categorii CUAD (selecție)
- Data acordului
- petreceri
- Legea aplicabilă
- Rezilierea pentru comoditate
- Anti-atribuire
- Non-concurență
- Nesolicitare
- Exclusivitate
- Despăgubiri
- Limitarea răspunderii
- Atribuirea proprietății IP
- Acordarea licenței
- Acord de nedivulgare
- Venituri/Partajarea profitului
- Restricții de preț
- Angajamentul minim
- Restricție de volum
- Asigurare
- Drepturi de audit
- Schimbarea controlului
Reglarea fină a LegalBERT pentru clasificarea clauzelor
Să vedem cum să antrenăm un clasificator de clauze folosind LegalBERT și setul de date CUAD. Procesul implică reglarea fină a modelului pre-antrenat pe exemple adnotate de clauze contractuale.
from transformers import (
AutoTokenizer,
AutoModelForSequenceClassification,
TrainingArguments,
Trainer,
)
from datasets import load_dataset, Dataset
from sklearn.metrics import f1_score, precision_score, recall_score
import numpy as np
from dataclasses import dataclass
from typing import Dict
@dataclass(frozen=True)
class ClauseLabel:
"""Label per la classificazione delle clausole."""
id: int
name: str
risk_level: str # 'low' | 'medium' | 'high' | 'critical'
# Definizione categorie di clausole
CLAUSE_LABELS = {
0: ClauseLabel(0, "termination", "high"),
1: ClauseLabel(1, "indemnification", "critical"),
2: ClauseLabel(2, "limitation_of_liability", "critical"),
3: ClauseLabel(3, "confidentiality", "medium"),
4: ClauseLabel(4, "non_compete", "high"),
5: ClauseLabel(5, "governing_law", "medium"),
6: ClauseLabel(6, "intellectual_property", "high"),
7: ClauseLabel(7, "payment_terms", "medium"),
8: ClauseLabel(8, "warranty", "high"),
9: ClauseLabel(9, "force_majeure", "medium"),
10: ClauseLabel(10, "assignment", "medium"),
11: ClauseLabel(11, "dispute_resolution", "medium"),
12: ClauseLabel(12, "general_provision", "low"),
}
MODEL_NAME = "nlpaueb/legal-bert-base-uncased"
def load_and_prepare_data(
dataset_name: str = "theatticusproject/cuad-qa"
) -> tuple:
"""Carica e prepara il dataset CUAD per classificazione."""
dataset = load_dataset(dataset_name)
# Trasforma da QA a classificazione
texts = []
labels = []
for example in dataset["train"]:
context = example["context"]
# Usa la categoria della domanda come label
category = example.get("category", "general_provision")
label_id = next(
(k for k, v in CLAUSE_LABELS.items()
if v.name == category),
12 # default: general_provision
)
texts.append(context[:512]) # Tronca a 512 token
labels.append(label_id)
return texts, labels
def compute_metrics(eval_pred) -> Dict[str, float]:
"""Calcola metriche di valutazione."""
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return {
"f1_macro": f1_score(
labels, predictions, average="macro"
),
"f1_weighted": f1_score(
labels, predictions, average="weighted"
),
"precision": precision_score(
labels, predictions, average="weighted"
),
"recall": recall_score(
labels, predictions, average="weighted"
),
}
def train_clause_classifier():
"""Addestra il classificatore di clausole."""
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
model = AutoModelForSequenceClassification.from_pretrained(
MODEL_NAME,
num_labels=len(CLAUSE_LABELS),
problem_type="single_label_classification",
)
texts, labels = load_and_prepare_data()
# Tokenizzazione
encodings = tokenizer(
texts,
truncation=True,
padding="max_length",
max_length=512,
return_tensors="pt",
)
# Crea dataset HuggingFace
dataset = Dataset.from_dict({
"input_ids": encodings["input_ids"],
"attention_mask": encodings["attention_mask"],
"labels": labels,
})
# Split train/eval
split = dataset.train_test_split(test_size=0.2, seed=42)
training_args = TrainingArguments(
output_dir="./legal-bert-clauses",
num_train_epochs=5,
per_device_train_batch_size=16,
per_device_eval_batch_size=32,
warmup_steps=500,
weight_decay=0.01,
logging_dir="./logs",
logging_steps=100,
eval_strategy="epoch",
save_strategy="epoch",
load_best_model_at_end=True,
metric_for_best_model="f1_macro",
learning_rate=2e-5,
fp16=True,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=split["train"],
eval_dataset=split["test"],
compute_metrics=compute_metrics,
)
trainer.train()
return trainer
# Inferenza su nuove clausole
def classify_clause(
text: str,
model,
tokenizer
) -> tuple:
"""Classifica una singola clausola contrattuale.
Returns:
Tuple di (label, confidence, risk_level)
"""
inputs = tokenizer(
text,
truncation=True,
padding="max_length",
max_length=512,
return_tensors="pt",
)
outputs = model(**inputs)
probs = outputs.logits.softmax(dim=-1)
predicted_id = probs.argmax().item()
confidence = probs.max().item()
clause_label = CLAUSE_LABELS[predicted_id]
return clause_label.name, confidence, clause_label.risk_level
Limitele reglajului fin pe CUAD
Setul de date CUAD este axat pe contractele comerciale în Limba engleză și în context Fuziuni și achiziții americane. Pentru contractele italiene, europene sau cu alte jurisdicții, acest lucru este necesar creați sau adaptați seturi de date specifice. O abordare eficientă este învăţare prin transfer în două etape: reglaj fin pe CUAD pentru a afla structura generală a contractului, apoi reglaj fin pe a set mic de date (200-500 de exemple) pentru domeniul dvs. specific.
Detectarea obligațiilor și analiza semantică
Pe lângă clasificarea clauzelor, un sistem Contract AI trebuie să identifice obligații contractuale: cine trebuie să facă ce, când și care sunt consecințele de nerespectare. Această sarcină este cunoscută ca Detectarea modalității deontice în Literatura juridică NLP.
Cele trei modalități deontice
- Obligație (TREBUIE/TREBUIE): „Furnizorul necesitate livrați mărfurile în 30 de zile"
- Permisiune (MAI/POT): „Clientul poate solicita modificari ale proiectului"
- Interdicție (NU TREBUIE/NU TREBUIE): „Partidele ei nu pot ceda contractul unor terti"
Implementare cu Clasificare Zero-Shot
Pentru urmărirea obligațiilor, o abordare eficientă este clasificare zero-shot cu modele precum DeBERTa, care nu necesită date specifice de antrenament adnotate.
from transformers import pipeline
from dataclasses import dataclass
from typing import List
@dataclass(frozen=True)
class Obligation:
"""Obbligo contrattuale rilevato."""
text: str
modality: str # 'obligation' | 'permission' | 'prohibition'
confidence: float
subject: str # chi ha l'obbligo
action: str # cosa deve fare
deadline: str # entro quando (se specificato)
def create_obligation_detector():
"""Crea il classificatore zero-shot per obblighi."""
return pipeline(
"zero-shot-classification",
model="MoritzLaurer/DeBERTa-v3-large-mnli-fever-"
"anli-ling-wanli",
device=0, # GPU, usa -1 per CPU
)
def detect_obligations(
clauses: List[str],
classifier
) -> List[Obligation]:
"""Rileva obblighi in una lista di clausole."""
candidate_labels = [
"obligation or duty",
"permission or right",
"prohibition or restriction",
"definition or description",
"condition or prerequisite",
]
results = []
for clause in clauses:
output = classifier(
clause,
candidate_labels,
multi_label=False,
)
top_label = output["labels"][0]
top_score = output["scores"][0]
# Filtra solo obblighi, permessi e divieti
modality_map = {
"obligation or duty": "obligation",
"permission or right": "permission",
"prohibition or restriction": "prohibition",
}
if top_label in modality_map and top_score > 0.6:
results.append(Obligation(
text=clause,
modality=modality_map[top_label],
confidence=top_score,
subject="", # Da estrarre con NER
action="", # Da estrarre con SRL
deadline="", # Da estrarre con NER
))
return results
# Esempio
classifier = create_obligation_detector()
clauses = [
"Il Fornitore deve consegnare i beni entro 30 giorni "
"dalla data dell'ordine.",
"Il Cliente può richiedere modifiche al progetto entro "
"la fase di design.",
"Le parti non possono cedere il presente contratto a "
"terzi senza previo consenso scritto.",
"Per 'Servizi' si intendono le attivita descritte "
"nell'Allegato A.",
]
obligations = detect_obligations(clauses, classifier)
for obl in obligations:
print(f"[{obl.modality.upper()}] "
f"({obl.confidence:.2f}) {obl.text[:60]}...")
Similaritate semantică între propoziții
O altă aplicație crucială este comparația semantică între clauze. Când o companie primește un nou contract, vrea să știe: „Această clauză de declinare a răspunderii este similară sau diferită de a noastră clauza standard?" Acolo asemănarea semantică pe baza înglobărilor permite această comparație.
from sentence_transformers import SentenceTransformer, util
from dataclasses import dataclass
from typing import List, Tuple
@dataclass(frozen=True)
class ClauseComparison:
"""Risultato del confronto tra clausole."""
clause_a: str
clause_b: str
similarity: float
is_substantially_similar: bool
deviation_areas: List[str]
def compare_clauses(
new_clause: str,
standard_clauses: List[str],
threshold: float = 0.85,
) -> List[ClauseComparison]:
"""Confronta una clausola con le clausole standard.
Args:
new_clause: Clausola dal contratto in revisione
standard_clauses: Clausole standard dell'azienda
threshold: Soglia di similarità (0-1)
Returns:
Lista di confronti ordinati per similarità
"""
model = SentenceTransformer(
"sentence-transformers/all-mpnet-base-v2"
)
new_embedding = model.encode(
new_clause, convert_to_tensor=True
)
std_embeddings = model.encode(
standard_clauses, convert_to_tensor=True
)
similarities = util.cos_sim(new_embedding, std_embeddings)
comparisons = []
for idx, sim_score in enumerate(similarities[0]):
score = sim_score.item()
comparisons.append(ClauseComparison(
clause_a=new_clause[:100],
clause_b=standard_clauses[idx][:100],
similarity=score,
is_substantially_similar=score >= threshold,
deviation_areas=(
[] if score >= threshold
else ["Possibile deviazione rilevata"]
),
))
# Ordina per similarità decrescente
return sorted(
comparisons, key=lambda c: c.similarity, reverse=True
)
# Esempio: confronto clausola di riservatezza
new_clause = (
"Le informazioni riservate non potranno essere divulgate "
"a terzi per un periodo di 5 anni dalla data di "
"risoluzione del contratto."
)
standard_clauses = [
"Le informazioni confidenziali non potranno essere "
"comunicate a terzi per 3 anni dalla cessazione del "
"rapporto contrattuale.",
"Il Fornitore si impegna a consegnare i prodotti entro "
"30 giorni lavorativi dall'ordine.",
"Le informazioni riservate saranno protette per un "
"periodo di 10 anni dalla firma del presente accordo.",
]
results = compare_clauses(new_clause, standard_clauses)
for r in results:
status = "SIMILE" if r.is_substantially_similar else "DIVERSA"
print(f"[{status}] Similarità: {r.similarity:.3f}")
print(f" Standard: {r.clause_b}...")
print()
Conductă end-to-end: de la OCR la Insight
Acum integrăm toate componentele într-o conductă completă care preia un PDF de contract și ieșiri un raport structurat cu entități, clauze clasificate și obligații identificate.
from dataclasses import dataclass, field
from typing import List, Optional
from datetime import datetime
import json
@dataclass(frozen=True)
class ContractAnalysis:
"""Risultato completo dell'analisi contrattuale."""
file_name: str
analyzed_at: str
page_count: int
ocr_source: str
ocr_confidence: float
parties: List[str]
dates: List[dict]
amounts: List[dict]
law_references: List[str]
clauses: List[dict]
obligations: List[dict]
risk_summary: dict
def analyze_contract(pdf_path: str) -> ContractAnalysis:
"""Pipeline completa di analisi contrattuale.
Fasi:
1. Triage e OCR
2. Segmentazione in clausole
3. NER per entità legali
4. Classificazione clausole
5. Rilevamento obblighi
6. Calcolo risk summary
"""
# Fase 1: OCR
ocr_result = process_contract(pdf_path)
# Fase 2: Segmentazione
clauses_text = segment_into_clauses(ocr_result.text)
# Fase 3: NER
nlp = create_legal_nlp()
all_entities = extract_entities(ocr_result.text, nlp)
parties = [
e.text for e in all_entities
if e.label in ("ORG", "PER", "PARTY")
]
dates = [
{"text": e.text, "position": e.start_char}
for e in all_entities if e.label == "DATE"
]
amounts = [
{"text": e.text, "position": e.start_char}
for e in all_entities if e.label == "AMOUNT"
]
law_refs = [
e.text for e in all_entities
if e.label == "LAW_REF"
]
# Fase 4: Classificazione clausole
classified = []
for clause in clauses_text:
label, conf, risk = classify_clause(
clause, model, tokenizer
)
classified.append({
"text": clause[:200],
"type": label,
"confidence": round(conf, 3),
"risk_level": risk,
})
# Fase 5: Rilevamento obblighi
obligation_detector = create_obligation_detector()
detected_obligations = detect_obligations(
clauses_text, obligation_detector
)
obligations_data = [
{
"text": o.text[:200],
"modality": o.modality,
"confidence": round(o.confidence, 3),
}
for o in detected_obligations
]
# Fase 6: Risk summary
risk_counts = {"low": 0, "medium": 0, "high": 0, "critical": 0}
for c in classified:
risk_counts[c["risk_level"]] = (
risk_counts.get(c["risk_level"], 0) + 1
)
return ContractAnalysis(
file_name=pdf_path,
analyzed_at=datetime.now().isoformat(),
page_count=ocr_result.page_count,
ocr_source=ocr_result.source,
ocr_confidence=round(ocr_result.confidence, 3),
parties=list(set(parties)),
dates=dates,
amounts=amounts,
law_references=list(set(law_refs)),
clauses=classified,
obligations=obligations_data,
risk_summary=risk_counts,
)
def segment_into_clauses(text: str) -> List[str]:
"""Segmenta il testo in clausole individuali.
Usa pattern tipici dei contratti italiani:
- Numerazione (1., 1.1, Art. 1, Articolo 1)
- Titoli in maiuscolo
- Separatori di sezione
"""
import re
# Pattern per inizio clausola
clause_pattern = re.compile(
r"(?:^|\n)"
r"(?:"
r"(?:Art(?:icolo)?\.?\s*\d+)" # Art. 1, Articolo 1
r"|(?:\d+\.\d*\s+[A-Z])" # 1. Titolo, 1.1 Sotto
r"|(?:[A-Z][A-Z\s]{10,})" # TITOLO IN MAIUSCOLO
r")"
)
splits = clause_pattern.split(text)
# Filtra clausole troppo corte (< 50 caratteri)
return [
clause.strip()
for clause in splits
if len(clause.strip()) > 50
]
# Esecuzione
result = analyze_contract("contratto_fornitura.pdf")
print(json.dumps(
{
"parties": result.parties,
"risk_summary": result.risk_summary,
"clause_count": len(result.clauses),
"obligation_count": len(result.obligations),
},
indent=2, ensure_ascii=False
))
Platforme LegalTech pentru Contract AI
Pe piața LegalTech 2026, mai multe platforme oferă soluții de analiză a contractelor bazate pe inteligență artificială. Finanțarea în sector a ajuns 4,3 miliarde de dolari în 2025, o creștere cu 54% față de 2024. Să vedem principalele platforme și abordările lor tehnologice.
| Platformă | Specializare | Tehnologia AI | Utilizatori tipici | Pret orientativ |
|---|---|---|---|---|
| Kira Systems (Literar) | Due Diligence M&A | ML hibrid + IA generativă | 70+ dintre primele 100 de firme de avocatură | Enterprise (personalizat) |
| Luminanță | Detectarea anomaliilor, conformitatea | AI de calitate juridică proprietară | Firme de avocatura, corporatiste | De la 500 USD/lună |
| Ironclad | Managementul ciclului de viață al contractului | AI Assist pentru revizuire și negociere | Operațiuni juridice, achiziții | De la 300 USD/lună |
| Cartea de vrăji | Redactare asistată și revizuire | GPT-4 + reglaj fin legal | Avocați, parajuriști | De la 400 USD/lună |
| LegalFly | Examinați contractele multilingve | LLM + NER specializat | Studii internaționale | De la 200 USD/lună |
| Sirion | Întreprindere Contract Intelligence | AI pentru extracție și analiză | Fortune 500, achiziții | Enterprise (personalizat) |
| John Snow Labs | Conducta NLP (600+ modele) | Spark NLP + Legal NLP | Echipa de știință a datelor | Open source + Enterprise |
Kira Systems: Referința pentru Due Diligence
Kira Systems, acum parte a Litera și folosit de aproximativ 80% din primele 25 de studiouri avocați specializați în fuziuni și achiziții la nivel mondial. Abordarea sa hibridă combină modele ML brevetate instruit pe peste un milion de contracte cu capacități generative de sinteză și analiză. The Puterea lui Kira constă în capacitatea sa de a extrage peste 1.000 de tipuri de clauze predefinite (dispoziții încorporate) și în posibilitatea antrenării modelelor personalizate pe clauze specifice clientului în câteva ore.
Luminanță: Detectarea anomaliilor pentru contracte
Luminanță se remarcă prin capacitatea de identificare anomalii în contracte: clauze care se abat de la standard, termeni neobișnuiți, condiții lipsă. Lui arhitectura AI de grad juridic, actualizat la începutul anului 2026, leagă istoria negocieri, raționament juridic și decizii de afaceri de-a lungul întregului ciclu de viață a contractului. Și mai ales puternic în recenziile transfrontaliere unde diferențele dintre jurisdicțiile creează riscuri ascunse.
Măsuri de evaluare și bune practici
Evaluarea unui sistem Contract AI necesită metrici specifice pentru fiecare etapă a conductei. Un sistem care realizează o acuratețe OCR de 98%, dar doar 60% la clasificarea clauzelor și mai puțin util decât unul cu 95% OCR și 85% la clasificare.
Valori în funcție de fază
| Fază | Metrica principală | Prag acceptabil | Prag excelent |
|---|---|---|---|
| OCR | Rata de eroare de caractere (CER) | < 5% | < 1% |
| Segmentarea | Limite F1 | > 80% | > 92% |
| NER | Entitatea F1 (strict) | > 75% | > 88% |
| Clasificarea clauzelor | Macro F1 | > 78% | > 88% |
| Detectarea obligației | F1 pentru clasa deontică | > 72% | > 85% |
| De la capăt la capăt | Rata de finalizare a sarcinilor | > 70% | > 90% |
Cele mai bune practici pentru proiecte de IA contractuale
10 reguli de aur
- Începeți cu triajul: Nu aplicați OCR PDF-urilor native. 60-70% din contracte afaceri moderne și deja în format digital.
- Investește în preprocesare: Calitatea OCR determină limita maximă performanța întregii conducte. O oră investită în preprocesare economisește zece în NER.
- Utilizați șabloane specifice domeniului: LegalBERT depășește BERT generic cu 12-15% pe sarcini legale. Costul reglajului fin este marginal în comparație cu profitul.
- Combinați regulile și ML: Regulile deterministe (regex pentru date, sume, referințe normative) sunt mai precise și explicabile decât ML pentru modele structurate.
- Măsurare de la capăt la capăt: Un NER perfect și inutil dacă segmentarea este greșită. Valoarea care contează este Rata de finalizare a sarcinilor: câte clauze utilizatorul final accepta fara corecturi.
- Omul în buclă: AI nu îl înlocuiește pe avocat, ci îl accelerează. Prezice întotdeauna o interfață de revizuire în care expertul confirmă, corectează și îmbunătățește predicțiile.
- Versiunea modelelor: Contractele evoluează (gândiți-vă la GDPR sau la Directiva NIS2). Modelele trebuie reinstruite periodic pe date actualizate.
- Confidențialitate prin design: Contractele conțin date sensibile (sume, părți, conditii comerciale). Procesați on-premise sau cu garanții contractuale de la furnizorul de cloud.
- Începeți cu un caz de utilizare specific: Nu încercați să construiți un sistem universale. Începeți de la un tip de contract (de exemplu, NDA, furnizare) și o sarcină (de exemplu, extragerea datei termen limită) și să demonstreze valoarea înainte de extindere.
- Monitorizați deriva de date: Limbajul contractual evoluează. Clauze privind AI, ESG, reziliența lanțului de aprovizionare au devenit comune doar în ultimii 2-3 ani. Modelul trebuie monitorizat pentru a identifica degradarea performanței.
Model de integrare cu sistemele de afaceri
Un sistem Contract AI este valoros doar dacă este integrat în procesele de afaceri existente. Ieșirea a conductei trebuie să curgă în Managementul ciclului de viață al contractului (CLM), Sisteme de management al documentelor (DMS) e fluxul de lucru de aprobare.
Arhitectura de integrare
- Bazat pe evenimente: Încărcarea unui nou contract în DMS îl activează automat conducta de analiză prin webhook sau coadă de mesaje (RabbitMQ, SQS).
- API-uri REST: Punct final pentru a trimite documente și a primi rezultate structurate în format JSON, compatibil cu orice CLM.
- Procesare lot: Pentru migrarea arhivelor de contracte istorice, cu procesare asincronă și notificări de finalizare.
- Tablouri de bord: Interfață web pentru vizualizarea rezultatelor, revizuire predicții și feedback pentru îmbunătățirea continuă a modelului.
Standarde și formate de ieșire
Pentru a maximiza interoperabilitatea, ieșirea conductei ar trebui să respecte standarde deschise: OASIS LegalDocML (Akoma Ntoso) pentru structura documentarului, SALI (Avansarea standardelor pentru industria juridică) pentru taxonomia de clauze, e JSON-LD pentru metadate structurate. Aceste standarde permit integrare cu sisteme terțe fără mapări personalizate.
Concluzii și pașii următori
Analiza automată a contractelor cu NLP nu mai este un proiect de cercetare academică: este un tehnologie matură folosită zilnic de firme de avocatură și departamente juridice de top companii din lume. Conducta pe care am construit-o în acest articol, de la OCR până la înțelegere semantică, reprezintă arhitectura fundamentală pe care se bazează toate platformele LegalTech modernă.
Puncte cheie de reținut:
- Conducta și în mai multe etape: OCR → structurare → NER → clasificare → analiză semantică. Fiecare fază are tehnologii și metrici specifice.
- I modele specifice domeniului (LegalBERT, CaseLaw-BERT) depășesc semnificativ modele generice privind sarcinile juridice. Reglarea fină a seturilor de date precum CUAD este, de asemenea, accesibilă echipa cu resurse limitate.
- Abordarea reguli hibride + ML iar cele mai eficiente: reguli pentru entitățile structurate (date, sume, referințe), ML pentru semantică și clasificare.
- Il om-în-buclă nu este un compromis, ci o necesitate: AI accelerează munca juridică nu o înlocuiește. Supravegherea umană este esențială pentru validare si imbunatatire continua.
- L'integrare cu CLM, DMS și fluxurile de lucru de afaceri este esențială pentru transformare analiza tehnică în valoarea afacerii.
În următorul articol din serie vom explora cercetare jurisprudențială cu AI, văzând modul în care motoarele de căutare semantică și graficele de cunoștințe transformă drumul avocații și juriștii accesează jurisprudență și doctrină.
Resurse pentru a afla mai multe
- Seturi de date CUAD: github.com/TheAtticusProject/cuad - 510 contracte adnotate, 41 de tipuri de clauze
- LegalBERT: HuggingFace nlpaueb/legal-bert-base-uncased - model legal pre-antrenat
- SpaCy Legal NER: github.com/openlegaldata/legal-ner - NER cu sursă deschisă pentru texte legale
- John Snow Labs Legal NLP: Peste 600 de șabloane pentru conducte complete ale întreprinderilor
- OASIS LegalDocML: Standard XML pentru documente juridice structurate







