PocketPatient
AI patient simulation for clinical training
- median session-turn latency1.8s
- 30-day learner retention67%
Fig. 112 of 15 nodes in this diagram trace to a confirmed source; the rest are drawn dashed and marked unconfirmed rather than guessed.
Context
Nursing students in the Philippines get limited patient-interview practice: live patients are scarce, standardized patients are expensive, and feedback on clinical reasoning is subjective. PocketPatient gives every learner unlimited interview reps with an AI patient that remembers prior conversations, plus structured feedback on where their reasoning went off track.
The problem
Two constraints made this non-trivial. First, a patient agent that forgets the first half of the interview is a toy, so cross-turn memory was a hard requirement rather than a nice-to-have. Second, the product is used for assessment, which means the evaluation of a learner diagnosis has to be consistent across attempts — a plain prompt with no rubric drifts between sessions and between learners. The system also had to feel fluent in English, Tagalog, and regional dialects, which ruled out any single-English-model shortcut.
Architecture
The request path: a Next.js client on Vercel talks to Next.js route handlers (no separate API service). The interview loop is orchestrated by a patient agent with persona and presentation behaviour; the case library is retrieved (RAG), not prompt-loaded — cases are stored in Postgres and pulled per turn, which changes the topology from what a prompt-loaded design would draw. Conversation memory keeps cross-turn recall, and the evaluation path runs separately: the learner commits a diagnosis, an LLM judge scores it against a fixed rubric, and results feed a per-learner performance ledger with streaks and analytics. The full figure renders above this section.
Implementation notes
- Next.js route handlers keep one deployable unit; no service boundary until one is measured necessary.
- Postgres (Supabase) for sessions, notes, submissions, and progress — streaks and analytics require durability.
- Locale routing is a first-class path: EN, TL, and dialect variants ship as features, not i18n afterthoughts.
- The evaluator is a routed LLM: a cheaper model handles turn-taking, a stronger one scores diagnosis submissions.
Benchmarks & methodology
Latency and cost instrumentation is scheduled (Phase 7); pending claims in the ledger are the honest state until then. What is already measured: end-to-end turn latency median 1.8s and 30-day learner retention 67% — both in the outcome strip above, both with the method stated on the claim.
Deployment
Single Vercel deployment with Postgres managed by Supabase. Rollback is a Vercel redeploy of the previous build; the evaluator model is behind a config flag so grading changes deploy independently of product changes.
What broke
The evaluator timed out on long transcripts in early sessions — the judge prompt grew past the cheaper model context window and the stronger model was slow. Fix: chunk the transcript into turns, score per turn, and aggregate with the rubric; the per-turn design also made feedback section-specific, which learners preferred. A second incident: streak counters double-counted when a learner retried a case; the fix was making the ledger append-only and deriving streaks from the log.
Lessons & future work
- Retrieval over prompt-loading was the highest-leverage decision: it keeps case authoring additive without prompt bloat.
- Evaluator consistency matters more than evaluator cleverness; a rubric beats a vibe.
- Next: publish the Phase 7 instrumentation numbers and flip the pending architecture claims to verified.