One personal AI, every channel
Here's the scene this cookbook is about. Your personal agent runs on OpenClaw. On Monday you message it on WhatsApp: "the dentist moved to Thursday". On Wednesday you ask it on Telegram: "what's my week look like?" It knows about the dentist. Not because the two chats share a transcript (they don't), but because both channels write to and read from the same Korely memory.
That's the whole trick: the channel is ephemeral, the memory is not. One memory store and one set of facts for the same end user. Every channel your agent lives on reads the same memory. The terms are in the Glossary.
Everything in this recipe runs over the Korely REST API or SDK.
Install korely-memory, set your kor_live_ key once, and give
your agent a tool of your own that calls korely.add and
korely.get_context. Every channel your agent runs in then writes and reads
from the same memory, with no per-channel configuration. The korely-openclaw
plugin, which will replace that wiring, is not published yet.
Setup: two steps
You need a Korely account and an API key. Get started in the quickstart. Then install the SDK once, wherever your agent runs:
$ pip install korely-memory
$ export KORELY_API_KEY=kor_live_...
Once the agent has that tool, every channel it bridges (WhatsApp, Telegram,
Discord, Slack and the rest) talks to the same agent, and the agent reads
and writes the same memory. The memory belongs to the
user_id you pass (here "me"), not to the chat
surface. Add a new channel next month and it inherits everything the agent
already knows about that user.
What flows where
The Monday-to-Wednesday scene, as actual tool calls:
Monday · WhatsApp
"Dentist moved to Thursday"
- One message, one channel
- Chat history stays on WhatsApp
Korely
add()
- Memory stored in Korely
- Fact extracted into the graph
Wednesday · Telegram
"What's my week look like?"
- Different channel
- No shared transcript
Korely
get_context()
- Active typed facts, assembled
- Closed facts left out
Answer
It knows about the dentist
- Context from Monday, surfaced Wednesday
The Wednesday read is a get_context() call: Korely takes the
user's active typed facts most relevant to the question, plus the memories
that match it (at most ten facts), and assembles them into one
ready-to-prompt context block.
Reads are retrieval, not generation. No generative model composes output on
the read path; your agent's own model does the reasoning on what comes
back. The query is embedded on Korely's own server, so the memory lookup
stays a small part of a chat reply.
Interactions to try
The recipes are prompt-level: you say things to your agent, the agent decides which SDK or REST calls to make. Three to start with, in any channel:
1. Teach it something
"Remember that Marta prefers morning meetings."
The agent calls add() and gets a confirmation back:
from korely_memory import Korely
korely = Korely() # reads KORELY_API_KEY from env
korely.add(
"Marta prefers morning meetings",
agent_id="personal-assistant",
user_id="me",
)
On the Korely side, the write path does the heavy lifting: the memory is
embedded and, usually within about ten seconds, a typed
fact such as (Marta, prefers, morning meetings) is extracted
from it, with a start date. It is readable from every channel until a
newer fact closes it or you forget it.
2. Ask it back, anywhere
"What do you know about Marta?"
The agent calls get_facts() filtered to the entity
Marta and gets back a flat list of her active typed facts. Always
pass user_id: a read without it covers every end user of the
project. For a broader "what's my week look like?" recall, the agent
reaches for get_context() instead, which assembles the
relevant active facts and memories into a ready-to-prompt block. Here we
want the precise per-entity facts:
facts = korely.get_facts(entity="Marta", user_id="me")
# flat list of typed fact triples, active only by default (attribute access).
# facts[0].subject -> "Marta"
# facts[0].predicate -> "prefers" (the relation, canonical)
# facts[0].predicate_raw -> "prefers" (as extracted)
# facts[0].object -> "morning meetings"
# facts[0].valid_from -> "2026-06-08T09:12:00+00:00"
# facts[0].invalid_at -> None (None = still active)
# Superseded facts (invalid_at set) are hidden by default.
# For facts grouped by family, use get_profile() instead.
Facts have a start and an end date: if you later say "Marta now prefers
afternoon meetings", a model judge can close the old fact instead of
deleting it, and the default read returns only the current one. The judge
can miss, so check with include_invalidated=True when it
matters. Same answer on Telegram, Discord, or Slack, because it's the same
memory. The read uses no generative model; on the Cloud it counts as one
query against your plan's monthly query quota.
3. Hand work to your future self
"Save this article draft for me to review."
The agent calls add() with the article draft as content.
Then you open the dashboard on your laptop and the memory is listed there,
next to the facts extracted from it. The same memory reaches every
channel: the agent writes from WhatsApp, and any channel reads it back
with get(id) or search. To change it, call
update with the new text and the facts are extracted again
from the new text.
Privacy and the off switch
The agent reads and writes only the data of
your account (and, with a project key, of that project),
through a kor_live_ key. Two controls matter day to day:
- Revoke anytime. Revoke your API key in the dashboard and every channel loses memory access from the next request, because they all authenticate with the same key.
- Forget once, for every channel. The Facts page of the
dashboard lets you correct or forget a fact. A fact you forget is closed
for every channel, because there is only one memory to close it
in. The text of the memory it came from stays searchable until you
delete that memory (
delete(id)) or the whole user (delete_all).
Memories are stored on Korely's server in Helsinki, and you can always read them back through the REST API and the dashboard.
Recall is agent-driven, by design
Every memory access is an explicit SDK or REST call. The agent decides when to read or write. Models reach for a memory tool most reliably on explicit asks ("remember…", "what do you know about…"), and less reliably otherwise. With the SDK, Korely reads or writes only when your code calls it, so you know what was read and what was written.
Where to go next
- OpenClaw integration guide: the plugin that will replace this wiring once it is published.
- Temporal facts: how contradiction detection keeps "current" answers current when your life changes.
- Human in the loop: how to let people see and edit what their agent remembers, and why we think they always should.
- SDK reference: all methods,
parameters, and response shapes for
korely-memory.
Built something on this? We want to see it. Email [email protected]. Cross-channel personal agents are exactly the use case we built Korely for, and real setups shape the product.