Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

65 Commits

Folders and files

Repository files navigation

Parkinson Interviewer Agent

A multi-agent LLM system that takes a plain list of questions and conducts it as a natural, neutral, multi-turn interview. Built for Parkinson's disease follow-up, but generic: swap in any questionnaire as a JSON file and the system administers it — asking one question at a time, following up when answers are incomplete, never suggesting symptoms, and remembering the patient across sessions. Runs entirely on local, open-source models (privacy and low cost), speaks four languages (French, English, German, Italian), and accepts voice input via Whisper.

System architecture

Features

  • Questionnaire in, interview out — a flat JSON list of questions becomes a conversational interview; no code change to add a new form.
  • Neutral by design — the interviewer asks the bank question faithfully and never presupposes or suggests symptoms.
  • Deterministic backbone — question coverage, follow-up dwell cap, force-advance and termination are plain code, not LLM judgment, so every interview provably covers every question and ends.
  • Long-term memory — durable facts distilled during conversations are recalled in later sessions via embeddings (with a lexical fallback when no embedding server is configured).
  • Full auditability — every turn stores the routing decision plus the complete prompt and raw output of every agent call; completed interviews export automatically as structured JSON + PDF.
  • Local & open-source models only — any OpenAI-compatible server works; no data leaves your infrastructure.
  • 4 languages — fr / en / de / it, selected per patient.
  • Voice input — Whisper transcription, remote or in-process.

Scope note: there is deliberately no automated safety/crisis-detection layer — this is a research prototype intended for use in a supervised setting with a clinician present.

Architecture

Three LLM agents around a deterministic frame:

Component Where Role
Orchestrator backend/modules/agents/orchestrator.py Understands the patient's message and routes it (answer / question / digression / refusal...) as strict JSON at temperature 0.
Deterministic frame backend/modules/frame.py Plain code: tracks question coverage, caps dwelling on one question at 3 turns, forces advancement, guarantees termination.
Interviewer backend/modules/agents/interviewer.py Writes every patient-facing message from a per-turn directive — there are no pre-written patient-facing strings anywhere.
Memory builder backend/modules/agents/memory_builder.py Every 5 patient turns, distils durable facts from the recent conversation window.
Memory store backend/modules/memory.py Flat memory_facts table (text + vector), cosine recall of the top 5 facts, Jaccard dedup at 0.7, lexical fallback without embeddings.
Audit & export backend/modules/audit.py, backend/modules/export.py Per-turn traces in turn_traces; automatic JSON + PDF export to backend/exports/conversations/ on completion.

Questionnaires are positional (q1..qN) and snapshotted per conversation, so editing a form never corrupts a running interview.

Quickstart

Prerequisites: Python 3.11+, Node.js 18+, a reachable PostgreSQL database, and any OpenAI-compatible chat server (vLLM, llama.cpp, LM Studio, ...).

Backend

cd backend
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

cp ../.env.example ../.env   # then edit the 3 REQUIRED values (see below)

uvicorn main:app --host 127.0.0.1 --port 8000

The database schema is created automatically at startup — no migration step. Health checks:

curl http://127.0.0.1:8000/api/health/live    # process is up
curl http://127.0.0.1:8000/api/health/ready   # process is up AND database reachable

Frontend

cd frontend
echo NEXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8000/api > .env.local
npm install
npm run dev

Open http://localhost:3000, register a patient account, and start an interview.

Configuration

Only three values are required; everything else is optional. See .env.example for the full annotated list.

Variable Required Purpose
DATABASE_URL yes Any reachable PostgreSQL database (schema auto-created).
OPENAI_COMPAT_BASE_URL yes Base URL of an OpenAI-compatible chat server.
OPENAI_COMPAT_MODEL yes Model name served there.
OPENAI_COMPAT_API_KEY no Leave empty for local servers.
SECRET_KEY recommended Auth token signing secret — set a long random value in production.
OPENAI_COMPAT_EMBEDDING_BASE_URL / _MODEL no Embeddings server for long-term memory. Leave empty to fall back to lexical recall.
WHISPER_BASE_URL / WHISPER_MODEL no Remote OpenAI-compatible Whisper server for voice input. Without it, chat still works — only the microphone fails.
WHISPER_LOCAL_ENABLED / WHISPER_LOCAL_MODEL no Set true to run faster-whisper in-process instead (e.g. large-v3-turbo).
FRONTEND_ORIGINS / FRONTEND_ORIGIN_REGEX no CORS allowlist for the frontend.

Trying it without a full setup

Offline, zero services — a deterministic simulation of the memory system and the frame (in-memory SQLite, hash-based fake embeddings, no model server). It checks fact storage, Jaccard dedup, cosine recall, the lexical fallback, per-patient scoping, question coverage, the dwell-cap force-advance, and termination, printing a PASS/FAIL line per check:

cd backend && source venv/bin/activate
python scripts/simulate_memory_system.py

Live, with a model server — LLM-driven adversarial "patients" (forgetful, rambling, refusing, contradicting, language-switching, prompt-injecting, ...) stress-test the real pipeline over an isolated in-memory SQLite. Transcripts and results land in backend/exports/adversarial/:

python scripts/adversarial_patients.py              # all personas
python scripts/adversarial_patients.py forgetful    # one persona
MAX_TURNS=8 python scripts/adversarial_patients.py forgetful   # quick smoke run

End-to-end over HTTP — drives the real /api/chat route with an LLM-simulated patient until the interview closes, then verifies full coverage, no duplicate questions, no robotic openers, and judges per-turn coherence with an LLM. Requires the backend running:

python scripts/full_interview_test.py    # API_BASE_URL overridable, default http://127.0.0.1:8000/api

Running the tests

cd backend
DISABLE_RATE_LIMIT=1 pytest tests/

76 tests covering routing, the chat pipeline, forms, parsing, auth, audit, export and rate limiting. No database or model server needed.

Authoring a new questionnaire

Drop a JSON file in backend/data/forms/:

{
  "_meta": { "name": "My follow-up", "description": "...", "version": 1 },
  "questions": [
    "How have you been overall recently?",
    "How is your sleep?"
  ]
}

That's it — no code change. Questions are numbered by position (q1..qN), and the form appears in the settings dropdown. Five forms ship by default (general and short Parkinson follow-up, diabetes, post-op, mood).

Data storage & exports

  • PostgreSQL holds users, conversations, messages, per-conversation form snapshots, question progress, memory_facts (text + vector) and per-turn turn_traces — the schema is created automatically at startup.
  • On interview completion, a named folder under backend/exports/conversations/ receives conversation.json (question hierarchy with nested probes, distilled memory facts, complete agent traces) and a formatted conversation.pdf.

Models & licences

Reference deployment — any OpenAI-compatible model can be substituted:

Role Model Licence
Chat (all 3 agents) Qwen3.6-35B-A3B (Qwen3 MoE family) Apache-2.0
Embeddings (memory) Qwen3-Embedding-8B Apache-2.0
Speech-to-text Whisper large-v3-turbo MIT

Project layout

backend/
  main.py                 # FastAPI app, schema auto-creation
  core/                   # settings, auth, rate limiting
  routes/                 # /api: auth, chat (per-turn pipeline), conversations, forms, audio, ...
  modules/
    agents/               # orchestrator, interviewer, memory_builder (+ tolerant JSON parsing)
    frame.py              # deterministic coverage / dwell / termination
    memory.py             # embedding memory + lexical fallback
    chatbot.py            # LLM client: chat + embeddings, retries, trace capture
    export.py             # JSON + PDF export
  data/forms/             # questionnaire JSON files
  scripts/                # simulations (offline + live)
  tests/                  # pytest suite (76 tests)
frontend/                 # Next.js app (login, chat, settings)
report/                   # scientific report (LaTeX sources + PDF)

Report

The full scientific report — motivation, architecture, evaluation — lives in report/ (LaTeX sources and compiled main.pdf).

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages