계약 분석을 위한 NLP: OCR에서 이해까지
매년 전 세계 기업은 수십억 건의 계약을 관리합니다. 공급 계약, 조항 기밀 유지, 서비스 약관, 고용 계약: 전통적으로 법적 텍스트의 바다 변호사와 법률 보조원이 수백 시간에 걸쳐 직접 검토해야 합니다. 2026년 시장은 글로벌 리걸테크 수요에 힘입어 350억 달러 초과 법률 문서 관리의 자동화가 증가하고 있습니다.
진정한 혁명은 단순히 문서를 디지털화하는 데 있는 것이 아니라 그들을 이해하다. 80페이지 계약서의 스캔된 PDF를 구조화된 데이터로 변환하고 자동으로 추출합니다. 계약 당사자, 의무, 기한 및 위험 조항: 이는 자연어 처리(NLP) 법적 도메인에 적용됩니다.
이 기사에서 우리는 시리즈를 시작합니다 법률기술과 AI 전체 파이프라인 탐색 종이 문서를 실행 가능한 지식으로 변환합니다.OCR (광학 문자 인식)알 NER (명명된 엔터티 인식), from 분류 조항 alla 의미론적 이해. 실제 Python 코드를 보고 비교해보겠습니다. 주요 OCR 엔진과 NLP 모델을 분석하고 그들이 재정의하고 있는 LegalTech 플랫폼을 분석하겠습니다. 부문.
이 기사에서 배울 내용
- 전체 계약 분석 파이프라인: 물리적 문서 → OCR → NLP → 구조화된 통찰력
- OCR 엔진 비교: Tesseract, AWS Textract, Azure Document Intelligence, Google Document AI
- 문서 전처리: 레이아웃 분석, 테이블 추출, 페이지 분할
- 계약에 대한 명명된 개체 인식(NER): 당사자, 날짜, 금액, 조항
- Transformer 모델을 사용한 조항 분류(LegalBERT, DeBERTa)
- spaCy, HuggingFace Transformers 및 Tesseract를 사용한 완벽한 Python 구현
- 실제 LegalTech 플랫폼: Kira Systems, Luminance, Ironclad
- Contract AI 프로젝트에 대한 평가 지표 및 모범 사례
LegalTech 및 AI 시리즈 개요
| # | Articolo | 집중하다 |
|---|---|---|
| 1 | 현재 위치 — 계약 분석을 위한 NLP | OCR부터 이해까지 |
| 2 | AI를 활용한 법학 연구 | 의미 검색 및 지식 그래프 |
| 3 | 스마트 계약과 블록체인 | 블록체인의 계약 자동화 |
| 4 | 법률 문서 요약 | LLM을 통한 자동 요약 |
| 5 | AI를 갖춘 규정 준수 엔진 | 자동화된 규정 준수 |
| 6 | e-Discovery 및 법의학 | 분쟁에 대한 문서 분석 |
| 7 | 전자 서명 및 디지털 신원 | eIDAS 2.0 및 SPID |
| 8 | GDPR 및 개인정보 보호 설계 | AI를 통한 개인정보 보호 준수 |
| 9 | 법률 부조종사 | 로펌을 위한 AI 비서 |
| 10 | 법률 데이터 통합 | 상호 운용성 및 표준 |
계약 분석 파이프라인
계약의 자동 분석은 단일 알고리즘이 아닌 하나의 알고리즘입니다. 다단계 파이프라인 각 구성 요소가 다음 구성 요소에 전원을 공급합니다. 전체적이고 기본적인 아키텍처를 먼저 이해하세요. 각 단계의 기술적 세부 사항을 자세히 살펴보세요.
파이프라인의 6단계
- 인수: 문서가 시스템에 입력됩니다(스캔, 업로드, 이메일, DMS API)
- 전처리 및 OCR: 레이아웃 분석, 기울기 조정, 이진화, 텍스트 추출
- 구조화: 섹션, 단락, 절, 표로 세분화
- 엔터티 추출(NER): 부품, 날짜, 수량, 규정 참조 식별
- 분류 및 분석: 조항의 종류, 위험 수준, 의무, 조건
- 출력 및 통합: 구조화된 JSON, 대시보드, 알림, CLM/DMS와의 통합
각 단계에는 특정 과제가 발생합니다. NER를 쓸모 없게 만드는 OCR 계단식 오류 더 정교해졌습니다. 잘못된 세분화로 인해 고지 사항이 분류될 수 있습니다. 간단한 정의로. 이러한 이유로 파이프라인의 품질이 측정됩니다. 엔드투엔드, 구성 요소별로 구성되지 않습니다.
| 단계 | 입력 | 출력 | 핵심기술 |
|---|---|---|---|
| 인수 | PDF, TIFF, DOCX, 이미지 | 정규화된 파일 | 아파치 티카, python-docx, PyPDF2 |
| OCR | 스캔 이미지/PDF | 원시 텍스트 + 좌표 | Tesseract, Textract, Document AI |
| 구조화 | 원시 텍스트 | 섹션, 조항, 테이블 | 레이아웃 파서, 정규식, 규칙 엔진 |
| NER | 구조화된 텍스트 | 주석이 달린 엔터티 | spaCy, Legal-NER, John Snow Labs |
| 분류 | 분리된 조항 | 라벨 + 신뢰도 점수 | 법률BERT, DeBERTa, GPT-4 |
| 출력 | 구조화된 데이터 | JSON, 대시보드, 경고 | REST API, 웹훅, CLM SDK |
법률 문서용 OCR: 기술 비교
OCR은 파이프라인의 첫 번째 병목 현상입니다. 계약서는 기본 PDF(텍스트 포함)로 도착할 수 있습니다. 선택 가능) 또는 종이 문서의 스캔으로. 두 번째 경우에는 OCR을 다시 작성해야 합니다. 픽셀의 텍스트, 다양한 스캔 품질 관리, 조밀한 법적 글꼴, 이중 열 레이아웃, 테이블 및 손으로 쓴 주석.
2025~2026년 OCR 환경은 4개의 엔터프라이즈 플랫폼과 1개의 오픈 소스 프로젝트가 지배합니다. 이는 필수적인 참고 자료로 남아 있습니다. 차이점을 살펴보겠습니다.
Tesseract OCR(오픈 소스)
테서렉트, Google에서 관리하고 현재 버전 5.x이며 오픈 소스 OCR 엔진 세계에서 가장 널리 퍼져 있습니다. 문자 인식을 위해 LSTM 네트워크를 사용하고 그 이상을 지원합니다. 100개 언어. 강점과 유연성: 별도의 작업 없이 모든 Python 파이프라인에 통합될 수 있습니다. 라이센스 비용을 절감하고 전처리를 완벽하게 제어할 수 있습니다.
그러나 Tesseract는 복잡한 법률 문서에 상당한 제한을 가지고 있습니다. 기본적으로 처리되지 않습니다. 테이블 추출이 레이아웃 구조(머리글, 바닥글, 열)를 인식하지 못합니다. 달성하려면 상당한 전처리(이진화, 기울기 보정, 노이즈 제거)가 필요합니다. 중간 품질 스캔에서 허용 가능한 결과입니다.
AWS 텍스트트랙트
아마존 텍스트트랙 추출 기능을 제공함으로써 전통적인 OCR을 뛰어넘습니다. 테이블, 형식(키-값 쌍) e 서명 기본 기능으로. 통합 AWS 에코시스템(S3, Lambda, Step Functions)을 사용하면 대용량 서버리스 파이프라인에 이상적입니다. 비동기식 처리를 통해 수백 페이지에 달하는 문서를 시간 초과 없이 처리할 수 있습니다.
Azure AI 문서 인텔리전스
Azure AI 문서 인텔리전스 (이전의 Form Recognizer)는 사전 훈련된 모델을 제공합니다. 송장, 영수증, 신분 증명서뿐만 아니라 강력한 레이아웃 템플릿 그 단락, 표, 선택 표시 및 바코드를 추출합니다. 힘과 가능성 훈련하다 맞춤 모델 귀하의 도메인과 관련된 문서에 대한 기본 독점 레이아웃이 있는 계약의 경우.
구글 문서 AI
구글 문서 AI 초고정밀 OCR과 특수 프로세서를 결합합니다. 특정 문서 유형의 경우. 그만큼 문서 OCR 프로세서 정밀도 달성 텍스트의 경우 98%, 맞춤형 프로세서는 특정 필드를 추출하도록 학습 가능 표준화된 계약을 통해 Vertex AI와 통합하면 엔드 투 엔드 ML 파이프라인을 구축할 수 있습니다.
법률 문서용 OCR 엔진 비교
| 특성 | 테서렉트 5 | AWS 텍스트트랙트 | Azure 문서 인텔리전스 | 구글 문서 AI |
|---|---|---|---|---|
| 텍스트 정확도 | 92-95% | 96-98% | 97-99% | 98%+ |
| 테이블 추출 | 네이티브 없음 | 예(기본) | 예(기본) | 예(기본) |
| 레이아웃 분석 | 기초적인 | 좋은 | 훌륭한 | 훌륭한 |
| 맞춤형 모델 | OCR 훈련 | 맞춤 쿼리 | 맞춤형 모델 | 맞춤형 프로세서 |
| 비용 | 무료(OSS) | $1.50/1000페이지 | $1.50/1000페이지 | $1.50/1000페이지 |
| 온프레미스 | Si | No | 예(컨테이너) | No |
| 지원되는 언어 | 100+ | ~20 | ~300 | ~200 |
| 필적 | 제한된 | 좋은 | 좋은 | 좋은 |
| 다음에 이상적입니다. | 프로토타입 제작, 저예산 | AWS 서버리스 파이프라인 | 엔터프라이즈마이크로소프트 | 고정밀, GCP |
OCR이 필요하지 않은 경우
많은 현대 계약은 디지털 방식으로 생성됩니다(Word, 기본 PDF). 이러한 경우 OCR e 불필요하고 잠재적으로 해로울 수 있음: 다음과 같은 라이브러리를 사용하여 기본 PDF에서 직접 텍스트 추출 PyMuPDF (핏츠) 또는 pdf배관공 더 빠르고 정확하며 비용도 들지 않습니다. 파이프라인의 첫 번째 작업은 항상 분류: 여부를 결정 문서에 OCR이 필요하거나 텍스트가 이미 사용 가능한 경우.
구현: Python의 OCR 파이프라인
기본 PDF와 스캔을 모두 처리하는 Python에서 강력한 OCR 파이프라인을 구축하는 방법을 살펴보겠습니다. 자동 전처리 및 서로 다른 엔진 간의 대체 기능을 사용합니다.
문서 분류 및 전처리
첫 번째 단계는 PDF에 선택 가능한 텍스트가 포함되어 있는지 또는 스캔한 이미지인지 확인하는 것입니다. 이 결정은 전체 후속 파이프라인을 주도합니다.
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)
모범 사례: DPI 및 전처리
글꼴이 작고 레이아웃이 조밀한 법률 문서의 경우 항상 최소 DPI를 사용하십시오. 300 래스터화를 위해. 전처리(이진화, 기울기 보정, 가장자리 제거) 평균 품질 스캔에서 OCR 정확도를 10-15% 향상시킬 수 있습니다. 그러나 스캔에서는 고품질의 공격적인 전처리가 가능합니다. 악화되다 결과: 항상 테스트해 보세요. 대표샘플.
법률 텍스트를 위한 NLP: 구체적인 과제
법률언어는 일반적인 자연어가 아닙니다. 만들어주는 기능이 있습니다 자동 처리는 가장 진보된 모델의 경우에도 특히 어렵습니다.
법률 언어의 복잡성
- 길고 복잡한 문장: 단일 계약 조항은 다음과 같이 확장될 수 있습니다. 여러 하위 항목, 판화 및 상호 참조가 포함된 200-500 단어. 변압기 모델 제한된 컨텍스트 창(BERT의 경우 512개 토큰)을 사용하면 전역적 의미를 잃을 수 있습니다.
- 전문 용어: '명시적 해지 조항', '형사적 해지 조항' 등의 용어 계약상의", "예외 inadimpleti contractus"는 사용법과 다른 정확한 의미를 갖습니다. 같은 단어의 공통점.
- 의도적인 모호성: 일부 조항은 의도적으로 모호하여 허용됩니다. 유연한 해석. NLP는 이러한 모호성을 해결하는 것이 아니라 이를 인식하고 보고해야 합니다. 임의로.
- 상호 참조: "제5조 3항 b)항에 따라"는 다음과 같이 요구됩니다. 문서에 대한 내부 및 외부 참조의 해결.
- 여러 부정문 및 조건: "이전 단락에 규정된 경우를 제외하고, 당사자는..."을 요구하지 않습니다."는 전체 텍스트 블록의 의미를 뒤집습니다.
- 다국어 사용: 국제 계약에는 여러 언어로 된 조항이 포함될 수 있습니다. 더욱 복잡한 수준을 추가하는 언어적 보급 조항이 포함되어 있습니다.
법률 분야의 NLP 모델
일반 언어 모델(BERT, RoBERTa)은 법률 텍스트에서 최적의 성능을 발휘하지 못합니다. 사전 훈련은 Wikipedia, 서적 및 일반 웹 페이지를 기반으로 하기 때문입니다. 이것이 그들이 태어난 이유이다 모델 도메인별 법률 말뭉치에 대한 교육을 받았습니다.
| 모델 | 기초적인 | 훈련 코퍼스 | 언어 | 주요 용도 |
|---|---|---|---|---|
| 법적-BERT | BERT 기본 | 12GB 법률 텍스트(EU, 영국, 미국) | 영어 | NER, 조항 분류 |
| CaseLaw-BERT | BERT 기본 | 미국 판례법(하버드) | 영어 | 법학 연구 |
| 이탈리아어-법률-BERT | BERT 기본 | IT 입법 및 법학 | 이탈리아 사람 | NER 이탈리아 법인 |
| 법적Pro-BERT | BERT 기본 | 전문법령 조항 | 영어 | 조항의 분류 |
| DeBERTa-v3-법적 | DeBERTa-v3 | 계약 + 입법 | 영어 | 법적 질문 답변 |
| John Snow Labs 법적 NLP | 스파크 NLP | 600개 이상의 법률 관련 템플릿 | 다국어 | 완전한 엔터프라이즈 파이프라인 |
CUAD(Contract Understanding Atticus Dataset)와 같은 특정 데이터세트에 대한 LegalBERT 미세 조정 41개 유형의 계약 조항 분류에서 F1 점수 83~88%에 도달했으며, 동일한 작업에서 일반 BERT에 비해 12-15% 향상되었습니다. 기본 모델의 선택 미세 조정 데이터의 품질만큼 중요합니다.
계약에 대한 명명된 엔터티 인식
Il 명명된 엔터티 인식(NER) 시스템이 식별하고 분류하는 단계 계약서의 핵심 요소. 사람, 장소를 인식하는 일반 NER와는 달리 및 조직, 법적 NER는 도메인별 엔터티를 추출해야 합니다.
주요 법인
| 실재 | 설명 | Esempio |
|---|---|---|
| 파티 | 계약 당사자 | "Acme S.p.A." (공급자), "Beta S.r.l." (고객) |
| 날짜 | 관련 날짜 | 서명 날짜, 발효 날짜, 만료, 갱신 |
| AMOUNT | 금액 | "연간 EUR 150,000.00", "50,000 USD" |
| 지속 | 기간 | "36개월", "서명일로부터 3년" |
| CLAUSE_REF | 조항에 대한 참조 | "제5조 3항", "12.1항" |
| LAW_REF | 규제 참조 | "입법령 50/2016", "Art. 1341 c.c." |
| 의무 | 계약상 의무 | "공급업체는 배송을 약속합니다..." |
| 관할권 | 관할법원 | "밀라노 코트" |
| GOVERNING_LAW | 준거법 | "이탈리아 법률", "영국 법률" |
spaCy를 사용한 NER 구현
spaCy는 맞춤형 NER 파이프라인 구축을 위한 유연한 프레임워크를 제공합니다. 어떻게 생성하는지 살펴보자 결정론적 규칙과 기계 학습을 결합한 이탈리아 계약을 위한 특정 NER 모델입니다.
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}")
예상 출력
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
Transformer를 사용한 조항 분류
조항 분류는 자동 계약 분석의 핵심입니다. 모든 조항 범주(책임, 기밀 유지, 해고, 처벌 등)에 할당됩니다. e 선택적으로 위험 수준을 평가합니다. 이 프로세스는 일반적으로 몇 시간이 걸립니다. 법적 업무: 훈련된 Transformer 모델이 단 몇 초 만에 작업을 수행합니다.
CUAD 데이터세트
Il 계약 이해 Atticus 데이터세트(CUAD) 및 참조 벤치마크 계약상의 분류. 수십 명의 변호사들의 기여로 Atticus Project에서 제작한 이 앱은 더 많이 포함되어 있습니다 주석 13,000개 510개의 실제 비즈니스 계약에 대해 M&A 거래 및 실사와 관련된 조항 41종.
41개 CUAD 카테고리(선택)
- 계약 날짜
- 당사자
- 준거법
- 편의를 위한 종료
- 할당 방지
- 비경쟁
- 비요청
- 독점성
- 보증
- 책임의 제한
- IP 소유권 할당
- 라이센스 부여
- 비공개 계약
- 수익/이익 공유
- 가격 제한
- 최소 약정
- 볼륨 제한
- 보험
- 감사 권한
- 통제권 변경
조항 분류를 위한 LegalBERT 미세 조정
LegalBERT와 CUAD 데이터 세트를 사용하여 조항 분류기를 훈련하는 방법을 살펴보겠습니다. 이 프로세스에는 주석이 달린 조항 예에 대해 사전 훈련된 모델을 미세 조정하는 작업이 포함됩니다. 계약적.
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
CUAD 미세 조정의 한계
CUAD 데이터 세트는 상업 계약에 중점을 두고 있습니다. 영어 그리고 맥락상 미국 M&A. 이탈리아, 유럽 또는 기타 관할권 계약의 경우 이는 필요합니다. 특정 데이터 세트를 생성하거나 조정합니다. 효과적인 접근 방식은 2단계 전이 학습: CUAD를 미세 조정하여 일반적인 계약 구조를 학습한 다음 특정 도메인의 소규모 데이터 세트(200-500개 예)
의무 감지 및 의미 분석
조항을 분류하는 것 외에도 계약 AI 시스템은 조항을 식별해야 합니다. 의무 계약상의: 누가 언제까지 무엇을 해야 하며 그 결과는 무엇인가 비준수. 이 작업은 다음과 같이 알려져 있습니다. Deontic 양식 감지 에서 법적 NLP 문헌.
세 가지 데온적 양식
- 의무(반드시/해야 함): "공급자 ~ 해야 하다 30일 이내에 상품을 배송하세요."
- 허가(5월/CAN): "고객 ~할 수 있다 프로젝트 변경을 요청하세요"
- 금지 사항(반드시/해서는 안 됨): "당사자들 그들은 할 수 없다 계약을 제3자에게 양도하다"
제로샷 분류를 통한 구현
의무 추적의 경우 효과적인 접근 방식은 다음과 같습니다. 제로샷 분류 특정 주석이 달린 훈련 데이터가 필요하지 않은 DeBERTa와 같은 모델을 사용합니다.
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]}...")
조항 간의 의미적 유사성
또 다른 중요한 적용은 절 간의 의미 비교입니다. 회사에서 새로운 정보를 받았을 때 계약서, 알고 싶습니다: "이 면책 조항은 당사의 면책 조항과 유사하거나 다릅니다. 표준 조항?" 거기 의미론적 유사성 임베딩을 기반으로 이러한 비교가 가능합니다.
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()
엔드투엔드 파이프라인: OCR에서 Insight까지
이제 모든 구성요소를 계약서 PDF를 가져와서 출력하는 완전한 파이프라인에 통합합니다. 엔터티, 분류된 조항 및 식별된 의무가 포함된 구조화된 보고서입니다.
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
))
Contract AI를 위한 LegalTech 플랫폼
2026년 LegalTech 시장에서는 여러 플랫폼이 AI 기반 계약 분석 솔루션을 제공합니다. 해당 부문의 자금이 도달했습니다. 2025년 43억 달러, 증가 2024년 대비 54% 증가했습니다. 주요 플랫폼과 기술적 접근 방식을 살펴보겠습니다.
| 플랫폼 | 전문화 | AI 기술 | 일반 사용자 | 참고 가격 |
|---|---|---|---|---|
| 키라시스템즈 (문호) | M&A 실사 | 하이브리드 ML + 생성 AI | 상위 100개 로펌 중 70개 이상 | 엔터프라이즈(커스텀) |
| 휘도 | 이상 탐지, 규정 준수 | 독점적인 법률 수준의 AI | 법률회사, 기업 | 월 $500부터 |
| 철갑 | 계약 수명주기 관리 | 검토 및 협상을 위한 AI 지원 | 법률 작전, 조달 | 월 $300부터 |
| 마법서 | 초안 작성 및 검토 지원 | GPT-4 + 법적 미세 조정 | 변호사, 법무사 | 월 $400부터 |
| LegalFly | 다국어 계약 검토 | LLM + 전문 NER | 국제학 | 월 $200부터 |
| 시리온 | 계약 정보 기업 | 추출 및 분석을 위한 AI | Fortune 500대 기업, 조달 | 엔터프라이즈(커스텀) |
| 존 스노우 연구소 | NLP 파이프라인(600개 이상의 모델) | 스파크 NLP + 법적 NLP | 데이터 과학팀 | 오픈소스 + 엔터프라이즈 |
Kira Systems: 실사를 위한 참고 자료
키라시스템즈, 현재 Litera의 일부이며 상위 25개 스튜디오 중 약 80%에서 사용됩니다. 전세계 M&A 전문 변호사. 독점 ML 모델을 결합한 하이브리드 접근 방식 합성 및 분석을 위한 생성 기능을 갖춘 백만 개가 넘는 계약에 대한 교육을 받았습니다. 는 Kira의 강점은 1,000가지 이상의 사전 정의된 조항(내장 조항)을 추출하는 능력에 있습니다. 몇 시간 안에 특정 고객 조항에 대한 맞춤형 모델을 교육할 수 있습니다.
휘도: 계약에 대한 이상 탐지
휘도 식별하는 능력이 뛰어납니다. 변칙 계약서: 표준에서 벗어난 조항, 비정상적인 조건, 누락된 조건. 그의 건축 법적인 수준의 AI, 2026년 초에 업데이트되어 전체 수명주기에 걸친 협상, 법적 추론 및 비즈니스 결정 계약의. 특히 국경 간 검토에서 강력합니다. 관할권은 숨겨진 위험을 만듭니다.
평가 지표 및 모범 사례
Contract AI 시스템을 평가하려면 파이프라인의 각 단계에 대한 특정 지표가 필요합니다. 시스템 OCR 정확도는 98%이지만 조항 분류에서는 60% 이하입니다. OCR이 95%이고 분류가 85%인 것보다 유용합니다.
단계별 지표
| 단계 | 주요 지표 | 허용되는 임계값 | 우수한 임계값 |
|---|---|---|---|
| OCR | 문자 오류율(CER) | < 5% | < 1% |
| 분할 | 경계 F1 | > 80% | > 92% |
| NER | 엔터티 F1(엄격) | > 75% | > 88% |
| 조항의 분류 | F1 매크로 | > 78% | > 88% |
| 의무 감지 | Deontic 클래스의 경우 F1 | > 72% | > 85% |
| 엔드투엔드 | 작업 완료율 | > 70% | > 90% |
계약 AI 프로젝트 모범 사례
10가지 황금률
- 분류부터 시작하세요. 기본 PDF에는 OCR을 적용하지 마십시오. 계약의 60-70% 현대 비즈니스는 이미 디지털 형식으로 되어 있습니다.
- 전처리에 투자하세요. OCR의 품질에 따라 최대 한도가 결정됩니다. 전체 파이프라인의 성능. 전처리에 1시간을 투자하면 NER에서 10시간이 절약됩니다.
- 도메인별 템플릿을 사용하세요. LegalBERT는 일반 BERT보다 12-15% 더 나은 성능을 발휘합니다. 법적 업무. 미세 조정 비용은 이익에 비해 미미합니다.
- 규칙과 ML 결합: 결정론적 규칙(날짜, 금액, 규범적 참조)은 구조화된 패턴의 경우 ML보다 더 정확하고 설명하기 쉽습니다.
- 엔드투엔드 측정: 분할이 잘못된 경우 완벽하고 쓸모없는 NER입니다. 중요한 측정항목은 작업 완료율: 최종 사용자가 몇 개의 절을 사용하는지 수정 없이 받아들입니다.
- 인간 참여형: AI는 변호사를 대신하는 것이 아니라 그를 가속시킨다. 예측 항상 전문가가 예측을 확인, 수정 및 개선하는 검토 인터페이스입니다.
- 모델 버전 관리: 계약은 진화합니다(GDPR 또는 NIS 지침2). 모델은 업데이트된 데이터에 대해 주기적으로 재학습되어야 합니다.
- 개인정보 보호 설계: 계약에는 민감한 데이터(금액, 당사자, 상업적 조건). 온프레미스에서 처리하거나 클라우드 제공업체의 계약 보증을 받아 처리합니다.
- 특정 사용 사례로 시작하세요. 시스템을 구축하려고 하지 마세요. 보편적. 계약 유형(예: NDA, 공급) 및 작업(예: 날짜 추출)에서 시작 기한) 확장하기 전에 가치를 입증합니다.
- 데이터 드리프트 모니터링: 계약 언어는 진화합니다. AI 조항, ESG, 공급망 탄력성은 지난 2~3년 동안 일반화되었습니다. 모델 성능 저하를 식별하려면 모니터링해야 합니다.
비즈니스 시스템과의 통합 패턴
Contract AI 시스템은 기존 비즈니스 프로세스에 통합된 경우에만 가치가 있습니다. 출력 파이프라인의 흐름이 계약 수명주기 관리(CLM), 문서 관리 시스템(DMS) e 승인 워크플로우.
통합 아키텍처
- 이벤트 기반: 새 계약을 DMS에 로드하면 자동으로 활성화됩니다. 웹훅 또는 메시지 큐(RabbitMQ, SQS)를 통한 분석 파이프라인.
- REST API: 문서를 제출하고 구조화된 결과를 수신하는 엔드포인트 JSON 형식으로 모든 CLM과 호환됩니다.
- 일괄 처리: 과거 계약 아카이브의 마이그레이션을 위해 비동기 처리 및 완료 알림.
- 대시보드: 결과 보기, 검토를 위한 웹 인터페이스 모델의 지속적인 개선을 위한 예측 및 피드백.
표준 및 출력 형식
상호 운용성을 극대화하려면 파이프라인 출력이 개방형 표준을 따라야 합니다. OASIS LegalDocML (Akoma Ntoso) 다큐멘터리 구조에 대해서는 SALI(법률산업 표준 발전) 분류학을 위해 조항, 전자 JSON-LD 구조화된 메타데이터의 경우 이러한 표준은 다음을 허용합니다. 사용자 지정 매핑 없이 타사 시스템과 통합됩니다.
결론 및 다음 단계
NLP를 사용한 자동 계약 분석은 더 이상 학문적 연구 프로젝트가 아닙니다. 선도적인 로펌과 법률 부서에서 매일 사용하는 성숙한 기술 세계의 기업. OCR부터 이해까지 이 기사에서 구축한 파이프라인 의미론은 모든 LegalTech 플랫폼의 기반이 되는 기본 아키텍처를 나타냅니다. 현대.
기억해야 할 핵심 사항:
- 파이프라인과 다단계: OCR → 구조화 → NER → 분류 → 의미 분석. 각 단계에는 특정 기술과 지표가 있습니다.
- I 도메인별 모델 (LegalBERT, CaseLaw-BERT)이 훨씬 더 나은 성과를 냈습니다. 법적 업무에 대한 일반 모델. CUAD와 같은 데이터 세트에 대한 미세 조정도 가능합니다. 자원이 제한된 팀.
- 접근 방식 하이브리드 규칙 + ML 가장 효과적인: 구조화기업에 대한 규칙 (날짜, 금액, 참조), 의미론 및 분류를 위한 ML.
- Il 인간 참여 루프 타협이 아니라 필수다: AI는 가속화한다 법적 작업은 이를 대체하지 않습니다. 검증에는 사람의 감독이 필수적입니다. 그리고 지속적인 개선.
- L'완성 CLM, DMS 및 비즈니스 워크플로우는 혁신에 필수적입니다. 비즈니스 가치에 대한 기술적 분석.
시리즈의 다음 기사에서는 AI를 활용한 법학 연구, 의미론적 검색 엔진과 지식 그래프가 어떻게 방식을 변화시키고 있는지 살펴보세요. 변호사와 법학자는 법학과 교리에 접근합니다.
자세히 알아볼 수 있는 리소스
- CUAD 데이터세트: github.com/TheAtticusProject/cuad - 주석이 달린 계약 510개, 조항 유형 41개
- 법률BERT: HuggingFace nlpaueb/legal-bert-base-uncased - 법적으로 사전 훈련된 모델
- spaCy 법적 NER: github.com/openlegaldata/legal-ner - 법률 텍스트를 위한 오픈 소스 NER
- John Snow Labs 법적 NLP: 완전한 엔터프라이즈 파이프라인을 위한 600개 이상의 템플릿
- OASIS LegalDocML: 구조화된 법률 문서를 위한 XML 표준







