Concepts
The memory model
Korely is memory for AI agents: what an agent learns in one session stays available in later sessions. Korely keeps two kinds of data: memories, the texts you write, and facts, short statements that Korely's extraction model pulls out of them. A run_id label groups memories by session.
This page shows what all three hold and which call reads or writes them.
Your agent saves text with add(). Before
your model answers,
get_context() returns a
context block to add to its
prompt: the current facts and the memories most similar to the question.
Terms
The Glossary defines the words used across the docs, and each one links there where it first appears. This page adds one.
- Korely's extraction model and your model: two different language models. Korely's extraction model is the one Korely runs to write facts from your memories (extraction). Your model is the one your app calls to answer, and it reads the context block.
A small example
You need Python 3.9 or later. Install the SDK:
pip install korely-memoryGet a free API key on the Quickstart page of these docs, then export it:
export KORELY_API_KEY=kor_live_...
Without the key, Korely() raises KorelyError: "No
API key. Pass api_key=..., set KORELY_API_KEY...".
The script saves one memory for the
end user customer-giulia and waits 20 seconds, about twice the usual
extraction time, to leave a margin. Then it prints each fact as
open (true now) or
closed (no longer true, kept as
history), and the context block. Checked with SDK 0.1.28.
import timefrom korely_memory import Korely
korely = Korely() # reads KORELY_API_KEY from the environmentkorely.add("Giulia is on the Advanced plan. She prefers email follow-ups. " "She is based in Turin.", user_id="customer-giulia")time.sleep(20) # extraction takes about ten seconds
for f in korely.get_facts(user_id="customer-giulia"): state = "closed" if f.invalid_at else "open" print(f.subject, "|", f.predicate, "|", f.object, "|", state)
result = korely.get_context("How should we follow up with Giulia?", user_id="customer-giulia")print(result.context) # prints the block for your modelAlways pass user_id when your project serves several customers.
Without it, a read covers every end user in the project. See Narrowing a read.
It prints facts like these:
Giulia | prefers | email follow-ups | openGiulia | based_in | Turin | openGiulia | subscribes_to | Advanced plan | openThen the context block, shortened: the real block starts with a fixed note for your model and lists the facts closest to your question (up to ten). The dates are the day you run the script.
## Known facts- Giulia prefers email follow-ups (since 2026-11-01)
## Relevant memories- [2026-11-01] Giulia is on the Advanced plan. She prefers email follow-ups. She is based in Turin.- Korely's extraction model picks predicate names, so yours may differ.
- The subject is usually the name in the text, but sometimes it is your
user_id, so do not rely on it being the name. - Empty list? Wait a few seconds and call
get_facts()again. Still empty after a minute? See Add a memory.
Memories and facts
flowchart LR
M["Memories<br/>texts you write"]
F["Facts<br/>with dates"]
R["run_id label"]
M -->|"extraction model reads"| F
R -.- M
style R stroke-dasharray: 5 5
| What it holds | How long it lasts | |
|---|---|---|
| Memories | The texts you write | Until you delete them or close your Korely account |
| Facts | Subject, predicate and object, with their dates | A closed fact stays as history until you close your Korely account or erase the end user (see Forget an end user) |
run_id label | A label on a memory, no data of its own | As long as the memory |
Memories
search() finds memories by
meaning, even when no word matches.
get_all() reads back every memory of an end user, page by
page.
Facts
You read facts with get_facts(). For each new fact, the
contradiction check asks
Korely's extraction model whether it replaces an older one. If it does,
Korely closes the old fact and keeps it as history. Illustration of the
format, where valid_from
and invalid_at are when a fact became true and when it
stopped:
(Giulia, subscribes_to, Starter plan) valid_from 2026-03-01 invalid_at 2026-06-07(Giulia, subscribes_to, Advanced plan) valid_from 2026-06-07 invalid_at null invalid_at null means the fact is still true. By default,
get_facts() returns only open facts. With
include_invalidated=True, closed facts come back too.
Temporal facts shows how to
read the facts true on a past date.
get_profile() lists the current facts of one end user.
correct_fact() replaces a wrong fact.
forget_fact() closes a fact at once: get_facts(),
get_profile() and the facts in get_context()
stop returning it, while include_invalidated=True still
shows it.
forget_fact() touches the fact only: it becomes a
closed fact, and the memory it
came from stays, so search() and get_context()
can still return that text. delete() takes the memory out of
reads (see Delete a memory).
Erasing the end user removes both.
The run_id label
Pass the same run_id to every add() of one long
task, such as a research session. A later search() or
get_all() with that run_id returns only those
memories. get_context() and get_facts() have no
run_id: they cover every session of the end user.
How a write flows
When an agent calls add():
- Korely saves the memory. Normally
search()can find it as soon as the call returns; under a burst the vector is computed a little later and search finds it once it exists. - After the call returns, Korely's extraction model extracts the facts, in about ten seconds.
- Each new fact goes through the contradiction check.
Add a memory explains how to follow a memory while Korely works on it.
How a read flows
Korely's extraction model runs only when you write: reading calls no language model. To find similar memories, Korely compares embeddings, lists of numbers that stand for the meaning of each text.
get_context() keeps the context block within a size limit,
the token budget.
search() returns the most similar memories first, each with
a score.
Search memories explains the score
and how to drop weak matches.
Which call touches what
| SDK method / REST call | Touches Memories | Touches Facts | What it does |
|---|---|---|---|
get_context() / GET /v1/context | Read | Read | Builds the context block |
search() / POST /v1/memories/search | Read | Finds similar memories | |
get_all() / GET /v1/memories | Read | Lists memories | |
add() / POST /v1/memories | Write | Write | Saves a memory, then extracts facts |
update() / PATCH /v1/memories/{id} | Write | Write | Changes a memory and re-extracts facts; facts the new text no longer states are closed (Update a memory) |
delete() / DELETE /v1/memories/{id} | Write | Deletes one memory | |
get_facts() / GET /v1/facts | Read | Lists facts | |
get_profile() / GET /v1/profile | Read | Lists one end user's current facts | |
add_fact_triple() / POST /v1/facts | Write | Writes one fact (subject, predicate, object) directly | |
correct_fact() / PATCH /v1/facts/{id} | Write | Replaces a wrong fact | |
forget_fact() / POST /v1/facts/{id}/forget | Write | Closes a fact |
The Node.js SDK spells the same calls
differently, such as getContext. The
API reference has every call in full.
Write what is true now.
Say "Giulia is on the Advanced plan", not "Giulia upgraded to Advanced". The second says something that already ended, so its fact can have no time when it is true now: Korely then stores it closed from the start. A plain get_facts() leaves out a fact such as Giulia / upgrades_to / Advanced plan; get_facts(include_invalidated=True) returns it. That case needs no contradiction check.
Details
- Narrowing a read. An API key reads only its own project. Inside it,
user_idnarrows a read to one end user,run_idto one session (onsearch()andget_all()only), andagent_idto what one agent wrote, such assupport-bot. - Regions. Two separate things. Where your data is stored: on Korely's servers in Helsinki, on every Cloud plan. Which model reads it to write the facts: the project's region decides.
global, the default, uses a model from Google, outside the EU;euuses Scaleway in Paris (Data governance: regions).
Known limits
- A memory can finish with no facts, and no error.
- The contradiction check can miss a conflict. Check important facts with
get_facts(include_invalidated=True). - In the
euregion, a past-tense change such as "Tom moved to Munich" can leave the old fact ("Tom lives in Berlin") open, so write the new state in the present tense (see Write what is true now).
See also
- Temporal facts: dates on facts, the contradiction check and reads at a past date.
- Get context: the block your model reads, part by part.
- Delete a memory and Forget an end user: what deleting removes and what it keeps.
- Get facts reference: every parameter of
get_facts(). - Human in the loop: let end users see, correct and forget what agents remember.