Korely

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_literal is true: 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 time
from 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 draft
Sarah Lin reports_to Dana Cho
Billing Service costs 99 dollars per seat
  • send is the predicate the model chose; another run can write will_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_id you 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
Two facts a run can give from the Quick start memory: three entities (nodes) joined by two facts (edges). The price is a value, so it stays on its fact.

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.

FamilyExample predicates
preferenceslikes, dislikes, prefers
peoplemarried_to, reports_to
placeslives_in
workworks_at
ownershipowns, uses
healthallergic_to
financialcosts
eventssend, will_send (a planned action)
identityrenamed_to, formerly
otherevery 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_at is 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_by is empty while the fact is open. On a closed fact it often names the fact that closed it, but not always: after forget_fact it stays empty, because no newer fact replaced it.
  • Closed facts appear only with include_invalidated=true. The API calls them invalidated.

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.

ParameterNotes
user_idOne end user.
entityFacts where this name is the subject or the object.
subjectFacts where this name is the subject only.
predicateOne relation, such as costs.
predicate_familyOne of the 10 families, any case. An unknown family returns an empty list, not an error.
include_invalidatedDefault false. true adds closed facts.
limit, offsetlimit 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) and delete_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).
  • 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