How We Built an AI-Native Real Estate Platform for Northern Cyprus

작성자

카테고리:

← 피드로
DEV Community · Onur Dokuzoğlu · 2026-07-22 개발(SW)


While building Evlek, an AI-native property platform for Northern Cyprus, we encountered a problem that appears in almost every AI search product:

A language model can understand what the user wants, but it should not be allowed to invent the facts used in the answer.

This becomes especially important in real estate.

A fabricated product recommendation may be inconvenient. A fabricated property price, title deed type, availability status, or location can influence a high-value decision.

We therefore designed our property-search architecture around a strict separation:

  • The language model interprets intent.
  • The application validates that intent.
  • The database provides property facts.
  • The ranking layer evaluates relevance.
  • The model explains the retrieved results.

The model never becomes the source of truth.

This article explains why we chose that architecture, how a natural-language property query becomes a structured search, and how we preserve uncertainty when the underlying data is incomplete.

The problem with adding chat to a conventional property portal

Most property platforms are built around filters:

  • Location
  • Property type
  • Number of bedrooms
  • Minimum and maximum price
  • Sale or rental
  • Selected amenities

Filters are effective when users already understand the market and know exactly what they want.

Many users do not begin with a filter configuration. They begin with a sentence:

Find me a modern two-bedroom apartment in Kyrenia below £180,000, close to the sea and suitable for investment.

This request contains both explicit and implicit information.

The explicit constraints include:

  • Kyrenia
  • Apartment
  • Two bedrooms
  • Maximum price of £180,000

The softer preferences include:

  • Modern
  • Close to the sea
  • Suitable for investment

A language model can interpret those signals well. The danger begins when it is also expected to provide the actual listings from memory or from an unverified text context.

A fluent answer is not necessarily a factual answer.

Our basic architectural rule

We use the model for language understanding, not property truth.

The simplified request flow looks like this:


text
Natural-language request
          │
          ▼
Intent extraction
          │
          ▼
Schema validation
          │
          ▼
Database retrieval
          │
          ▼
Deterministic filtering
          │
          ▼
Relevance ranking
          │
          ▼
Grounded explanation

Every property presented to the user must originate from a real record returned by the application.

The language model may explain why that property is relevant, but it cannot create a new listing, alter its price, add an unconfirmed amenity, or infer a legal field that is missing.

Converting language into structured intent

The first step is converting an open-ended sentence into a predictable search object.

A simplified TypeScript representation might look like this:

type PropertySearchIntent = {
  transaction?: "sale" | "rent" | "daily";
  propertyTypes?: Array<
    "apartment" | "villa" | "house" | "land" | "commercial"
  >;
  city?: string;
  districts?: string[];
  bedrooms?: number;
  minimumPriceGbp?: number;
  maximumPriceGbp?: number;
  features?: string[];
  preferences?: {
    nearSea?: boolean;
    newBuild?: boolean;
    investmentFocused?: boolean;
    furnished?: boolean;
  };
};

The model is asked to populate this structure rather than produce a list of properties.

For the example request, the result could resemble:

{
  "transaction": "sale",
  "propertyTypes": ["apartment"],
  "city": "Kyrenia",
  "bedrooms": 2,
  "maximumPriceGbp": 180000,
  "features": [],
  "preferences": {
    "nearSea": true,
    "newBuild": true,
    "investmentFocused": true
  }
}

This object is not trusted automatically.

It still passes through application validation.

Validation must happen outside the model

A model can return malformed, contradictory, or unsupported values.

Examples include:

A negative maximum price
A city that does not exist in the platform
Unsupported transaction types
Bedroom values represented as text
A district assigned to the wrong city
A currency that the search service does not support

The application must normalise and validate these fields before any query is executed.

A simplified example:

function validateSearchIntent(
  input: PropertySearchIntent
): PropertySearchIntent {
  const output: PropertySearchIntent = {};

  if (
    input.transaction === "sale" ||
    input.transaction === "rent" ||
    input.transaction === "daily"
  ) {
    output.transaction = input.transaction;
  }

  if (
    typeof input.maximumPriceGbp === "number" &&
    input.maximumPriceGbp > 0
  ) {
    output.maximumPriceGbp = input.maximumPriceGbp;
  }

  if (
    typeof input.bedrooms === "number" &&
    Number.isInteger(input.bedrooms) &&
    input.bedrooms > 0
  ) {
    output.bedrooms = input.bedrooms;
  }

  return output;
}

The real implementation also needs controlled vocabularies, location resolution, aliases, multilingual values, and relationships between regions.

The important principle is that the application—not the language model—decides which values are valid.

Hard constraints and soft preferences are different

Not every part of a user request should become a strict database filter.

Consider:

I want a sea-view apartment in Kyrenia below £180,000.

The budget may be a hard constraint. The sea view may be a strong preference.

If we require every result to contain a confirmed sea_view = true field, the search may return nothing—even when relevant listings mention sea proximity in another structured attribute.

We therefore divide the request into different signal types.

Hard constraints

These normally exclude a listing when they are not satisfied:

Transaction type
Maximum price
Required bedroom count
Explicit property type
Required city or district
Strong preferences

These influence ranking but may not exclude every result:

Sea view
New development
Furnished
Walking distance to amenities
Investment potential
Contextual preferences

These may require further interpretation:

Good for a family
Suitable for students
Quiet area
Strong rental potential
Modern design

This distinction gives the system room to return useful alternatives without pretending that every result is an exact match.

Retrieval comes before explanation

After validation, the application queries the property database.

The retrieval layer should return structured records such as:

type PropertyRecord = {
  id: string;
  title: string;
  transaction: "sale" | "rent" | "daily";
  propertyType: string;
  city: string;
  district?: string;
  bedrooms?: number;
  priceGbp?: number;
  features: string[];
  titleDeedType?: string;
  publicationStatus: "active" | "inactive";
  publishedAt: string;
  updatedAt: string;
};

The language model does not create or modify these records.

A result can only be shown if:

It exists in the database
It is available to the current user
Its publication status permits display
It satisfies the validated hard constraints
It passes any additional business rules

Only after this retrieval step does the model receive the approved result set.

Preventing fabricated attributes

Suppose a listing contains:

{
  "bedrooms": 2,
  "priceGbp": 175000,
  "features": ["shared_pool", "balcony"],
  "titleDeedType": null
}

The model must not transform this into:

This two-bedroom apartment includes a shared pool, balcony, and Turkish title deed.

The title deed field is unknown.

The grounded explanation should instead say:

This listing matches the requested bedroom count and budget. It includes a shared pool and balcony. The title deed type is not specified in the available listing data.

This is less polished than inventing a complete answer.

It is also the correct answer.

We found that explicit handling of missing information is one of the most important features of trustworthy AI search.

Representing uncertainty in the response

Missing data and negative data are not the same.

These statements have different meanings:

The property does not have a pool.
The listing does not mention a pool.
Pool information has not been confirmed.

A useful property model should distinguish among them.

For example:

type AttributeStatus<T> =
  | { status: "confirmed"; value: T }
  | { status: "not_available" }
  | { status: "not_specified" }
  | { status: "requires_verification" };

This allows the interface and explanation layer to preserve uncertainty instead of converting every empty field into a confident conclusion.

For high-value marketplaces, uncertainty is part of the product experience.

It should not be hidden.

Explainable ranking

A search may return several valid properties. The user still needs to understand why one result appears above another.

We calculate relevance from multiple signals, such as:

Hard-constraint compliance
Budget proximity
Location relevance
Feature overlap
Listing freshness
Data completeness
Confirmation status
Preference matching

A simplified scoring function could look like this:

type RankingSignals = {
  hardConstraintMatch: number;
  locationMatch: number;
  budgetMatch: number;
  featureMatch: number;
  freshness: number;
  dataCompleteness: number;
};

function calculateScore(signals: RankingSignals): number {
  return (
    signals.hardConstraintMatch * 0.35 +
    signals.locationMatch * 0.2 +
    signals.budgetMatch * 0.15 +
    signals.featureMatch * 0.15 +
    signals.freshness * 0.1 +
    signals.dataCompleteness * 0.05
  );
}

The exact weights depend on the product and the request.

The more important decision is to preserve the signals that produced the ranking.

This makes it possible to explain a result:

Within your stated budget
Matches the requested property type
Matches the requested bedroom count
Located in the preferred city
Recently updated
Sea-view information is not confirmed

An unexplained AI score may look impressive, but an understandable match explanation is more useful.

Multilingual search without duplicating the truth

Evlek is designed for users across multiple languages.

The same concept may appear differently across Turkish, English, German, Russian, and Arabic.

For example:

Canonical value: transaction_type = sale

English: For sale
Turkish: Satılık
German: Zu verkaufen
Russian: Продажа
Arabic: للبيع

We try to keep canonical property facts independent from the display language.

The language layer can translate or interpret the request, but the search ultimately operates on canonical values.

This avoids a serious data problem: five translated versions gradually becoming five different descriptions of the same property.

The same principle applies to:

Property types
Room counts
Amenities
Transaction types
Location aliases
Legal and market terminology

Translation belongs in the presentation and interpretation layers. Property truth belongs in the canonical data model.

Source dates matter

Property information changes.

Listings become inactive. Prices are updated. Market statistics belong to a specific period. A statement that was accurate three months ago may no longer be current.

Whenever possible, the system should preserve:

Publication date
Last update date
Price-change date
Market-data reference period
Source information
Review or confirmation status

This allows the assistant to say:

The listing was last updated on 15 July 2026.

or:

The market figure refers to the second quarter of 2026.

Without source dates, AI-generated explanations can make historical information appear current.

For real estate research, data freshness is not metadata. It is part of the answer.

External AI access should use the same rules

We also provide selected read-only access to structured property capabilities through the Model Context Protocol.

The objective is not to make a separate AI product.

The objective is to allow compatible clients to use controlled tools rather than scraping pages or depending on model memory.

The same principles apply:

Inputs are validated
Tools have defined scopes
Results originate from structured records
Personal contact information is not exposed unnecessarily
Missing fields remain missing
The client cannot create listings through a read-only search tool

A simplified tool boundary looks like this:

External AI client
        │
        ▼
Defined MCP tool
        │
        ▼
Input validation
        │
        ▼
Authorised read-only query
        │
        ▼
Structured property response

MCP is an access layer over the same property system.

It does not replace the underlying validation, permissions, or data-quality requirements.

What about virtual staging and marketing agents?

Natural-language search is only one part of the wider platform.

We are also working on workflows for virtual staging and AI-assisted property marketing.

Those systems follow the same grounding principle.

A marketing workflow should receive confirmed property facts instead of an unstructured prompt.

For example:

{
  "propertyType": "apartment",
  "city": "Kyrenia",
  "bedrooms": 2,
  "priceGbp": 175000,
  "confirmedFeatures": [
    "shared_pool",
    "balcony"
  ],
  "unconfirmedFields": [
    "title_deed_type"
  ],
  "restrictions": [
    "Do not claim guaranteed investment returns",
    "Do not invent legal status",
    "Do not describe unconfirmed amenities"
  ]
}

The generated description may change in tone or language, but the factual boundary should remain fixed.

Virtual staging needs a similar boundary.

A staged image should help a buyer visualise a space without pretending that the furniture exists or altering permanent architectural facts.

That requires:

Keeping the original image available
Labelling the staged version
Reviewing generated outputs
Avoiding misleading structural changes
Rejecting unsuitable images

Generative features need product rules, not only model prompts.

Lessons from building the system
1. Better prompts do not replace structured data

Prompt engineering can improve model behaviour, but it cannot reliably repair missing, inconsistent, or unverified property records.

2. The model should interpret, not author, marketplace facts

The model is useful for understanding intent and explaining results. The application remains responsible for truth.

3. Missing information must remain visible

A blank field should not become a confident claim.

4. Retrieval and ranking are separate problems

Finding valid results is not the same as ordering them intelligently.

5. AI features inherit the quality of the underlying inventory

An advanced search interface cannot compensate for outdated or incomplete listings.

6. High-value decisions require stronger guardrails

A property-search assistant should behave differently from a casual content generator.

Accuracy, provenance, uncertainty, and review are product requirements.

Final architecture

The architecture can be summarised as:

User request
    │
    ▼
Language-model intent extraction
    │
    ▼
Application validation and normalisation
    │
    ▼
Database retrieval
    │
    ▼
Deterministic filtering
    │
    ▼
Explainable ranking
    │
    ▼
Grounded response generation
    │
    ▼
User-visible sources, dates and uncertainty

The language model is an important component.

It is not the database, the permission system, the legal authority, or the source of property facts.

Final thought

The most important design decision in our AI property search was not which model to use.

It was deciding what the model was not allowed to do.

By separating language interpretation from property truth, we can create a more natural search experience without allowing fluent generated text to override real marketplace data.

That principle applies beyond real estate.

Any AI product working with financial, medical, legal, marketplace, or other high-value information should ask the same question:

Which parts of the answer may be generated, and which parts must always come from a verified system of record?

Disclosure: AI was used to help refine the English, structure the article, and create the cover artwork. I reviewed the technical claims and final content based on our actual work building Evlek.

Enter fullscreen mode Exit fullscreen mode

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다