Concepts
The knowledge graph
The facts Korely extracts from your memories, connected by the people, companies and products they name.
You save plain text with add(). Korely reads each
memory and extracts
facts: short statements such as
Sarah Lin / reports_to / Dana Cho. The people, companies and
products that facts name are entities.
Names are matched as written: two facts that both say Sarah Lin share that
entity, but "Sarah" and "Sarah Lin" are two entities. The entities, joined
by the facts, form the knowledge graph.
The graph answers questions that follow connections, such as "Who does Sarah report to?" or "What does this service depend on?". You do not design a data model or run a separate graph database.
This page is for developers who build agents on Korely.
Terms
The Glossary defines the words used across the docs. These are the ones specific to this page.
- Node: one entity in the graph.
- Edge: a fact that joins two entities.
- Value: an amount or a date as the object of a fact. It stays on its fact. In the API its
object_is_literalistrue: a literal is a value written as it is, not the name of an entity.
Quick start
Install with pip install korely-memory (Python 3.9 or later)
or npm install korely-memory (Node.js 18 or later). Then
get a free API key (Quickstart) and run
export KORELY_API_KEY=kor_live_.... Tested with
SDK 0.1.28 (Python) and 0.1.17 (Node.js).
The code saves notes from a sales call for the
end user acme-account, waits, then reads the extracted facts. The
Node.js example is under Reference.
import timefrom korely_memory import Korely
korely = Korely() # reads KORELY_API_KEY from the environment
memory = korely.add( "Call with Sarah Lin (Acme Corp). Renewal agreed at 99 dollars per seat " "for the Billing Service rollout. Sarah now reports to Dana Cho. " "Send the updated contract draft by Friday.", user_id="acme-account",)time.sleep(20) # facts usually take about ten seconds
facts = korely.get_facts(user_id="acme-account")for f in facts: print(f.subject, f.predicate, f.object)You see lines like these (illustrative: the predicates and the number of facts can vary):
acme-account send updated contract draftSarah Lin reports_to Dana ChoBilling Service costs 99 dollars per seatsendis the predicate the model chose; another run can writewill_send, or add Sarah Lin / works_at / Acme Corp.- The note names nobody as the sender of the draft, so here the model gave the task to the
user_idyou passed,acme-account. The context block shows this subject as "The user", marked[planned, not done]: an action still to do. An end user can be an account, not only a person.
Right after add() the memory's
status is processing
and get_facts returns an empty list. The status then becomes
ready, or error if the
extraction failed
(Add a memory explains what to do).
korely.get(memory.id).status shows it, where
memory is what add() returns. If the list is empty,
read again after a few seconds. Check that both calls use the same key and
the same user_id (case counts).
If a fact is still missing, write it again as one simple sentence in the
present tense. A memory can finish with status ready and no
facts.
What the graph contains
flowchart LR
S["Sarah Lin"] -->|works_at| A["Acme Corp"]
S -->|reports_to| D["Dana Cho"]
style A fill:#6366f1,color:#fff,stroke:#6366f1
style S fill:#1e293b,color:#a5b4fc,stroke:#6366f1
style D fill:#1e293b,color:#94a3b8,stroke:#4b5563
Each fact also records when it became true (valid_from) and
when it stopped (invalid_at, a date once the fact is
closed).
source_memory_ids lists the ids of the memories the fact was
extracted from; korely.get(id) reads one.
Families
Each relation sits in one of 10 families, broad groups you can filter on. The model writes most predicates in its own words, so the table shows typical examples.
| Family | Example predicates |
|---|---|
| preferences | likes, dislikes, prefers |
| people | married_to, reports_to |
| places | lives_in |
| work | works_at |
| ownership | owns, uses |
| health | allergic_to |
| financial | costs |
| events | send, will_send (a planned action) |
| identity | renamed_to, formerly |
| other | every relation that fits none of the families above |
Only the identity predicates have fixed names: renamed_to,
formerly, also_known_as and
codename. They link an old and a new name of a company or
product. Fact: Orbit Labs / formerly / Nimbus Works: Orbit Labs
was formerly called Nimbus Works. A read for the old name,
get_facts(entity="Nimbus Works"), also returns the facts of
Orbit Labs. The model writes these facts when it reads your text; a
declared name is one you
tell Korely yourself.
The graph can vary. A model extracts the facts, so the
same text can give a different graph: three facts for one end user, six
for another. The family can vary too: reports_to can be
people or work. So read by
entity first, and filter by predicate only
after you see that predicate in your own data.
How you read it
Your agent reads the graph in two ways. No
language model writes anything when
you read: get_context only turns the question into numbers
(an embedding), and
get_facts uses no model.
Always pass user_id when your agent serves more than
one end user. Without it, both calls read every end user your key
can see. In a test, get_facts without user_id
returned 88 facts of several end users, and get_context
mixed memories of three.
1. get_context: one block for your prompt
korely.get_context(query, user_id=...) returns
one block of text for your model. It holds up to 10
open facts: the ones closest in
meaning to the question. A fact that a memory confirmed recently moves up
the list. Then come the memories closest to the question.
For the Quick start memory, the block looks like this (shortened):
[note for your model, shortened]
## Known facts- The user send updated contract draft (since 2026-06-09) [planned, not done]- Billing Service costs 99 dollars per seat (since 2026-06-09)- Sarah Lin reports_to Dana Cho (since 2026-06-09)
## Relevant memories- [2026-06-09] Call with Sarah Lin (Acme Corp). Renewal agreed at 99 dollars per seat [...]After "The user" comes the predicate as the model wrote it. The response also lists the ids of the facts and memories it used. Get context explains every line and field.
2. get_facts: the facts themselves
korely.get_facts(entity="...") returns a list of facts
and a total. With entity, you get every extracted
fact where that entity is the subject or the object.
The match ignores case but is otherwise exact: "Acme" does
not find "Acme Corp". The one exception is an old name, as
in Families: entity="Nimbus Works"
finds the facts of Orbit Labs.
The example reads Billing Service, closed facts included. The API reference lists every field.
// GET /v1/facts?entity=Billing%20Service&user_id=acme-account&include_invalidated=true{ "facts": [ { "id": "fct_b91e", "subject": "Billing Service", "predicate": "costs", "object": "99 dollars per seat", "object_is_literal": true, "predicate_family": "financial", "valid_from": "2026-06-09T14:02:11.418203+00:00", "invalid_at": null, "invalidated_by": null, "source_memory_ids": ["mem_6e0f"] }, { "id": "fct_07d2", "subject": "Billing Service", "predicate": "costs", "object": "120 dollars per seat", "object_is_literal": true, "predicate_family": "financial", "valid_from": "2026-02-14T09:30:52.100114+00:00", "invalid_at": "2026-06-09T14:02:11.418203+00:00", "invalidated_by": "fct_b91e", "source_memory_ids": ["mem_18ba"] } ], "total": 2}- Order. The most recently confirmed facts come first:
last_confirmed_atis the last time a memory stated the fact. - The closed fact. An earlier memory said 120 dollars per seat. A newer memory said 99, so Korely closed the 120 fact and set its
invalid_at. invalidated_byis empty while the fact is open. On a closed fact it often names the fact that closed it, but not always: afterforget_factit stays empty, because no newer fact replaced it.- Closed facts appear only with
include_invalidated=true. The API calls them invalidated.
Why a graph, not only similarity search
Similarity search ranks saved texts by how close their
meaning is to the question. It cannot follow two facts in a row. "What does
the service Marta manages depend on?" needs Marta / manages / Ledger
service and Ledger service / depends_on / Postgres cluster.
get_facts(entity="Ledger service") returns both in one read.
Reference
Endpoint: GET /v1/facts. SDK: korely.get_facts(...)
in Python, korely.getFacts({...}) in Node.js, with
the same names as the REST parameters.
| Parameter | Notes |
|---|---|
user_id | One end user. |
entity | Facts where this name is the subject or the object. |
subject | Facts where this name is the subject only. |
predicate | One relation, such as costs. |
predicate_family | One of the 10 families, any case. An unknown family returns an empty list, not an error. |
include_invalidated | Default false. true adds closed facts. |
limit, offset | limit defaults to 50, maximum 200. Use offset to read the next page; total is the number of matching facts, not the number returned. |
from korely_memory import Korely
korely = Korely() # reads KORELY_API_KEY from the environment
facts = korely.get_facts( entity="Billing Service", predicate_family="financial", user_id="acme-account",)for f in facts: print(f.subject, f.predicate, f.object, f.invalid_at)print(facts.total) Errors: 401, key missing or wrong.
403, a key without the read
permission, or a
confined key for other end users.
422, an invalid parameter. 429, too many
requests (rate limit).
Details
- Correct and forget. The Graph and Facts pages of the
dashboard (Korely's web app) have Correct and Forget on each fact; Graph
draws at most 1,000 facts. The SDKs offer
correct_fact(replaces a fact),forget_fact(closes it) anddelete_all(erases all data of one end user). - Values in the dashboard. The Graph page also draws
values (amounts, dates) as nodes. In the API a value is still not an
entity (
object_is_literal). - Reading a big graph in the dashboard. Since 1.0.7, when you select a node the Graph page names at most twelve neighbours, the ones that share the most facts with it, and shows the rest as dimmed dots. The Focus button keeps only the selected node and its strongest neighbours, with a button to widen the view and one to leave it. A node's colour shows how recently it was mentioned and its size shows its number of facts; a closed fact is drawn fainter. There are also a 3D view, full screen, zoom and a pause for the layout. These controls were tried on graphs of about 1,000 facts, not yet on larger accounts or on phones.
- Where the data lives. To extract facts, Korely sends the text of each memory to the model of your project's region; Data governance says where it runs.
See also
- Quickstart: get an API key.
- Add a memory: a memory's status.
- Get context: the prompt block, line by line.
- Search memories: the closest memories as a scored list.
- Temporal facts: how facts change over time.