Korely

A patient assistant that never forgets the chart

Picture a physiotherapy clinic with an intake assistant in the booking flow. Patient Marco opens the chat before his fourth visit. With a stateless bot, Marco gets the same intake questionnaire every single time: "Any allergies? Any current medication? Where does it hurt?" He answered all of this in January, February, and March. With a Korely-backed bot, the assistant opens with what it already knows: the rotator cuff strain from January, the penicillin allergy, the medication change in March, the session note that he responded well to exercise B. The chat starts where the last visit ended, because the developer wired in the four calls on this page.

Read this before anything else. If you're deploying in a regulated health context, talk to us first at [email protected]. We handle health deployments case-by-case, including self-host and on-prem options. Korely gives you the data-control primitives: per-patient deletion in one call, a bi-temporal audit trail, and all cloud data EU-hosted on our own infrastructure. The regulatory review of your application is yours. This cookbook shows the memory mechanics. It is not a turnkey medical device.

The scoping model in one sentence

One clinic app = one agent_id ("intake-assistant"). Every patient = one user_id ("patient-marco", or better, your internal patient identifier). End users are unlimited on every tier. 10,000 patients is still one agent_id with 10,000 user_id values. Filters are additive: both together return only what this app knows about this patient.

Call 1: intake opens, load the chart context

When Marco opens the chat, before the LLM says a word, your backend pulls his health facts:

from korely_memory import Korely
korely = Korely(api_key="kor_live_...")
facts = korely.get_facts(
user_id="patient-marco",
predicate_family="health",
)

The response is a flat list of the typed facts Korely extracted from prior sessions. A fact is live when invalid_at is null; superseded facts carry the date they were invalidated (see Call 3):

{
"facts": [
{"id": "fct_19ce", "subject": "patient-marco", "predicate": "allergic_to",
"predicate_raw": "allergic_to", "object": "penicillin",
"predicate_family": "health", "confidence": 0.95,
"valid_from": "2026-01-12T09:30:00Z", "invalid_at": null,
"invalidated_by": null, "source_memory_id": "mem_4a1c"},
{"id": "fct_22b0", "subject": "patient-marco", "predicate": "diagnosed_with",
"predicate_raw": "diagnosed_with", "object": "rotator cuff strain (right shoulder)",
"predicate_family": "health", "confidence": 0.92,
"valid_from": "2026-01-12T09:30:00Z", "invalid_at": null,
"invalidated_by": null, "source_memory_id": "mem_4a1c"},
{"id": "fct_4f81", "subject": "patient-marco", "predicate": "takes_medication",
"predicate_raw": "takes_medication", "object": "naproxen 250 mg as needed",
"predicate_family": "health", "confidence": 0.9,
"valid_from": "2026-03-02T10:05:00Z", "invalid_at": null,
"invalidated_by": null, "source_memory_id": "mem_91f3"}
],
"total": 3
}

These facts go straight into the system prompt. Reads from the fact store are deterministic SQL lookups, no model calls, typically under 50 ms even with thousands of facts in the store. Cheap enough to run on every single chat open. The health family is one of nine predicate families Korely extracts natively. You don't define a schema, the typed predicates already exist:

FamilyExample predicates
preferenceslikes, dislikes, prefers
peopleknows, married_to, works_with
placeslives_in, visited
workworks_at, role_is
ownershipowns, uses
healthallergic_to, diagnosed_with, takes_medication
financialpays_for, subscribed_to
eventsattended, scheduled
othercatch-all typed edges

Call 2: during the session, write the note

After the visit, the assistant (or the therapist's dictation flow) stores what happened:

memory = korely.add(
content="Responded well to exercise B, pain 3/10 (was 6/10)",
user_id="patient-marco",
agent_id="intake-assistant",
)

The raw note is stored and searchable as-is. The write path is where the intelligence runs: document and chunk embeddings, entity extraction on our own infrastructure, typed-fact extraction with contradiction checking and bi-temporal validity. About a tenth of a cent per memory, all included. Anything that fits a typed predicate is added to Marco's graph. Unstructured observations ("responded well to exercise B") stay retrievable via semantic search; structured ones (allergies, diagnoses, medication) become the facts Call 1 returns.

Call 3: the treatment history, never silently lost

In March, Marco's dosage changed. A naive store would overwrite the old value, and a clinic cannot afford "we don't know what he was taking in February." Korely facts are bi-temporal: a contradiction doesn't delete the old fact, it invalidates it with a date. Pull the full chain:

history = korely.get_facts(
user_id="patient-marco",
include_invalidated=True,
)
PredicateValueValid fromInvalidated
takes_medication naproxen 500 mg twice daily 2026-01-12 2026-03-02
takes_medication naproxen 250 mg as needed 2026-03-02
allergic_to penicillin 2026-01-12
diagnosed_with rotator cuff strain (right shoulder) 2026-01-12

Contradiction detection is two-stage and runs on every write: a cheap candidate match on subject and predicate, then a focused conflict check before anything is invalidated. By the time your agent reads, the conflict is already resolved. Temporal facts covers the mechanism in depth.

Point-in-time queries

Because every fact carries valid_from and invalid_at, the API exposes as_of: "what did we know at the March visit?" For anything resembling a clinical audit trail, this is the difference between "the database says X today" and "we can prove what the system knew on March 1st."

march_view = korely.get_facts(
user_id="patient-marco",
predicate_family="health",
as_of="2026-03-01",
)
# -> takes_medication: naproxen 500 mg twice daily
# (the fact that was valid on that date, audit-grade)

Who controls the data

Two deployment shapes, two control models:

  • Clinic scenario (this cookbook): control stays API-side, in your hands. A patient asks you to erase everything about them? One call wipes everything this app knows about them. Per-patient, bulk, done:
Terminal window
curl -X DELETE https://api.korely.ai/v1/users/patient-marco/memories \
-H "Authorization: Bearer kor_live_..."
# 200 OK
{"user_id": "patient-marco", "memories_forgotten": 14, "facts_invalidated": 6, "audit_id": "aud_77c2"}
  • Personal memory store scenario: if the patient uses Korely directly as their own account, they see and control every fact through the Memory Panel: edit a wrong dosage, forget a fact entirely. No support ticket, no developer in the loop.

The whole loop

Visit 1: January

Intake session

  • korely.add("Responded well to exercise B...")
  • user_id: patient-marco
  • agent_id: intake-assistant

Korely

Typed facts, bi-temporal

  • allergic_to: penicillin
  • diagnosed_with: rotator cuff strain
  • takes_medication: 500 mg → 250 mg (chain kept)

Visit 2, and every visit after

Greeting with context

  • get_facts(predicate_family="health") on chat open
  • "Welcome back Marco. How is the shoulder since we switched to exercise B?"
  • Deterministic read, typically under 50 ms

The health family, the struck-through superseded dosage, the dates: the exact behavior this cookbook builds on. If you're building something health-adjacent, talk to us before you ship it ([email protected]). We are honest about where our data-control primitives end and your regulatory review begins, and the self-host conversation is shorter than you'd expect.