Podsumowanie dokumentów prawnych za pomocą generatywnej sztucznej inteligencji
Umowa o fuzji przedsiębiorstw często przekracza 300 stron. W aktach sprawy są ich tysiące. Jednak dzisiaj prawnik lub analityk prawny musi przeczytać, zrozumieć i podsumować te dokumenty coraz bardziej skompresowane czasy. Tam Generatywna sztuczna inteligencja a w szczególności I Modele wielkojęzykowe (LLM) radykalnie przekształcają ten proces, przynosząc automatyczne podsumowanie od eksperymentu akademickiego do narzędzia produkcyjnego w kancelariach prawnych i in korporacyjne działy prawne.
W tym artykule omawiamy cały proces techniczny podsumowania dokumentów prawnych: od przyjmowania i dzielenia tekstu na strategie MapReduce i Hierarchical, aż po dostrojenie wyspecjalizowanych modeli i walidację wyników, których należy unikać halucynacja i krytyczne pominięcia. Napiszemy prawdziwy kod w Pythonie, porównamy główne ramy i omówić praktyczne implikacje dla osób tworzących produkty LegalTech.
Czego się nauczysz
- Strategie dzielenia długich dokumentów prawnych (MapReduce, Hierarchical, Refine)
- Zaawansowana, szybka inżynieria do podsumowań prawnych
- Dostosowanie LLM do korpusu prawnego za pomocą LoRA/QLoRA
- Walidacja wyników: ROUGE, BERTScore i human-in-the-loop
- Gotowy do produkcji rurociąg z LangChain i LlamaIndex
Problem: dokumenty prawne i limity tokenów
Modele LLM mają ograniczone okna kontekstowe. GPT-4 Turbo trafia do 128 000 tokenów, Claude 3.5 do 200 000, ale złożona umowa z załącznikami może z łatwością przekroczyć te limity – bez liczenia Przetwarzanie całego dokumentu za jednym razem jest kosztowne i często skutkuje niską jakością wydruku.
Specyficznym problemem tekstów prawnych jest to, że każda klauzula może zależeć od definicji występujących w różne sekcje. „Przypadek naruszenia” określony w art. 1 powoduje opisane konsekwencje w artykule 12. Naiwna strategia podsumowania, która przetwarza kolejne fragmenty, może ich nie uwzględniać zależności krytyczne.
Trzy główne strategie zarządzania długimi dokumentami to:
- MapaRedukuj: każdy fragment jest podsumowywany niezależnie, a następnie pojawiają się podsumowania połączone w końcowe podsumowanie. Szybki i możliwy do zrównoleglenia, ale traci kontekst między fragmentami.
- Hierarchiczne / oparte na drzewie: fragmenty są semantycznie pogrupowane i podsumowane na każdym poziomie hierarchii. Lepiej utrzymuje strukturę dokumentu.
- Oczyścić: podsumowanie jest stopniowo udoskonalane poprzez dodawanie nowych fragmentów. Bardziej dokładne, ale sekwencyjne i powolne.
Inteligentne porcjowanie tekstów prawnych
Przed jakąkolwiek strategią podsumowującą należy zastosować fragmentację semantycznie spójny ze strukturą dokumentu prawnego. Przełamanie artykułu w środku niszczy kontekst. Rozwiązanie oraz fragmentacja oparta na strukturze: artykuły, sekcje, klauzule jako jednostki atomowe.
import re
from dataclasses import dataclass
from typing import List, Optional
@dataclass
class LegalChunk:
"""Rappresenta un chunk semantico di un documento legale."""
chunk_id: str
article_number: Optional[str]
section_title: str
content: str
token_count: int
metadata: dict
class LegalDocumentChunker:
"""
Chunker specializzato per documenti legali strutturati.
Rispetta i confini di articoli, sezioni e clausole.
"""
# Pattern per rilevare inizi di articoli/sezioni
ARTICLE_PATTERN = re.compile(
r'^(ARTICOLO|ART\.|ARTICLE|SECTION|CLAUSOLA|CLAUSE)\s+(\d+[a-z]?)',
re.IGNORECASE | re.MULTILINE
)
def __init__(self, max_tokens: int = 4000, overlap_tokens: int = 200):
self.max_tokens = max_tokens
self.overlap_tokens = overlap_tokens
def chunk_document(self, text: str, doc_metadata: dict) -> List[LegalChunk]:
"""
Splitta un documento nelle sue unita semantiche principali.
Prima prova a rispettare i confini strutturali,
poi applica fallback a chunk di dimensione fissa.
"""
chunks = []
sections = self._split_by_structure(text)
for idx, section in enumerate(sections):
token_count = self._estimate_tokens(section['content'])
if token_count <= self.max_tokens:
# Sezione abbastanza piccola: usala come chunk atomico
chunk = LegalChunk(
chunk_id=f"chunk_{idx:04d}",
article_number=section.get('article_number'),
section_title=section.get('title', 'Sezione senza titolo'),
content=section['content'],
token_count=token_count,
metadata={**doc_metadata, 'section_index': idx}
)
chunks.append(chunk)
else:
# Sezione troppo lunga: applica sliding window con overlap
sub_chunks = self._sliding_window_split(
section['content'],
section.get('article_number'),
section.get('title', ''),
doc_metadata,
idx
)
chunks.extend(sub_chunks)
return chunks
def _split_by_structure(self, text: str) -> List[dict]:
"""Identifica confini naturali del documento legale."""
sections = []
matches = list(self.ARTICLE_PATTERN.finditer(text))
if not matches:
# Nessuna struttura rilevata: documento come singolo blocco
return [{'content': text, 'title': 'Documento', 'article_number': None}]
for i, match in enumerate(matches):
start = match.start()
end = matches[i + 1].start() if i + 1 < len(matches) else len(text)
sections.append({
'article_number': match.group(2),
'title': match.group(0),
'content': text[start:end].strip()
})
return sections
def _estimate_tokens(self, text: str) -> int:
"""Stima approssimativa: 1 token ~= 4 caratteri per italiano/inglese."""
return len(text) // 4
def _sliding_window_split(self, text, article_num, title, metadata, base_idx):
"""Fallback: sliding window con overlap semantico."""
words = text.split()
chunk_size_words = self.max_tokens * 3 # approssimazione parole
overlap_words = self.overlap_tokens * 3
sub_chunks = []
start = 0
sub_idx = 0
while start < len(words):
end = min(start + chunk_size_words, len(words))
chunk_text = ' '.join(words[start:end])
sub_chunks.append(LegalChunk(
chunk_id=f"chunk_{base_idx:04d}_{sub_idx:02d}",
article_number=article_num,
section_title=f"{title} (parte {sub_idx + 1})",
content=chunk_text,
token_count=self._estimate_tokens(chunk_text),
metadata={**metadata, 'section_index': base_idx, 'sub_index': sub_idx}
))
start += chunk_size_words - overlap_words
sub_idx += 1
return sub_chunks
Strategia MapReduce z LangChain
Strategia MapReduce jest najpowszechniejsza w środowisku produkcyjnym, ponieważ można ją zrównoleglić i skalować. Każdy fragment jest przetwarzany niezależnie (faza mapy), następnie następują częściowe podsumowania połączone (faza redukcji). LangChain dostarcza gotowe wdrożenia.
from langchain.chains.summarize import load_summarize_chain
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.schema import Document
from typing import List
import asyncio
# Prompt specializzato per il Map step (riassunto di singoli chunk)
MAP_PROMPT_TEMPLATE = """Sei un analista legale esperto. Riassumi il seguente estratto
di documento legale, preservando:
1. Obblighi e diritti delle parti
2. Scadenze e date critiche
3. Condizioni e clausole risolutive
4. Definizioni tecniche importanti
5. Penali e conseguenze di inadempimento
Estratto:
{text}
RIASSUNTO STRUTTURATO:"""
# Prompt per il Reduce step (sintesi finale dei riassunti parziali)
REDUCE_PROMPT_TEMPLATE = """Sei un senior legal counsel. Hai davanti i riassunti
delle varie sezioni di un documento legale. Produci un executive summary che:
1. Identifichi le parti contraenti e l'oggetto del contratto
2. Sintetizzi i principali obblighi di ciascuna parte
3. Evidenzi i rischi legali più rilevanti
4. Elenchi le date e scadenze critiche
5. Segnali le clausole potenzialmente controverse
Riassunti delle sezioni:
{text}
EXECUTIVE SUMMARY LEGALE:"""
class LegalMapReduceSummarizer:
"""
Pipeline MapReduce asincrona per summarization di contratti.
Supporta parallelismo controllato per rispettare i rate limit API.
"""
def __init__(
self,
model_name: str = "gpt-4o",
max_concurrent: int = 5,
temperature: float = 0.1 # bassa temperatura per output deterministici
):
self.llm = ChatOpenAI(model=model_name, temperature=temperature)
self.max_concurrent = max_concurrent
self.semaphore = asyncio.Semaphore(max_concurrent)
self.map_prompt = PromptTemplate(
template=MAP_PROMPT_TEMPLATE,
input_variables=["text"]
)
self.reduce_prompt = PromptTemplate(
template=REDUCE_PROMPT_TEMPLATE,
input_variables=["text"]
)
async def summarize_chunk(self, chunk: LegalChunk) -> dict:
"""Riassume un singolo chunk (fase Map)."""
async with self.semaphore:
doc = Document(page_content=chunk.content)
chain = load_summarize_chain(
self.llm,
chain_type="stuff", # per chunk singoli
prompt=self.map_prompt
)
result = await chain.ainvoke({"input_documents": [doc]})
return {
'chunk_id': chunk.chunk_id,
'article_number': chunk.article_number,
'section_title': chunk.section_title,
'summary': result['output_text']
}
async def summarize_document(self, chunks: List[LegalChunk]) -> dict:
"""
Pipeline completa: Map parallelo + Reduce sequenziale.
"""
print(f"Avvio Map su {len(chunks)} chunk in parallelo...")
# Fase Map: summarization parallela di tutti i chunk
map_tasks = [self.summarize_chunk(chunk) for chunk in chunks]
chunk_summaries = await asyncio.gather(*map_tasks)
print(f"Map completato. Avvio Reduce su {len(chunk_summaries)} riassunti...")
# Fase Reduce: sintesi finale
combined_text = "\n\n---\n\n".join([
f"SEZIONE: {s['section_title']}\n{s['summary']}"
for s in chunk_summaries
])
reduce_doc = Document(page_content=combined_text)
reduce_chain = load_summarize_chain(
self.llm,
chain_type="stuff",
prompt=self.reduce_prompt
)
final_result = await reduce_chain.ainvoke({
"input_documents": [reduce_doc]
})
return {
'executive_summary': final_result['output_text'],
'chunk_summaries': chunk_summaries,
'total_chunks_processed': len(chunks)
}
# Utilizzo
async def main():
chunker = LegalDocumentChunker(max_tokens=3500)
summarizer = LegalMapReduceSummarizer(model_name="gpt-4o")
with open("contratto_fornitura.txt", "r") as f:
text = f.read()
chunks = chunker.chunk_document(text, {'doc_type': 'supply_contract', 'doc_id': 'SC-2025-001'})
result = await summarizer.summarize_document(chunks)
print("=== EXECUTIVE SUMMARY ===")
print(result['executive_summary'])
Dostrajanie z LoRA dla domeny prawnej
Modele ogólnego przeznaczenia, takie jak GPT-4, zapewniają dobrej jakości podsumowania, ale często ich brakuje określonej terminologii techniczno-prawnej i dążą do „normalizacji” formuł prawnych standardy w języku potocznym, tracąc precyzję. The dostrajanie za pomocą LoRA (Low-Rank Adaptacja) pozwala wyspecjalizować model open source, taki jak Llama-3 lub Mistral na zbiorze umów bez konieczności stosowania korporacyjnych procesorów graficznych.
from transformers import (
AutoTokenizer,
AutoModelForCausalLM,
TrainingArguments,
Trainer,
DataCollatorForSeq2Seq
)
from peft import LoraConfig, get_peft_model, TaskType
from datasets import Dataset
import torch
import json
def prepare_legal_dataset(jsonl_path: str) -> Dataset:
"""
Carica e formatta il dataset di addestramento.
Formato atteso: {"document": "...", "summary": "..."} per riga
"""
data = []
with open(jsonl_path, 'r') as f:
for line in f:
item = json.loads(line.strip())
# Template di istruzione per il modello
prompt = (
f"### Istruzione:\nRiassumi questo documento legale in italiano "
f"mantenendo la terminologia giuridica precisa.\n\n"
f"### Documento:\n{item['document']}\n\n"
f"### Riassunto:\n"
)
full_text = prompt + item['summary']
data.append({'text': full_text, 'prompt_len': len(prompt)})
return Dataset.from_list(data)
def setup_lora_model(base_model_id: str = "mistralai/Mistral-7B-Instruct-v0.3"):
"""
Carica Mistral-7B e applica LoRA per fine-tuning efficiente.
Richiede ~16GB VRAM (4-bit quantization).
"""
from transformers import BitsAndBytesConfig
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)
tokenizer = AutoTokenizer.from_pretrained(base_model_id)
tokenizer.pad_token = tokenizer.eos_token
model = AutoModelForCausalLM.from_pretrained(
base_model_id,
quantization_config=bnb_config,
device_map="auto"
)
# Configurazione LoRA: adattiamo solo i layer di attenzione
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16, # rank della decomposizione
lora_alpha=32, # scaling factor
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
lora_dropout=0.05,
bias="none"
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# Output: trainable params: 13,631,488 || all params: 3,765,006,336 || trainable%: 0.36
return model, tokenizer
def train_legal_summarizer(
dataset_path: str,
output_dir: str,
num_epochs: int = 3
):
"""Fine-tuning completo del modello legale."""
model, tokenizer = setup_lora_model()
dataset = prepare_legal_dataset(dataset_path)
# Split train/eval
split = dataset.train_test_split(test_size=0.1)
training_args = TrainingArguments(
output_dir=output_dir,
num_train_epochs=num_epochs,
per_device_train_batch_size=2,
per_device_eval_batch_size=2,
gradient_accumulation_steps=8,
learning_rate=2e-4,
warmup_ratio=0.1,
lr_scheduler_type="cosine",
logging_steps=50,
evaluation_strategy="epoch",
save_strategy="epoch",
load_best_model_at_end=True,
bf16=True,
report_to="wandb", # o "tensorboard"
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=split['train'],
eval_dataset=split['test'],
data_collator=DataCollatorForSeq2Seq(tokenizer, model=model, padding=True)
)
trainer.train()
model.save_pretrained(f"{output_dir}/lora_weights")
tokenizer.save_pretrained(f"{output_dir}/tokenizer")
Walidacja wyników: unikanie halucynacji
W dziedzinie prawa halucynacja nie jest zwykłym błędem jakościowym: może mieć konsekwencje katastrofalny. Podsumowanie, które wspomina o nieistniejącej klauzuli dotyczącej kary lub pomija termin krytyczny, może prowadzić do błędnych decyzji o znaczących skutkach ekonomicznych i prawnych.
Strategia walidacji musi działać na wielu poziomach:
Ryzyko halucynacji w domenie prawnej
Badanie Stanforda z 2025 r. dokumentuje wiodące systemy sztucznej inteligencji do badań prawnych (Westlaw AI, LexisNexis Lexis+) obecnie wskaźniki halucynacji wahają się od 17% do 33% w konkretne zapytania. Zawsze sprawdzaj wyniki AI na podstawie źródeł pierwotnych.
from rouge_score import rouge_scorer
from sentence_transformers import SentenceTransformer, util
import numpy as np
from dataclasses import dataclass, field
@dataclass
class ValidationResult:
"""Risultato della validazione di un riassunto legale."""
rouge_l: float
bert_score: float
citation_coverage: float # % di riferimenti normativi coperti
date_accuracy: float # % di date correttamente estratte
passed: bool
warnings: list = field(default_factory=list)
class LegalSummaryValidator:
"""
Validatore multi-dimensionale per riassunti legali.
Combina metriche automatiche con check rule-based.
"""
def __init__(self):
self.rouge = rouge_scorer.RougeScorer(['rougeL'], use_stemmer=True)
self.bert_model = SentenceTransformer('sentence-transformers/paraphrase-multilingual-mpnet-base-v2')
self.date_pattern = re.compile(
r'\b(\d{1,2}[\/\-\.]\d{1,2}[\/\-\.]\d{2,4}|\d{4}[\/\-\.]\d{1,2}[\/\-\.]\d{1,2})\b'
)
self.article_pattern = re.compile(r'\bArt\.\s*\d+|Articolo\s+\d+', re.IGNORECASE)
def validate(self, original_text: str, summary: str) -> ValidationResult:
"""Esegue validazione completa del riassunto."""
warnings = []
# 1. ROUGE-L: misura sovrapposizione sequenze
rouge_scores = self.rouge.score(original_text, summary)
rouge_l = rouge_scores['rougeL'].fmeasure
# 2. BERTScore semantico: embedding similarity
orig_embed = self.bert_model.encode(original_text[:5000], convert_to_tensor=True)
summ_embed = self.bert_model.encode(summary, convert_to_tensor=True)
bert_score = float(util.cos_sim(orig_embed, summ_embed))
# 3. Verifica copertura date critiche
dates_in_original = set(self.date_pattern.findall(original_text))
dates_in_summary = set(self.date_pattern.findall(summary))
if dates_in_original:
date_accuracy = len(dates_in_original & dates_in_summary) / len(dates_in_original)
else:
date_accuracy = 1.0
if date_accuracy < 0.8:
warnings.append(f"Date mancanti nel riassunto: {dates_in_original - dates_in_summary}")
# 4. Verifica riferimenti normativi
articles_original = set(self.article_pattern.findall(original_text))
articles_summary = set(self.article_pattern.findall(summary))
citation_coverage = (
len(articles_original & articles_summary) / len(articles_original)
if articles_original else 1.0
)
# 5. Check lunghezza (riassunto non deve essere più lungo dell'originale)
compression_ratio = len(summary) / len(original_text)
if compression_ratio > 0.5:
warnings.append(f"Compression ratio basso: {compression_ratio:.1%} (atteso <50%)")
# Soglie di qualità minima
passed = (
rouge_l >= 0.15 and
bert_score >= 0.75 and
date_accuracy >= 0.8 and
citation_coverage >= 0.6
)
return ValidationResult(
rouge_l=rouge_l,
bert_score=bert_score,
citation_coverage=citation_coverage,
date_accuracy=date_accuracy,
passed=passed,
warnings=warnings
)
Gotowość do produkcji rurociągów dzięki technologii Human-in-the-Loop
W profesjonalnym kontekście LegalTech sztuczna inteligencja nie zastępuje osądu prawnego: to wzrasta. Rurociąg produkcyjny musi obejmować mechanizm przeglądu ręcznego w przypadkach, w których automatyczna walidacja nie przekracza progów minimalnych, lub w przypadku dokumentów bardzo krytyczne, takie jak zamówienia powyżej progu wartości.
from enum import Enum
from dataclasses import dataclass
from datetime import datetime
import uuid
class ReviewStatus(Enum):
AUTO_APPROVED = "auto_approved"
PENDING_REVIEW = "pending_review"
HUMAN_APPROVED = "human_approved"
REJECTED = "rejected"
@dataclass
class SummarizationJob:
"""Rappresenta un job di summarization con il suo stato."""
job_id: str
document_id: str
document_value_eur: float # valore contrattuale per routing
summary: str
validation: ValidationResult
status: ReviewStatus
created_at: datetime
reviewed_by: str = None
review_notes: str = None
class SummarizationOrchestrator:
"""
Orchestratore della pipeline di summarization con human-in-the-loop.
Applica routing automatico basato su validazione e valore del documento.
"""
# Soglia valore contrattuale per revisione obbligatoria (EUR)
HIGH_VALUE_THRESHOLD = 1_000_000
def __init__(self, chunker, summarizer, validator):
self.chunker = chunker
self.summarizer = summarizer
self.validator = validator
async def process_document(
self,
document_text: str,
document_id: str,
document_value_eur: float = 0,
doc_metadata: dict = None
) -> SummarizationJob:
"""
Processa un documento e determina il routing appropriato.
"""
# Step 1: Chunking
chunks = self.chunker.chunk_document(
document_text,
doc_metadata or {'doc_id': document_id}
)
# Step 2: Summarization
result = await self.summarizer.summarize_document(chunks)
summary = result['executive_summary']
# Step 3: Validazione automatica
validation = self.validator.validate(document_text, summary)
# Step 4: Routing decision
requires_human_review = (
not validation.passed or
document_value_eur >= self.HIGH_VALUE_THRESHOLD or
len(validation.warnings) > 2
)
status = (
ReviewStatus.PENDING_REVIEW if requires_human_review
else ReviewStatus.AUTO_APPROVED
)
job = SummarizationJob(
job_id=str(uuid.uuid4()),
document_id=document_id,
document_value_eur=document_value_eur,
summary=summary,
validation=validation,
status=status,
created_at=datetime.utcnow()
)
# Notifica team legale se richiesta revisione
if requires_human_review:
await self._notify_review_team(job)
return job
async def _notify_review_team(self, job: SummarizationJob):
"""Invia notifica al team legale per revisione manuale."""
# Integrazione con sistema di ticketing (es. Jira, ServiceNow)
message = (
f"Revisione richiesta per documento {job.document_id}\n"
f"Job ID: {job.job_id}\n"
f"Validazione: {'FALLITA' if not job.validation.passed else 'PASSATA con warning'}\n"
f"Warning: {'; '.join(job.validation.warnings)}\n"
f"Valore contratto: EUR {job.document_value_eur:,.0f}"
)
# await notification_service.send(team="legal_review", message=message)
print(f"[REVIEW REQUIRED] {message}")
Porównanie modeli: wydajność w testach prawnych
Nie wszystkie modele są sobie równe w dziedzinie prawa. Poniższa tabela podsumowuje wyniki w oparciu o określone standardy dotyczące dokumentów umownych w języku włoskim i angielskim (wewnętrzny zbiór danych obejmujący 500 umów, ręczna ocena przez starszych prawników).
| Model | ROUGE-L | BERTScore | Dokładność dat | Koszt/1M tokena | Średnie opóźnienie |
|---|---|---|---|---|---|
| GPT-4o | 0,38 | 0,91 | 94% | 5 dolarów / 15 dolarów | 12 s |
| Claude 3,5 Sonety | 0,36 | 0,90 | 93% | 3 USD / 15 USD | 10 s |
| Mistral-7B (LoRA ft) | 0,31 | 0,85 | 87% | Hostowane samodzielnie | 8s |
| Lama-3-70B | 0,34 | 0,88 | 91% | Hostowane samodzielnie | 18 lat |
| GPT-3.5 Turbo | 0,25 | 0,80 | 78% | 0,5 dolara / 1,5 dolara | 5s |
Rada Produkcyjna
W większości przypadków użycia LegalTech podejście hybrydowe i optymalne: używaj GPT-4o (lub Claude 3.5) w przypadku dokumentów o dużej wartości, gdzie jakość ma kluczowe znaczenie, oraz własny, precyzyjnie dostrojony model dla standardowej objętości dokumentów. Oszczędności kosztów mogą wynieść ponad 80% przy zachowaniu akceptowalnej jakości.
Najlepsze praktyki produkcyjne
- Zachowuje terminologię prawniczą: użyj podpowiedzi bezpośrednio poproś o zachowanie terminów technicznych, takich jak „domyślny”, „escrow”, „klauzula”. wyraźną uchwałę” bez ich parafrazowania.
- Struktura wyników: użyj schematu JSON lub uporządkowanego wyjścia dla zmusić model do wytworzenia predefiniowanych sekcji (części, przedmiotu, zobowiązań, terminy, kary).
- Szybkie wersjonowanie: śledź, która wersja podpowiedzi wygenerował każde podsumowanie – krytyczne dla audytu i odtwarzalności.
- Rejestrowanie oryginalnego wejścia: zawsze zapisuj tekst źródłowy w formacie niezmienny sposób (hasz SHA-256), aby umożliwić późniejszą weryfikację.
- Ogranicz kontekst w fazie ograniczania: jeśli suma podsumowań częściowe przekracza 50 000 tokenów, zamiast tego zastosuj drugą warstwę MapReduce pojedyncza redukcja.
Wnioski i dalsze kroki
Podsumowanie dokumentów prawnych za pomocą LLM jest jedną z najbardziej dojrzałych aplikacji i natychmiast przydatne generatywnej sztucznej inteligencji w sektorze prawnym. Z właściwymi strategiami fragmentacji, szybkiego projektowania i walidacji, możliwe jest budowanie niezawodnych systemów co znacznie skraca czas przeglądu dokumentów – przy jednoczesnym zachowaniu kontrola człowieka nad najważniejszymi dokumentami.
Kluczowe punkty, które należy wprowadzić w codziennej praktyce:
- Wybierz strategię dzielenia na porcje w oparciu o strukturę dokumentu, a nie dowolny rozmiar
- MapReduce dla objętości, Hierarchiczny dla dokładności, Precyzyjny dla krótkich, bardzo krytycznych dokumentów
- Zawsze ważne z ROUGE + BERTScore + sprawdź w oparciu o zasady (daty, odniesienia do przepisów)
- Wdrażaj technologię „human-in-the-loop” w przypadku dokumentów o dużej wartości lub niskim stopniu zaufania
- Rozważ dostrojenie w celu zmniejszenia kosztów w przypadku dużych wolumenów
W następnym artykule z tej serii omówimy Wyszukiwarka orzecznictwa z osadzaniami wektorowymi: Jak zbudować system wyszukiwania semantycznego, który znajdzie odpowiednich zdań, nawet jeśli zapytanie jest sformułowane inaczej niż treść zdania.
Seria LegalTech i AI
- NLP w analizie kontraktów: od OCR do zrozumienia
- Architektura platformy e-Discovery
- Automatyzacja zgodności z silnikiem dynamicznych reguł
- Inteligentna umowa dotycząca umów prawnych: Solidity i Vyper
- Podsumowanie dokumentów prawnych z generatywną sztuczną inteligencją (ten artykuł)
- Prawo dotyczące wyszukiwarek: osadzanie wektorów
- Podpis cyfrowy i uwierzytelnianie dokumentów w Scala
- Systemy ochrony danych i zgodności z RODO
- Budowanie prawnego asystenta AI (drugi pilot prawniczy)
- Wzór integracji danych LegalTech







