NLP pro analýzu smluv: od OCR k porozumění
Společnosti po celém světě ročně spravují miliardy zakázek. Smlouvy o dodávkách, doložky důvěrnost, podmínky služby, pracovní smlouvy: oceán právních textů, které tradičně vyžaduje stovky hodin manuální kontroly právníky a asistenty. V roce 2026 trh globální of LegalTech přesáhla 35 miliard dolarů, tažená poptávkou rostoucí automatizace správy právních dokumentů.
Skutečná revoluce není jen v digitalizaci dokumentů, ale v rozumět jim. Transformujte naskenovaný PDF s 80stránkovou smlouvou na strukturovaná data, která se automaticky extrahují smluvní strany, závazky, lhůty a rizikové doložky: toto je území Zpracování přirozeného jazyka (NLP) se vztahuje na právní oblast.
V tomto článku zahajujeme sérii LegalTech a AI prozkoumání celého potrubí která proměňuje papírový dokument v akceschopné znalosti: odOCR (Optický znak Uznání) al NER (Pojmenovaný Entity Recognition), od klasifikace doložky na sémantické porozumění. Uvidíme skutečný kód Pythonu, porovnáme hlavní OCR motory a NLP modely a budeme analyzovat platformy LegalTech, které nově definují odvětví.
Co se dozvíte v tomto článku
- Kompletní kanál analýzy smluv: fyzický dokument → OCR → NLP → strukturované náhledy
- Porovnání OCR enginů: Tesseract, AWS Textract, Azure Document Intelligence, Google Document AI
- Předzpracování dokumentu: analýza rozložení, extrakce tabulky, segmentace stránky
- Rozpoznání pojmenované entity (NER) pro smlouvy: strany, data, částky, doložky
- Klasifikace vět s transformátorovými modely (LegalBERT, DeBERTa)
- Dokončete implementaci Pythonu pomocí spaCy, HuggingFace Transformers a Tesseract
- Skutečné platformy LegalTech: Kira Systems, Luminance, Ironclad
- Metriky hodnocení a osvědčené postupy pro projekty smluvní AI
Přehled sérií LegalTech a AI
| # | Položka | Soustředit |
|---|---|---|
| 1 | Jste zde — NLP pro analýzu smluv | Od OCR k porozumění |
| 2 | Právní výzkum s AI | Sémantické vyhledávání a graf znalostí |
| 3 | Chytré smlouvy a blockchain | Automatizace kontraktů na blockchainu |
| 4 | Sumarizace právních dokumentů | Automatické souhrny s LLM |
| 5 | Compliance Engine s AI | Automatizované dodržování předpisů |
| 6 | e-Discovery a forenzní | Analýza dokumentů pro spory |
| 7 | Elektronický podpis a digitální identita | eIDAS 2.0 a SPID |
| 8 | GDPR a Privacy by Design | Soulad s ochranou soukromí s AI |
| 9 | Právní druhý pilot | Asistent AI pro advokátní kanceláře |
| 10 | Integrace právních údajů | Interoperabilita a standardy |
Průběh analýzy smluv
Automatická analýza smlouvy není jediný algoritmus, ale jeden vícestupňové potrubí kde každá součást napájí další. Nejprve pochopte celkovou a základní architekturu abyste se ponořili do technických detailů každé fáze.
6 fází potrubí
- Získávání: Dokument vstoupí do systému (skenování, nahrání, e-mail, DMS API)
- Předzpracování a OCR: Analýza rozložení, de-skew, binarizace, extrakce textu
- Strukturování: Segmentace do oddílů, odstavců, doložek, tabulek
- Extrakce entity (NER): Identifikace částí, data, částky, regulační reference
- Klasifikace a analýza: Druh doložky, míra rizika, závazky, podmínky
- Výstup a integrace: Strukturovaný JSON, dashboard, upozornění, integrace s CLM/DMS
Každá fáze přináší specifické výzvy. Chyba v kaskádách OCR, díky níž je NER k ničemu sofistikovanější. Nesprávná segmentace může způsobit klasifikaci prohlášení o vyloučení odpovědnosti jako jednoduchou definici. Z tohoto důvodu se měří kvalita potrubí end-to-end, ne komponenta po komponentě.
| Fáze | Vstup | Výstupy | Klíčové technologie |
|---|---|---|---|
| Získávání | PDF, TIFF, DOCX, obrázky | Normalizovaný soubor | Apache Tika, python-docx, PyPDF2 |
| OCR | Naskenovaný obrázek/PDF | Hrubý text + souřadnice | Tesseract, Textract, Document AI |
| Strukturování | Syrový text | Oddíly, věty, tabulky | Analyzátor rozložení, regulární výraz, modul pravidel |
| NER | Strukturovaný text | Komentované entity | spaCy, Legal-NER, John Snow Labs |
| Klasifikace | Izolované klauzule | Štítek + skóre spolehlivosti | LegalBERT, DeBERTa, GPT-4 |
| Výstupy | Strukturovaná data | JSON, řídicí panel, upozornění | REST API, webhooky, CLM SDK |
OCR pro právní dokumenty: Porovnání technologií
OCR je prvním úzkým hrdlem v procesu. Smlouva může dorazit jako nativní PDF (s textem volitelně) nebo jako sken papírového dokumentu. Ve druhém případě je nutné OCR znovu sestavit text z pixelů, správa proměnlivé kvality skenování, husté legální fonty, dvousloupcové rozložení, tabulky a ručně psané poznámky.
V letech 2025–2026 dominují prostředí OCR čtyři podnikové platformy a jeden open-source projekt který zůstává zásadním odkazem. Pojďme se podívat na rozdíly.
Tesseract OCR (otevřený zdroj)
Tesseract, spravovaný společností Google a nyní ve verzi 5.x, a open-source OCR engine nejrozšířenější na světě. Využívá síť LSTM pro rozpoznávání znaků a podporuje další 100 jazyků. Jeho síla a flexibilita: Lze jej integrovat do jakéhokoli potrubí Pythonu bez náklady na licence a plnou kontrolu nad předzpracováním.
Tesseract má však značná omezení u složitých právních dokumentů. Nativně to nezvládá extrakce tabulky nerozpozná strukturu rozložení (záhlaví, zápatí, sloupce) a vyžaduje značné předzpracování (binarizace, odstranění zešikmení, odstranění šumu). Přijatelné výsledky na skenech střední kvality.
Textový výpis AWS
Amazon Texttract přesahuje tradiční OCR tím, že nabízí extrakci tabulky, formulář (páry klíč–hodnota) e podpisy jako nativní funkce. Integrace s ekosystémem AWS (S3, Lambda, Step Functions) je ideální pro velkoobjemové potrubí bez serveru. Asynchronní zpracování umožňuje zpracovávat dokumenty o stovkách stránek bez časového limitu.
Azure AI Document Intelligence
Azure AI Document Intelligence (dříve Form Recognizer) nabízí předem vycvičené modely pro faktury, účtenky a doklady totožnosti, stejně jako mocný šablona rozložení že extrahuje odstavce, tabulky, výběrové značky a čárové kódy. Síla a možnost trénovat vlastní modely na dokumentech specifických pro vaši doménu, základní pro smlouvy s proprietárním uspořádáním.
AI dokumentů Google
AI dokumentů Google kombinuje extrémně vysoce přesné OCR se specializovanými procesory pro konkrétní typy dokumentů. The Procesor OCR dokumentů dosahuje přesnosti 98 % na textu, zatímco vlastní zpracovatelé mohou být vyškoleni k extrahování konkrétních polí ze standardizovaných smluv. Integrace s Vertex AI vám umožňuje budovat end-to-end ML potrubí.
Porovnání OCR motorů pro právní dokumenty
| Charakteristický | Tesseract 5 | Textový výpis AWS | Azure Doc Intelligence | AI dokumentů Google |
|---|---|---|---|---|
| Přesnost textu | 92–95 % | 96–98 % | 97–99 % | 98 %+ |
| Extrakce tabulky | Žádný domorodec | Ano (nativní) | Ano (nativní) | Ano (nativní) |
| Analýza rozložení | Základní | Dobrý | Vynikající | Vynikající |
| Vlastní modely | OCR školení | Vlastní dotazy | Vlastní modely | Vlastní procesory |
| Náklady | Zdarma (OSS) | 1,50 $/1000 stran | 1,50 $/1000 stran | 1,50 $/1000 stran |
| On-premise | Si | No | Ano (kontejner) | No |
| Podporované jazyky | 100+ | ~20 | ~300 | ~200 |
| Rukopis | Omezený | Dobrý | Dobrý | Dobrý |
| Ideální pro | Prototypování, nízký rozpočet | AWS potrubí bez serveru | EnterpriseMicrosoft | Vysoká přesnost, GCP |
Když NEPOTŘEBUJETE OCR
Mnoho moderních smluv je generováno digitálně (Word, nativní PDF). V těchto případech OCR e nadbytečné a potenciálně škodlivé: přímá extrakce textu z nativních PDF pomocí knihoven jako PyMuPDF (fitz) popř pdf instalatér a rychleji, přesněji a bez nákladů. První operace v potrubí by měla být vždy třídění: určit, zda dokument vyžaduje OCR nebo pokud je text již dostupný.
Implementace: OCR Pipeline v Pythonu
Podívejme se, jak vytvořit robustní OCR kanál v Pythonu, který zvládne nativní PDF i skenování, s automatickým předzpracováním a přechodem mezi různými motory.
Třídění a předzpracování dokumentů
Prvním krokem je určit, zda soubor PDF obsahuje volitelný text nebo zda se jedná o naskenovaný obrázek. Toto rozhodnutí řídí celý následující plynovod.
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)
Nejlepší praxe: DPI a předběžné zpracování
U právních dokumentů s malým písmem a hustým rozvržením vždy používejte rozlišení alespoň DPI 300 pro rastrování. Předzpracování (binarizace, odstranění zkosení, odstranění hran) může zlepšit přesnost OCR o 10–15 % při skenování průměrné kvality. Nicméně na skenech vysoce kvalitní agresivní předzpracování může zhoršovat se výsledky: vždy testujte na a reprezentativní vzorek.
NLP pro právní text: Specifické výzvy
Právní jazyk není obyčejný přirozený jazyk. Má vlastnosti, které dělají automatické zpracování je obzvláště obtížné, a to i u nejpokročilejších modelů.
Složitost právního jazyka
- Dlouhé a složité věty: Jedna smluvní doložka může být prodloužena 200-500 slov s více podřízenými, rytinami a křížovými odkazy. Modely transformátorů s omezeným kontextovým oknem (512 tokenů pro BERT) mohou ztratit globální význam.
- Odborná terminologie: Pojmy jako „doložka o výslovném ukončení“, „trestní“. smluvní“, „exceptio inadimpleti contractus“ mají přesný význam, který se liší od použití společných stejných slov.
- Záměrná nejednoznačnost: Některé klauzule jsou záměrně vágní, aby to umožnily flexibilní výklady. NLP musí tuto nejednoznačnost rozpoznat a nahlásit, nikoli ji vyřešit libovolně.
- Křížové odkazy: „Podle čl. 5 odst. 3 písm. b)“ vyžaduje rozlišení interních a externích odkazů na dokument.
- Několik negativ a podmínek: "S výjimkou případů uvedených v předchozím odstavci, strana nebude povinna...“ obrací význam celých bloků textu.
- Mnohojazyčnost: Mezinárodní smlouvy mohou obsahovat doložky ve více jazycích, s doložkami o jazykové prevalenci, které přidávají další úroveň složitosti.
Modely NLP pro právní doménu
Obecné jazykové modely (BERT, RoBERTa) nemají optimální výkon na právní text protože jejich předškolní příprava je založena na Wikipedii, knihách a obecných webových stránkách. To je důvod, proč se narodili modely doménově specifické vyškoleni na právnické korpusy.
| Model | Základní | Školicí korpus | Jazyky | Hlavní použití |
|---|---|---|---|---|
| Právní - BERT | BERT-základní | 12GB právní texty (EU, Spojené království, USA) | angličtina | NER, klasifikace doložek |
| Judikatura-BERT | BERT-základní | Americká judikatura (Harvard) | angličtina | Právní výzkum |
| Italsko-právní-BERT | BERT-základní | IT legislativa a judikatura | italština | NER italské právnické osoby |
| LegalPro-BERT | BERT-základní | Specializovaná právní ustanovení | angličtina | Klasifikace rezerv |
| DeBERTa-v3-legální | DeBERTa-v3 | Smlouvy + legislativa | angličtina | Odpověď na právní otázku |
| John Snow Labs Legal NLP | Spark NLP | Více než 600 šablon specifických pro právní záležitosti | Vícejazyčný | Kompletní podnikový kanál |
LegalBERT dolaďování konkrétních datových sad, jako je CUAD (Contract Understanding Atticus Dataset) dosahuje skóre F1 83–88 % při klasifikaci 41 typů smluvních doložek, 12-15% zlepšení oproti generickému BERT při stejném úkolu. Výběr základního modelu a stejně důležité jako kvalita dolaďovacích dat.
Uznání pojmenované entity pro smlouvy
Il Rozpoznávání pojmenované entity (NER) a fázi, ve které systém identifikuje a klasifikuje klíčové subjekty ve smluvním textu. Na rozdíl od generického NER, který rozpoznává lidi, místa a organizace, právní NER potřebuje extrahovat entity specifické pro doménu.
Klíčové právnické osoby
| Entita | Popis | Příklad |
|---|---|---|
| STRANA | Smluvní strany | "Acme S.p.A." (dodavatel), "Beta S.r.l." (Zákazník) |
| DATA | Příslušná data | Datum podpisu, datum účinnosti, vypršení platnosti, obnovení |
| MNOŽSTVÍ | Peněžní částky | "150 000,00 EUR ročně", "50 000 USD" |
| TRVÁNÍ | Časová období | "36 měsíců", "3 roky od data podpisu" |
| CLAUSE_REF | Odkazy na doložky | "čl. 5 odst. 3", "oddíl 12.1" |
| LAW_REF | Regulační odkazy | "Legislativní vyhláška 50/2016", "čl. 1341 c.c." |
| POVINNOST | Smluvní závazky | "Dodavatel se zavazuje dodat..." |
| JURISDIKCE | Příslušný soud | "Milánský soud" |
| VLÁDNÍ_PRÁVO | Rozhodné právo | "Italské právo", "Zákony Anglie" |
Implementace NER s spaCy
spaCy nabízí flexibilní rámec pro stavbu vlastních NER potrubí. Podívejme se, jak vytvořit specifický model NER pro italské smlouvy, který kombinuje deterministická pravidla se strojovým učením.
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}")
Očekávaný výstup
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
Klasifikace vět s transformátorem
Klasifikace doložek je srdcem automatické analýzy smluv. Každá klauzule je přiřazena do kategorie (odpovědnost, důvěrnost, ukončení, sankce atd.). volitelně hodnocena na míru rizika. Tento proces tradičně trvá hodiny legální práce: vyškolený model Transformer to zvládne během několika sekund.
Datová sada CUAD
Il Contract Understanding Atticus Dataset (CUAD) a referenční benchmark pro smluvní klasifikace. Vytvořil The Atticus Project s příspěvky desítek právníků, obsahuje více 13 000 anotací na 510 reálných obchodních smluv, pokrývajících 41 typů doložek relevantních pro transakce M&A a due diligence.
41 kategorií CUAD (výběr)
- Datum dohody
- Večírky
- Rozhodné právo
- Ukončení z důvodu pohodlí
- Anti-Asignment
- Nesoutěžit
- Nežádání
- Exkluzivita
- Odškodnění
- Omezení odpovědnosti
- Přidělení vlastnictví IP
- Udělení licence
- Smlouva o mlčenlivosti
- Sdílení příjmů/zisku
- Cenová omezení
- Minimální závazek
- Omezení hlasitosti
- Pojištění
- Práva auditu
- Změna ovládání
Jemné doladění LegalBERT pro klasifikaci doložek
Podívejme se, jak trénovat klasifikátor klauzule pomocí LegalBERT a datové sady CUAD. Proces zahrnuje jemné doladění předem připraveného modelu na anotovaných příkladech klauzulí smluvní.
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
Limity jemného ladění na CUAD
Dataset CUAD je zaměřen na komerční smlouvy v anglický jazyk a v kontextu americké M&A. Pro italské, evropské nebo jiné smlouvy o jurisdikci je to nezbytné vytvářet nebo upravovat konkrétní datové sady. Efektivní přístup je dvoustupňové přenosové učení: jemné doladění na CUAD, abyste se naučili obecnou strukturu smlouvy, pak další doladění na a malá datová sada (200–500 příkladů) vaší konkrétní domény.
Detekce závazků a sémantická analýza
Kromě klasifikace klauzulí musí systém smluvní AI identifikovat závazky smluvní: kdo má co udělat, do kdy a jaké to má důsledky nesouladu. Tento úkol je známý jako Deontic Modality Detection v Právní NLP literatura.
Tři deontické modality
- Povinnost (MUSÍ/MUSÍ): "Dodavatel mošt dodání zboží do 30 dnů"
- Povolení (MAY/CAN): "Zákazník může požadovat změny v projektu"
- Zákaz (NESMÍ/NESMÍ): „Večírky nemohou postoupit smlouvu třetím stranám"
Implementace s klasifikací Zero-Shot
Pro sledování závazků je efektivní přístup klasifikace zero-shot s modely jako DeBERTa, které nevyžadují specifická anotovaná trénovací data.
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]}...")
Sémantická podobnost mezi větami
Další zásadní aplikací je sémantické srovnávání mezi větnými členy. Když společnost obdrží nový smlouvy, chce vědět: „Toto vyloučení odpovědnosti je podobné nebo odlišné od našeho standardní klauzule?" Tam sémantickou podobnost na základě vložení umožňuje toto srovnání.
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()
End-to-End Pipeline: Od OCR po Insight
Nyní integrujeme všechny komponenty do kompletního potrubí, které zahrnuje smluvní PDF a výstupy strukturovanou zprávu se subjekty, utajovanými doložkami a identifikovanými závazky.
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
))
Platformy LegalTech pro smluvní AI
Na trhu LegalTech v roce 2026 nabízí několik platforem řešení pro analýzu smluv na bázi umělé inteligence. Finanční prostředky v sektoru dosáhly 4,3 miliardy dolarů v roce 2025, zvýšení o 54 % oproti roku 2024. Podívejme se na hlavní platformy a jejich technologické přístupy.
| Platforma | Specializace | technologie AI | Typičtí uživatelé | Orientační cena |
|---|---|---|---|---|
| Kira Systems (Literární) | Due Diligence fúzí a akvizic | Hybridní ML + generativní AI | 70+ ze 100 nejlepších advokátních kanceláří | Enterprise (vlastní) |
| Jas | Detekce anomálií, compliance | Proprietární legální AI | Právnické firmy, korporátní | Od 500 $/měsíc |
| Opláštěný | Správa životního cyklu smlouvy | AI Assist pro kontrolu a vyjednávání | Právní operace, zadávání zakázek | Od 300 $/měsíc |
| Kniha kouzel | Asistované navrhování a revize | GPT-4 + právní jemné doladění | Právníci, asistenti | Od 400 $/měsíc |
| LegalFly | Zkontrolujte vícejazyčné smlouvy | LLM + Specialized NER | Mezinárodní studia | Od 200 $/měsíc |
| Sirion | Podnik smluvního zpravodajství | AI pro extrakci a analýzu | Fortune 500, nákup | Enterprise (vlastní) |
| John Snow Labs | NLP potrubí (600+ modelů) | Spark NLP + Legal NLP | Tým vědy o datech | Open source + Enterprise |
Kira Systems: Reference pro due diligence
Kira Systems, nyní součástí Litery a využívá jej asi 80 % z 25 nejlepších studií právníci specializující se na M&A po celém světě. Jeho hybridní přístup kombinuje proprietární modely ML vyškoleni na více než milion kontraktů s generativními schopnostmi pro syntézu a analýzu. The Síla Kiry spočívá v její schopnosti extrahovat více než 1 000 typů předdefinovaných klauzulí (vestavěná ustanovení) a v možnosti školení vlastních modelů na konkrétní zákaznické doložky během několika hodin.
Luminance: Detekce anomálií pro smlouvy
Jas vyniká svou schopností identifikovat anomálie ve smlouvách: doložky, které se odchylují od standardu, neobvyklé podmínky, chybějící podmínky. Jeho architektura AI na právní úrovni, aktualizovaný na začátku roku 2026, spojuje historii jednání, právní úvahy a obchodní rozhodnutí v průběhu celého životního cyklu smlouvy. A zvláště silné v přeshraničních recenzích, kde jsou rozdíly mezi jurisdikce vytvářejí skrytá rizika.
Metriky hodnocení a osvědčené postupy
Hodnocení smluvního systému AI vyžaduje specifické metriky pro každou fázi procesu. Systém který dosahuje 98% přesnosti OCR, ale pouze 60% klasifikace doložky a méně užitečnější než ten s 95 % OCR a 85 % klasifikace.
Metriky podle fáze
| Fáze | Hlavní metrika | Přijatelný práh | Vynikající prahová hodnota |
|---|---|---|---|
| OCR | Míra chybovosti znaků (CER) | < 5 % | < 1 % |
| Segmentace | Hranice F1 | > 80 % | > 92 % |
| NER | Entita F1 (přísná) | > 75 % | > 88 % |
| Klasifikace vět | Makro F1 | > 78 % | > 88 % |
| Detekce povinností | F1 pro deontickou třídu | > 72 % | > 85 % |
| End-to-End | Míra dokončení úkolu | > 70 % | > 90 % |
Nejlepší postupy pro smluvní projekty umělé inteligence
10 zlatých pravidel
- Začněte tříděním: Neaplikujte OCR na nativní soubory PDF. 60-70 % smluv moderní podnikání a již v digitálním formátu.
- Investujte do předběžného zpracování: Kvalita OCR určuje maximální limit výkon celého potrubí. Hodina investovaná do předběžného zpracování ušetří deset v NER.
- Použijte šablony specifické pro doménu: LegalBERT překonává generický BERT o 12–15 %. právní úkoly. Náklady na doladění jsou ve srovnání se ziskem marginální.
- Kombinujte pravidla a ML: Deterministická pravidla (regulární výraz pro data, částky, normativní odkazy) jsou pro strukturované vzory přesnější a srozumitelnější než ML.
- End-to-end měření: Perfektní a zbytečný NER, pokud je segmentace špatná. Důležitá je metrika Míra dokončení úkolu: kolik klauzulí má koncový uživatel přijmout bez oprav.
- Člověk ve smyčce: AI nenahrazuje právníka, ale zrychluje ho. Předvídat vždy kontrolní rozhraní, kde odborník potvrzuje, opravuje a zlepšuje předpovědi.
- Verze modelů: Smlouvy se vyvíjejí (vzpomeňte si na GDPR nebo např směrnice NIS2). Modely je třeba pravidelně přeškolovat na aktualizovaných datech.
- Ochrana soukromí již od návrhu: Smlouvy obsahují citlivé údaje (částky, strany, obchodní podmínky). Proces on-premise nebo se smluvními zárukami od poskytovatele cloudu.
- Začněte s konkrétním případem použití: Nesnažte se budovat systém univerzální. Začněte od typu smlouvy (např. NDA, dodávka) a úkolu (např termín) a prokázat hodnotu před rozšířením.
- Sledovat posun dat: Smluvní jazyk se vyvíjí. klauzule o AI, ESG, odolnost dodavatelského řetězce se stala běžnou teprve v posledních 2-3 letech. Modelka musí být monitorovány, aby se zjistilo snížení výkonu.
Vzor integrace s obchodními systémy
Systém smluvní umělé inteligence je hodnotný pouze tehdy, je-li integrován do stávajících obchodních procesů. Výstup potrubí musí proudit do Správa životního cyklu smlouvy (CLM), Systémy správy dokumentů (DMS) e schvalovací pracovní postup.
Integrační architektura
- Na základě událostí: Načtením nové smlouvy do DMS se tato automaticky aktivuje analytický kanál prostřednictvím webhooku nebo fronty zpráv (RabbitMQ, SQS).
- REST API: Koncový bod pro předkládání dokumentů a přijímání strukturovaných výsledků ve formátu JSON, kompatibilní s jakýmkoli CLM.
- Dávkové zpracování: Pro migraci historických smluvních archivů, s asynchronní zpracování a oznámení o dokončení.
- Řídicí panely: Webové rozhraní pro prohlížení výsledků, kontrolu predikce a zpětná vazba pro neustálé zlepšování modelu.
Standardy a výstupní formáty
Pro maximalizaci interoperability by měl výstup potrubí odpovídat otevřeným standardům: OASIS LegalDocML (Akoma Ntoso) za dokumentární strukturu, SALI (pokročilý standard pro právní průmysl) pro taxonomii klauzule, e JSON-LD pro strukturovaná metadata. Tyto normy umožňují integrace se systémy třetích stran bez vlastního mapování.
Závěry a další kroky
Automatická analýza smluv s NLP již není akademickým výzkumným projektem: je to a vyspělá technologie, kterou denně používají přední advokátní kanceláře a právní oddělení společnosti na světě. Potrubí, které jsme vybudovali v tomto článku, od OCR k porozumění sémantika, představuje základní architekturu, na které jsou založeny všechny platformy LegalTech moderní.
Klíčové body k zapamatování:
- Potrubí a vícestupňové: OCR → strukturování → NER → klasifikace → sémantická analýza. Každá fáze má specifické technologie a metriky.
- I doménově specifické modely (LegalBERT, CaseLaw-BERT) výrazně překonávají obecné modely právních úkolů. Jemné ladění datových sad, jako je CUAD, je také přístupné tým s omezenými zdroji.
- Přístup hybridní pravidla + ML a nejúčinnější: pravidla pro strukturované entity (data, částky, odkazy), ML pro sémantiku a klasifikaci.
- Il human-in-the-loop není to kompromis, ale nutnost: AI zrychluje legální práce to nenahrazuje. Pro validaci je nezbytný lidský dohled a neustálé zlepšování.
- L'integrace s CLM, DMS a obchodními pracovními postupy je pro transformaci zásadní technická analýza na obchodní hodnotu.
V dalším článku série prozkoumáme právní výzkum s AI, vidět, jak sémantické vyhledávače a znalostní grafy mění způsob právníci a právníci mají přístup k judikatuře a doktríně.
Zdroje, kde se dozvíte více
- Datové sady CUAD: github.com/TheAtticusProject/cuad – 510 anotovaných smluv, 41 typů doložek
- LegalBERT: HuggingFace nlpaueb/legal-bert-base-uncased - právní předtrénovaný model
- spaCy Legal NER: github.com/openlegaldata/legal-ner - Open source NER pro právní texty
- John Snow Labs Legal NLP: Více než 600 šablon pro kompletní podnikové kanály
- OASIS LegalDocML: Standard XML pro strukturované právní dokumenty







