Appearance
Atlas AI Chatbot
The Atlas chatbot is a property search assistant that lets users find investment properties through natural language. It enforces a strict 3-step workflow — extract filters → preview cost → execute — with confirmation gates between each step to avoid accidental credit usage.
Model
| Setting | Value |
|---|---|
| Model | GPT-5.2 (chat-model) |
| Provider | Vercel AI SDK (@ai-sdk/openai) |
| Streaming | Server-Sent Events (SSE), word-level chunking |
| Max duration | 60 seconds per request |
| Title model | GPT-5.2 (title-model, max 80 chars) |
| Reasoning model | GPT-5.2 extended thinking (chat-model-reasoning) |
3-Step Workflow
The AI is instructed never to skip or reorder these steps:
AI Tools
analyzeSearchQuery — Step 1
Parses natural language into a structured filter object. Called first, before any credits are touched.
Inputs:
- User message
- Existing filters (if updating a previous search)
Supported filter categories:
| Category | Examples |
|---|---|
| Location | zip, city, county, state |
| Property type | residential, commercial, vacant land, multi-family |
| Value range | min/max estimated value |
| Ownership | owner-occupied, absentee, corporate, LLC |
| Equity | equity %, LTV ratio |
| Building | beds, baths, sqft, pool, garage, year built |
| Motivation signals | sale propensity score |
| Status | foreclosure, pre-foreclosure, tax-default, vacant |
| Quick lists | preforeclosure, free-and-clear, high-equity, etc. |
| Result count | how many records the user wants |
Output: Structured filter object + diff summary (what changed from previous search).
previewSearchCost — Step 2
Queries BatchData for a count without fetching full records. Shows the user how many results match and how many credits it will cost before charging anything.
Inputs: Filters, user ID
Output:
| Field | Description |
|---|---|
totalCount | Total matching properties in BatchData |
viewable | How many the user can actually view (limited by available credits) |
creditsNeeded | Total credits for all results |
userFriendlyMessage | Plain-text cost summary shown in chat |
executePropertySearch — Step 3
Runs the actual search, deducts credits, and returns property records. Only called after explicit user confirmation.
Inputs: Confirmed filters, user ID, page, limit
Output: Properties array, credits used, remaining balance, pagination info.
The first 3 records in any search are free (preview). Credits are only charged for records beyond that.
Streaming Architecture
- Endpoint:
POST /api/chat - Format: SSE via
JsonToSseTransformStream - Chunking:
smoothStream({ chunking: "word" })— streams word by word rather than token by token for better UX - Resumable streams: Optional Redis-backed stream resumption (if connection drops mid-response)
Context Injected Per Request
| Context | Source |
|---|---|
| Full conversation history | getMessagesByChatId() → passed as convertToModelMessages() |
| Current search filters | Chat.filters from DB |
| Filter version | Chat.filterVersion — tracks filter changes across turns |
| User ID + role | Session (USER / ADMIN) |
| Credit balance | Queried before stream starts |
| Rate limit check | Message count today vs. daily allowance |
Rate Limits
| Role | Messages / day |
|---|---|
USER | 20 |
ADMIN | 100 |
Enforced at the /api/chat route before the stream starts. Returns an error message if exceeded.
Database
Chat
| Field | Purpose |
|---|---|
id | MongoDB ObjectId |
title | Auto-generated by title-model on first message |
userId | Owner |
filters | Current search filter state (Json) |
filterVersion | Incremented each time filters change |
propertiesId | ObjectIds of properties found in this session |
messages | Relation to Message[] |
streams | Relation to Stream[] (for resumable streams) |
Message
| Field | Purpose |
|---|---|
chatId | Parent chat |
role | user | assistant |
parts | Json — message content including tool call results |
attachments | Json — any file attachments |
votes | Relation — user thumbs up/down on messages |
Voice
The chatbot supports two voice modes:
Realtime Voice (/api/chat/session)
- Uses OpenAI Realtime API (
gpt-4o-realtime-preview-2024-12-17) - Returns a
client_secretfor a WebRTC direct connection from the browser - Server-side VAD (voice activity detection) with 200ms silence threshold
- Full duplex — audio streams both ways in real time
Fallback Voice Transcription (/api/chat/voice)
- Accepts audio blobs:
webm,wav,mpeg,mp4,aac,ogg - Transcribes using
gpt-4o-mini-transcribe - Quality validation: minimum 2 words, rejects heavily repetitive output
- Returns clean transcript or a rejection reason (user is prompted to retry)
System Prompt Rules
Key constraints enforced in the system prompt (property-prompts.ts):
- Scope guard — Refuses non-real-estate questions
- Step ordering — Must always call tools in order: analyze → preview → execute
- No skipping — Cannot call
executePropertySearchwithout first callingpreviewSearchCost - Confirmation gates — Waits for explicit user "yes" before advancing to the next step
- No emojis — Text-only responses
- Credit transparency — Always shows cost before charging
- Filter diff — Summarizes what changed when filters are updated