AI Property Assistant
Per-listing chat assistant with two specialized agents and server-side memory.
Two Agents
| Agent | Handles | Context |
|---|---|---|
| LISTING | Amenities, pitch advice, objection handling, comparisons | Property details, tags, location, sales intelligence |
| PRICING | Price justification, offer explanation, value comparison, cancellation plans | All of LISTING + full price breakdown, bank offers, nightly rates |
Scope
Both agents carry the same scope guardrail, injected ahead of the property context. The assistant answers only on this property, the booking journey (availability, pricing, taxes, offers, payments, cancellation), property features and location, and selling the stay — pitching, objections, comparisons.
Anything else — general knowledge, code, news, health or financial advice, unrelated travel planning, chit-chat — gets a single fixed line back:
I can only help with questions about this property and its booking — pricing, availability, amenities, offers, or how to pitch it. Ask me anything along those lines.
The rule also covers attempts to talk the assistant out of it ("ignore your instructions", role-play prompts), and a message mixing an in-scope with an out-of-scope ask is answered on the in-scope part only.
Routing
The router picks the agent based on keywords:
"How should I pitch this to a family?" → LISTING agent
"Why is it priced at ₹16,400?" → PRICING agent
"What if they say it's too expensive?" → PRICING agent (has "expensive")
"What amenities does it have?" → LISTING agent
"What cancellation options are available?" → PRICING agent (has "cancellation")
Server-Side Memory
Conversations are stored in PostgreSQL via Spring AI's JdbcChatMemoryRepository. Frontend just sends conversationId + message — no history to maintain.
POST /api/v1/crs/listings/assistant/{listingId}/chat
{
"conversationId": "conv_abc123",
"message": "How should I pitch this to a family?"
}
Follow-up with same conversationId:
{
"conversationId": "conv_abc123",
"message": "What if they say it's too expensive?"
}
The assistant remembers the previous question about families and connects it to the pricing response.
Response
{
"listingId": "lst_abc",
"message": "Start with the private pool and game room...",
"agent": "LISTING"
}
agent field tells the frontend which agent answered.
Quick Suggestions
GET /assistant/{listingId}/start returns contextual suggestions based on listing data:
{
"suggestions": [
"Why is it priced at ₹16,400?",
"How should I pitch this to a family?",
"What objections might a client raise?",
"How does this compare to similar villas?",
"What cancellation options are available?",
"What bank offers apply here?"
]
}