← Back
Home › Systems › Web, content and learning

legal-research

Case-law and statute research with a citator graph, point-in-time statutes, lexical and ranked search, and AI drafting limited to retrieved sources.

Westlaw/CoCounsel, Lexis+ AI, vLex, Bloomberg Law

Open the legal-research pagelegal-research Citation groundingagentic-ai: Attaching answer spans to retrieved source passages and verifying each claim is supportedHdr histogramdata-structures: Log-linear bucketed histograms with bounded relative error for latency percentilesDeterministic simulationtestingdistributed: Run the whole system in one thread under a seeded scheduler with simulated network, disk and clock, so every failure replays from its seed.›Primary-backup replicationdistributed: One node orders all writes and ships them to standbys; the acknowledgement rule (none, one, all) sets what a crash loses, and an external arbiter decides who takes over.›Transitive closure indexgraphs: Segment/interval labelling of DAGs (commit graphs) for fast reachability queriesProcess supervisionhardware-os: A parent process starts services, reaps their exits and restarts them under a delay and burst-limit policy, then stops them with SIGTERM before SIGKILL.›Embedding inferencemachine-learning: Serve a pretrained embedding model: tokenize, batch requests, run the encoder, pool and normalize vectors, cache them; no trainingEntity linkingmachine-learning: Map each mention to a knowledge-base id, or to NIL, by generating candidates from an alias table, scoring them on prior and context, and thresholding.›Layout analysismachine-learning: Detecting page regions (text blocks, tables, figures) and reading order in documentsLearning to rankmachine-learning: Train a scorer on query-grouped data so that the order it induces maximizes NDCG; LambdaMART (boosted trees with NDCG-weighted pair gradients) is the production default.›LLM-as-judgemachine-learning: Grade open-ended outputs with a language model under a rubric, then correct its known biases by order swapping, voting and calibration against human labels.›Named entity recognitionmachine-learning: Find the character spans of entities in text and give each a type, by labelling every token and decoding a valid label sequence.›Optical character recognition(text recognition)machine-learning: Find text lines in an image, read each crop with a CTC or attention model, then correct with a lexicon and score confidence for review routing.›PDF text extractionmachine-learning: Rebuild words, lines, columns and reading order from the glyph positions a born-digital PDF stores, and route pages without usable text to OCR.›Relation extractionmachine-learning: Extracting (subject, relation, object) triples from text with classifiers or LLMsText classificationmachine-learning: Assign one or more labels to a text; climb a ladder from regexes to linear models on n-grams, embeddings, fine-tuned encoders and LLM prompts, only as far as the data and budget demand.›Aho-Corasick multi-patternmatchingsearch-sort: Find all occurrences of many fixed patterns in one pass over the text, in time independent of the pattern count, using a trie with failure links.›BM25 rankingsearch-sort: The standard lexical relevance score: IDF-weighted term frequency with saturation (k1) and length normalization (b); default in Lucene, Elasticsearch and SQLite FTS5.›Boolean expression indexingsearch-sort: Indexing conjunctive targeting expressions so one event finds all matching ads/subscriptions fastCross-encoder rerankingsearch-sort: Re-score the top-k candidates of a cheap first stage with a model that reads query and candidate together; more accurate than separate embeddings, but one model call per pair.›Finite state transducersearch-sort: Minimal acyclic FST mapping sorted keys to values; compact term dictionaries with prefix/fuzzy searchHybrid search and rank fusionsearch-sort: Merge a keyword (BM25) result list and a vector (ANN) list into one ranking, by ranks (RRF) or by normalized scores (convex combination).›Linear-time regular expressionmatchingsearch-sort: Match a regular expression in O(pattern x text) worst case by tracking all live parse positions at once, then cache the state sets as a lazily built DFA for speed.›Stemmingsearch-sort: Porter/Snowball suffix stripping and lemmatisation for term normalisationCoverage-guided fuzzingsecurity: Mutate inputs, run an instrumented target, and keep any input that reaches a new code edge; feedback turns blind guessing into incremental search.›Mutual TLSsecurity: TLS where the client also proves a certificate key, so every connection carries a verified, rotatable machine identity.›Bitemporal datastorage: Valid time plus transaction time on every record for as-of queries, back-dating and correctionsInverted indexstorage: Map each term to a sorted, compressed list of the documents containing it; the core of full-text search and other fast set queries.›Point in time joinstorage: As-of join that picks the latest feature value at or before each event timestampPoint-in-time recoverystorage: Periodic base backups plus a continuously archived change log let you rebuild the state as of any instant, then branch from there.›Realtime query subscriptionsstorage: Live queries that push incremental result diffs to clientsTriple store indexingstorage: Storing RDF/property triples with SPO/POS/OSP permutation indexes and dictionary-encoded idsGraceful drainingsystems: On a termination signal, stop taking new work, finish in-flight work until a deadline, tell clients to reconnect elsewhere, then exit; clients resume from a token.›Text chunkingmachine-learning: Cut documents into token-bounded pieces along structure boundaries, keeping offsets and section paths, so each piece embeds well and can be cited.›Fuzzy string matchingsearch-sort: Edit distance, Jaro-Winkler and trigram indexes for approximate name and address matchingCooperative schedulingconcurrency: Green threads/coroutines yielding at await points on a user-space schedulerFault injectiondistributed: Inject latency, errors, kills and partitions via proxies, netem or hooksRecord replaydistributed: Record every nondeterministic input (time, randomness, I/O, model and tool outputs, per-tick player input) and re-execute deterministically for debugging, replays and state rebuildLeasesdistributed: A lock that expires on its own: bounded-time ownership that survives crashes and partitions, given bounded clock drift and fencing tokens on writes.›Health checkingsystems: Decide per backend whether to route to it using probes and real-traffic errors, with consecutive-count hysteresis, capped ejection and a panic fallback.›BFS and DFSgraphs: The two basic graph traversals; BFS gives hop distances and levels, DFS gives discovery/finish order for cycles, components and topological order.›Topological sortgraphs: Order the vertices of a DAG so every edge points forward; the core of build systems, schedulers and dependency resolution, with cycle detection for free.›Dynamic batchingml-training: Queue independent requests and dispatch them as one batch when it is full or the oldest has waited max_delay; bounded extra latency buys throughput.›Weight quantizationml-training: Store weights as 8/4-bit integers with per-group scales; decode is memory-bound, so 4-bit weights give about 3-4x tokens/s and fit 4x larger models.›Embeddingsmachine-learning: A learned table mapping discrete ids to dense vectors: gather forward, sparse scatter-add backward, row-wise optimizers to keep huge tables cheap.›+ 44 moreTransparent interceptionnetworking: Redirect traffic to a local proxy (iptables TPROXY/REDIRECT, eBPF) without app changesSyscall interceptionsecurity: Intercept syscalls via ptrace, seccomp user notify or a user-space kernelFencing tokensdistributed: A number that grows with every lock grant and is checked by the protected resource, so a paused ex-holder's late write is rejected.›Sparse matrices (CSR)numerics: Compressed sparse row stores only nonzeros as row pointers, column indices and values; SpMV streams them once, so it is memory-bandwidth bound.›MPMC bounded queueconcurrency: Vyukov's array queue for many producers and consumers: each slot carries a sequence number, so one CAS per operation claims a slot without locks.›Backpressuresystems: Bounded queues and credit-based flow so slow consumers throttle producersHierarchical timing wheelsystems: Timers bucketed by expiry into wheels of slots, coarser wheels for far deadlines: O(1) insert and cancel for millions of timeouts.›Low precision number formatsnumerics: Bf16, fp16, fp8 (E4M3, E5M2), MX block formats and int4: ranges, rounding, scalingLU and Cholesky factorizationnumerics: Factor A = PLU (any square matrix, partial pivoting) or A = LLᵀ (SPD) once in O(n³), then solve each right-hand side in O(n²).›AdamWml-training: Adam's per-parameter adaptive steps with weight decay applied directly to the weights; the default optimizer for transformers and LLM pretraining.›Reverse-mode automaticdifferentiationml-training: Record the forward computation on a tape, then sweep it backwards once to get the gradient of a scalar loss for all inputs.›+ 21 moreEbpfhardware-os: Verified in-kernel programs on hooks (kprobes, XDP, tc) with mapsPtrace process controlhardware-os: Ptrace: attach, read and write registers and memory, single-step, intercept signalsSeccomp bpfsecurity: Syscall filtering with seccomp BPF programsFalse sharingconcurrency: Independent variables written by different cores on one cache line force the line to bounce between cores; pad or regroup hot data to fix it.›Memory orderingconcurrency: What Relaxed, Acquire, Release and SeqCst atomics actually guarantee, what they cost on x86 and ARM, and which one each pattern needs.›Slab allocatorsystems: Pre-carve pages into equal-size slots per size class with an in-place free list: O(1) alloc and free, no external fragmentation, cache-warm reuse.›+ 4 moreCPU caches and the memoryhierarchyhardware-os: Sizes, line widths and latencies of L1/L2/L3/DRAM on modern x86 and Apple M-series, and the data-layout rules that follow from them.›

How it is built

Architecture overview

A legal research platform lets a lawyer find the decisions and statutes that govern a question, check that they are still good law, and increasingly draft text that cites them. Its value sits in a layer a search engine lacks: a citator graph. Every decision is parsed for the citations it makes, each citation is labelled with how the citing court treated the cited one (followed, distinguished, criticized, overruled), and the labels are combined with a court hierarchy that says which court binds which. A hit is therefore never just "relevant"; it is relevant, binding or only persuasive for this court, and still good law or not. Around it sit ingest, an editorial layer of headnotes, point-in-time storage for statutes, a lexical search stack with the terms-and-connectors syntax lawyers rely on, and an AI drafting layer that may only say what retrieved passages support. The one idea: the text is searched, but the graph decides what to trust. Closed examples are Westlaw (with CoCounsel), Lexis+ AI, vLex and Bloomberg Law; the open reference is CourtListener with the Free Law Project tools.

Parts:

 court feeds, dockets, statute feeds
        │ cursors
        ▼
 ingest ─► text + offsets ─► citation/entity layer ─► citator graph ◄── editors
 (PDF/OCR)      │            (regex, Aho-Corasick,     (typed edges,
                │             NER, linking)             court tree)
                ▼                                           │
        inverted index ◄── topics ◄── taxonomy       bitemporal store
        (positions, FST)                             (statutes, treatments)
                └────► search: connectors ► BM25 ► LTR ► rerank ◄─┘
                              │            (authority, hierarchy, as-of date)
          alerts ◄────────────┴────► drafting: retrieve ► generate ► ground ► citator check

There are two workloads: a write path per new document (minutes are fine, correctness is not negotiable) and an interactive read path that returns results, ranking and good-law flags together.

How the parts fit together

Ingest

Courts publish PDFs, HTML and bulk feeds in inconsistent forms. Born-digital PDFs go through PDF text extraction; pages without a usable text layer (older scans, stamped filings) go to OCR. Layout analysis (no page yet) separates body text from footnotes, running headers and tables: a header repeated on every page corrupts proximity searches. Docket ingest is a polling problem: each court source keeps an incremental sync cursor, and the cursor is saved only after the document is stored.

Contract with the next part: one record per document with its court id, decision date, a docket or reporter identifier, and text plus character offsets that never change afterwards. Citations, headnotes and grounding all store spans against these offsets, so a re-extraction that shifts them creates a new document version.

Citation and entity layer

Legal text is full of rigid-grammar strings: reporter citations, short forms, "id." and "supra", statute sections. The open tool eyecite finds them with a reporter pattern database and an Aho-Corasick prefilter (source: eyecite README). The same split fits here: a cheap multi-pattern pass (Aho-Corasick) finds the few places where any reporter abbreviation occurs, then a linear-time regex engine runs the full patterns only there. Linear-time matching matters because filings are untrusted text and a backtracking engine turns a crafted one into a denial of service.

Judges, courts, parties and agencies are found by Named entity recognition and resolved by Entity linking against a knowledge base of courts and judges (alias table, then context score, then NIL for unknown names). "The Ninth Circuit" and "9th Cir." must land on one court id, because the hierarchy and the citator key on it. A wrong link is worse than no link: threshold, and send doubtful ones to editors.

Citator

The citator turns citation spans into edges: for each (citing, cited) pair, the span, a treatment label and a confidence. Relation extraction (no page yet) is the task: the sentence around the citation says "we decline to follow" or "overruled on other grounds", and a text classifier over that window assigns the label. Climb only as far as the data demands: patterns for explicit phrases ("overruled by"), a fine-tuned encoder for the rest, an LLM prompt for the hard tail, and an editor queue for anything that would raise a negative flag.

Two stores back it. Triple store indexing (no page yet) holds (subject, relation, object) facts with permutation indexes: case cites case with label X, court A appeals to court B, case belongs to topic T. Triples carry the same provenance fields on every fact (source, model version, editor id). Transitive closure index (no page yet) answers the reachability questions that otherwise need a graph walk per query: "is court A above court B?" and "does any negative subsequent history lie on the appeal chain of this decision?". The court tree is small and nearly static, so interval labelling makes each test two integer comparisons.

The rule that combines them: a treatment counts against a decision only if the treating court is above or equal to the decision's court, or is an appellate disposition of this very case. A district court "criticizing" a Supreme Court decision does not flag it. That is why the hierarchy is data, not code.

Editorial taxonomy

Headnotes state one legal point decided in a case, filed under a topic tree. Editors write them; the model triages. Vectors from a pretrained embedding model (embedding-inference (no page yet), run at serving time, no training of its own) for passages and topic descriptions shortlist candidates, a classifier scores them, and the candidate-score-NIL pattern of entity linking attaches the passage to a taxonomy node. The output feeds facets, "same point of law" grouping and the topic feature in ranking. Low-confidence proposals go to an editor, whose answers become training data for the next classifier.

Law over time

Statutes change; decisions apply the version in force when the facts happened. The store is Bitemporal data (no page yet): every row has valid time (when the text was law) and transaction time (when the platform learned it). A section amended in March with the amendment commencing in July is one row with a valid-from in July recorded in March; the UK legislation service exposes the same idea as enacted, dated and prospective versions of one provision (source: legislation.gov.uk developer docs). A citator label corrected by an editor in October keeps its old row, so "what did the platform show the day the brief was filed" stays answerable. Legislative XML such as Akoma Ntoso (an OASIS standard) gives sections stable identifiers for the rows to hang on (source: OASIS spec).

The Point-in-time join (no page yet) ties a question to a version: for each event date (facts, filing), pick the latest valid version at or before it. Point-in-time recovery is a different thing, restoring the database to an instant, and belongs to Operations.

Lawyers search two ways. Terms and connectors is an exact Boolean language with proximity: root expanders and wildcards, "within n words", "same sentence", "same paragraph". It is predictable (the same query returns the same set) and cannot be replaced by embeddings. Natural language is the ranked path.

Both run on one Inverted index that stores positions. Sentence and paragraph connectors need boundary markers: the indexer inserts a boundary token with a position gap, so "same sentence" becomes a window test on positions. The splitter must know legal abbreviations ("v.", "Cir.") or it cuts citations in half. Root expansion ("negligen!") and wildcards use the term dictionary: a Finite-state transducer (no page yet) keeps the sorted terms compact and enumerates prefixes quickly, and a regex engine handles the general wildcard case over the enumerated terms. Stemming (no page yet) is applied for the natural-language path but never silently to a connectors query.

Ranking has stages, cheap to expensive. BM25 scores the lexical candidates. For natural-language queries a vector list from pretrained-model vectors (embedding-inference (no page yet)) is merged by Hybrid search and rank fusion, so a query also finds decisions that never use its words. A Learning-to-rank model then orders the top few hundred with features only a legal platform has: court level and whether it binds the user's jurisdiction, treatment-weighted in-links, recency, headnote topic match, and the decision's flag. Finally a cross-encoder reads the query with each of the top few dozen passages. Ranking may demote a decision; it may never hide the citator's warning.

Alerts

A lawyer follows a topic or saved query and expects a note when a new decision matches. That is the inverse of search: many stored queries, one document. Boolean expression indexing (no page yet) indexes the queries as conjunctions of conditions (court in a set, date at least d, term present) so that one document looks up only queries whose conditions it can satisfy; the search-engine route is a stored-query ("percolator") field, which Elasticsearch offers (source: Elasticsearch reference). Realtime query subscriptions (no page yet) pushes result-set diffs to an open session instead of polling. Alerts are deduplicated per decision, since an opinion is often published twice (slip opinion, then reporter).

Grounded drafting

The generation layer must not be the source of law. Retrieval comes first (filtered by the matter's court and date), generation second, grounding last. Citation grounding (no page yet) splits the draft into claims, attaches each to the passages it relies on, scores whether the passage supports the claim (a classifier or a cross-encoder used as an entailment check), and removes or flags failures. Then two checks only this system can make: every cited decision must exist in the platform (a lookup, not a model call; CourtListener's citation lookup API is described as a guardrail against hallucinated citations, source: Free Law Project wiki), and its citator status as of the matter's date must not be negative.

A preregistered study of commercial RAG legal research tools found that Lexis+ AI and Westlaw's AI tools each hallucinated between 17 % and 33 % of the time (source: Magesh et al. 2024); general-purpose models asked verifiable questions about federal cases hallucinated between 58 % and 88 % of the time (source: Dahl et al. 2024). Quality is measured on a gold set of research questions with an LLM judge calibrated against lawyer labels; the judge never replaces the citator check. LegalBench is a public benchmark of legal reasoning (source: Guha et al. 2023).

Operations

Primary-backup replication keeps one writer for the citator and statute stores, because edits must be ordered. Parsers read hostile input, so coverage-guided fuzzing targets the PDF reader, citation parser and query parser; deterministic simulation testing exercises the ingest cursor and alert delivery under crashes. Services authenticate with Mutual TLS, deploy with graceful draining, restart under process supervision, and record latency percentiles in HDR histogram (no page yet).

Data and control flow

A new opinion arrives (write path):

  1. The docket poller reads entries since its cursor and downloads the PDF.
  2. Text extraction runs, OCR for pages without a text layer; text and offsets are stored as version 1.
  3. The citation pass runs (Aho-Corasick prefilter, then regex); recognition and linking assign court, judges, statutes.
  4. Each cited decision becomes an edge; the classifier labels its treatment; negative labels go to the editor queue, the rest are written with a valid-from of the decision date.
  5. The topic classifier proposes headnotes; editors accept or correct.
  6. The document is indexed with positions; the alert matcher runs the stored queries against it. Time goes mostly to OCR and editor review.

A terms-and-connectors search (read path):

  1. The parser builds a tree of terms, expanders and proximity operators; syntax errors are reported with a position.
  2. Root and wildcard terms expand through the term dictionary into a bounded set of concrete terms.
  3. Filters (court, date, jurisdiction) run first because they are cheap; position lists are then intersected under the window rules.
  4. Matches are scored (BM25 plus authority features), cut to the top set, and each gets its citator status as of the chosen date.

A drafted paragraph (AI path): the matter's court and date filter retrieval; the model drafts quoting passage ids; grounding scores each claim against its passages and removes or marks failures; each cited decision is looked up and its status checked; the lawyer sees draft, sources and flags together.

Scaling

In the order you would pull the levers:

  1. Editor throughput limits quality first, because negative labels need a person. Lever: confidence thresholds that route only doubtful cases, and a classifier retrained on every correction.
  2. Index size and query latency. Positions make the index larger. Lever: shard by court and year (queries nearly always filter on both), keep the term dictionary in memory, run cheap filters first.
  3. Ranking cost. Cross-encoders cost one model call per pair. Lever: cut candidates hard first, cache by query, rerank passages rather than full opinions.
  4. Ingest backlog. OCR dominates. Lever: parallelize per document, prefer born-digital sources.
  5. Alert fan-out. Lever: the expression index, so cost follows matching alerts, not all alerts.
  6. Citator reads. Interval labels and a cached good-law flag per decision; recompute when a negative edge arrives.

Failure modes

FailureSymptomCauseFix
False "good law"a lawyer cites an overruled casetreatment missed; hierarchy data wrongeditor review of negative signals, court-tree tests
Hallucinated citationdraft cites a case that does not existmodel generated a plausible stringexistence lookup on every cite; block when missing
Right case, wrong claimcited case does not say what the draft saysgrounding checks the case, not the propositionpassage-level entailment check; show the supporting span
Wrong statute versionadvice based on repealed textas-of date not carried from the matterbitemporal lookup on the event date; show the version date
Citation parse errorsmissing edges, wrong cited caseOCR noise, abbreviation missing from the databasenormalize OCR, update reporter data, fuzz the parser
Silent re-extractiongrounding spans point to wrong texta new extractor changed offsetsversioned documents, never mutate offsets

Real implementations

namewhat it isrepolicencenotes
CourtListeneropen legal research site: opinions, dockets, citation lookup, alertsfreelawproject/courtlistenerAGPL-3.0the open end-to-end reference (Django code)
eyecitecitation extraction, resolution of short forms, annotationfreelawproject/eyeciteBSD-2-ClauseAho-Corasick prefilter, optional Hyperscan tokenizer; reads the BSD-style reporters-db pattern data; JOSS paper
juriscraperscrapers that pull opinions and dockets from court sitesfreelawproject/juriscraperBSD-style, see repothe ingest edge
Caselaw Access ProjectHarvard's bulk corpus of US case lawharvard-lil/capstonesee repodata at static.case.law
LexNLPlegal text segmentation, entity and fact extractionLexPredict/lexpredict-lexnlpAGPL-3.0text extraction, not a full platform
legislation.gov.ukUK statute service with enacted, dated and prospective versionsdeveloper docssee sitepoint-in-time statute model, public API
Westlaw, Lexis+ AIthe closed market leadersnoneproprietaryevaluated in the study cited below

The alternative: plain full-text search over a document dump with an LLM on top. It is far cheaper and good for orientation, but without the citator and the hierarchy it cannot say whether a decision is binding or still good law, which is where the studies below locate the risk.

Sources

  • V. Magesh et al., Hallucination-Free? Assessing the Reliability of Leading AI Legal Research Tools, 2024. paper
  • M. Dahl et al., Large Legal Fictions: Profiling Legal Hallucinations in Large Language Models, 2024. paper
  • N. Guha et al., LegalBench: A Collaboratively Built Benchmark for Measuring Legal Reasoning in Large Language Models, 2023. paper
  • J. Cushman, M. Dahl, M. Lissner, eyecite: A Tool for Parsing Legal Citations, Journal of Open Source Software, 2021. paper
  • eyecite README (tokenizers): source
  • Free Law Project, Citation Lookup and Verification API: docs
  • legislation.gov.uk developer docs, URIs and versions: docs
  • Elasticsearch reference, percolator field type: docs
  • OASIS Akoma Ntoso (LegalDocML) vocabulary, the XML standard for legislative documents: spec

Similar systems

Share the most building blocks with this one.