A sales agent that connects the dots
You mention "Acme Corp" to your sales assistant. A stateless agent answers from whatever thread you happen to be in. It doesn't know that Marco from Acme is the champion you met in March, that the renewal slipped to Q3, or that you promised their CTO a security review, those live in three different meeting notes it never connected.
With Korely in the loop, one mention of Acme Corp surfaces the champion,
the requirements and the budget stated with it, pulled from all your
notes, not the open thread. That works because memories become typed
(subject, predicate, object) facts: Acme Corp
has_champion Marco Rossi, Acme Corp requires a
security review, Acme Corp has_budget approved. Ask for Acme and
you get its direct edges; the deal is one hop away, through Marco. This
cookbook walks the pattern at the call level.
The snippets use the Python SDK (pip install korely-memory).
The same calls work over the
REST API and the
Node SDK.
The scoping: one rep, every account
A sales rep's book is one user_id, the end user the data is
about. Every account, contact, and deal lives in that namespace, so a fact
written from the Acme call and a fact written from the Globex call both land
where a single get_facts can connect them.
from korely_memory import Korely
korely = Korely(api_key="kor_live_...", region="eu")
REP = "rep-jordan" # the sales rep this book of business belongs to1. After a call: drop the notes in
When a meeting ends, hand Korely the notes. Fact extraction runs server-side and turns the prose into typed edges that connect the people, the company, and the deal, no schema to define, no triples to hand-author.
korely.add( "Discovery call with Acme Corp. Marco Rossi (VP Eng) is the champion and " "owns the Enterprise renewal, now targeting Q3. Their CTO asked us to run a " "security review before they sign. Budget is approved.", user_id=REP, run_id="call-acme-2026-06-12", metadata={"account": "Acme Corp", "stage": "discovery"},){ "id": "mem_8d21", "content": "Discovery call with Acme Corp. Marco Rossi (VP Eng) is the champion...", "user_id": "rep-jordan", "agent_id": null, "run_id": "call-acme-2026-06-12", "metadata": {"account": "Acme Corp", "stage": "discovery"}, "client": "korely-python/0.1.28", "kind": "note", "status": "processing", "created_at": "2026-06-12T15:04:22+00:00", "updated_at": null, "facts": []}
Note facts: [] in that response, extraction is asynchronous, so
the typed triples land a few seconds after the write returns. The next
section reads them once they're in.
2. Mention an account: pull everything tied to it
This is the move. get_facts(entity="Acme Corp") returns every
fact where Acme appears on either side of the triple, as a
subject (Acme Corp requires …) or as an object. The opposite
endpoint of each fact is a connected entity: the champion, the requirements,
the budget. One call, one account, its direct neighborhood.
facts = korely.get_facts(entity="Acme Corp", user_id=REP)
for f in facts: print(f.subject, f.predicate, f.object)# Acme Corp has_champion Marco Rossi# Acme Corp requires security review# Acme Corp has_budget approved entity matches the exact name, or a known alias, on either
side of a fact, not a substring.
{ "facts": [ { "id": "fct_a1", "subject": "Acme Corp", "predicate": "has_champion", "predicate_raw": "has_champion", "object": "Marco Rossi", "subject_type": "organization", "object_is_literal": false, "user_id": "rep-jordan", "agent_id": null, "invalid_at": null, "invalidated_by": null, "source_memory_id": "mem_8d21" }, { "id": "fct_a2", "subject": "Acme Corp", "predicate": "requires", "predicate_raw": "requires", "object": "security review", "subject_type": "organization", "object_is_literal": true, "user_id": "rep-jordan", "agent_id": null, "invalid_at": null, "invalidated_by": null, "source_memory_id": "mem_8d21" } ], "total": 3}The list above is shortened to two of the three facts, and each fact to its main fields.
Because entity matches the subject or object side,
the champion surfaces from the same query as the requirement Acme stated.
Nothing in the agent's open thread had to mention Marco for him to come back
when you asked about his company. The renewal Marco owns is one hop further,
in step 4.
In the chat
You mention "Acme Corp"
- No special prompt
- Resolves to user_id "rep-jordan"
Korely
get_facts(entity="Acme Corp")
- Every triple where Acme is subject OR object
- Across every note, not one thread
Surfaced
The whole neighborhood
- Marco (champion), the approved budget
- The security review Acme requires
3. Write an explicit edge yourself
Sometimes you already know the connection and want it recorded exactly, not
inferred from prose. add_fact_triple writes the edge directly,
it skips extraction but still runs the server-side contradiction check, so a
new edge that conflicts with an old one supersedes it cleanly.
fact = korely.add_fact_triple( "Marco Rossi", "works_at", "Acme Corp", user_id=REP, subject_type="person", confidence=0.97,)# fact.invalidated -> ids of any earlier conflicting edge this one replaced{ "id": "fct_b1", "subject": "Marco Rossi", "predicate": "works_at", "object": "Acme Corp", "predicate_family": "work", "valid_from": "2026-06-12T15:09:40+00:00", "invalidated": [], "tense": "current", "invalid_at": null, "observation_count": 1, "user_id": "rep-jordan", "agent_id": null}
The note in step 1 never states where Marco works, so this write creates a
new fact: a fresh id, a valid_from equal to the write time, and
observation_count 1.
If Marco later moves to Globex, write Marco Rossi works_at
Globex and the contradiction detector returns the superseded edge's id
in invalidated, the old employment fact gets an
invalid_at timestamp and stops being served. The history stays
intact; the current answer stays correct.
4. Pivot to a person
The same one-hop lookup walks the other way. Ask for a person and you get
every fact they touch, what they own, their role, who they champion. To follow the graph one more step, call get_facts again on
a neighbor you found.
marco = korely.get_facts(entity="Marco Rossi", user_id=REP)# Marco Rossi owns Enterprise renewal# Marco Rossi has_role VP Eng
# walk one hop further: from the deal Marco ownsdeal = korely.get_facts(entity="Enterprise renewal", user_id=REP)# Enterprise renewal targets Q3Each hop is a deterministic SQL lookup against the facts table, no model call, no embedding on the read path. You're traversing typed edges one at a time, choosing where to go next from what came back.
5. Point in time: who was on the account back then?
Because facts are bi-temporal, you can ask the graph as it stood on any date. Useful for a deal post-mortem, who the champion was before a contact moved on, what the renewal target was last quarter.
# the account's connections as they stood on 30 June, before Marco moved to Globex (step 3)before = korely.get_facts(entity="Acme Corp", user_id=REP, as_of="2026-06-30")
# include superseded edges to see the full history, each with its invalid_athistory = korely.get_facts(entity="Acme Corp", user_id=REP, include_invalidated=True) as_of="2026-06-30" returns the edges that were valid on that
date, so Marco's Acme edge is still there even if he has since moved;
include_invalidated=True adds the edges that have since been
superseded, each with its invalid_at, so you can see how the
account map changed.
Why this beats a flat note store
- One query, the whole account. A vector store returns the
notes that mention "Acme" by similarity.
get_facts(entity="Acme Corp")returns every typed edge Acme touches, across every note it was stated in, not just the notes that rank closest to the word "Acme". - Connections are first-class. Memories become
(subject, predicate, object)facts, so "who champions Acme" and "what Acme requires" are the same lookup from different sides of the triple, not a re-read of every transcript and a hope the model joins them. - On every plan. The entity view is part of every Korely plan, including the free one.
One honest caveat: get_facts(entity=...)
matches the subject or object side of a triple, so connections
surface through shared typed facts, it is a one-hop lookup over the facts
table, not a server-side multi-hop graph-traversal API (there is no
get_related on the agents SDK; you walk further by calling
get_facts again on a neighbor). And
korely.add extracts those facts asynchronously, so the typed
edges from a fresh note land a few seconds after the write returns, ready
at the next get_facts, not inside the same call. When you need
an edge recorded instantly and exactly, write it with
add_fact_triple.
Where to go next
Temporal facts explains
supersession, as_of, and the contradiction check under the hood;
get context assembles a
prompt-ready block when you'd rather hand the model the account state than
raw facts; and the
multi-session
research cookbook applies the same typed-fact pattern to a long-running
research thread.