Overview System Design Data Historical Model Structured Model Modern Extension Cross-Era Hybrid Model Agent Findings
Legal NLP  |  Agentic AI  |  LEGAL-BERT + QLoRA

Verdict: Predicting Appellate
Case Outcomes from Case Text

A from-scratch legal NLP pipeline built entirely on public-domain CourtListener bulk data. Two fine-tuned LEGAL-BERT models predict how a US appellate court ruled: affirmed, reversed, dismissed, and four other dispositions. A structured-metadata model and an Optuna-tuned hybrid fusion model round out four honestly reported classification experiments, and a three-step agent, classify, retrieve precedent, then explain with a QLoRA fine-tuned language model, turns the prediction into a grounded rationale.

PythonPyTorchTensorFlow LEGAL-BERTHuggingFace Transformers QLoRAPEFTFAISS OptunaFastAPICourtListener Bulk Data
55.8%
Historical text model accuracy
76.5%
Modern full-text model accuracy
75.9%
Hybrid text + structured accuracy
18,482
Labeled cases across both eras
System Design

The Whole System, Before the Detail

Two data sources feed two preprocessing pipelines, which train four models, which the agent sits on top of and serves through one API. Every box below is covered in its own section further down this page.

DATA SOURCES opinion-clusters 2.46GB, disposition labels + historical syllabi opinions 54.56GB, full text joined on cluster_id PIPELINES Historical Filter 1860 to 1935 14,083 cases Modern Join 2019 to 2023 4,399 cases MODELS Historical LEGAL-BERT 55.8% accuracy Modern LEGAL-BERT 76.5% accuracy Structured Model Random Forest, 36.1% Hybrid Fusion Optuna-tuned, 75.9% AGENT + SERVING THE AGENT 1. Classify (LEGAL-BERT) 2. Retrieve (FAISS, 4,399 cases) 3. Explain (QLoRA Qwen2.5-3B) FastAPI /classify · /agent/analyze detailed in the Agent section below
Data Foundation

Court Data, Straight From the Source

Every case in this project comes from CourtListener, a project of the Free Law Project that publishes bulk exports of US court data for free, no API key required. Two exports were used: opinion-clusters (2.46GB compressed, one row per case with a disposition field and, for older cases, a written syllabus) and opinions (54GB compressed, one row per individual opinion, holding the actual full text). Both are streamed and decompressed directly from a public S3 bucket rather than downloaded in full: the historical dataset came from the first 600MB (about 24%) of the smaller file.

Why the historical model is scoped to 1860-1935. Cases filed after 1990 almost always have a disposition label but an empty syllabus. Writing a formal case syllabus was a publishing convention of the old West and official state reporters, digitized here via Harvard's Caselaw Access Project: modern digital opinions don't carry it. 1860-1935 is where disposition labels and summary text both exist in volume.

Historical Case Outcome Distribution

14,083 cases, each with a disposition normalized from dozens of inconsistent raw forms (including OCR-garbled text from scanned reporters, like "Aeeirmed" for "Affirmed") into 7 clean classes, sampled with per-class caps via reservoir sampling so the two largest classes don't drown out the rest.

Bar chart of historical case counts by disposition class
Final class distribution. Affirmed (4,000) and reversed (3,500) dominate, as expected for appellate courts, down to mixed rulings at 255 examples, the thinnest class in the set.
Model One

LEGAL-BERT on Historical Case Syllabi (1860-1935)

Base checkpoint: nlpaueb/legal-bert-base-uncased, pretrained on legal text rather than generic web text. Fine-tuned in PyTorch on the case's syllabus, summary, procedural history, and posture. The actual disposition sentence is never included, so the model has to infer the outcome from the facts and legal question, not read the answer off the page.

Extract
Stream + Decompress
600MB of the clusters export streamed and bz2-decompressed directly, no full download.
Normalize
7-Class Disposition
Ordered regex rules, including OCR-garbling fallbacks, collapse messy raw text into clean labels.
Weight
Class-Weighted Loss
Inverse-frequency weights in CrossEntropyLoss so the model can't win by mostly guessing "affirmed."
Evaluate
Stratified 70/15/15
9,857 / 2,113 / 2,113 split, held-out test set, no leakage of the answer into the input text.
55.8%
Test accuracy (vs. 28.4% majority baseline)
0.544
F1 macro across 7 classes
2,113
Held-out test cases
Confusion matrix for the historical LEGAL-BERT model across seven disposition classes
Procedural outcomes are easy, the substantive call is hard. Granted/denied and dismissed are cleanly separated (formulaic language), but 181 of 447 true "affirmed" cases are predicted "reversed," and 85 of 525 true "reversed" cases are predicted "affirmed." The model struggles specifically at inferring which way a contested ruling went from a summary that never states the answer, not at reading legal text in general.
Model Two, and a Model Family Comparison

How Much Signal Lives in Case Bookkeeping Alone?

Three model families, a TensorFlow MLP, XGBoost, and Random Forest, trained on structured metadata only: no text, no legal reasoning, to measure directly how much of a case's outcome is predictable from citation count, filing date, and case type alone. This matters because the export carries no court or docket-type field for this era: checked directly, 0% of all 14,083 cases had a populated nature_of_suit field.

Case name patternAffirmed rateReversed rateInterpretation
Ex parte …0.0%1.1%Almost never a clean affirm/reverse: these are motions and writs
People v. / Commonwealth v.48.1%36.9%Notably higher affirm rate than ordinary civil cases
Ordinary civil X v. Y28.1%25.1%Baseline rate, the majority pattern

Checked correlations from the case name alone, used as a case_type feature alongside citation count and filing year (both log-scaled to handle their right-skew).

36.1%
Random Forest accuracy, the strongest structured-only model
0.280
Random Forest F1 macro
15
Structured features, no text at all
Bar chart comparing majority baseline, TensorFlow MLP, XGBoost, and Random Forest accuracy and F1 on the structured-metadata-only task
The model family matters as much as the features. Random Forest (36.1% / F1 0.280) and XGBoost (32.3% / F1 0.276) both clear the 28.4% majority-class floor; the TensorFlow MLP (26.8% / F1 0.247) does not. Random Forest's feature importances independently confirm the case-type finding above: case_type_ex_parte is its single most important feature.
Bar chart comparing majority baseline, structured metadata model, and LEGAL-BERT text model accuracy and F1
Text still wins by a wide margin. Even the best structured-only model (36.1%) falls well short of the text model's 55.8%. The honest ceiling for case-bookkeeping-only prediction on this data is modest.
Recovering Modern Data

2019-2023, From a 54GB File With No Sort Order

Modern cases have disposition labels but no syllabus. The actual full opinion text exists in CourtListener's separate opinions export: 54GB compressed, joinable on cluster_id, but with no useful sort order and, confirmed with a direct HTTP range request, no ability to resume from a byte offset (a single continuous bz2 stream, not block-concatenated).

Target
4,405 Cases
2019-2023 disposition-labeled cluster IDs identified from the smaller clusters export.
Stream
Full Streaming Join
Complete scan of the 54.56GB opinions file, matching cluster_id, 10.8 million rows read.
Recover
4,399 Full Opinions
99.9% of the 4,405 target cases, median 14,843 characters, about 9× richer than the historical syllabi's median of 1,665.
76.5%
Modern text model accuracy
0.704
F1 macro
24/263
Affirmed cases misread as reversed
Confusion matrix for the modern LEGAL-BERT model trained on full opinion text
Full reasoning text resolves the historical model's exact weakness. With the court's own reasoning available instead of a short syllabus, affirmed/reversed confusion drops from 40.5% (historical model) to 9.1% (modern model): direct confirmation that the bottleneck was information, not model capacity.
A Cross-Check

Neither Model Transfers Across the Era Gap

With two trained models, the obvious next question: does either say anything useful about the other's era? Each model was run on the other's held-out test set.

Bar chart comparing in-domain and cross-era accuracy and F1 for the historical and modern models
Both fall well short of their in-domain performance outside their own era. The historical model reaches 42.6% accuracy on modern cases but only 0.299 F1-macro: procedurally-formulaic classes like dismissed are catchable across eras, but it still fails badly on the substantive classes specifically. The modern model fares similarly in reverse (29.8% accuracy, 0.256 F1-macro) on historical cases. The 1860-1935 and 2019-2023 corpora are close to different languages for this task.
Model Three: Fusion

Combining Text and Structure, Tuned with Optuna

A late-fusion model on the modern (full-text) data: a frozen 768-dimensional embedding pulled from the already fine-tuned modern text model, concatenated with the same structured feature vector used in the metadata model, then a small fresh classification head trained on the combined 781-dimensional vector. Freezing the encoder is what makes a full hyperparameter search practical, since each trial only trains the small head, seconds rather than minutes.

Learning rateValidation F1 macroEarly-stopped at epoch
0.0018860.73586
0.0005440.733118
0.0035190.73152
0.0009990.731020
0.0004580.730730

Optuna search, 30 trials, log-uniform learning rate between 1e-5 and 1e-1. The "best number of epochs" was not searched as a separate dimension: every trial trains with early stopping (patience of 10 epochs on validation F1 macro), so the effective epoch count falls directly out of where the winning trial's own early stopping triggered.

Training and validation loss curve over 15 epochs for the hybrid model
Loss and F1 diverge, and that's expected. The checkpoint actually kept was epoch 5 (best validation F1 macro), while raw validation loss bottoms out immediately, at epoch 1, then rises for the rest of training. Cross-entropy loss can keep worsening on confidently-wrong minority-class predictions even as the argmax decisions, what F1 measures, keep improving elsewhere.
Global gradient L2 norm over all training steps for the hybrid model
What gradient descent looks like in practice. Global L2 norm across all 720 training steps of the final run: a sharp early decline as the head moves out of random initialization, then a long, genuinely noisy plateau. Not a smooth textbook curve, but healthy convergence for a small batch size on a class-weighted loss.
75.9%
Hybrid model test accuracy
0.683
Hybrid model F1 macro
76.5%
Text-only model accuracy, for comparison

Text alone (76.5%) edges out the text-plus-structure fusion model (75.9%) on this dataset. Structured metadata's contribution is small once the text signal is already strong, and its value depends on how much text data is available.

Bar chart comparing text-only and hybrid model accuracy and F1
The effect per class is genuinely mixed. Affirmed and reversed improve slightly with structure added; reversed_remanded, dismissed, granted_denied, and mixed all dip slightly. Structured features nudge the model toward the classes where citation and case-type patterns are most informative, at a small cost elsewhere: a trade-off, not a uniform win.
Model Four: The Agent

The Agent: Classify, Retrieve, Explain

Every model so far answers "what disposition." The agent answers "why," and does it as three separately verifiable steps rather than a single LLM call dressed up as reasoning. Given a new case, it classifies the disposition with LEGAL-BERT, retrieves genuinely similar precedent from a vector index instead of letting a language model recall (and potentially invent) citations from memory, then generates a grounded rationale with a language model fine-tuned specifically for this task.

Step 1
Classify
The fine-tuned modern LEGAL-BERT model predicts the disposition and a full probability distribution across all six classes.
Step 2
Retrieve
A FAISS index over the same frozen embeddings finds genuinely similar precedent from all 4,399 modern cases, returned separately so it can be checked, not silently merged into the generated text.
Step 3
Explain
A QLoRA fine-tuned Qwen2.5-3B generates a rationale grounded in the case facts, in the style of the judicial reasoning it was trained on.

System Architecture

The actual request path, not a simplified summary: a new case's text is classified and embedded independently, a decision gate checks whether the document actually contains enough substance to explain before ever calling the language model, and the retrieved precedent is returned as its own field rather than folded into the generated text, so every part of the response traces back to a specific, checkable step.

New Case Text raw facts + posture THE AGENT: THREE SEPARATELY VERIFIABLE STEPS LEGAL-BERT Classifier modern_text_classifier disposition + confidence scores 6-way softmax Frozen Embedding same checkpoint, [CLS] pooled 768-dim vector FAISS Precedent Index 4,399 cases, cosine top-k IndexFlatIP, L2-normalized Decision Gate under 150 words, or outcome already stated verbatim in the text? no yes QLoRA Generator Qwen2.5-3B, 4-bit NF4 base + LoRA adapter (0.96% params) prompt: facts + disposition sampled, temperature 0.7 Fallback Message explains why, no generation call Response prediction retrieved_precedent generated_rationale returned as three separate fields, never merged into one blob FastAPI POST /agent/analyze · lazy-loaded

Fine-Tuning With QLoRA

Qwen2.5-3B is loaded in 4-bit (NF4 quantization) and adapted with LoRA rather than fully fine-tuned, the standard efficient approach for adapting a multi-billion-parameter model on a single consumer GPU. Only the adapter layers train; the quantized base model stays frozen throughout.

29.9M
Trainable LoRA parameters
0.96%
Of the model's 3.1B total parameters
3,393
Training pairs

A Walkthrough

On a held-out case (R.F. v. Cecil County Public Schools, an IDEA special-education dispute), the agent's three steps in order:

1. Classification

Predicted affirmed at 70.1% confidence. Actual disposition: affirmed.

2. Retrieval

Surfaced precedent including Griffin v. Dep't of Labor Fed. Credit Union (91.0% similarity) and Anthony Wright v. Kenneth Lassiter (90.9% similarity), both affirmed dispositions.

3. Explanation

Generated reasoning grounded specifically in IDEA and IEP annual-goal standards, the legal framework this case actually turns on, not generic legal boilerplate.

Knowing when there's nothing to explain. Some appellate filings are purely procedural, a one-paragraph order dismissing an appeal for a missed filing deadline, with no substantive reasoning to reconstruct because the document already states its own outcome directly. The agent recognizes this pattern, both from document length and from the disposition already appearing verbatim in the text, and responds by explaining that no further rationale applies rather than generating text with nothing to ground it in.
Summary

Key Findings

Text Beats Paperwork, By a Wide Margin

Case text predicts outcome far better than case bookkeeping: 55.8 to 76.5% accuracy from text versus 36.1% from the best structured-only model.

Full Data Recovery, Full Text

4,399 of 4,405 modern cases (99.9%) recovered with full opinion text via a complete streaming join across a 54.56GB export. The modern model reaches 76.5% accuracy, with affirmed/reversed confusion down to 9.1%.

The Model Family Matters as Much as the Features

Random Forest reaches 36.1% accuracy on structured metadata alone, clearing the majority-class floor where a neural net (26.8%) does not: the conventional strong choice for small tabular data, confirmed directly.

Neither Model Crosses the Century Gap

Both the historical and modern models fall well short of in-domain performance on each other's era: legal language and disposition-writing conventions don't transfer across a century.

Structure Helps Most When Text Is Scarce

The hybrid text-plus-structure model outperforms text alone on a smaller dataset but trails it narrowly at full scale: structured metadata's marginal value shrinks as the text model itself gets stronger.

An Agent, Not a Chatbot Wrapper

Classification, retrieval, and generation are three separately verifiable steps: a FAISS index over precedent and a QLoRA fine-tuned Qwen2.5-3B generating rationale grounded in the case's own facts, with training pairs sourced entirely from actual judicial reasoning.