Skip to content
Mohammad Hasan Kaman edited this page Oct 20, 2026 · 2 revisions

AI Counseling Bot — Wiki

A guided intake in a chat window, a 28-question wellbeing screen, and a matching engine that gets better every time a counselor turns out to be the right choice. All of it running inside one Python process on a Bale messenger bot — no LLM API, no cloud inference, no per-message cost.


What this is, and who it is for

This project is a counselor-matching assistant for the "خانواده نیک‌روان" family counseling center, delivered as a bot on the Bale messenger. A visitor lands in a chat window, answers a short guided intake (name, phone, age, gender, the problem they want help with, therapy history, what they expect from counseling, preferred counselor gender, preferred branch), optionally takes the GHQ-28 wellbeing screening, and receives a ranked shortlist of two counselors with a "why this match" explanation and a direct link to their booking page.

It is for two audiences at once: the person seeking help — who gets a fast, structured, private way to find the right counselor instead of phone tag — and the clinic's staff — who run the whole thing from inside the same bot: statistics, Excel export and import, mass announcements, a scheduled availability crawler, and the feedback loop that trains the matcher.

The unusual part, and the reason the code looks the way it does: the "AI" is not a language model. It is a local, deterministic engine — clinical concept extraction from free text, fuzzy name matching, age-bound parsing, weighted scoring, and a feedback table that re-weights future matches. Nothing a user types is sent to a third-party AI service, and the bot keeps working without an internet-hosted model, an API key, or a per-request bill.


Quick navigation

Page What you will find there
Architecture The layered design (Bale → conversation → AI engine → storage), the end-to-end data flow of a single session, a component diagram, and the two-pass ranking algorithm.
Matching Engine Deep dive into SpiralMatchEngine: the public API, hard filters, weighted scoring, concept extraction, name normalization, age-bound parsing, and the feedback-learning loop.
GHQ-28 Screening What the 28-question screen measures, the four subscales, how it is asked and scored, how the score is interpreted, how it feeds ranking, and the ethical boundaries.
Admin Panel Every admin command as reference tables: the session model, the Excel export shape, the broadcast confirmation flow, the counselors upload with hot-reload, and the learning commands.
Setup and Deployment Clone → venv → pip install → .env → run. Every environment variable explained, plus a production checklist and the data files to back up.
Troubleshooting Symptom → cause → fix table for the problems that actually happen: bad tokens, missing files, locked databases, crawls that return nothing, names that won't match.
Contributing Dev setup, the code conventions the repo observes, the data pipeline, how to add a synonym or a screening question safely, and what a good PR looks like.

Why the AI is inside the bot

Component What it does
SpiralMatchEngine Two-pass ranking: hard filters (gender, branch, age bounds, legal-specialty guard) followed by weighted scoring over topic fit, clinical-concept match, ability, and live free-appointment count.
Concept extraction Maps free-text topics and expectations to clinical concepts through an expanded synonym dictionary with Persian/Arabic variants (e.g. والد_فرزند, زوج_ازدواج, پزشکی_روانپزشکی).
Name normalization Fuzzy counselor-name matching across spacing, Arabic/Persian letter variants (ي/ی, ك/ک), titles (آقای, دکتر), and one-character typos — so a name typed by an admin or scraped from a booking page still resolves.
Clinical triage GHQ-28 screening (28 questions) scored across four subscales, interpreted into risk bands, and fed into the ranking as a routing signal.
Feedback learning Every recommendation and every admin rating writes to ai_knowledge.db; positive/negative signals re-weight future matches for that (counselor × concept) pair.
Availability awareness A crawler refreshes open-slot counts every 2 hours so the engine can prefer counselors the user can actually book right now.

At a glance

Language Conversation UI in Persian (RTL); all code, comments, commit messages and documentation in English.
License MIT (© 2026 Mohammad Hasan Kaman).
Runtime Python 3.9+ per the README — in practice 3.10+, because the code uses PEP 604 X | None annotations without from __future__ import annotations.
AI service required None. No OpenAI/Anthropic/local-LLM dependency; requirements.txt contains no model runtime.
Does user data leave the machine? No AI vendor sees it. The only outbound traffic is the Bale bot API (https://tapi.bale.ai/bot) and the crawler's read of the center's own public team page (https://nikravan.org/team/). Data lives in local SQLite files.
Messaging platform Bale, driven by python-telegram-bot==22.8 pointed at https://tapi.bale.ai/bot.
Entry point python main.py — starts polling, the APScheduler crawl job, and the initial background crawl.

Repository layout

main.py                 Bot entry point: conversation flows, admin handlers, scheduler
internal_ai_engine.py   SpiralMatchEngine — matching, triage, feedback learning
ghq_analyzer.py         GHQ-28 questionnaire, scoring and interpretation
user_db.py              SQLite storage for users and request history
admin_tools.py          Excel export and broadcast helpers
crawler.py              Appointment availability scraper (scheduled)
config.py               Paths, scheduler settings, env-based credentials
excel_to_json.py        Excel roster → consultants_profile_full.json
build_clean_mapping.py  Builds the name → profile-URL mapping
extract_links_from_html.py  Extracts team profile links from saved HTML
load_clean.py           Legacy loader (not runnable as it stands — see Contributing)
mapping.json            Counselor name → profile URL map
docs/                   hero.png, engine.png, conversation.png — project screenshots
references/             Saved copy of the public team page (crawler input sample)
All 12 conversation states, in order

NAME → PHONE → GENDER → AGE → TOPIC → PREV_THERAPY → PREV_DETAIL → EXPECTATION → PREFERRED_GENDER → BRANCH → ASK_GHQ → GHQ_QUESTION

The first ten are the intake; the last two are the optional screening. PREV_DETAIL is only entered when the user says they have been in therapy before. Every state accepts the شروع مجدد button, the word شروع, or /start as a way out.


How a session flows

  1. Greeting — /start (or the "شروع مجدد" button) opens the intake and clears any half-finished session.
  2. Intake (10 steps) — name → phone → gender → age → topic → previous therapy → (details) → expectation → preferred counselor gender → branch. The phone must be 11 digits starting with 09; if that number is already registered under a different name, the bot refuses to continue, to keep two people's records apart.
  3. Welcome back — after the phone number is verified, a returning visitor (from the second request onward, matched on the phone) gets a "this is my Nth visit" message before the intake continues.
  4. GHQ-28 offer — the bot explicitly says the screening is optional and asks "بله / خیر". Answering "خیر" skips straight to matching.
  5. 28 questions — one question per message, with a numbered reply keyboard; the answer is stored as the 0-based option index.
  6. Scoring — four subscale sums plus a total, interpreted into severity bands and shown to the user with a plain-language message.
  7. Matching — SpiralMatchEngine.match() runs hard filters, then weighted scoring, then returns the top 2 counselors.
  8. Result card — each recommendation shows the counselor's name, the reason for the match, and a booking link resolved from mapping.json.
  9. Write-back — the request is stored in user_records.db, and a positive learning signal is recorded for each recommended counselor against the clinical concept detected from the topic.
  10. Learn — admins rate outcomes with /feedback; the weight for that (counselor × concept) pair moves, and the next match reflects it.

Disclaimer

Important

This bot supports — it does not replace — professional care.

  • The GHQ-28 output is a screening signal, not a medical diagnosis. A score says "this person reports more distress on these four areas"; it is not a clinical assessment, and no diagnosis should ever be inferred from it.
  • Counselor matching is advisory. The engine ranks candidates by documented rules over structured data; the user makes the final choice, and the clinic's own intake process remains authoritative.
  • A high GHQ score is used for routing — pushing psychiatry-capable counselors higher in the list — and never for automated clinical decisions.
  • The recorded responses are sensitive health-adjacent data stored in local SQLite files. Treat them accordingly: never commit them, and restrict access.

Where to go next

Clone this wiki locally