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 every person,
deal, and commitment tied to it, pulled from all your notes, not
the open thread. That works because memories become typed
(subject, predicate, object) facts: Marco
works_at Acme Corp, Acme Corp has_deal "Enterprise
renewal", you committed_to a security review. Ask for Acme and
you get the neighborhood. This is the graph Mem0 removed from its OSS in
April 2026, 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"}, "created_at": "2026-06-12T15:04:22Z", "updated_at": "2026-06-12T15:04:22Z", "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 has_deal …) or as an object (Marco works_at
Acme Corp). The opposite endpoint of each fact is a connected entity:
the people, the deals, the commitments. One call, one account, the whole
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_deal Enterprise renewal# Marco Rossi works_at Acme Corp# rep-jordan committed_to security review for Acme Corp# Acme Corp renewal_target Q3 2026{ "facts": [ { "id": "fact_a1", "subject": "Marco Rossi", "predicate": "works_at", "object": "Acme Corp", "predicate_family": "employment", "subject_type": "person", "object_is_literal": false, "confidence": 0.94, "user_id": "rep-jordan", "agent_id": null, "valid_from": "2026-06-12T15:04:25Z", "invalid_at": null, "invalidated_by": null, "source_memory_id": "mem_8d21", "created_at": "2026-06-12T15:04:25Z" }, { "id": "fact_a2", "subject": "Acme Corp", "predicate": "has_deal", "object": "Enterprise renewal", "predicate_family": "other", "subject_type": "organization", "object_is_literal": true, "confidence": 0.91, "user_id": "rep-jordan", "agent_id": null, "valid_from": "2026-06-12T15:04:25Z", "invalid_at": null, "invalidated_by": null, "source_memory_id": "mem_8d21", "created_at": "2026-06-12T15:04:25Z" } ]}
Because entity matches the subject or object side, a
person who works at Acme surfaces from the same query as a deal Acme owns.
Nothing in the agent's open thread had to mention Marco for him to come back
when you asked about his company.
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 renewal, the Q3 target
- The security review you committed to
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": "fact_a1b", "subject": "Marco Rossi", "predicate": "works_at", "object": "Acme Corp", "predicate_family": "employment", "subject_type": "person", "object_is_literal": false, "confidence": 0.97, "user_id": "rep-jordan", "agent_id": null, "valid_from": "2026-06-16T09:10:00Z", "invalidated": [], "source_memory_id": null, "created_at": "2026-06-16T09:10:00Z"}
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, where they work, what they own, what was promised to
them. 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 works_at Acme Corp# Marco Rossi owns_deal Enterprise renewal# Marco Rossi prefers async demos over live calls
# walk one hop further: from the deal Marco owns back out to the accountdeal = korely.get_facts(entity="Enterprise renewal", user_id=REP)Each 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 the reorg, what the renewal target was last quarter.
# the account's connections as they stood before the June reorgbefore = korely.get_facts(entity="Acme Corp", user_id=REP, as_of="2026-05-31")
# include superseded edges to see the full history, struck throughhistory = korely.get_facts(entity="Acme Corp", user_id=REP, include_invalidated=True) as_of="2026-05-31" returns the edges that were valid on that
date; include_invalidated=True adds the ones that have since
been superseded 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, including the contact who never co-occurs with the company name in a single note. - Connections are first-class. Memories become
(subject, predicate, object)facts, so "who works at Acme" and "what deals Acme has" are the same lookup from different sides of the triple, not a re-read of every transcript and a hope the model joins them. - The capability Mem0 dropped. Mem0 removed its graph from the OSS in April 2026. This entity-connected view is exactly what they no longer ship.
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.