Korely

LangGraph

LangGraph builds agents as graphs of steps, on top of LangChain, so the code below uses LangChain's tool and agent helpers. A LangGraph agent forgets everything when a run ends. This page shows how to give it a memory that outlives the run, with the Korely Python SDK. You pick one of two patterns:

  • Tools. Wrap korely.search and korely.add as LangChain tools and let the agent decide when to call them. This suits a chat agent that should judge for itself whether a lookup is needed. search returns the saved memories that match the question best.
  • Context in the prompt. Call korely.get_context() before the run and put the result at the start of the system prompt (the standing instructions you give the model). It returns a ready-made context block: the facts that are true now plus the best-matching memories. The agent never has to decide whether to look. This suits a fixed pipeline. The section "Context in the prompt" below shows it.

Requirements

You need Python 3.10 or later (LangGraph requires it), the Korely SDK, LangGraph and a chat model. The examples were run with korely-memory 0.1.28, langgraph 1.2.19 and langchain 1.4.7, with a fake chat model, so no model provider was called.

Terminal window
pip install korely-memory langgraph "langchain[openai]"

The examples name the model openai:gpt-4.1, which needs an OPENAI_API_KEY. Any chat model that supports tool calling works: install the matching extra and change the string, for example to anthropic:.... You also need a Korely API key (kor_live_...). Create an account at korely.ai and copy the key from the dashboard, then export it. The SDK reads it from the environment.

Terminal window
export KORELY_API_KEY="kor_live_..."
export OPENAI_API_KEY="sk-..."

Tools

A tool is a Python function that the model can choose to call. Here there are two, recall and remember, each wrapping one SDK call. The docstring of each tool is what the model reads to decide when to use it. Save the code as agent.py.

A new account has no memories, so first save one for the agent to find (run this once, for example in a Python shell):

from korely_memory import Korely
Korely().add(
"Lease renewal call with Anna: the deadline is July 1. She confirmed a "
"3% increase, from 1,200 to 1,236 EUR per month starting in August.",
user_id="me",
)

The memory is about one person, so it carries a user_id. Use the same one in every call below. Since Korely 1.0.12, a memory written with an agent_id and no user_id is read as the agent's own memory, not as a person's: its facts would not be about "the user". Who the memory is about has the rule.

Then run python agent.py. A search can find the memory right away. Without a saved memory, recall returns "No memories yet." and the agent says it does not know.

agent.py
import asyncio
from langchain.agents import create_agent
from langchain_core.tools import tool
from korely_memory import Korely
korely = Korely() # reads KORELY_API_KEY from the environment
USER_ID = "me" # the person the memories are about
@tool
def recall(query: str) -> str:
"""Search this user's memory before answering."""
hits = korely.search(query, user_id=USER_ID, limit=5)
return "\n".join("- " + (h.snippet or "") for h in hits) or "No memories yet."
@tool
def remember(content: str) -> str:
"""Save a durable fact to memory."""
m = korely.add(content, agent_id="assistant", user_id=USER_ID)
return "Saved as " + m.id
agent = create_agent(
model="openai:gpt-4.1",
tools=[recall, remember],
system_prompt=(
"You are an assistant with persistent memory. "
"Call recall before answering questions about the user's past. "
"Call remember when the user tells you something durable."
),
)
async def main() -> None:
result = await agent.ainvoke(
{
"messages": [
{
"role": "user",
"content": "What did I decide about the lease renewal?",
}
]
}
)
print(result["messages"][-1].content)
asyncio.run(main())

Keep the rule in the system prompt. The agent only knows what you give it. The line "call recall before answering" is what makes it look in memory. Without it, the model sometimes answers from its own training instead.

What a run looks like

This is the loop the code above produces. The agent calls recall, which calls korely.search. The agent then answers from the snippets (short pieces of the saved memory text) it gets back. The wording of the memories and of the answer depends on what you saved and on your model, so yours will differ.

langgraph agent python
$ python agent.py

→ tool call recall(query="lease renewal decision")

← korely.search returns 2 hits (example output):

   - Renewal deadline is July 1. Anna confirmed a 3% increase,
     1,200 to 1,236 EUR per month starting August. She wants the
     signed copy by post.

   - Lease, insurance certificate, meter readings, deposit receipt...

stdout › The renewal deadline is July 1. Anna confirmed a 3% increase,
from 1,200 to 1,236 EUR per month starting in August, and she wants
the signed copy by post.

search returns scored hits, each with a snippet of the matching memory. No language model runs when Korely answers a read; your agent's own model does the reasoning. Reads are cheap on our side, so every plan allows at least 100 times more queries than writes per month (see the pricing page). To get the facts that are true now instead of raw snippets, use get_context or get_facts.

Older LangGraph. Before LangChain 1.0 the agent factory was create_react_agent in langgraph.prebuilt. Use create_react_agent(model, tools) with the same tools list and the same ainvoke call. Both factories build a LangGraph graph.

One memory per end user

The example above keeps everything for one person, the fixed USER_ID. That is right for a personal assistant or an internal tool. A product that serves many people needs a separate memory for each person. Pass a user_id on every write and every read, and each end user gets an isolated memory. End users are unlimited on every plan: quotas count writes and queries, never people.

Build the agent per request and put the user_id inside the tools, so the model never sees it and cannot change it:

from langchain.agents import create_agent
from langchain_core.tools import tool
from korely_memory import Korely
korely = Korely() # reads KORELY_API_KEY from the environment
def build_agent_for(user_id: str):
@tool
def recall(query: str) -> str:
"""Search everything this user has told us before."""
hits = korely.search(query, user_id=user_id, limit=5)
return "\n".join("- " + hit.snippet for hit in hits) or "No memories yet."
@tool
def remember(content: str) -> str:
"""Save a durable fact about this user."""
memory = korely.add(content, agent_id="support-bot", user_id=user_id)
return "Saved as " + memory.id
return create_agent(
model="openai:gpt-4.1",
tools=[recall, remember],
system_prompt=(
"Call recall before answering anything about this customer. "
"Call remember when they tell you something durable."
),
)
# One agent per request, scoped to the end user in the session
agent = build_agent_for(user_id="customer-4812")

Always pass user_id on reads in a product with many users. A read without it covers every end user of the project. That suits an internal tool and is wrong inside a customer chat. To erase a customer, call korely.delete_all(user_id=...): it erases every memory of that user in one call. We checked that a search for one user_id returns nothing written under another.

Context in the prompt

To use the second pattern, call get_context yourself and add the block to the system prompt. user_id and token_budget (the size limit of the block, 800 by default) are keyword arguments. The agent has no tools here, because the memory is already in its prompt:

from langchain.agents import create_agent
from korely_memory import Korely
korely = Korely()
context = korely.get_context(
"What did I decide about the lease renewal?",
user_id="customer-4812",
token_budget=800,
)
agent = create_agent(
model="openai:gpt-4.1",
tools=[],
system_prompt="You are an assistant with memory.\n\n" + context.context,
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "When is the renewal deadline?"}]}
)
print(result["messages"][-1].content)

The full client, including get_facts with point-in-time as_of reads and batch import, is on the Python SDK page. The exact requests and responses are in the API reference.

The SDK's own LangGraph module (experimental)

The SDK also ships korely_memory.integrations.langgraph, with ready-made create_korely_tools(client, user_id), korely_context(client, user_id, query) and a KorelyStore for code that expects a LangGraph store. Install it with pip install 'korely-memory[langgraph]'. It is experimental: its names and behavior can change, and this page does not cover it beyond this paragraph. The tools and the context helper import and run against a local test server; KorelyStore was not exercised. A LangGraph store is LangGraph's built-in place for saving data across runs.

Troubleshooting

SymptomFix
401 Unauthorized from api.korely.ai The key is missing or revoked. Check that KORELY_API_KEY is set in the environment and starts with kor_live_. You can also pass it directly: Korely(api_key="kor_live_...").
ModuleNotFoundError: No module named 'korely_memory' Run pip install korely-memory (a dash in the package name). The import name has an underscore: korely_memory.
The agent answers without calling the memory tools Keep the "recall before answering" rule in system_prompt. If the model still skips the tools, give each node only the tools it needs and keep every tool description short.
ImportError on langchain.agents Your LangChain is older than 1.0 and has no create_agent. Use from langgraph.prebuilt import create_react_agent with the same tools list, or upgrade langchain and langgraph.
korely.get_facts() returns nothing right after a write The facts are written about ten seconds after the memory arrives (extraction), so read again a little later. The memory itself can be found by a search at once. get_facts works on every plan, including Hobby, and needs only the memories:read scope.

Something not working? Email [email protected] with your korely-memory version and the traceback. We read every message.