Real-estate AI · Multilingual workflows · Production
Alinia
A live property platform where AI can interpret a buyer's request, but only approved records from the operational listing system can become results.
The difficult part was preserving truth across boundaries
A conversational property search has two conflicting requirements. It must understand loose, multilingual requests such as a follow-up that changes only the area or budget, while still obeying exact constraints such as rent versus sale, listing status, agency scope, and approval state.
The engineering problem was therefore not simply to generate a fluent answer. It was to preserve intent across turns, retrieve broadly enough to be useful, reject semantically plausible but operationally invalid matches, and carry the conversation into a staff workflow when automation was no longer appropriate.
What I personally worked on
I worked with Tayseer Laz and the Aligned Tech team on this employer-owned system. My repository history covers the public and authenticated application, backend services, AI and search behavior, authentication and access boundaries, agency and CRM workflows, Hader integration, performance work, deployment, and production fixes.
Within the discovery path described below, my commits include multilingual filter extraction and fallbacks, multi-turn state corrections, location and landmark resolution, strict result filtering, inventory-count correctness, lifecycle-aware cache invalidation, and web and WhatsApp handoff behavior. This was team delivery; the claims here describe my evidenced contribution rather than sole authorship of Alinia.
Technical deep dive: from request to authoritative listing
The retrieval system is intentionally layered. Probabilistic components help interpret and rank a request, but deterministic checks and the transactional listing store decide what the user is allowed to see.
Execution trace: a multilingual, multi-turn property search
- 01 · RestoreLoad conversation state
The request arrives with channel and session context. Previously collected criteria, shown listing IDs, pagination state, language, and any pending action are restored before the new turn is interpreted.
- 02 · ExtractUse the cheapest reliable parser first
A deterministic pass handles common intents and concrete fields. Ambiguous language falls through to a constrained model-based extractor, after which deterministic extraction can backfill fields the model omitted.
- 03 · ReconcileMerge this turn with prior intent
Transaction and property type remain sticky when a refinement does not mention them. Explicit changes overwrite only the supplied fields; a newly shared location pin clears stale price and transaction constraints from an older search.
- 04 · NormalizeResolve place meaning
Typed areas, broader regions, landmarks, and coordinates are converted into canonical search names or proximity context. A landmark in the current turn can supersede an earlier, broader location.
- 05 · GateRequire a complete search contract
Search waits until the required location, property type, and transaction direction are resolved. An explicit “anywhere” is treated as a deliberate absence of a location filter, not as a parsing failure.
- 06 · RetrieveGenerate candidates through two retrieval modes
Structured constraints accompany lexical and semantic retrieval. Results are source-tagged, fused by rank, deduplicated by listing ID, and bounded before they move downstream.
- 07 · VerifyRe-read candidate facts
A strict pass checks the stored property type, category, area, transaction direction, and applicable price fields. Candidates that only look relevant semantically are removed.
- 08 · HydrateBuild cards from live records
The application fetches the surviving IDs from the transactional listing store in ranked order and requires both the public status and workflow state to be active before returning a card.
Mechanism one: preserving intent without giving the model authority
A single model call is not treated as the search contract. Common phrases can bypass model latency through deterministic extraction, while less regular English, Arabic, and Arabizi requests can use model interpretation. The outputs are then reconciled with session state and known location vocabulary.
This became important in follow-up turns. If a buyer first asks for a rental and later says only “make it an apartment near this landmark,” a stateless extractor can silently lose the transaction direction. Alinia keeps selected constraints sticky, distinguishes a modification from a new search, and removes stale values only when the interaction provides a stronger contradictory signal.
- Selected: deterministic fast path with model fallback
- Reason: predictable requests do not need generative interpretation, while genuinely ambiguous phrasing still benefits from it. Cost: rules, aliases, and backfill behavior must evolve alongside real language use.
- Rejected: rebuild the full filter object on every turn
- Reason: omission is not the same as deletion in a conversation. Rebuilding state from the latest message allowed previously settled constraints to disappear. Cost of the selected design: reset and contradiction semantics must be explicit.
Mechanism two: approximate retrieval, deterministic admission
Lexical retrieval is useful for exact place and listing language; semantic retrieval improves recall when the wording differs from the listing text. The current smart-chat path runs both, tags their provenance, combines their ranks, and deliberately leaves generative query rewriting and model reranking disabled. That keeps an additional inference step out of the critical result-ordering path while the surrounding grounding rules remain the stronger correctness concern.
Retrieval produces candidate IDs, not user-visible truth. The system re-reads authoritative fields, removes strict mismatches, and then asks the application backend to hydrate cards from records that are still active and approved. A stale search-index entry can therefore be selected as a candidate without automatically becoming a displayed property.
The displayed inventory count is also computed separately from the bounded retrieval page. This prevents “number of candidates retained for ranking” from being presented as “number of listings that satisfy the structured filters.” If the count path fails, the response falls back to at least the number of validated results rather than claiming that no inventory exists.
Listing lifecycle and cache consistency
Approval changes the listing status, workflow state, approval record, and audit event as one transactional operation. Search indexing happens outside that database transaction. Rejection, archival, sale, rental, and deletion likewise make the authoritative record unavailable first, then request removal from discovery indexes.
Public listing reads use versioned cache keys. A visibility-changing mutation advances the version so old entries become unreachable without scanning and deleting every possible filter key; cache failures fall through to the database. The tradeoff is explicit: search indexing and cache invalidation are best-effort side effects, so a successful database change can briefly precede those secondary systems. Final chat-card hydration rechecks active workflow state, but this design does not claim zero staleness across every public read.
Continuing the workflow with a person
Human handoff is represented as durable conversation state rather than a prompt instruction. An authenticated web session can open or reuse a staff conversation, persist the lead-up, and attach the relevant listing and language context. Subsequent messages check that the session belongs to the user and whether a handoff is active; active conversations are relayed to staff while automated replies stay silent.
Initiation is idempotent per chat session and the pause lease is refreshed while the conversation remains active. If the external handoff operation fails, the request returns a usable response instead of leaving the user in an unacknowledged state. This favors continuity, while logs and stored handoff status provide a recovery path.
How the behavior is checked
- Parser and state regressions
Automated cases exercise “anywhere,” decline responses, required-field progression, and preservation of transaction direction across turns.
- Grounding gates
Search candidates are checked against stored fields, then hydrated through a query that only returns active, approved listings.
- Lifecycle paths
Approval, rejection, updates, archival, reassignment, rental, sale, and deletion participate in public-cache invalidation; inactive records are removed from discovery indexes on the relevant paths.
- Repository evidence
Commit history records fixes for wrong-transaction leakage, stale filters, location resolution, result-source attribution, inventory counts, cache invalidation, handoff behavior, and production performance.
Alinia is live in production and owned by Aligned Tech. This case study describes system behavior and my evidenced contribution without publishing private source code, prompts, provider configuration, listing or customer data, administrative screens, credentials, infrastructure, client identities, or confidential operating metrics.
What this case study does not claim
It does not claim that semantic retrieval is always correct, that distributed cache and index updates are atomic, or that every conversational request can be resolved without clarification. It also does not publish unsupported scale, latency, conversion, or accuracy numbers.
The demonstrated engineering is the set of boundaries around uncertainty: explicit search state, structured constraints, candidate provenance, authoritative record checks, lifecycle controls, staff takeover, and recovery when a secondary service is unavailable.