Skip to content
cd ../featured-projects

cat ./projects/lexasa.md

[LIVE · VERCEL]

01 /

LexaSA

South African regulatory intelligence that cites the actual Act.

LexaSA interface
A grounded answer with every citation resolved to an Act, a section and a page.
A grounded answer with every citation resolved to an Act, a section and a page.
Risk assessment grading each topic independently against the evidence it retrieved.
Risk assessment grading each topic independently against the evidence it retrieved.

// stack

  • Python
  • FastAPI
  • LangGraph
  • React 19
  • TypeScript
  • Vite
  • Supabase
  • PostgreSQL/pgvector
  • Groq
  • GitHub Actions
  • Vercel
  • Azure App Service

// THE PROBLEM

Most RAG demos stop at "it retrieves something and an LLM summarises it." That is fine for a product FAQ and unacceptable for legislation. A South African employer asking whether a policy complies with the BCEA needs to see the section it came from, and needs to know whether the version they are reading has since been amended. The harder question this project was built to answer is not "can a model retrieve law" but "what does it take to make that trustworthy?"

// WHAT I BUILT

A regulatory question-answering platform over POPIA, the BCEA, the LRA, the EEA and the Companies Act. Hybrid retrieval fuses dense vector search over pgvector with keyword search using reciprocal rank fusion, and the result feeds a LangGraph agent that can search iteratively rather than answering from a single pass. The corpus tracks superseded legislation alongside current versions and flags staleness explicitly. Compliance risk assessment runs two ways: a deterministic rule-based checklist with no LLM in the path, and free-text submission where an LLM extracts discrete topics, retrieves evidence for each, and grades them independently so one badly-evidenced topic cannot drag down a well-evidenced one. Supabase Auth gates exactly one endpoint — the admin audit log — because that is the only place with a genuine confidentiality requirement.

// THE KEY DECISION

Making insufficient_evidence a first-class outcome rather than a failure mode. The easy path is to always return something: if retrieval comes up thin, let the model fill the gap from its parametric memory and nobody notices until the answer is wrong in a way that matters. Treating "I could not find grounding for this" as a distinct, reportable result means the system can be honest at exactly the moment it would be most tempting not to be. The same instinct drove version-aware citations — a citation that does not track staleness is a citation that quietly lies as the law moves underneath it.

// WHAT IT TAUGHT ME

That the interesting engineering in RAG is almost entirely outside the retrieval call. Seventeen architecture documents came out of this project, one per milestone, each recording the alternatives considered and why they were rejected — why hybrid retrieval over pure vector search, why authentication is scoped to one endpoint instead of gating the whole app, why the CORS regex has to pin an account scope and not just a project name. Writing the decision down at the time it is made is what makes it reviewable later. It also taught me the value of an evaluation framework over a test suite: retrieval recall, MRR and hit@k measured against a hand-authored golden dataset run as a zero-cost regression gate in CI, with the LLM-as-judge tier gated behind a flag so it can never burn quota by accident.

// REAL-WORLD IMPACT

A small South African business can ask a compliance question in plain language and get an answer pinned to a named section of a named Act, with an explicit signal when the corpus cannot support a confident answer. The deployment is deliberately split across two clouds — frontend on Vercel, backend on Azure App Service — because that is the topology where the genuinely hard problems live: CORS across providers, monorepo-aware CI, and credential-free deploys via OIDC federated credentials rather than a stored publish profile. Free tier throughout, with the costs of that constraint documented rather than hidden.