e-Keşif Platformu Mimarisi: Alma, İşleme ve Yapay Zeka İncelemesi
2024 yılında platformlarda yapay zekanın benimsenmesi e-Keşif yasal ve sadece bir yıl içinde %19'dan %79'a sıçradı. Bu veri istatistiksel bir merak değil, bir şeyi yansıtıyor Hukuk firmalarının, şirketlerin ve adli otoritelerin hukuksal süreçleri yönetme biçimindeki yapısal dönüşüm Hukuki ve cezai işlemlerde belgesel deliller. Bir dava milyonlarca e-postayı, mesajı içeriyorsa Slack, SharePoint belgeleri ve günlük dosyalarının geleneksel manuel inceleme modelidir. avukat yardımcısı artık ekonomik veya lojistik açıdan sürdürülebilir değil.
L'e-Keşif (Elektronik Keşif) ve bir davanın taraflarının takip ettiği süreç belgesel kanıtları yasal olarak tanımlamak, toplamak, korumak, işlemek, incelemek ve üretmek elektronik format. Amerika Birleşik Devletleri'nde ve Federal Medeni Usul Kuralları (FRCP) tarafından düzenlenmektedir, Avrupa'da ulusal düzeydeki benzer çerçevelerden Modern platformların petabaytlarca veriyi işlemesi gerekiyor. Yasal olarak geçerli gözetim zincirlerine saygı gösterin ve incelenecek belgelerin hacmini azaltın çok yüksek düzeyde hatırlamayı sürdürmek: ilgili hiçbir belge kaybolamaz.
Bu makalede kurumsal düzeyde bir e-Keşif platformunun eksiksiz mimarisini oluşturuyoruz: itibaren büyük miktarda yutma heterojen belgelerin dağıtılmış işleme, itibaren tekilleştirme al tahmine dayalı kodlama AI modelleri ile yukarı içinEDRM XML formatında dışa aktarma. Hepsi gerçek Python kod örnekleri ve analiziyle piyasanın önde gelen platformlarından.
Bu Makalede Neler Öğreneceksiniz?
- EDRM (Elektronik Keşif Referans Modeli) modeli ve aşamaları
- Büyük miktarda alım için mikro hizmet mimarisi: Kafka, Elasticsearch, MinIO
- İşleme hattı: metin çıkarma, meta veriler, MD5/SHA veri tekilleştirme ve neredeyse tekilleştirme
- Teknoloji Destekli İnceleme (TAR) ve Sürekli Aktif Öğrenme (CAL)
- Scikit-learn ve cümle dönüştürücülerle Tahmine Dayalı Kodlama
- Gözetim zinciri yönetimi ve değişmez denetim izi
- Platform karşılaştırması: Relativity, DISCO, Everlaw, Logikcull
- EDRM XML dışa aktarımı ve vaka yönetimi sistemleriyle entegrasyon
LegalTech ve AI Serisindeki Konumu
| # | Öğe | Odak |
|---|---|---|
| 1 | Sözleşme Analizi için NLP | OCR, NER, madde sınıflandırması |
| 2 | Buradasınız — e-Keşif Mimarisi | Alma, işleme, AI incelemesi |
| 3 | Uyumluluk Otomasyonu | Kural motoru ve RegTech |
| 4 | Akıllı Sözleşmeler | Sağlamlık, Vyper, uygulanabilirlik |
| 5 | Üretken Yapay Zeka ile Özetleme | Yüksek Lisans, RAG, çıktı doğrulama |
| 6 | Hukuki Arama Motoru | Vektör yerleştirmeleri ve anlamsal arama |
| 7 | Dijital İmza ve eIDAS 2.0 | PKI, zaman damgası, iş akışı |
| 8 | GDPR Uyumluluk Sistemleri | Tasarım gereği gizlilik, DSR, veri eşleme |
| 9 | Yasal Yapay Zeka Yardımcı Pilotu | Yasal külliyat ve korkuluklara ilişkin RAG |
| 10 | Veri Entegrasyonu LegalTech | ECLI, API mahkeme sistemleri, XBRL |
EDRM Modeli: e-Keşif için Referans Çerçevesi
L'Elektronik Keşif Referans Modeli (EDRM) ve bunu açıklayan fiili standart Elektronik keşif sürecinin aşamaları. 2005 yılında geliştirilen ve sürekli güncellenen model her kurumsal platformun desteklemesi gereken dokuz ardışık aşamayı tanımlar.
| EDRM aşaması | Tanım | Teknik Bileşen |
|---|---|---|
| 1. Bilgi Yönetişimi | Saklama politikaları, veri sınıflandırması, veri haritası | MDM, politika motoru, CMDB |
| 2. Kimlik | Potansiyel saklayıcıları ve ilgili veri kaynaklarını bulun | Tarayıcı, dizin taraması, LDAP sorgusu |
| 3. Koruma | Yasal bekletme: kötüye kullanımı önlemek için verileri dondurun | Bekletme yönetimi, değişmez depolama, bildirim iş akışı |
| 4.Koleksiyon | Gözaltı zinciriyle adli tıp koleksiyonu | Adli toplayıcı, karma doğrulama, saklama günlüğü |
| 5.İşleme | Metin çıkarma, meta veriler, tekilleştirme, NIST filtreleme | Apache Tika, Elasticsearch alma ardışık düzeni |
| 6.İnceleme | Uygunluk/ayrıcalık sınıflandırması, TAR/CAL | Tahmine dayalı kodlama, aktif öğrenme, inceleme platformu |
| 7. Analiz | Desen, zaman çizelgesi, varlık ağı, konu modelleme | Grafik analizi, NLP, LDA/BERTopic |
| 8. Üretim | Kararlaştırılan formatta dışa aktarın (TIFF, yerel, PDF) | EDRM XML dışa aktarma, Bates numaralandırma, redaksiyon motoru |
| 9. Sunum | Deneme sunumları, ifadeler, görsel zaman çizelgeleri | Deneme direktörü, sergi yönetimi |
Kurumsal e-Keşif için Mikro Hizmet Mimarisi
Modern bir e-Keşif platformu yekpare bir yapı olamaz. Veri hacimleri birkaç ila birkaç arasında değişir Büyük nedenlerden dolayı gigabaytlardan yüzlerce terabayta kadar. Mimarlık olmalı elastik olarak ölçeklenebilir, hataya dayanıklı ve değişmez denetim izleri sağlar Kanıt için uygunluk şartlarını yerine getirin. Birleştirilmiş mimari desen birleştirir olay akışı, nesne depolama ve dağıtılmış arama motoru.
# docker-compose.yml per ambiente e-Discovery locale
version: '3.9'
services:
# Message broker per ingestion asincrona
kafka:
image: confluentinc/cp-kafka:7.5.0
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
KAFKA_NUM_PARTITIONS: 12
depends_on: [zookeeper]
zookeeper:
image: confluentinc/cp-zookeeper:7.5.0
environment:
ZOOKEEPER_CLIENT_PORT: 2181
# Object storage per documenti originali e derivati
minio:
image: minio/minio:RELEASE.2024-01-16T16-07-38Z
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: ediscovery
MINIO_ROOT_PASSWORD: ${MINIO_PASSWORD}
volumes:
- minio_data:/data
# Search engine per full-text e metadata search
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
environment:
- discovery.type=single-node
- xpack.security.enabled=true
- ELASTIC_PASSWORD=${ES_PASSWORD}
- ES_JAVA_OPTS=-Xms2g -Xmx2g
volumes:
- es_data:/usr/share/elasticsearch/data
# Worker per processing documenti
tika:
image: apache/tika:2.9.1-full
ports:
- "9998:9998"
# Database relazionale per metadata e catena custodia
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: ediscovery
POSTGRES_USER: ediscovery
POSTGRES_PASSWORD: ${PG_PASSWORD}
volumes:
minio_data:
es_data:
Büyük Belge Besleme İşlem Hattı
Alma en kritik aşamadır: belgeleri heterojen kaynaklardan (e-posta sunucusu, dosyalar) toplamalıdır. adli bütünlüğü korurken paylaşım, bulut depolama, SaaS uygulamaları). Edinilen her belge mutlaka sahip olmak doğrulanabilir kriptografik karma ve ne zaman, nasıl ve nasıl olduğuna dair değişmez bir kayıt toplanmıştır.
"""
ediscovery/ingestion/collector.py
Collettore forense con chain of custody
"""
import hashlib
import json
import uuid
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional
import boto3
from kafka import KafkaProducer
import psycopg2
class ForensicCollector:
"""
Raccoglie documenti con hash SHA-256 e registra
la catena di custodia in PostgreSQL.
"""
def __init__(self, config: dict):
self.minio = boto3.client(
's3',
endpoint_url=config['minio_url'],
aws_access_key_id=config['minio_user'],
aws_secret_access_key=config['minio_password']
)
self.producer = KafkaProducer(
bootstrap_servers=config['kafka_brokers'],
value_serializer=lambda v: json.dumps(v).encode('utf-8')
)
self.pg_conn = psycopg2.connect(config['postgres_dsn'])
self.bucket = 'ediscovery-originals'
def collect_file(
self,
file_path: Path,
matter_id: str,
custodian_id: str,
collector_id: str
) -> dict:
"""
Raccoglie un file con verifica integrita e registra custody event.
Restituisce il documento event per il topic Kafka.
"""
# 1. Calcola hash SHA-256 prima del trasferimento
sha256 = self._compute_sha256(file_path)
md5 = self._compute_md5(file_path)
file_size = file_path.stat().st_size
# 2. Genera Document ID univoco
doc_id = str(uuid.uuid4())
# 3. Upload su MinIO con metadata
s3_key = f"matters/{matter_id}/originals/{doc_id}/{file_path.name}"
self.minio.upload_file(
str(file_path),
self.bucket,
s3_key,
ExtraArgs={
'Metadata': {
'doc-id': doc_id,
'sha256': sha256,
'custodian-id': custodian_id,
'matter-id': matter_id
}
}
)
# 4. Verifica integrita post-upload
response = self.minio.head_object(Bucket=self.bucket, Key=s3_key)
uploaded_size = response['ContentLength']
if uploaded_size != file_size:
raise ValueError(
f"Integrita compromessa: atteso {file_size} bytes, "
f"caricato {uploaded_size} bytes"
)
# 5. Registra custody event in PostgreSQL
collection_timestamp = datetime.now(timezone.utc).isoformat()
custody_event = {
'event_id': str(uuid.uuid4()),
'doc_id': doc_id,
'matter_id': matter_id,
'custodian_id': custodian_id,
'collector_id': collector_id,
'event_type': 'COLLECTION',
'timestamp': collection_timestamp,
'source_path': str(file_path),
'sha256': sha256,
'md5': md5,
'file_size': file_size,
's3_key': s3_key
}
self._record_custody_event(custody_event)
# 6. Pubblica su Kafka per processing asincrono
document_event = {
'doc_id': doc_id,
'matter_id': matter_id,
'custodian_id': custodian_id,
's3_key': s3_key,
'filename': file_path.name,
'file_size': file_size,
'sha256': sha256,
'collection_timestamp': collection_timestamp,
'status': 'COLLECTED'
}
self.producer.send('ediscovery.documents.collected', document_event)
return document_event
def _compute_sha256(self, file_path: Path) -> str:
h = hashlib.sha256()
with open(file_path, 'rb') as f:
for chunk in iter(lambda: f.read(65536), b''):
h.update(chunk)
return h.hexdigest()
def _compute_md5(self, file_path: Path) -> str:
h = hashlib.md5()
with open(file_path, 'rb') as f:
for chunk in iter(lambda: f.read(65536), b''):
h.update(chunk)
return h.hexdigest()
def _record_custody_event(self, event: dict) -> None:
with self.pg_conn.cursor() as cur:
cur.execute("""
INSERT INTO custody_events (
event_id, doc_id, matter_id, custodian_id,
collector_id, event_type, timestamp,
source_path, sha256, md5, file_size, s3_key
) VALUES (
%(event_id)s, %(doc_id)s, %(matter_id)s, %(custodian_id)s,
%(collector_id)s, %(event_type)s, %(timestamp)s,
%(source_path)s, %(sha256)s, %(md5)s, %(file_size)s, %(s3_key)s
)
""", event)
self.pg_conn.commit()
Apache Tika ile İşleme ve İçerik Çıkarma
Boru hattının işlenmesi ve teknik kalbi. Her belge metne dönüştürülmelidir aranabilir, meta veriler çıkarılmış (yazar, tarihler, e-posta dizisi, belge özellikleri) ve normalleştirilmiş ortak bir şemada. Apaçi Tika 1.500'den fazla farklı dosya formatını yönetir, bu da onu e-Keşifte içerik çıkarımı için fiili standart haline getiriyor.
"""
ediscovery/processing/processor.py
Worker di processing documenti con Apache Tika
"""
import json
import requests
from kafka import KafkaConsumer, KafkaProducer
from elasticsearch import Elasticsearch
import boto3
class DocumentProcessor:
"""
Consumer Kafka che processa ogni documento raccolto:
estrae testo e metadata con Tika, indicizza su ES.
"""
TIKA_URL = "http://tika:9998"
def __init__(self, config: dict):
self.consumer = KafkaConsumer(
'ediscovery.documents.collected',
bootstrap_servers=config['kafka_brokers'],
group_id='document-processor',
value_deserializer=lambda v: json.loads(v.decode('utf-8'))
)
self.producer = KafkaProducer(
bootstrap_servers=config['kafka_brokers'],
value_serializer=lambda v: json.dumps(v).encode('utf-8')
)
self.es = Elasticsearch(
config['elasticsearch_url'],
basic_auth=('elastic', config['es_password'])
)
self.minio = boto3.client('s3', endpoint_url=config['minio_url'])
def process_documents(self) -> None:
for message in self.consumer:
event = message.value
try:
processed = self._process_document(event)
self._index_document(processed)
self.producer.send(
'ediscovery.documents.processed',
{**event, **processed, 'status': 'PROCESSED'}
)
except Exception as exc:
self.producer.send(
'ediscovery.documents.errors',
{**event, 'error': str(exc), 'status': 'ERROR'}
)
def _process_document(self, event: dict) -> dict:
# Scarica documento da MinIO
response = self.minio.get_object(
Bucket='ediscovery-originals',
Key=event['s3_key']
)
file_content = response['Body'].read()
# Estrai testo con Tika (PUT /tika)
tika_response = requests.put(
f"{self.TIKA_URL}/tika",
data=file_content,
headers={
'Accept': 'text/plain',
'Content-Type': 'application/octet-stream'
},
timeout=120
)
extracted_text = tika_response.text
# Estrai metadata con Tika (PUT /meta)
meta_response = requests.put(
f"{self.TIKA_URL}/meta",
data=file_content,
headers={
'Accept': 'application/json',
'Content-Type': 'application/octet-stream'
},
timeout=60
)
metadata = meta_response.json()
return {
'extracted_text': extracted_text,
'text_length': len(extracted_text),
'tika_metadata': metadata,
'author': metadata.get('dc:creator', ''),
'created_date': metadata.get('dcterms:created', ''),
'modified_date': metadata.get('dcterms:modified', ''),
'content_type': metadata.get('Content-Type', ''),
'language': metadata.get('language', ''),
'page_count': metadata.get('xmpTPg:NPages', 0)
}
def _index_document(self, doc: dict) -> None:
"""Indicizza documento su Elasticsearch per ricerca full-text."""
self.es.index(
index=f"ediscovery-{doc['matter_id']}",
id=doc['doc_id'],
document={
'doc_id': doc['doc_id'],
'matter_id': doc['matter_id'],
'custodian_id': doc['custodian_id'],
'filename': doc['filename'],
'content': doc['extracted_text'],
'author': doc.get('author', ''),
'created_date': doc.get('created_date'),
'modified_date': doc.get('modified_date'),
'content_type': doc.get('content_type', ''),
'language': doc.get('language', ''),
'page_count': doc.get('page_count', 0),
'file_size': doc['file_size'],
'sha256': doc['sha256'],
'collection_timestamp': doc['collection_timestamp'],
'status': 'PROCESSED',
'review_status': 'UNREVIEWED',
'relevance_score': None,
'privilege': False,
'tags': []
}
)
Tekilleştirme: Tam ve Kopyaya Yakın Tespit
Tipik bir e-Keşif koleksiyonunda belgelerin %40-70'i kopya veya neredeyse kopyadır. Orada tekilleştirme yalnızca inceleme maliyetlerini azaltmak değil, aynı zamanda Yasal gereklilik: Aynı e-postanın binlerce aynı kopyasını üretmek, kuralları ihlal eder keşfeder ve karşı tarafın maliyetlerini artırır. Tekilleştirmenin iki düzeyi vardır:
e-Keşifte veri tekilleştirme düzeyleri
- Tam Dedup (Karma tabanlı): Aynı SHA-256'ya sahip belgeler tam kopyalardır. "Gözaltı kopyasını" daha alakalı meta verilerle saklarsınız ve diğerlerini kopya olarak bağlarsınız.
- Neredeyse Tekilleştirme (MinHash/LSH): Benzer ancak aynı içeriğe sahip olmayan belgeler (imza eklenmiş e-posta, taslak versiyonlar). Yerelliğe Duyarlı MinHash gibi algoritmalar Hashing, Jaccard benzerliği > 0,85 olan belgeleri tanımlar.
- E-posta Dizisi: E-postaları konuşmalar (konu dizileri) halinde gruplama Yalnızca en yeni mesajı tüm bağlamıyla birlikte sunarak inceleme hacmini azaltın.
"""
ediscovery/processing/deduplication.py
Near-deduplication con MinHash e LSH
"""
from datasketch import MinHash, MinHashLSH
import hashlib
from typing import Optional
class DeduplicationEngine:
def __init__(self, num_perm: int = 128, threshold: float = 0.85):
self.lsh = MinHashLSH(threshold=threshold, num_perm=num_perm)
self.num_perm = num_perm
self.processed: dict[str, str] = {} # sha256 -> doc_id
def is_exact_duplicate(self, sha256: str) -> Optional[str]:
"""Restituisce doc_id del duplicato esatto o None."""
return self.processed.get(sha256)
def register_document(self, doc_id: str, sha256: str, text: str) -> None:
"""Registra un documento nel sistema di dedup."""
self.processed[sha256] = doc_id
minhash = self._compute_minhash(text)
self.lsh.insert(doc_id, minhash)
def find_near_duplicates(self, text: str, query_doc_id: str) -> list[str]:
"""
Trova near-duplicati del testo con Jaccard sim >= threshold.
Restituisce lista di doc_id simili.
"""
minhash = self._compute_minhash(text)
results = self.lsh.query(minhash)
return [r for r in results if r != query_doc_id]
def _compute_minhash(self, text: str) -> MinHash:
minhash = MinHash(num_perm=self.num_perm)
# Shingling a livello di parola (3-gram)
tokens = text.lower().split()
shingles = [
' '.join(tokens[i:i+3])
for i in range(len(tokens) - 2)
]
for shingle in shingles:
minhash.update(shingle.encode('utf8'))
return minhash
Teknoloji Destekli İnceleme ve Tahmine Dayalı Kodlama
İnceleme aşaması tarihsel olarak en pahalı aşamadır: Kıdemli avukatlar her belgeyi okur. ilgili, ilgisiz veya ayrıcalıklı (avukat-müvekkil ayrıcalığı kapsamında) olarak sınıflandırın. Teknoloji Destekli İnceleme (TAR) ile Sürekli Aktif Öğrenme (CAL) bu maliyeti büyük ölçüde azaltır: model, incelemecilerin kararlarından ve önceliklerinden öğrenir oranı en yüksek olduğunda incelemeyi durdurmanıza olanak tanıyan en muhtemel ilgili belgeler beklenen geri çağırma bir eşiği aşıyor (genellikle TREC Legal Track standardına göre %75).
"""
ediscovery/review/predictive_coding.py
Predictive Coding con Sentence Transformers e Active Learning
"""
from sentence_transformers import SentenceTransformer
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
import numpy as np
from typing import Optional
class PredictiveCodingEngine:
"""
Implementa TAR 2.0 (Continuous Active Learning).
Il reviewer classifica documenti, il modello re-addestra
e riordina la review queue.
"""
def __init__(self, model_name: str = 'all-mpnet-base-v2'):
self.encoder = SentenceTransformer(model_name)
self.classifier = LogisticRegression(
class_weight='balanced',
max_iter=1000
)
self.scaler = StandardScaler()
self.training_texts: list[str] = []
self.training_labels: list[int] = [] # 1=rilevante, 0=non rilevante
self.is_trained = False
def add_review_decision(
self,
doc_text: str,
is_relevant: bool
) -> None:
"""Aggiunge una decisione di review al training set."""
self.training_texts.append(doc_text)
self.training_labels.append(1 if is_relevant else 0)
# Re-addestra quando ci sono abbastanza esempi (>=10 per classe)
pos_count = sum(self.training_labels)
neg_count = len(self.training_labels) - pos_count
if pos_count >= 5 and neg_count >= 5:
self._retrain()
def _retrain(self) -> None:
embeddings = self.encoder.encode(
self.training_texts,
batch_size=32,
show_progress_bar=False
)
embeddings_scaled = self.scaler.fit_transform(embeddings)
self.classifier.fit(embeddings_scaled, self.training_labels)
self.is_trained = True
def predict_relevance(
self,
texts: list[str]
) -> list[dict]:
"""
Predice la rilevanza di una lista di documenti.
Restituisce lista di {text_index, relevance_prob} ordinata per score desc.
"""
if not self.is_trained:
# Prima del training, restituisce ordine casuale
return [
{'index': i, 'relevance_prob': 0.5}
for i in range(len(texts))
]
embeddings = self.encoder.encode(
texts,
batch_size=32,
show_progress_bar=False
)
embeddings_scaled = self.scaler.transform(embeddings)
probabilities = self.classifier.predict_proba(
embeddings_scaled
)[:, 1] # probabilità classe positiva
results = [
{'index': i, 'relevance_prob': float(p)}
for i, p in enumerate(probabilities)
]
return sorted(results, key=lambda x: x['relevance_prob'], reverse=True)
def estimate_recall(
self,
reviewed_count: int,
total_count: int,
relevant_found: int
) -> float:
"""
Stima il recall atteso usando il metodo seed+sample
secondo TREC Legal Track guidelines.
Semplificazione: usa il tasso di prevalenza osservato.
"""
if reviewed_count == 0:
return 0.0
prevalence = relevant_found / reviewed_count
estimated_total_relevant = prevalence * total_count
if estimated_total_relevant == 0:
return 1.0
return min(relevant_found / estimated_total_relevant, 1.0)
e-Keşif Platformlarını Karşılaştırma
E-Keşif platformlarına yönelik pazar konsolide olmakla birlikte, piyasanın baskısı altında hızla gelişmektedir. AI'nın. İşte 2025-2026'da mevcut olan ana çözümlerin bir karşılaştırması:
| platformu | Güçlü yönler | Sınırlamalar | Fiyatlandırma Modeli | Yapay Zeka Özellikleri |
|---|---|---|---|---|
| Görelilik | Kurumsal standartlar, geniş ekosistem, olgun API'ler | Kurulum karmaşıklığı, yüksek maliyetler | SaaS + kendi kendine barındırılan, GB başına | RelevanceAI, kavramsal arama, anormallik tespiti |
| DİSK | Bulutta yerel, modern kullanıcı deneyimi, yerel olarak entegre yapay zeka | Görelilik'ten daha küçük ekosistem | Abonelik + kullanım | DISCO AI: tahmine dayalı kodlama, otomatik etiketleme, belgelerde Soru-Cevap |
| Everlaw | Gerçek zamanlı işbirliği, mükemmel kullanıcı deneyimi, denemeye hazır | Toplu vakalar için daha az özellik | GB/ay başına | EverAI: tahmine dayalı kodlama, özet, biriktirme Soru-Cevap |
| Logikcull | Self servis, şeffaf fiyatlandırma, hızlı katılım | Çok karmaşık durumlar için daha az uygundur | GB veya abonelik başına | Otomatik etiketleme, destekli arama, gelişmiş tekilleştirme |
| Ortaya çıkarmak | Gelişmiş Yapay Zeka (Brainspace), tescilli NLP | Dik öğrenme eğrisi | Kurumsal lisanslama | Konu kümeleme, kavram arama, anormallik tespiti |
EDRM XML Dışa Aktarımı ve Doküman Üretimi
Son aşama, belgelerin mutabakata varılan formata göre karşı tarafa üretilmesidir. EDRM XML standardı, belgelerin tüm özellikleriyle alışverişi için bir XML şeması tanımlar. meta veriler, herhangi bir platforma yüklemeyi kolaylaştırır. Belgeler genellikle gelir olan ürünler Bates numaralandırması (benzersiz aşamalı numaralandırma) ve editör kadrosuyla ayrıcalıklı içeriğe uygulanır.
"""
ediscovery/production/edrm_exporter.py
Generazione export EDRM XML con Bates numbering
"""
import xml.etree.ElementTree as ET
from datetime import datetime, timezone
from typing import Iterator
def generate_edrm_xml(
documents: list[dict],
matter_id: str,
bates_prefix: str = "PROD",
start_bates: int = 1
) -> str:
"""
Genera EDRM XML per un set di documenti prodotti.
Ogni documento riceve un Bates number univoco.
"""
root = ET.Element('Root')
root.set('DataInterchangeType', 'Processed')
root.set('DateCreated', datetime.now(timezone.utc).isoformat())
root.set('Encoding', 'UTF-8')
root.set('MajorVersion', '1')
root.set('MinorVersion', '2')
batch = ET.SubElement(root, 'Batch')
documents_el = ET.SubElement(batch, 'Documents')
for idx, doc in enumerate(documents):
bates_num = f"{bates_prefix}{str(start_bates + idx).zfill(7)}"
doc_el = ET.SubElement(documents_el, 'Document')
doc_el.set('DocID', doc['doc_id'])
# Tags con metadata
tags_el = ET.SubElement(doc_el, 'Tags')
def add_tag(name: str, value: str, data_type: str = 'Text') -> None:
tag = ET.SubElement(tags_el, 'Tag')
tag.set('TagName', name)
tag.set('TagValue', value)
tag.set('TagDataType', data_type)
add_tag('BatesNumber', bates_num)
add_tag('DocID', doc['doc_id'])
add_tag('MatterID', matter_id)
add_tag('Custodian', doc.get('custodian_id', ''))
add_tag('FileName', doc.get('filename', ''))
add_tag('DateCollected', doc.get('collection_timestamp', ''), 'DateTime')
add_tag('DateCreated', doc.get('created_date', ''), 'DateTime')
add_tag('Author', doc.get('author', ''))
add_tag('FileSize', str(doc.get('file_size', 0)), 'LongInteger')
add_tag('SHA256', doc.get('sha256', ''))
add_tag('ContentType', doc.get('content_type', ''))
add_tag('ReviewStatus', doc.get('review_status', ''))
add_tag('IsPrivileged', str(doc.get('privilege', False)), 'Boolean')
add_tag(
'RelevanceScore',
str(round(doc.get('relevance_score', 0), 4)),
'Decimal'
)
return ET.tostring(root, encoding='unicode', xml_declaration=True)
Kritik Yasal Hususlar
- Mülksüzleştirme: Bundan sonra delillerin yok edilmesi veya değiştirilmesi ve makul bir şekilde öngörülebilir yasal işlemler, olumsuz çıkarımlar da dahil olmak üzere ciddi yaptırımlara yol açabilir talimatlar (jüriye, yok edilen delillerin olumsuz olduğunu varsayma talimatı verilmesi).
- Ayrıcalık İncelemesi: Avukat-müvekkil imtiyazı veya işi kapsamındaki belgeler Ürün doktrini üretimden önce belirlenmeli ve taslak haline getirilmelidir. Bir üretim ayrıcalıklı belgelerin kazara kullanımına geri alma anlaşmaları (FRE 502(d)) ile itiraz edilebilir.
- Sınır Ötesi Veri Gizliliği: Avrupalı çalışanlardan veri toplayın ABD'nin keşfi, derinlemesine GDPR analizi gerektirir. ABD-AB Veri Gizliliği Çerçevesi (2023) transferleri basitleştirdi ancak gerekli özenin gösterilmesi hâlâ hayati önem taşıyor.
- Yapay Zeka Savunması: Mahkemeler, kullanılan TAR yöntemleri konusunda şeffaflığa ihtiyaç duyar. Seçilen protokol ve geri çağırma eşiği belgelenmeli ve savunulabilir olmalıdır.
Gözetim Zinciri için Veritabanı Şeması
İlişkisel veritabanı ve platformun yasal omurgası. Her belgedeki her eylem zaman damgası, kullanıcı ve eylem nedeni ile takip edilmelidir. Bu denetim izi mutlaka değiştirilemez: hiçbir kayıt geriye dönük olarak silinemez veya değiştirilemez.
-- Schema PostgreSQL per e-Discovery con audit trail immutabile
-- Matters (cause/procedimenti)
CREATE TABLE matters (
matter_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
matter_name TEXT NOT NULL,
matter_number TEXT UNIQUE NOT NULL,
client_id UUID NOT NULL,
status TEXT NOT NULL DEFAULT 'ACTIVE',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
closed_at TIMESTAMPTZ
);
-- Custodians (soggetti i cui dati vengono raccolti)
CREATE TABLE custodians (
custodian_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
matter_id UUID REFERENCES matters(matter_id),
full_name TEXT NOT NULL,
email TEXT NOT NULL,
department TEXT,
hold_applied BOOLEAN NOT NULL DEFAULT FALSE,
hold_date TIMESTAMPTZ
);
-- Documents (indice master dei documenti)
CREATE TABLE documents (
doc_id UUID PRIMARY KEY,
matter_id UUID REFERENCES matters(matter_id),
custodian_id UUID REFERENCES custodians(custodian_id),
filename TEXT NOT NULL,
file_size BIGINT NOT NULL,
sha256 CHAR(64) NOT NULL,
md5 CHAR(32) NOT NULL,
s3_key TEXT NOT NULL,
content_type TEXT,
is_duplicate_of UUID REFERENCES documents(doc_id),
is_near_dup_of UUID REFERENCES documents(doc_id),
collection_timestamp TIMESTAMPTZ NOT NULL,
status TEXT NOT NULL DEFAULT 'COLLECTED',
review_status TEXT NOT NULL DEFAULT 'UNREVIEWED',
relevance_score NUMERIC(5,4),
privilege BOOLEAN NOT NULL DEFAULT FALSE,
bates_number TEXT UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Custody events (audit trail immutabile)
-- Constraint: nessuna UPDATE/DELETE permessa
CREATE TABLE custody_events (
event_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
doc_id UUID REFERENCES documents(doc_id),
matter_id UUID REFERENCES matters(matter_id),
custodian_id UUID,
collector_id UUID,
event_type TEXT NOT NULL, -- COLLECTION, PROCESSING, REVIEW, PRODUCTION, etc.
timestamp TIMESTAMPTZ NOT NULL,
source_path TEXT,
sha256 CHAR(64),
md5 CHAR(32),
file_size BIGINT,
s3_key TEXT,
user_id UUID,
notes TEXT
);
-- Impedisce UPDATE e DELETE sulla tabella eventi
CREATE RULE no_update_custody_events AS
ON UPDATE TO custody_events DO INSTEAD NOTHING;
CREATE RULE no_delete_custody_events AS
ON DELETE TO custody_events DO INSTEAD NOTHING;
-- Review decisions
CREATE TABLE review_decisions (
decision_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
doc_id UUID REFERENCES documents(doc_id),
reviewer_id UUID NOT NULL,
decision TEXT NOT NULL, -- RELEVANT, NOT_RELEVANT, PRIVILEGED, NEEDS_REDACTION
confidence TEXT, -- HIGH, MEDIUM, LOW
review_timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
notes TEXT
);
Sonuçlar ve Sonraki Adımlar
Kurumsal düzeyde bir e-Keşif platformu oluşturmak, üçünü dengeleyen bir mimari gerektirir zorunluluklar sıklıkla birbirleriyle gerilim içindedir: teknik performans (milyonları işleyin belgelerin makul bir sürede teslim edilmesi), yasal doğruluk (gözaltı zinciri değişmez, geri çağırma belgelenebilir, imtiyaz incelemesi savunulabilir) e kullanılabilirlik (inceleyenler veri bilimcileri değil, avukatlardır).
Araştırdığımız temel bileşenler: SHA-256 ile adli veri alımı, işleme Apache Tika ile dağıtılır, MinHash/LSH ile veri tekilleştirme ve Active ile Tahmine Dayalı Kodlama Öğrenme — 2025-2026'da sektörün son durumunu temsil eder. Yapay zekanın benimsenmesi TAR'ı deneysel teknolojiden pazar standardına dönüştürdü: platformların %79'u bugün yerel olarak entegre ediyor.
Serinin bir sonraki makalesinde nasıl inşa edileceğini göreceğiz. Uyumluluk Motoru Düzenleyici izlemeyi gerçek zamanlı olarak otomatikleştirmek için dinamik kural motorlarıyla.
Kaynaklar ve Analizler
- EDRM (Elektronik Keşif Referans Modeli): edrm.net
- Görelilik Platformu Belgeleri: relativity.com/artificial-intelligence
- TREC Yasal Takip Geri Çağırma Yönergeleri: Measure.it geri çağırma standardı
- FRE 502(d): Yanlışlıkla üretilen ayrıcalıklı belgeler için geri alma anlaşmaları
- datasketch - MinHash/LSH için Python kütüphanesi
- cümle dönüştürücüler: Tahmine Dayalı Kodlama için yerleştirmeler







