This is the technical companion to the overview of the Paralegal Advisor. The overview explains what the tool does and why it's free; this piece is for people who build these systems and want to understand the decisions underneath it.

The short version: for high-stakes, domain-specific AI, the hard part isn't the model — it's the discipline around it. A general chatbot that sounds like a lawyer is easy. One you can trust with a compliance question is not. Almost every decision below is in service of that difference.

The constraints that shaped everything

Before any architecture, the problem set the rules:

  • Wrong answers are expensive. This touches compliance and the rule of law. A confident hallucination isn't a bug, it's a liability.
  • The law moves. Ukraine's humanitarian legal framework has been amended continuously since February 2022. The corpus is a moving target.
  • Two languages. Source texts are Ukrainian; many users work in English.
  • Non-expert users. Coordinators, not lawyers.
  • It has to be verifiable. Users must be able to check what they're told.
  • It has to be cheap and durable. It's a free public tool, so runaway cost or fragility would kill it.

Architecture at a glance

INGEST                         SERVE
official legislation           user question
  → extract & normalize          → embed the query
  → chunk                        → retrieve across 3 corpora (law-first)
  → embed (Gemini)               → reason over sources (tool agent)
  → vector store (Qdrant)        → answer + cited sources
        ▲                              │
        └── incremental updates        └── same-origin proxy → chat UI

The stack is deliberately boring: Google Gemini for embeddings and generation, Qdrant as the vector store, a low-code orchestration layer for the serving graph, and a thin same-origin web layer. Boring is a feature — the value is in the choices, not the novelty.

The decisions that matter

Retrieval, not fine-tuning

The corpus changes every week. Fine-tuning a model on legislation would mean retraining every time a law is amended, and it would still blur the line between "the model learned this" and "the model is guessing." Retrieval-Augmented Generation keeps the knowledge in a store you can update in minutes and inspect directly, and it makes citation natural: the model answers from passages it just retrieved. For a domain where provenance is the whole point, RAG isn't a shortcut — it's the correct architecture.

Embeddings: quality and language first

The corpus was migrated to Gemini's embedding model at its full dimensionality. Two reasons drove it. First, multilingual quality — the same model has to place a Ukrainian statute and an English question near each other in vector space. Second, query/document asymmetry: questions and legal passages are embedded with different task types, so a short question retrieves long dense text well. These are the kinds of details that quietly determine whether retrieval works at all.

Chunking sized to the law, not to a default

Legal text has natural units — articles and sections. Chunk too small (a habit inherited from older, smaller embedding models) and you shred an article across fragments that each lose the thread. The chunk size here is tuned so that most individual provisions stay whole while comfortably fitting the embedding model's input budget. Chunking is unglamorous and one of the highest-leverage decisions in any RAG system.

Three corpora, law-first

Not all sources carry equal authority, and mixing them would be misleading. The knowledge base is split into three:

  1. Primary legislation — the authoritative statutory text.
  2. A curated humanitarian set — provisions hand-selected for NGO operations, enriched with relevance metadata.
  3. Secondary analysis — expert commentary that reviews the law.

The system always leads with primary law and treats analysis as clearly-labelled commentary, tagged with its date. Encoding the authority hierarchy into the retrieval layer — rather than hoping the model infers it — is what stops "an expert's opinion" from being served as "the law."

A tool agent, not a single-retriever chain

The obvious first build is one retriever feeding one prompt. It can't serve three corpora with different roles. So the serving graph is a tool-using agent: the model has a separate retrieval tool per corpus and decides which to consult, always starting with primary law. The trade-off is honest — several model calls per question instead of one, so a few extra seconds of latency. In exchange you get source separation, law-first behaviour, and the ability to say "the sources don't cover this" instead of forcing an answer. For this domain, that trade is worth it.

Metadata, dates, and ranking

Every chunk carries structured metadata: area of law, enactment/amendment date, and (for the curated set) an operational-relevance score. Dates are normalized to a sortable form so the store can do real range queries — "only laws since X" — rather than string matching. Ranking by operational relevance means the corpus reflects field experience, not just whatever the scraper found first.

Citations as a first-class output

Every answer returns the specific sources behind it — title, date, link — rendered as numbered cards, with analysis visually distinguished from statute. This isn't decoration. In a trust-sensitive domain, verifiability is the product; a citation the user can open is worth more than any amount of confident phrasing.

Guardrails, by design

Three families of guardrail, all specified rather than hoped for:

  • Grounding. The assistant answers from retrieved text and is instructed to say when the material doesn't cover a question — never to invent a law, article, or date.
  • Humility. Consequential answers point the user to a qualified lawyer. It's a first stop, not a substitute.
  • Boundaries. It stays a legal assistant: it won't be talked off-topic, and it won't disclose its own internals. Guardrails you can't state precisely are guardrails you don't really have.

Serving and access

The web layer never sees an API key — requests are proxied same-origin, with the credential injected server-side. Access is then shaped by layered controls: an origin check to stop other sites embedding the tool, plus rate limiting as a fair-use and cost ceiling. The principle worth stating: an origin check is a browser-scope control, so it's treated as one layer among several, not as authentication. Defense in depth, appropriate to a free public demo.

Cost and durability

A free tool has to stay free. That means a cost-efficient generation model, streaming responses so the interface feels quick even while the agent works, and a spending ceiling so a traffic spike can't turn into a surprise bill. Keeping the corpus current is handled by incremental updates and, when the embedding model itself changes, a zero-downtime rebuild into a fresh collection before switching over — plus quality gates on extraction so malformed documents never reach the store.

Honest trade-offs

No system is finished, and pretending otherwise is a red flag. A few things I'd flag openly: the agent's multi-call design trades latency for quality; translation quality for citation titles is good but not a substitute for a native reading of the statute; and a proper offline evaluation harness — measuring retrieval precision and answer faithfulness against a labelled question set — is the natural next investment for a system like this.

Why write this up

The tools here are commodities; anyone can wire them together. What makes a domain AI product trustworthy is the judgment — knowing that chunking matters more than model choice, that authority hierarchy belongs in the retrieval layer, that verifiability beats fluency, and that guardrails have to be specified, not assumed. That judgment comes from having lived the problem, not just read about it.

That's exactly the kind of work I do with organizations: taking a real operational problem and building an AI product that's accurate, verifiable, and durable enough to rely on. If that's a fit for your team, let's talk.