Skip to content

KieFitz/acmeDentalAgent

Repository files navigation

Acme Dental Agent — Aria

AI-powered receptionist chatbot for Acme Dental clinic. Aria handles appointment bookings, cancellations, FAQs, and clinic queries via a chat interface backed by a LangGraph + FastAPI agent and the Calendly API.

For architectural decisions and design rationale, see PROJECT_BRIEF.md.


Tech Stack

Layer Technology
LLM Google Gemini 2.0 Flash (via langchain-google-genai)
Agent LangGraph create_react_agent with MemorySaver
API FastAPI + Uvicorn
Appointments Calendly API (v2)
Database SQLite (SQLAlchemy) — persisted via Docker volume
Email SMTP (Gmail/Outlook) via aiosmtplib
Security bcrypt PIN hashing, 3-attempt lockout
Containerisation Docker + Docker Compose
Package manager uv
Testing pytest

Project Structure

backend/
  agent.py               # LLM + LangGraph agent assembly
  guardrails.py          # Input/output validation (injection, length, topic)
  main.py                # FastAPI app + CORS + lifespan
  KNOWLEDGE_BASE.md      # Clinic FAQ loaded by search_faq tool
  db/
    database.py          # SQLAlchemy engine + session + init_db()
    models.py            # AppointmentPin, ConversationMessage, SessionBooking, ConversationReview
  routes/
    chat.py              # POST /chat — main chat endpoint
    admin.py             # Admin API — conversation list, transcript view, review status
  services/
    email_service.py     # Async SMTP confirmation emails
    pin_service.py       # PIN generation, hashing, verification, lockout
  tools/
    __init__.py          # Exports all_tools
    calendly.py          # Booking, lookup, cancel, reschedule, available slots (Calendly API)
    clinic.py            # Clinic info, services, FAQ search, datetime, opening hours tools
frontend/
  index.html             # Patient-facing chat UI (HTML/CSS/JS)
  admin.html             # Admin UI — browse conversations, view transcripts, mark sessions
  nginx.conf             # Nginx — serves frontend, proxies /chat/ and /admin-api/ to backend
tests/
  tools/
    test_clinic.py       # Tests for clinic tools
    test_calendly.py     # Slot filtering, booking validation, opening hours tests
  services/
    test_pin_service.py  # PIN generation, verification, lockout tests
Dockerfile               # Backend image (uv + Python 3.13)
docker-compose.yml       # backend + nginx frontend + SQLite volume

Getting Started

Prerequisites

  • Docker Desktop
  • A Google Gemini API key (free tier: 1,500 req/day for gemini-2.0-flash)
  • A Calendly account with a PAT token
  • An email account with an App Password (for SMTP)

1. Configure environment

Copy .env.example to .env and fill in your keys:

GEMINI_API_KEY=your_gemini_api_key
Calendly_API_Key=your_calendly_pat_token

SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=you@gmail.com
SMTP_PASSWORD=your_gmail_app_password
SMTP_FROM=you@gmail.com

2. Run with Docker

docker compose up --build

Open http://localhost:3000 in your browser.

3. Run locally (development)

uv run uvicorn backend.main:app --reload

Open frontend/index.html directly in your browser.


How It Works

Booking flow

  1. Patient asks for available slots → agent calls get_available_slots(date) → real Calendly API
  2. Patient selects a slot → agent calls book_appointment(name, email, slot) → creates local record + bcrypt PIN
  3. Confirmation email sent with appointment details, 6-digit security PIN, and appointment reference
  4. PIN is never shown in chat — only delivered via email

Cancel / Lookup flow

  1. Patient provides name + PIN → agent calls lookup_appointment or cancel_appointment
  2. PIN verified against bcrypt hash in DB (3-attempt lockout on failure)
  3. Cancellation forwarded to Calendly API using the patient's email to find the active event
  4. Works from any session — no session memory required

Guardrails (pre-agent)

  • Input length limit (1,000 chars)
  • Prompt injection detection
  • Data fishing detection
  • Off-topic and sensitive medical topic blocking
  • Max 3 bookings per session, unique patient names per session (this is to allow bookings for family members if convinient to book for kids at the same time.)

Conversation logging & admin review

Every message pair is stored in ConversationMessage with a session_id.

The admin dashboard (http://localhost:3000/admin.html) lets you:

  • Browse all conversation sessions with timestamps and a first-message preview
  • View the full transcript for any session
  • Mark each session with a review status: unreviewed / safe / risky / dangerous
  • Add free-text notes to flag specific issues

Review statuses are stored in a ConversationReview table and are intended to inform tuning of the system prompt, guardrails, and tool behaviour over time.

Raw API endpoints:

GET  /admin-api/conversations                    # list all sessions with review status
GET  /admin-api/conversations/{session_id}       # full transcript + review for a session
POST /admin-api/conversations/{session_id}/review  # set status and notes

Running Tests

uv run pytest tests/ -v

API Endpoints

Method Path Description
POST /chat/ Send a message to Aria
GET /health Health check
GET /admin-api/conversations List sessions with review status (admin)
GET /admin-api/conversations/{id} Full transcript + review for a session (admin)
POST /admin-api/conversations/{id}/review Set review status and notes (admin)

TODO

High priority

  • Refine prompts — review conversation logs to identify where Aria gives robotic or unclear responses; improve the system prompt and tool docstrings accordingly
  • Confirm live bookings with Calendly — wire up book_appointment to create a real Calendly event via the scheduling links API or a Calendly webhook flow; currently bookings are recorded locally only
  • Test fraud prevention end-to-end — manually test PIN lockout, wrong-name rejection, and 3-booking-per-session limit with live data; verify bcrypt timing is acceptable
  • Agent to ammend bookings if session same - Agent should be able to ammend or cancel bookings if the session is still the same, without requirment for PIN. PIN should only be for if there is a new session.
  • Make Sure AI doesn't invent opening hours

Medium priority

  • Edge case hunting — test corner cases: same patient name different email, booking on a Saturday[failed], cancelling an already-cancelled appointment, lookup when Calendly API is down, rescheduling for same time and day.
  • Wire up Calendly available slots — confirm get_available_slots is returning real times from GET /event_type_available_times for the correct event type
  • Reschedule flow — implement reschedule_appointment(patient_name, pin, new_slot) tool; currently only cancel is supported. reschedule not supported in API from Calendly.

Lower priority

  • Persistent MemorySaver — replace in-memory MemorySaver with langgraph-checkpoint-sqlite so conversation context survives container restarts
  • Rate limiting — add per-IP or per-session message rate limiting to prevent API abuse
  • Admin dashboard — UI to browse conversation logs, view full transcripts, and mark sessions as safe / risky / dangerous with notes to inform future guardrail and prompt tuning

About

Dental agent for ACME clinic

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors