PROJECT AI ARCHITECT Updated July 2026

Lette AI: Intelligent Triage

Designed to triage 200+ daily messages across 5+ buildings with a 0.85 autonomy threshold, ~60% LLM cost reduction via model routing, and full audit traceability.

Tech Stack
FastAPILangChainLangGraphPostgreSQLCeleryRedisReactPydantic v2Gemini 2.5 Flash

Architecture

Frontend

  • React 18 + Vite
  • Real-time Autonomy Slider
  • AI Reasoning Detail Panels
  • Shared Schema Types

Backend

  • LangGraph state machine with human-in-the-loop interrupts
  • Postgres CQRS via materialized views
  • Celery + Redis async ingestion
  • Row-Level Security multi-tenancy

Challenges & Solutions

Problem

Adding Groq/Llama as a cheaper, faster model option broke LangChain's structured-output tool-calling validation — Llama models are noticeably less reliable than Gemini/Claude at strict function-calling schemas, so triage requests silently failed.

Solution

Made the Pydantic schema more lenient to absorb provider-specific field-naming quirks, and added a runtime fallback: if the LangChain structured chain throws, drop to raw JSON-mode generation with the schema embedded directly in the prompt, then manually validate the result against the same Pydantic model.

Problem

The readiness probe called out to the database engine outside its try/except block, so a malformed DATABASE_URL raised an unhandled error instead of being caught — meaning a broken DB config could make the health check misreport as ready.

Solution

Moved the engine call inside the try block, widened the caught exception types, and shipped a regression test asserting the endpoint returns a hard 503 with an explicit 'unreachable' status for a bad URL — enforcing fail-closed behavior instead of silent false-positive health.

Problem

The LLM classifier systematically conflated two independent dimensions: domain urgency (what the message is about) and functional intent (what the user actually wants done). A message describing a critical leak that was really just an analytical data question kept getting mis-routed as an emergency.

Solution

Added a deterministic, zero-cost rules layer that runs after the LLM and can veto its triage-vs-question verdict, scoring five signals (analytical verb count, sub-task patterns, compound sentence structure, trailing question marks, incident-pattern matches) — an explainable correction layer instead of just re-prompting and hoping.

Problem

A known Postgres/pgvector issue (updating any column on a row with an HNSW-indexed vector column forces a full index re-evaluation) would have made routine status updates increasingly slow as update volume grew.

Solution

Split the schema so vector embeddings live in an immutable, insert-only table, joined at query time to the mutable table that actually receives frequent status updates — sidestepping the index-thrash entirely by design rather than patching around it later.

Key Achievements

Two-tier model routing for ~60% LLM cost reduction

10-question scored benchmark harness (`make benchmark`)

Confidence-gated knowledge-base search

Documented, research-backed scaling plan with concrete migration triggers

Deep Dive

Eliminating the “Dropped Ball” in Property Management

Lette AI is an autonomous triage engine designed for the high-stakes world of property management. Across a portfolio of 5+ buildings handling 200+ messages daily, a missed water leak or a legal notice can result in thousands of euros in damages or fines.

Lette AI acts as a senior property manager that never sleeps: it reads every inbound message, classifies urgency, identifies risks, and either handles it automatically via the knowledge base or escalates to a human with full context.


The Architecture: Human-in-the-Loop by Design

The triage pipeline is a LangGraph state machine, not a single LLM call — six nodes (load context, entity pre-linking, assessment, case linking, graph merge, memory update) that use LangGraph’s native interrupt_before to pause after the AI produces its assessment and resume from a Postgres-backed checkpoint once a manager approves or rejects it. Human review is a first-class state in the graph, not a bolted-on approval flag.

Data is modeled around real-world cases — a property, unit, and issue — rather than raw email threads, so related messages from different senders (a tenant and their legal advisor, say) get grouped into one coherent view instead of scattered across separate threads. Multi-tenancy is enforced at the database layer via Postgres Row-Level Security, not just application code, and the read-heavy queue/audit-log views are served from a materialized view that’s explicitly refreshed after every write — decoupling the write path from the read path.


The Engineering: Cost-Aware, Explainable AI

Every triage result includes its reasoning, the exact evidence snippets that triggered a risk flag, and contradiction detection against the property’s own knowledge base. Structured output is enforced via Pydantic schemas end-to-end, with a JSON-mode fallback when a provider’s tool-calling breaks down (see Challenges).

Model selection is cost-aware: routine messages (“rent inquiry,” “maintenance request”) route to a small, fast model, while anything containing ambiguity signals (“urgent,” “legal,” “hazard,” “emergency”) escalates to a larger model — a two-tier routing scheme designed for roughly a 60% cost reduction over always calling the larger model. Knowledge-base retrieval is confidence-gated: if the best semantic match scores below a fixed threshold, the system explicitly declines to feed weak context to the LLM rather than risk a plausible-sounding wrong answer. Reliability is checked with a 10-question scored benchmark suite wired into the project’s make benchmark command, not just manual spot-checks.

Built specifically for the Irish property context, the system understands RTB (Residential Tenancies Board) legal timelines and RPZ (Rent Pressure Zone) caps, injecting property-specific facts directly into the prompt so mundane questions get instant, accurate answers while emergencies are flagged within seconds of arrival.


Scaling Beyond the Demo

Ingestion runs through Celery workers backed by Redis, with deliberate at-least-once delivery settings rather than Celery’s defaults, and token-level streaming uses Redis Streams instead of plain pub/sub specifically because it’s resumable across client reconnects. Rather than guess at scaling needs, the project has a researched, concrete scaling plan with explicit migration triggers — for example, moving from the current Celery/Redis queue to Kafka/Flink only becomes necessary past roughly 10,000 emails/day across multiple regions, which is 1-2 orders of magnitude beyond the current 200/day — so the current architecture has clear headroom before it needs to change.