A research agent that builds on every session
You're three weeks into researching "EU AI Act compliance for our SaaS" with an agent. A stateless agent opens session 5 by asking what you're researching, re-derives the obligations you already pinned down in session 2, and has no idea you overturned one of them after the legal review. You spend the first ten minutes re-grounding it.
With Korely in the loop, session 5 opens already knowing the four
obligations you flagged, which two are still open, and which earlier
conclusion was overturned and why. Each session's findings accumulate into
typed facts; contradictions supersede the old answer instead of piling up;
and as_of can replay exactly what you believed on any date.
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 project, many sessions
A research thread is one user_id (the project), and each
session is a run_id. Findings written under any session
accumulate under the project, so get_context and
get_facts see all of them; run_id is there when you
want to narrow back down to a single session.
from korely_memory import Korely
korely = Korely(api_key="kor_live_...", region="eu")
PROJECT = "proj-eu-ai-act"SESSION = "session-2026-06-12" # one run_id per working session1. Session start: load everything known so far
Before the first turn of a new session, assemble the project's state into a
prompt-ready block. get_context returns the active facts plus
the most relevant memories, already fitted to a token budget, one call, no
model on the read path.
ctx = korely.get_context( query="EU AI Act obligations, open questions, decisions so far", user_id=PROJECT, token_budget=1200,)
# ctx.context -> a Markdown block to drop into your system prompt# ctx.tokens -> how many tokens it used# ctx.sources -> the memory ids it drew from{ "context": "Active facts:\n- proj-eu-ai-act has_obligation \"register high-risk system (Art. 6)\"\n- proj-eu-ai-act has_obligation \"human oversight (Art. 14)\"\n- proj-eu-ai-act open_question \"does the hiring module count as high-risk?\"\n\nRelevant findings:\n- Session 3: legal review concluded the hiring module qualifies for the Annex III research exemption...", "tokens": 412, "sources": ["mem_7c1a", "mem_2b40", "mem_9f08"]}The agent now opens the session already grounded. No "remind me what we're working on."
Session 5
You re-open the research thread
- Three weeks in
- Resolves to user_id "proj-eu-ai-act"
Korely
get_context(query=...)
- Active facts + relevant findings, every session
- Assembled, not re-generated
Turn 1
Agent is already grounded
- Knows the 4 obligations + 2 open questions
- Knows the overturned conclusion
2. During the session: search the back-catalogue
When the agent needs a specific past finding, search the project's memories. Scoped to the project, ranked by relevance, no model in the path beyond the query embedding.
hits = korely.search( "annex III exemption hiring module", user_id=PROJECT, limit=5,)# each hit: {id, score, snippet, user_id, agent_id, metadata}3. On a finding: write it down (any session)
When the agent reaches a conclusion, store it. Tag the session with
run_id; it still accumulates under the project.
korely.add( "Legal review: the hiring module qualifies for the Annex III research " "exemption, so Article 6 high-risk registration does not apply.", user_id=PROJECT, run_id=SESSION,)
Fact extraction runs server-side: this finding becomes typed
(subject, predicate, object) triples a few seconds after the
write returns, ready at the next get_facts / get_context.
4. When a finding overturns an earlier one
Session 2 concluded "Article 6 high-risk registration applies."
Session 3's exemption finding contradicts it. You don't write supersede
logic: the two-stage contradiction detector finds the earlier fact (same
predicate, conflicting object) and invalidates it. The old conclusion isn't
deleted, it gets an invalid_at timestamp and stops being served.
facts = korely.get_facts(user_id=PROJECT, predicate_family="other")# returns only what's TRUE NOW — the Art. 6 "applies" fact is gone from this list,# replaced by the "does not apply" conclusion. No stale answer lingers.5. Point in time: what did we believe back then?
This is the part a vector store can't do. Replay the project's knowledge as it stood on any date, before the legal review, say, for an audit trail or to understand how a conclusion changed.
# what we believed before the June 5 legal reviewbefore = korely.get_facts(user_id=PROJECT, as_of="2026-06-04")
# the full point-in-time profile, grouped by familysnapshot = korely.get_profile(user_id=PROJECT, as_of="2026-06-04") as_of="2026-06-04" returns the facts that were valid on that
date: the "Article 6 applies" conclusion is back, because on June 4 it was
still live. The history is intact; you're just choosing which moment to
read from.
Why not dump the transcripts into a vector DB?
- Staleness. A transcript store holds both "Article 6
applies" (session 2) and "Article 6 does not apply" (session 3), and you're
trusting the LLM to pick the right one every turn.
get_factsreturns only what's valid now, the contradiction is resolved before the model sees it. - Token cost. Re-feeding five sessions of transcript is
thousands of tokens every turn, growing forever. A
get_contextblock is a few hundred tokens and grows with what's worth remembering, not with how long you've been researching. - No point in time. A vector store has no concept of "what was true on June 4". Bi-temporal facts do.
One honest caveat: korely.add runs fact
extraction asynchronously, so a finding's typed facts land a few seconds
after the write returns, ready by the next session's
get_context, not inside the same call. Write findings
fire-and-forget during the session; read the assembled state at the start
of the next one.
Where to go next
Temporal facts explains
supersession and as_of under the hood;
get context is the call in
step 1; the memory-as-a-tool
cookbook hands these same operations to the model as callable tools.