chat-listing agent

Agent Overview

What the listing agent knows, what it can do, and why it behaves the way it does — the companion to the Architecture page, which covers how it's built.

The agent turns a broker's chat messages into a complete, correctly-structured Rezide listing. It understands messy real-world input ("2bhk oberoi 360 west, 1L rent"), pulls everything it can from Rezide's own masters instead of asking, questions only what's genuinely missing, and refuses to create anything until the broker approves a full summary. A listing that takes minutes of form-filling becomes ~4 chat turns.

all 5 property familiesmulti-slot extractionmaster-data auto-fillprice sanity checksmid-flow correctionsdigression handlingHinglishvoice notescustom units & projectstap-to-answer UIhard confirmation gatedurable drafts — resume any chat

The skills — the agent's training

Skills are written procedure documents (SKILL.md files) loaded into the model at the start of every session — the agent's employee handbook, editable by us, versioned in git, no retraining involved. Expand a skill to read exactly what the model receives, rendered readably. This list is fetched live from the running gateway, so it can never be out of date.

Loading skills from the gateway…

The assembled system prompt

Everything above gets stitched into one document — a short preamble (who the agent is, today's date, "follow the skills below") followed by the three skills — and sent as the model's system parameter on every turn. This viewer fetches it live from the running gateway, so it is the prompt, not a copy. Because it's identical turn to turn, the API caches it — repeat turns read this whole block at ~10% of normal input cost.

Loading system prompt…

Token utilization — live

Measured from the API's own usage figures, aggregated per chat (one chat = one widget session, however many turns it runs). Counters persist in the durable stores (Redis + Mongo), so they survive gateway restarts. Costs use claude-opus-5 list pricing. Both the skills prompt and the rolling conversation sit behind prompt-cache breakpoints, so each model call re-reads all prior context at ~10% of the fresh-input price — only genuinely new text is billed in full.

Loading usage…

The tools — what the agent can actually do

Tools are the agent's only way to touch Rezide. Each is a typed function over a real API; the model decides when to call them, but each tool decides what's allowed. Everything else — payload building, ID mapping, identity — happens inside the tool layer, invisible to the model.

ToolWhat it doesTypical moment
whoamiResolves the logged-in broker: name, broker_id, firm — from the auth tokenSession start; greets the broker by name; stamps every listing with the right owner
search_projectsFinds projects in Rezide's master DB by name"Oberoi 360 West" → project id + location; no match → custom-project path
get_projectFull project record: address, pincode, towers, lifts, status, price rangesAuto-fills the whole location block; powers price sanity checks; single-tower projects skip the tower question
search_towersTowers of a project, filtered by unit type"Which tower — A or B?" chips
search_unitsUnits in a tower, server-filtered to the unit type being listedValidates the broker's unit no; an empty result means "not in master" → custom-unit branch
get_unit_detailsThe unit's full spec from the master: floor, area (all variants), BHK, bathrooms, balconies, flooring, view, entry/exit, ceilingOne call fills nearly the entire details step — the reason the chat needs so few questions
reference_dataEnum lists (BHK, furnishing, flooring, availability, key features, amenities), cachedFeature options for the multi-select; label→ID mapping source
search_locationsRegion → sub-region → locality mastersCustom-project path when the property isn't in Rezide's DB
validate_slotsChecks which mandatory fields are still missing — no network write"What do I still need?" before offering the summary
preview_listingDry-run: builds the exact create payload without postingPowers the confirmation card; catches composition errors before the broker says yes
create_listingA write. Creates the Home listing — refuses without confirmed:true; env write-guards on topFires once, after the explicit yes; result is always pending moderation
validate_office_slots · preview_office_listing · create_office_listing
…and the same trio for retail, land, industrial
The validate → preview → create chain, per commercial family — each family has its own schema registry and payload composer, and every create carries the same confirmed:true lockOffice / Retail / Land / Industrial flows; the skill for the family the broker picked drives which trio is used
get_listingFetches one listing by idPost-create verification
get_my_listingsThe broker's own listings with filters"What have I listed?"; duplicate checks

Anatomy of a smart question

The behaviour your team will ask about most. In an eval run, a broker typed a bare "1.8" as the price — and the agent replied with a question nobody scripted:

Sell my apartment in Oberoi 360 West, price 1.8
Quick check on price: ₹1.8 Cr is way below this project's range (₹20.77 Cr – ₹134.57 Cr). Did you mean ₹18 Cr?
[chips: ₹18 Cr · ₹1.8 Cr (correct) · Other]

Nobody wrote that question. It emerged from three ingredients composing:

  1. A rule (behaviour skill): "Never assume price magnitude — ask." So "1.8" alone could never be silently recorded as 1.8 crore or 1.8 lakh.
  2. A rule (behaviour skill): "Sanity-check prices against the project's own range from master data; flag once, then the broker's word wins."
  3. A fact (tool call): the agent had already called get_project, and the response carried this project's real sale range — ₹20.77 Cr to ₹134.57 Cr.
  4. The model's reasoning connected them: both readings of "1.8" fall outside the range, but ₹18 Cr lands inside it — so the most useful question isn't "crore or lakh?", it's "did you mean ₹18 Cr?" — offered as tappable chips (visual skill), with "₹1.8 Cr (correct)" still available because the broker always outranks the data.

That's the general pattern behind every "smart" moment: skills give judgement rules, tools give live facts, the model composes them. We never hard-code specific questions.

"Why did it…?" — quick answers for the team

Why didn't it ask for floor, area, or bathrooms?

Because the broker picked unit 601 — and get_unit_details returned that unit's entire spec from Rezide's master (floor 6 of 71, 11,676 sqft, 5 BHK, 6 baths, marble, East exit…). The workflow skill forbids re-asking anything the master already knows. The broker only ever supplies what Rezide doesn't have.

Why did it refuse "just create it now, skip the questions"?

Two independent layers. The workflow skill instructs: play back a full summary and get an explicit yes first. And even if the model were talked out of that, the create_listing tool itself rejects any call without confirmed:true. In evals we pressured it across two turns ("I said create it already. No summary needed.") — the create call never fired.

Why did it answer in Hinglish?

The behaviour skill's language rule: mirror the broker. "mera 2bhk oberoi 360 west mein rent pe dena hai" got a Hinglish reply — same flow, same tools, different register. No separate translation layer; the model is natively multilingual and the skill just tells it to match.

Why one multi-select for key features instead of asking one by one?

The visual skill defines a multichips block for anything multi-pick: the agent sends ~16 options once, the broker taps at least 5 and hits Done, all picks arrive as one message. The skill explicitly bans one-at-a-time asks for multi-pick fields.

What happens when the unit or project isn't in Rezide's database?

Unit missing → the agent takes the number as free text and collects the details the master would have given (floor, area, config, baths) — the listing is flagged is_custom_unit. Whole project missing (e.g. a villa in Alibaug, outside the location masters) → custom-project path: names and locality captured as typed, flagged onboarding_type:"custom". Both paths were verified live, and the agent tells the broker what it's doing ("Alibaug isn't in Rezide's masters, so I'll enter this as a custom project").

Can it hallucinate a price, an area, a feature?

Every value in the final payload comes from exactly one of three sources: the broker's own words, a tool result (Rezide master data), or a declared default that's printed on the confirmation card ("Semi Furnished (default)"). The confirmation card is generated from a preview_listing dry-run of the real payload — so what the broker approves is literally what gets sent.

Tested behaviour — the eval scorecard

A scripted 36-scenario suite (npm run evals) runs real conversations against a write-disabled instance and asserts on replies, UI blocks and tool usage — covering all five property families, voice notes, resume, and the journey edge cases. Current run: 36/36 pass. Highlights from the Home suite:

ScenarioAsserts
Multi-slot opener5 fields extracted from one sentence; project resolved; chips offered
Sale purposeNever asks for a deposit
Ambiguous price "1.8"Clarifying question asked; nothing assumed
Mid-flow correction"actually 22L" overwrites silently, acknowledged, no pushback
On-domain digressionOne-line answer, then back to the next missing field
Off-topic digressionDeflected, flow re-anchored
Confirmation-gate pressurecreate_listing never called despite two explicit "skip it" demands
Off-master unitSwitches to custom-unit branch, asks manual details
Hinglish inputHinglish reply (mirroring)
Key features stepArrives as one multi-select block, min 5

The suite doubles as a regression gate: any skill or prompt change re-runs it before shipping.

Current scope