Part 1 of 18 · 7 min read
Start with the patient workflow
Durable patient records, a complete booking example and the implementation history.
Arunank Sharan · Kuzushi Labs ·
In this article
A patient relationship platform becomes useful when it helps someone finish a piece of work. Find the right patient. Arrange the next appointment. Move a follow-up forward. Give the next member of staff enough context to continue.
That is where I want to start this story. We built a FastAPI service and a Next.js dashboard around those operations, then developed a conversational interface that could turn a staff member's request into a structured action. The interesting work sits between understanding the sentence and changing the right record.
Give the work a durable home
The core model separates patients, appointments, journeys, communications and tickets. Each object answers a different question.
| Object | What it keeps track of |
|---|---|
| Patient | The person and their identifiers, contact information and preferences |
| Appointment | A scheduled interaction and its status |
| Journey definition | The ordered stages of a repeatable process |
| Journey instance | One patient's progress through that process |
| Communication | A message, its recipient, channel and delivery state |
| Ticket | Work that needs an owner and resolution |
This separation matters when a conversation ends. The follow-up must remain visible to the team even if the chat session expires. An assistant can help create or update the work, but its transcript cannot be the only place that work exists.
The patient service illustrates the foundation. Registration maps validated input into the database model, generates a medical record number, checks for possible duplicates, persists the record and publishes a creation event. The duplicate check warns; it does not automatically merge people. Those are different operations with very different consequences.
Build the interface around the current task
The dashboard gives staff conventional screens for patients, appointments, journeys, communications and tickets. The assistant sits alongside that workspace, while a command bar provides a faster entry point.
I like this arrangement because a conversation and a screen can support each other. A staff member may begin with “find the patient,” inspect the result, then ask for a follow-up. At other times the patient is already selected, and the assistant should make that context explicit before preparing an action.
The frontend and session types carry concepts such as current patient, current page, selected entities and recent actions. Defining those fields gives us a shared vocabulary. It does not automatically keep every view synchronized; the connection between selection, request context and backend execution still has to be built and tested.
How the build developed
The repository history gives this work a concrete progression. The Next.js application and early frontend agent layer appeared in November 2025. A command-bar interaction followed that month. Backend orchestration arrived in January 2026, followed by a broader domain-agent catalogue and a unified model client with Gemini support in February.
The architectural direction was toward putting action execution behind the backend. The browser could capture intent and present a preview; domain services could own the operation. That creates a clearer place to validate inputs, manage state and report what actually happened.
I would not describe every surface in that history as equally complete. The assistant panel calls the backend. The command bar in this snapshot still uses simulated parsing and execution. Several tool adapters need reconciliation with the services they target. The series follows the mechanisms we built and explains those remaining boundaries where they affect the story.
Follow one request through the whole product
Consider a worked example: a coordinator has selected patient p-001 and asks, “Arrange the next available follow-up, then send the confirmation.” I would decompose that into observable states before choosing a model. The patient must be resolved; availability must come from the scheduling authority; a particular slot must be reviewed; the appointment must commit; outreach must then progress independently.
The following is a reference contract, using synthetic identifiers. It makes the split visible to both frontend and backend:
{
"request_id": "req-001",
"context": {
"patient_id": "p-001",
"patient_version": 7,
"screen": "patient_detail",
"timezone": "Asia/Kolkata"
},
"intent": "appointment.prepare",
"after_success": "offer_confirmation_message"
}
Notice what is absent: the browser does not declare its own tenant or permission set. The authenticated server context supplies those. The patient ID is a selection to validate, not authority to read that patient. after_success is a supported follow-on workflow, not an arbitrary function name to execute.
The phrase “next available” also needs a boundary. Does it mean any practitioner, the previous practitioner, a particular specialty or the same location? The application should resolve those constraints before showing a slot. A precise clarification—“Same practitioner and location?”—is more useful than a generic request for more information.
Persist the invariants before the narrative
The original service already gives records a durable home. For the reference lab, I reduce the schema to the smallest useful booking model:
CREATE TABLE appointments (
tenant TEXT NOT NULL,
id TEXT NOT NULL,
patient_id TEXT NOT NULL,
slot_id TEXT NOT NULL,
proposal_id TEXT NOT NULL,
PRIMARY KEY (tenant, id),
UNIQUE (tenant, slot_id),
UNIQUE (tenant, proposal_id),
FOREIGN KEY (tenant, patient_id)
REFERENCES patients(tenant, id),
FOREIGN KEY (tenant, slot_id)
REFERENCES slots(tenant, id)
);
This deliberately models one appointment per discrete slot. A group clinic needs capacity accounting instead; variable-length appointments need overlap protection. Those are different scheduling products, so I would not silently reuse this constraint for them.
The composite foreign keys prevent an appointment in one tenant from pointing to another tenant's patient or slot. The proposal constraint protects a different invariant: one reviewed proposal must not create two appointments. Neither constraint answers whether a staff member is allowed to book; that remains a service-layer check.
There is another important distinction: a locally generated medical record number is an identifier, not proof that two records describe the same person. Our original duplicate warning was therefore a useful boundary. In an integration, I would keep the local identity and external identifiers separately, including the identifier's assigning system. Part 7 develops that connector.
Make ownership explicit at each handoff
| Question | Authority | What the assistant may do |
|---|---|---|
| Which person is selected? | Tenant-scoped patient service and staff verification | Present candidates and ask for selection |
| Is the slot still available? | Scheduling system at commit time | Search and prepare a proposal |
| What did the user approve? | Versioned proposal | Explain the exact fields |
| Was it booked? | Persisted appointment or verified external receipt | Report that result |
| Was the message delivered? | Delivery callback or provider reconciliation | Display the current status |
This table prevents a common implementation mistake: treating the generated answer as the source of truth for every column. The answer is a presentation of evidence collected from several owners.
Run the smallest useful vertical slice
The downloadable lab uses Python and SQLite so the transaction boundary can be inspected without a cloud account. From its reference-lab directory:
python3 prm_lab.py
python3 -m unittest discover -s tests -v
The demonstration prepares a proposal, confirms it, writes one appointment and one outbox event, then records one simulated provider receipt. The IDs change on each run; the counts should all be one. The fake connector performs no network calls.
I would keep the same acceptance story when reconnecting the full application. Close the browser after booking, reopen the patient and verify the appointment. Kill the outreach worker and verify that the pending event remains. Try the same confirmation again and verify that no second appointment appears. Those observations make the workflow real in a way a polished chat transcript cannot.
What makes this agentic
For me, the meaningful step is from a request to an explicit operation: classify the task, extract its arguments, choose the relevant domain, prepare a plan and return either a result or something the user can review.
That is a stronger product contract than a helpful paragraph. It also makes failure more specific. We can ask whether the wrong patient was selected, an argument was missing, a service call failed or an action remained unconfirmed.
The next article follows that path through the backend. Once that is clear, we can look at how the interface makes the plan visible—and why a successful-looking card still needs a confirmed result behind it.
The implementation kit includes the articles, diagrams and an offline reference lab. Code identified as a reference design is a new teaching implementation; it is not a claim that the original project already shipped that behavior.