§ LexOfis AI
System walkthrough
How the system works

What happens between a question and an answer.

LexOfis is a retrieval-augmented legal assistant for KKTC (North Cyprus) law. This page traces one real question through every component it touches: what runs, in what order, and how the raw text turns into a cited, verified legal analysis.

LexOfis is a closed platform. Access is granted to approved law firms only, so there is no public sign-up. The main site is where a firm applies for access.

The parts

Architecture at a glance

Four layers. A request enters at the top and the answer streams back up through the same path.

Clientwhat the lawyer sees
Next.js TypeScript streamed SSE in-line PDF citations
↓ HTTPS · one question
APIorchestration & guards
FastAPI input sanitisation rate limit + quota answer cache session history
↓ enqueue · Redis Streams
Workersthe heavy lifting
Celery (gevent) retrieval pipeline Gemini generation hallucination detector
↕ read / write
Data & AIknowledge and models
Qdrant · vectors + BM25 Gemini 2.5 Flash / Pro PostgreSQL Redis cache
Who can use it

A closed, invitation-only platform

LexOfis isn't a public tool. There is no open sign-up. Access is provisioned to vetted law firms and, within each firm, to individual lawyers. An outsider cannot create an account.

STEP 1

A firm applies

The only public entry point is an application form. A law firm submits a request. It does not create a live account.

STEP 2 · GATE

A super-admin approves

Every application is reviewed and approved manually. No self-service account is ever created; nobody provisions themselves.

STEP 3

The firm invites its lawyers

Inside an approved firm, its admin adds lawyers and assistants by email invitation. Staff are provisioned, never self-registered.

STEP 4

Access, scoped to the firm

Each user works only within their own firm's tenant. Data, sessions and history are isolated per firm.

A paid, subscription service

Access is tied to an active subscription. Payment is handled by an integrated provider, and entitlement stays in sync automatically. When a subscription changes or lapses, the platform reflects it without manual work.

Lemon Squeezy checkout webhook-synced entitlement per-plan quotas token / usage metering auto-expiry on lapse
What it contains

The knowledge base

The corpus isn't stored as whole PDFs. It's split so that each retrievable unit maps to a single legal thought, and tagged so search can filter precisely.

~190,000

indexed passages

Drawn from 4,000+ legal PDFs (statutes, regulations and court decisions), each embedded for dense search and indexed for BM25 keyword search.

Two chunking strategies

Statutes are split per article, so one provision is one unit. Court decisions are split by section (reasoning, ruling, outcome), so a precedent's parts stay coherent.

belge_turu fasil_no madde_no aktif page

Article control cards: the layer above the text

Provisions that are easy to misapply (those with a mandatory condition, like “offender is a public official” or “stolen from a vehicle”) carry a structured madde_karti extracted from the article's own wording: its required elements, what it is commonly confused with, and its penalty. These are injected into the model's context only when that article is retrieved, and every element is verified against the source text before a card is stored.

The request lifecycle

One question, traced end to end

Every step below is a real stage in the pipeline. The example query is threaded through so you can watch the text transform.

The example query
“Müvekkile hakaret edildi, tazminat davası açabilir miyiz?” “My client was defamed, can we file for damages?” · everyday Turkish, no legal citation
1

Receive & sanitise

The message is checked for ownership (session belongs to this user and tenant), sanitised against prompt-injection, and saved. The original text goes to the database; a sanitised copy goes to the model.

FastAPIsanitize_user_inputmulti-tenant check
2

Rate limit, quota & answer cache

Per-IP and per-user rate limits run first. If the exact same question was already answered for this tenant, and that answer passed verification and was built by the current pipeline version, it's replayed instantly and the pipeline stops here.

gatewayversion-stamped cacheHTTP 429 / 402
3

Fan out: three things at once

To save several seconds, three independent operations launch concurrently: the quota gateway, the retrieval pipeline, and the first-stage triage. Nothing waits in line that doesn't have to.

⇄ gateway · retrieval · triage run in parallel via asyncio.gather
4

Optimise the query & bridge the vocabulary

The raw sentence is reduced to its legal keywords, then bridged from colloquial Turkish to the statute's actual terminology. This single step is why the right article is even findable.

“hakaret” (everyday: insult) “zem ve kadih” (the real term in Chapter 148)
optimize_rag_queryKKTC terminology mapchapter classifier
5

Hybrid search, fused

The bridged query hits Qdrant twice at once: dense vectors for meaning, BM25 for exact tokens and article numbers. The two rankings are merged with reciprocal-rank fusion. Conditional follow-ups then pull the full chapter and any matching precedents. Small talk short-circuits here and returns nothing.

Qdrantdense + BM25RRF fusion24h RAG cache
6

De-duplicate, re-rank & attach cards

Candidates are de-duplicated, then an LLM re-ranker scores each provision for true relevance to the facts. A representation quota guarantees the context carries both statute and precedent, and any control cards for the surfaced articles are attached.

Gemini Flash re-rankstatute / precedent quotamadde_karti injection
7

Triage: is this even a legal scenario?

The first-stage result decides the path: a complete scenario proceeds; a scenario missing decisive facts returns targeted clarifying questions; a greeting or thank-you returns a short courtesy reply. Only real scenarios reach full analysis.

[TAMAM] complete · [EKSİK_BİLGİ] needs facts · [GENEL_SORU] small talk
8

Generate: grounded, structured, streamed

A Celery worker calls Gemini with the retrieved provisions and the control cards, writing the analysis in a fixed six-part structure. It's produced token by token and pushed onto a Redis Stream, which the API relays to the browser as server-sent events, so the lawyer watches it appear.

Celery workerGemini 2.5 Pro / FlashRedis Streams → SSE
9

Verify against the source

A hallucination detector checks each cited article against the chunk it supposedly came from. Only genuinely verified citations are marked as such; if nothing verifies, the answer says so rather than manufacturing confidence.

hallucination_detectorverified citation flags
10

Persist & link the sources

The finished message is saved with its citations and the pipeline version that produced it. Each citation resolves to the real source document, so a click opens the PDF at the exact cited page. For this query, that's Chapter 148, the “zem ve kadih” provisions the vocabulary bridge made reachable in step 4.

PostgreSQLcitations + pipeline_versionsource PDF @ page
Two details that keep it honest

Where correctness is easy to lose

Caches that can't lie

Two caches speed things up: a 24-hour retrieval cache and a tenant-wide answer cache. Both are guarded: the answer cache carries a pipeline_version stamp and only replays answers that passed verification, so an improvement to the pipeline is never hidden behind a stale reply, and a flawed answer can't outlive its fix.

Ask instead of assume

When a fact would change which article applies but isn't stated, the system doesn't guess or silently drop it. It answers with the base provision and adds a targeted question that names the consequence. This applies only to facts that actually move the law, never for evidence or procedure.

At a glance

What handles what

ConcernHow it's handled
Finding the right lawhybrid dense + BM25 · RRF fusion · terminology bridge
Ranking what mattersLLM re-rank · statute/precedent quota
Not over-applying a provisionextractive article cards · gated conditions
Not fabricating a citationhallucination detector · verify-against-chunk
Responsivenessparallel fan-out · Redis Streams · SSE token streaming
Correctness over timeversion-stamped answer cache · verified-only replay
Isolation & limitsmulti-tenant checks · per-IP/user rate limit · quota