ChartPilot: Project Story
Inspiration
I trained as a doctor, and the part of clinic nobody warns you about is the reading. Before a patient even walks in, there can be years of notes waiting. Old discharge summaries, lab trends, a medication list that has been rewritten four times, a scanned referral from some other hospital that someone typed up at 2am. On a busy day you get a few minutes per chart. So you skim. And when you skim across that much history, you miss things. I have missed things. A potassium quietly creeping up over three visits. A drug interaction sitting two pages back that nobody flagged.
People like to tell me a large language model can already read all of that, and they are half right. Dump the files in, write a decent prompt, and it will hand you a tidy summary. The thing I could never find was a tool built the way a doctor actually works. Not another summary generator, but something that makes sure I do not miss the one thing that matters, shows me where it got that from, and then lets me act on it or wave it away on purpose. Summaries I have plenty of. A safety net I could actually trust, I did not.
That gap is the whole reason ChartPilot exists.
What it does
ChartPilot reads a patient's record before clinic and gives me a one-page safety brief. Findings are ranked by how much they should worry me, and every single one is tied back to the exact data point it came from, so I can check the source in one click instead of trusting a wall of prose.
The rule underneath it is strict, and honestly it is the part I am proudest of. Every fact on that page comes from plain deterministic code reading the record, never from the model's imagination. The model is allowed to be an editor and nothing more, phrasing what the code already found. Sitting on top of that is an evidence layer that throws out any claim without a citation, and then a second model, kept blind to the first, whose only job is to try and prove each claim wrong. If a claim cannot survive that cross-examination, a final gate quietly drops it before I ever see it. And when something breaks, the page says so out loud and flags itself for review. It never shows me a calm empty "nothing found," because that is the most dangerous thing a chart tool can do.
The part I get excited about is that it does not sit still. As I work, confirming a finding, overriding one, correcting another, those small human signals feed a loop that rewrites and tests the system's own prompts. The software gets better at the job the more doctors use it, and it does that without anyone retraining a model from scratch.
How we built it
The backbone is deliberately boring where it needs to be. A Python and FastAPI service ingests a patient's FHIR record, normalises it, and runs deterministic rules that own every clinical fact. Only after the facts exist does a Gemini model come in as the editor, and a second Gemini pass, blinded to the first, acts as the adversarial reviewer that tries to falsify each claim. A citation layer and a fail-closed gate stand between all of that and the doctor.
Around this sits the self-improving outer loop, which is the idea I most wanted to prove. When clinicians label the output, a proposer drafts a new candidate prompt, the loop runs it against held-out synthetic patients, and it only promotes the new prompt if the claims survive review at a higher rate than the old one. A durable ledger in Firestore records which prompt version is live, so improvement is a real recorded event, not a vibe.
It all runs on Google Cloud, wired so the sensitive work never touches the open internet. Cloud Scheduler triggers Cloud Tasks, which calls a private Cloud Run service, which talks to Gemini and writes results to Firestore, and only a read-only UI is public. The frontend is Next.js and TypeScript, with an evidence drawer and a manual-review panel built for how a doctor's eye moves down a page.
One more thing worth saying plainly. We built ChartPilot itself with AI coding agents, an orchestrating agent that planned and verified and builder agents that wrote the code, with every change gated by an offline test suite before it counted. So the project is both a use of agents and a small experiment in whether agents can build something this safety-sensitive without cutting corners.
Challenges we ran into
The hardest problem was keeping the model honest. Language models want to be helpful, and a helpful model will happily fill a gap in a chart with something plausible and wrong. Getting to a design where the model is structurally unable to introduce a fact, rather than just politely asked not to, took more rework than anything else.
The blinded second reviewer was fiddly too. If it can see the first model's reasoning, it just agrees. Keeping it genuinely independent, and then deciding what to do when the two disagree, is where a lot of the safety actually lives.
The self-improving run gave us a very real, very unglamorous problem. Partway through, our Gemini monthly spend cap tripped and returned a hard error, which paused the run after the first round. We raised the cap and the full four-round run then completed cleanly, promoting a better prompt every round. I left that story in the evaluation on purpose, because pretending a live system never hits a billing wall would be dishonest.
Latency is still not fully solved. A single fresh patient can take longer than I would like when the reasoning model is cold, so the judged demo uses a precomputed run that loads instantly while the live path stays real but slower. I would rather show that honestly than fake a stopwatch.
Accomplishments that we're proud of
The safety architecture holds together. Deterministic facts, an editor model that cannot invent, a blind adversary, and a gate that fails loud. It behaves the way I would want a tool touching patient decisions to behave.
The self-improving loop actually improved. Across four rounds on thirty-two synthetic patients, with real clinician labels in the loop, it promoted a better-scoring prompt every round. That is the claim I most wanted to be able to make and mean.
And it is genuinely live on Google Cloud, in an isolated project, with the private backend that a real deployment would need rather than a laptop demo.
What we learned
The biggest lesson is that you do not have to retrain a model to make it meaningfully better at a clinical task. Changing and testing the prompts, guided by what doctors actually confirm or reject, moved the numbers. That matters, because it means the improvement signal is something a doctor generates just by doing their normal work, and those prompt changes become a quiet stream of training data for future medical AI, without anyone stopping to annotate a dataset.
I also learned how much friction decides everything in healthcare. A tool that adds two clicks to a doctor's day dies, no matter how clever it is. The only version worth building is one that removes work while it collects the signal it needs to improve.
What's next for ChartPilot
The honest next step is the unglamorous one, which is privacy and safety. Anything that touches real patient data needs a strict privacy review and a proper safety profile before it goes near a clinic, and that comes before any new feature.
After that, small pilots. Real records, real doctors, in a setting that reduces their workload instead of adding to it, measured carefully. I am convinced the way healthcare AI actually gets good is by becoming a low-friction part of a doctor's day early, and then improving on the signal that produces, all while staying inside the law.
Longer term, I want the prompt-improvement data ChartPilot generates to feed the broader project of better medical AI, so that every doctor who uses a tool like this is quietly making the next version better for the next doctor. None of this replaces the clinician. I believe a doctor using a tool like this becomes a better doctor, with more attention left for the actual human in front of them, because we were never only treating a disease. We treat people, and every one of them is different, and a little unpredictable, and worth not missing anything on.
Log in or sign up for Devpost to join the conversation.