Skip to main content

No project description provided

Project description

Dubai Real-Estate Lead Qualification Agent

A conversational AI agent built with agents-builder, LangGraph, and Ollama. The agent talks naturally with a prospective buyer, extracts useful details from the full conversation, chooses the next qualification question, and returns a scored lead when enough information is available.

The conversation is not a rigid form. A customer can provide several details in one message, use approximate language, name any Dubai area, or describe a property in their own words.

Install

This project uses the local editable agents-builder checkout at ../agents-builder.

uv sync --group dev --all-extras

Agent Structure

src/real_estate_lead_agent/
├── agent.py      # QualificationAgent and LangGraph wiring
├── nodes.py      # QualificationNode turn processing
├── prompts.py    # Structured qualification prompt
├── models.py     # Lead and LLM output schemas
├── scoring.py    # Deterministic lead scoring
├── integrations/
│   ├── __init__.py       # Generic interface and component loading
│   ├── settings.py       # Discriminated integration settings
│   ├── csv.py            # CSV integration
│   └── google_sheets.py  # Google Sheets integration
├── cli.py        # Interactive agent runner
└── adapters/
    ├── conversation.py # Shared per-session conversation state
    ├── telegram.py     # Telegram bot adapter
    └── web_api.py      # FastAPI web chat adapter

The modules follow the same separation used by the agents-builder agent template: state contracts, prompts, nodes, and graph composition remain independent and directly testable.

Configure Ollama

The default configuration is:

configs/qualification-agent.yaml

It follows the Paper Buddy Ollama configuration style:

store:
  kind: memory

agency_registry:
  kind: memory
  agencies:
    - agency_id: demo
      name: Demo Agency
      public_key_hash: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
      allowed_origins:
        - http://localhost:4173
      branding:
        assistant_name: Demo Assistant
      agent:
        module_path: real_estate_lead_agent.agent.QualificationAgent
        llm_factory:
          module_path: agents_builder.llm.factory.LLMFactory
          roles:
            qualification:
              module_path: agents_builder.llm.llm.OllamaLLM
              model: gpt-oss:120b-cloud
              base_url: https://ollama.com
              kind: ollama
            scoring:
              module_path: agents_builder.llm.llm.OllamaLLM
              model: gpt-oss:120b-cloud
              base_url: https://ollama.com
              kind: ollama
        mcps: []
      integrations:
        - kind: csv
          enabled: true
          path: leads.csv
        - kind: google_sheets
          enabled: false
          spreadsheet_id: your-google-spreadsheet-id
          range: Leads!A:O
          credentials_file: credentials/google-service-account.json

widget_auth:
  secret_env: WIDGET_SESSION_SECRET

For a local Ollama model, change model and base_url, for example:

model: gpt-oss:20b
base_url: http://localhost:11434

Run The Agent

uv run real-estate-lead-agent

Or provide another config:

uv run real-estate-lead-agent --config configs/qualification-agent.yaml

Widget-qualified leads are sent to the enabled integrations configured on that agency. The CLI can write completed leads to CSV with:

uv run real-estate-lead-agent --output data/qualified-leads.csv

The command starts the real AI conversation. The agent can understand input such as:

I want a modern two-bedroom apartment near the beach, around 3 million AED,
and I hope to buy in the next two months.

It extracts all available facts, asks one useful follow-up question, and keeps the accumulated lead profile. Once the minimum profile is known, it prints the final lead JSON with lead_score and lead_status.

Run As A Telegram Bot

  1. Open @BotFather, send /newbot, and follow the prompts to create a bot and receive its token.
  2. Create a .env file in the project root. Do not commit it:
TELEGRAM_BOT_TOKEN=your-bot-token
  1. Start the bot with long polling:
uv run real-estate-lead-telegram

To use another agent configuration:

uv run real-estate-lead-telegram --config configs/qualification-agent.yaml

Use a different dotenv file with --env-file path/to/.env. An already exported TELEGRAM_BOT_TOKEN takes precedence over the dotenv value.

Open the bot in Telegram and send /start. The bot sends the welcome and first qualification question as two separate messages, in that order, before the user replies. Each Telegram chat gets independent conversation state. /reset starts that chat again, and completed leads are sent to the same enabled CSV or Google Sheets integrations used by the CLI. The final Telegram response confirms how many integrations received the completed lead.

Run As A Web API

Start the FastAPI adapter:

uv run real-estate-lead-web-api \
  --allowed-origin http://localhost:3000

Use --allowed-origin more than once when the widget is hosted on multiple domains. The default bind address is 127.0.0.1:8000; use --host 0.0.0.0 when running inside a container.

The API exposes:

GET  /api/health
POST /api/widget/session
POST /api/chat/start
POST /api/chat/message
POST /api/chat/reset
GET  /api/docs

When agency_registry and widget_auth are configured, browser widgets must first call /api/widget/session with their agency ID and public key. The API validates the request Origin, returns server-controlled branding and a short-lived signed token, and requires that token for /api/chat/*.

Start a widget session:

curl -X POST http://localhost:8000/api/widget/session \
  -H 'Origin: http://localhost:3000' \
  -H 'Content-Type: application/json' \
  -d '{"agency_id":"mccone","public_key":"pk_test_mccone"}'

Send a message using the returned session ID:

curl -X POST http://localhost:8000/api/chat/message \
  -H 'Origin: http://localhost:3000' \
  -H 'Authorization: Bearer WIDGET_SESSION_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "session_id": "fb94bb2e-52a0-48ec-9d29-b50a2c23419f",
    "message": "I want a two-bedroom apartment in Dubai Marina"
  }'

The response contains session_id, message, and completed. Sessions are stored in memory by default. Configure the validated store section as kind: redis with redis_url for multi-instance production deployments. See deploy/cloud/gcp/README.md for the Cloud Run and Memorystore deployment procedure.

Google Sheets Integration

Create a Google Cloud service account, enable the Google Sheets API, and share the target spreadsheet with the service account email. Then enable the google_sheets integration under the target agency and set its spreadsheet ID and range:

agency_registry:
  kind: memory
  agencies:
    - agency_id: demo
      # ...
      integrations:
        - kind: google_sheets
          enabled: true
          spreadsheet_id: 1abc123...
          range: Leads!A:O
          credentials_file: credentials/google-service-account.json

When credentials_file is omitted, Application Default Credentials are used. Each qualified lead is appended as one row in the documented eight-column result order.

Additional providers define an IntegrationSettings subclass with a unique kind and module_path, add it to the discriminated ConfiguredIntegrationSettings union, and implement ResultIntegration[SettingsType].send. Runtime components are loaded with load_class(config.module_path).from_config(config).

The qualification schema contains six fields:

Field
first_name
phone
budget
property_type
location
timeline

The agent asks only about unresolved fields. A field is considered addressed even when the customer does not know the answer, refuses to provide it, or gives an invalid value. In those cases its value is null.

Every final JSON result contains the six lead fields in the documented order, followed by lead_score and lead_status. The CSV uses the same fixed columns; null values are written as empty cells.

Scoring Strategies

The agent uses LLMLeadScorer by default. After qualification, the scoring model receives both the normalized lead and the full customer conversation. It returns a score from 0 to 100, a HOT/WARM/COLD status, and concise reasoning. Reasoning is validated internally but is not added to the final CSV.

A deterministic alternative is also available:

from real_estate_lead_agent import CodeBasedLeadScorer, QualificationAgent

agent = QualificationAgent(config, scorer=CodeBasedLeadScorer())

CodeBasedLeadScorer applies fixed budget, timeline, and completeness rules. Both scorers return the same LeadQualificationResult schema, so JSON and CSV output do not change.

Quality

make ci

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

real_estate_lead_agent-1.2.0.tar.gz (152.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

real_estate_lead_agent-1.2.0-py3-none-any.whl (35.4 kB view details)

Uploaded Python 3

File details

Details for the file real_estate_lead_agent-1.2.0.tar.gz.

File metadata

  • Download URL: real_estate_lead_agent-1.2.0.tar.gz
  • Upload date:
  • Size: 152.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for real_estate_lead_agent-1.2.0.tar.gz
Algorithm Hash digest
SHA256 7c8ef97842f491fb09be6a0aff3bd2351b8615cf0ed97650548a22daec5d47cb
MD5 1c71ddb4aa28de24a646f02ecb75b4ff
BLAKE2b-256 e3a81c9998a6d6a9a7c5e654a09d739443d5e193ba217c11b57f3e707c1f4e73

See more details on using hashes here.

File details

Details for the file real_estate_lead_agent-1.2.0-py3-none-any.whl.

File metadata

  • Download URL: real_estate_lead_agent-1.2.0-py3-none-any.whl
  • Upload date:
  • Size: 35.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for real_estate_lead_agent-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 193925bef19bb4db115b0ecc5360daf5334d28bfeb9f514ee1ee5d337070a093
MD5 b27673730884fdafc3769dfb613ae385
BLAKE2b-256 cf95d9f8d67fda3bfbb39e0c0c41ca4f11b8931896ba40b69c1989e8424ffcc2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page