⚑ Sangsakala Proxy 🏠 Home
Skip to content

About

Multilingual AI receptionist (Macedonian, Albanian, Serbian, English) for small businesses in North Macedonia and the Balkans. WhatsApp + voice, grounded answers, safe handoff.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

2 Commits

Folders and files

Repository files navigation

AI Receptionist

A multilingual AI receptionist for small businesses in North Macedonia and the Balkans. It answers phone calls and WhatsApp messages, answers FAQs only from the business's own knowledge base, books/moves/cancels appointments, takes messages when it can't help, and hands off to a human when it's unsure, the caller is upset, or it looks like a medical emergency.

The first target customer type is a private dental clinic. No clinic has signed up yet β€” this is still a pre-sales build.

Status: Phase 2 β€” text receptionist running in a local simulator. No deployment, no customers, no WhatsApp or voice yet.

Phase 2 implements the Phase 1 design below for one fictional demo business (a Skopje dental clinic β€” all data invented, see src/receptionist/kb/demo_data.py) and a local CLI/HTTP chat simulator in place of the real WhatsApp webhook. Full details: docs/PHASE2.md.

Architecture

 Caller (phone)                          Patient (WhatsApp)
     |                                         |
  Twilio number                        WhatsApp Cloud API (Meta)
     |  (Phase 3)                              |  webhook (Phase 2)
     v                                         v
+-------------------------------------------------------------+
|                   FastAPI backend (Railway)                 |
|                                                             |
|  channels/            core/                                 |
|   whatsapp.py  --->   conversation.py  (one turn loop)      |
|   voice.py     --->   language.py      (detect + pin lang)  |
|                       agent.py         (LLM + tool loop)    |
|                       tools.py         (6 tools, schemas)   |
|                       validators.py    (code checks args)   |
|                       policy.py        (disclosure, safety  |
|                                         keywords, handoff)  |
|                       handoff.py       (notify owner)       |
|                       logging.py       (turn + run records) |
|                                                             |
|  llm/  LLMProvider interface -> GroqProvider (others later) |
|  voice/ STT/TTS interface -> ElevenLabs (Phase 3)           |
+------+---------------------+-------------------+------------+
       |                     |                   |
   Postgres               Redis             OpenTelemetry
 (businesses, KB,     (session state,       (traces -> OTLP
  bookings, logs)      locks, dedupe)        collector)
       ^
       |  REST (JWT)
+------+--------------------+
| Admin UI (Next.js + TS)   |  edit KB, bookings, transcripts
+---------------------------+

This diagram is the Phase 1 design target; Redis, the admin UI, voice and the real WhatsApp webhook aren't built yet (Phase 2 uses a local simulator instead of the WhatsApp box, and an in-memory store instead of Redis β€” see docs/PHASE2.md). Full design detail, including the DB schema, API endpoints, agent tool contracts and known risks, is in docs/ARCHITECTURE.md. The actual Phase 2 module layout is in "Repo layout" below.

How one turn works

  1. A message comes in (the CLI/HTTP simulator in Phase 2; a WhatsApp webhook once that's wired). Session state (business, language, failure counters) loads from an in-memory store, or is created β€” a new conversation always opens with the AI + recording disclosure, enforced in code even if the model forgets it.
  2. Language is detected from the caller's message and pinned for the session; it only switches on a confident detection of a different language.
  3. A code-level safety check runs first: emergency keywords, medical-advice requests, anger/complaint signals, or an explicit request for a human. An emergency skips the model entirely.
  4. The LLM gets the system prompt, the business knowledge base, the conversation so far and the seven tool schemas.
  5. Any tool call the model makes is re-validated in code (dates in the future, inside opening hours, no double booking, phone format, matching dentist/service) before it touches the database β€” the DB's exclusion constraint is the final backstop.
  6. A tool failure, two consecutive validation failures, or a KB topic missed twice means the agent says so plainly and hands off β€” it never claims an action worked when it didn't.
  7. The turn (input, model output, tool calls/args/results, validation errors, latency, tokens) is persisted to Postgres, keyed to the prompt version/model/KB hash, and the reply is sent. receptionist.cli replay <conversation_id> re-runs a stored conversation and diffs the output.

See docs/PHASE2.md for what's actually implemented vs. still a Phase 1 design placeholder (Redis, voice, the admin UI, WhatsApp itself).

Roadmap

  • Phase 1 β€” Design. Architecture, database schema, folder structure, API/tool contracts, known risks.
  • Phase 2 β€” Text receptionist, local simulator. Conversation loop, seven agent tools, argument validation, replayable logging, CLI + HTTP simulators. WhatsApp itself is still a 501 stub β€” see docs/PHASE2.md.
  • Phase 3 β€” Voice. Twilio for calls, ElevenLabs for STT/TTS, the voice media stream.
  • Phase 4 β€” Admin UI. Next.js app for editing the knowledge base, managing bookings, and reading transcripts.
  • Phase 5 β€” Evaluation. 20 scripted test conversations across the 4 languages, plus an eval script to score them.

Quickstart (Phase 2)

Requires Python 3.12+ and a Postgres 17 server (the commands below assume one running locally on port 55432 with trust auth β€” adjust DATABASE_URL / TEST_DATABASE_URL otherwise; docker compose up postgres also works).

# 1. venv + install
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

# 2. apply the schema and seed the fictional demo clinic
export DATABASE_URL="postgresql://postgres@127.0.0.1:55432/receptionist"
python -m receptionist.db.migrate
python -m receptionist.kb.seed_demo_clinic

# 3. run the tests (unit tests need no key/DB; integration tests need TEST_DATABASE_URL)
export TEST_DATABASE_URL="postgresql://postgres@127.0.0.1:55432/receptionist_test"
python -m receptionist.db.migrate "$TEST_DATABASE_URL"
pytest -q

# 4a. chat in the terminal (needs a real LLM key -- mock has no real answers)
export LLM_PROVIDER=gemini GEMINI_API_KEY=...
python -m receptionist.cli chat --phone +38970123456

# 4b. or chat in a browser
uvicorn receptionist.api.main:app --reload
# -> http://localhost:8000/simulator

A Makefile wraps the common commands (make install, make init-db, make seed, make test, make cli-chat, make api), and docker compose up --build runs Postgres + the API together (docker-compose.yml).

Languages

Macedonian, Albanian, Serbian (Cyrillic and Latin script), English. Language is detected from the caller's first message and held for the rest of the conversation unless the caller clearly switches.

Agent tools

The model can call seven tools; code validates every argument before anything touches the database (src/receptionist/tools/). The Phase 1 design doc below describes the original six-tool sketch; Phase 2's actual tool names and shapes (and why they changed) are in docs/PHASE2.md and prompts/receptionist.v2.md.

  • get_business_info(topic) β€” answer from the knowledge base, or report "not found"
  • check_availability(service, date, staff?) β€” look up open slots
  • book_appointment(service, staff, starts_at, name, phone) β€” confirm a booking
  • reschedule_appointment(reference, phone, new_starts_at, new_staff?) β€” move an existing booking
  • cancel_appointment(reference, phone) β€” cancel an existing booking
  • lookup_appointment(phone, reference?) β€” find a booking by phone (+ reference if ambiguous)
  • handoff_to_human(reason, summary) β€” end AI involvement, notify the owner

Safeguards

  • Every conversation opens by disclosing it's an AI assistant and that the conversation may be recorded.
  • FAQ answers are grounded only in the business's own knowledge base. The agent never invents a price or policy; a missing topic becomes a message for the owner, not a guess.
  • The model decides what to do; code validates every tool argument and is the only thing that writes to the database.
  • A database exclusion constraint (not just application logic) makes double-booking the same staff/resource impossible β€” see docs/SCHEMA.sql.
  • Failures are loud: a tool error or a repeated validation failure is told to the caller plainly and triggers a handoff, never a silent retry or a false "done".
  • Handoff to a human is triggered by: a medical emergency, an upset/angry caller, an explicit request for a human, repeated misunderstanding, or a knowledge-base miss asked twice.
  • No medical advice or diagnosis, ever β€” emergencies get the clinic's emergency number and an immediate handoff.

GDPR / EU AI Act notes

This is not legal advice. A clinic deploying this must have its own compliance review. Notes on what the design currently accounts for:

  • Transparency. Under the EU AI Act, a system interacting with people must disclose that they're talking to an AI. Every conversation opens with that disclosure, in the caller's language.
  • Recording notice. The same opening message also discloses that the conversation may be recorded, before any content is collected.
  • Data minimisation. The agent is instructed to collect only name, phone number and reason for a booking or message β€” not medical history or symptom detail.
  • Retention. Transcripts auto-delete after a configurable period, default 90 days (TRANSCRIPT_RETENTION_DAYS). Bookings and messages outlive the transcript they came from (the conversation link is nulled, not the record).
  • Erasure. A planned DELETE /admin/customers/{id}/data endpoint anonymizes a customer's personal data on request.

Known risks

  • WhatsApp Cloud API: replies outside the 24h window need pre-approved templates; Meta business verification can take days. Start this early.
  • Twilio numbers in North Macedonia: local numbers may not be available; the usual workaround is a foreign number with call forwarding from the clinic's line.
  • Albanian and Macedonian speech recognition quality varies β€” this will be measured in Phase 3, not assumed.
  • Groq's free tier has rate limits; fine for building, but per-clinic cost needs checking before pricing this.
  • Keyword-based emergency detection is a safety net, not a guarantee. The prompt, keyword list and emergency number must be reviewed by each clinic.

Repo layout

README.md
LICENSE  .gitignore  .env.example
pyproject.toml  Makefile  Dockerfile  docker-compose.yml
docs/
  ARCHITECTURE.md          # Phase 1 full design: schema, endpoints, tool contracts, risks
  PHASE2.md                # what Phase 2 actually built, deviations, known limitations
  SCHEMA.sql                # Postgres 17 schema (tested) -- mirrored in db/migrations/0001
  schema_smoke_test.sql     # proves the double-booking guard works
  examples/                 # real-Gemini smoke-test transcripts (mk/sq/en)
prompts/
  receptionist.v1.md        # Phase 1 design-time system prompt
  receptionist.v2.md        # Phase 2 runtime system prompt (tool names match the code)
src/receptionist/
  config.py                 # pydantic-settings
  cli.py                    # `python -m receptionist.cli chat|replay`
  api/                       # FastAPI app: health, simulator routes + static chat page
  channels/                  # ChannelAdapter, CLI simulator, WhatsApp 501 stub
  core/                      # conversation loop, language, disclosure, policy, prompts, replay
  llm/                        # LLMProvider: mock, gemini, groq
  tools/                      # tool arg schemas, validators, executor
  kb/                          # knowledge base repository + the fictional demo clinic fixture
  db/                           # connection, migration runner, migrations/*.sql, turn logging
tests/
  unit/        # no DB, no API key
  integration/ # needs TEST_DATABASE_URL, skips cleanly without it

Still a Phase 1 design placeholder, not yet built: the admin/ Next.js UI (Phase 4), the voice channel and railway.toml deployment (Phase 3).

License

MIT β€” see LICENSE.

About

Multilingual AI receptionist (Macedonian, Albanian, Serbian, English) for small businesses in North Macedonia and the Balkans. WhatsApp + voice, grounded answers, safe handoff.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages