This project is a test of a boundary. If a voice runtime is designed correctly, the expensive part — the real-time loop — should be domain-independent, and moving industries should cost only the domain layer. If the boundary is drawn wrongly, the second industry costs nearly as much as the first.
This is the engineering half of the case study. The business read — and the fact that this project concluded at internal stage without deployment — is a separate document.
The problem
Real estate breaks a voice agent differently from insurance, in three specific ways.
The language mix is harder. Insurance calls code-switch between English and Hindi. Property conversations do it inside a clause — "2BHK chahiye, but near the metro" — and the pauses fall in different places than either language alone would produce. Turn detection tuned on one language, or even on clean alternation between two, degrades here.
The output is not an answer, it is accumulated state. An insurance caller asks a question that has an answer. A buyer describes a set of constraints across several turns, revises them, and trades them off against each other. Extracting a query from the final turn loses everything that made it meaningful. The system needs a preference model that survives the whole conversation.
Inventory is live and inconsistent. Locality schemas differ between portals, so "near Indiranagar" does not resolve identically across sources — and a listing can go while the conversation is happening. An agent that offers something already gone has failed worse than one that offered nothing.
And the latency tolerance is tighter than insurance, not looser. An insurance caller waiting a beat assumes the system is checking their policy. A buyer waiting a beat assumes they are talking to a bot.
-
Turn detection robust to intra-clause code-switching, not just to two languages
-
Constraint state accumulated across turns, not extracted from one
-
Search grounded in current inventory, resolving inconsistent locality schemas
-
Sub-1.3s in the slowest language path, because the illusion is the product
Architecture
The system is deliberately split at the line the project exists to test.
Ported intact from insurance: the real-time listen-reason-act-reply loop, turn detection and its noise handling, interruption and barge-in behaviour, the latency instrumentation, the session and turn logging, and the service split between voice workers and API that lets the thing scale. None of this is real-estate code. None of it needed to be.
Rebuilt for the domain: a typed tool layer for locality search, amenities, commute-time lookup and CRM lead functions; a data plane modelling listings, areas, projects, amenities, sessions, preferences and qualification signals under one schema; and real-time context loaders that ground answers in current inventory.
Extended, not ported: the preference graph. Insurance did not need one — a policy question resolves within a turn. Conversational search does, because the buyer's constraint set is built, revised and traded off across many turns and is itself the valuable output.
The business read: why the reuse claim is a commercial oneDesign decision: the loop knows nothing about real estate and the tools know nothing about audio. That boundary is the whole thesis. Where it leaks — where a domain concept reaches into the conversation loop — the port stops being a configuration change and becomes a rewrite.
How it works
Detect the turn, in three languages at once
The ported detector handles interruption and noise; what needed attention here was intra-clause switching, where the prosodic cues a detector leans on are inconsistent within a single sentence.
Transcribe across the mix
English, Hindi and Hinglish on one line, with no per-language flow. A language-switching state machine would have been the obvious design and the wrong one — buyers switch mid-clause, so there is no clean point at which to switch modes.
Update the preference graph
Constraints, intents and qualification signals accumulate and revise across turns. "Actually, three bedrooms if the commute is better" modifies an existing constraint rather than starting a new search.
Resolve against live inventory
Typed tools query current listings, with locality resolution reconciling schemas that differ between sources. The constraint set, not the last utterance, is what is searched.
Reply inside the budget
Roughly a second in English, under 1.3 seconds in Hindi and Hinglish — the slower path being the one that sets the perceived quality.
Sync qualification to CRM
The preference graph is the lead. It transfers as structure, so the human who picks it up inherits the reasoning rather than a filled-in form.
Control planes
Governance
The agent acts only through typed real-estate tools. Current inventory and the captured preference state bound what it may recommend — recommendations are constrained rather than generated, which is what makes "we do not offer properties that are gone" a property of the system instead of a hope about the model.
Orchestration
Per turn: detect, transcribe, update the preference graph, resolve constraints against live inventory through a tool, reply, persist. Across the session: preference state carried and revised, qualification signals accumulated, CRM synced. The per-turn loop is insurance's; the state model is not.
Observability
Turns, sessions and actions log through configuration rather than through new code paths — which is a consequence of the port rather than a feature of this build. Per-stage latency instrumentation came across with the loop, which is what made it possible to tell early that the Hindi and Hinglish paths were spending their extra time in transcription rather than reasoning.
When it fails
The interesting failures are constraint failures, not conversation failures — the conversation failures were already solved upstream in insurance.
A locality that does not resolve consistently across sources produces a search that is confidently scoped to the wrong area. A constraint revised late in a conversation has to modify the graph rather than append to it, or the buyer ends up searching for a contradiction. Inventory going stale mid-conversation has to surface as an absence, not as a recommendation the tool can no longer support.
The correct behaviour throughout is that the agent narrows or admits it cannot find something, rather than widening silently to produce a result. A property search that quietly relaxes the buyer's constraint to return three listings is worse than one that returns none, because the buyer cannot see that it happened.
Results
The boundary held. The loop ported; the domain layer was rebuilt; the conversational quality survived the move.
-
Under 1.2s in English and under 1.3s in Hindi and Hinglish, end to end
-
Turn stability up 15–35 points from quiet rooms through call-centre noise
-
One orchestration path across three languages, with no per-language special-case flows
-
The real-time loop reused without modification, with only tools, data plane and preference model rebuilt
Evidence
Directly demonstrable in the built system: the multilingual loop with interruption handling, the typed real-estate tool layer, the unified inventory and preference data plane, CRM sync, and the reuse of the insurance runtime itself.
Internal results requiring reproduction: the sub-1.3-second reply times and the 15–35 point turn-stability improvement. Both are measured against our own baseline on our own audio path.
Status, stated plainly: a real client project that reached internal stage and was not deployed.
The transferable lesson is about where you draw the line in your own system. If your conversation loop imports a domain concept anywhere — if it knows what a policy is, or what a locality is — you do not have a runtime, you have one product that happens to talk. The test is whether a second industry costs you the tool layer or the whole build.
How we classify these numbers, and what "not deployed" means for them