OpenAI Agents SDK
The OpenAI
Agents SDK is a small Python framework for agents. Its main pieces are
Agent, Runner and function tools. A function tool
is a Python function with the @function_tool decorator: the
function name becomes the tool name, the docstring becomes the description
and the type hints become the JSON schema. This page adds three tools that
call the Korely Python SDK. Once they are
attached to an Agent, the model recalls, searches and saves on
its own.
Facts, not just snippets
A search returns the memories that
match a question. get_context returns more: a
context block with the
facts that are true now. When a user
upgrades from Free to Pro, the old fact is not deleted. It is closed, the
new one replaces it, and both keep their dates. So the recall tool below
gives the model a settled view ("Luca is on Pro", "prefers async
standups") and not a pile of snippets to re-read each turn.
Install
You need Python 3.10 or later, the OpenAI Agents SDK and the Korely Python
package. The install name and the import name of the Agents SDK differ: you
run pip install openai-agents but import from
agents.
pip install openai-agents korely-memory
The examples were run with openai-agents 0.24.0 and
korely-memory 0.1.28, with a fake model server, so no OpenAI call was made. You need two keys: your OpenAI key
(the model runs on OpenAI) and your Korely
API key (copy it from
API Keys in the dashboard at agent.korely.ai). Export
both:
export OPENAI_API_KEY="sk-..."export KORELY_API_KEY="kor_live_..."Define the memory tools
Three tools cover the loop: recall (the facts that are
true now; the model should use it first), save (write
something worth remembering) and search (find the one
memory that mentioned a thing). Each is a plain function with
@function_tool. The docstring is what the model reads to decide
when to call the tool, so write it for the model.
from agents import function_toolfrom korely_memory import Korely
korely = Korely() # reads KORELY_API_KEY from the environment
@function_tooldef recall_memory(query: str) -> str: """Recall settled facts and relevant memories about the user.
Reach for this first, before answering anything personal. It returns an assembled block of the user's active typed facts plus relevant memories.
Args: query: What you want to remember about, e.g. "where the user lives". """ ctx = korely.get_context(query=query, token_budget=800) return ctx.context
@function_tooldef save_memory(content: str) -> str: """Save something worth remembering for later turns.
Args: content: A short, self-contained statement to remember, e.g. "User just moved to Berlin". """ memory = korely.add(content) return f"Saved (id={memory.id})."
@function_tooldef search_memory(query: str) -> str: """Find the exact memory that mentioned something.
Use this when you need a specific past detail rather than a settled fact.
Args: query: A keyword-style query, 1 to 5 words. """ hits = korely.search(query, limit=5) if not hits: return "No matching memories." return "\n".join(f"- ({h.score:.2f}) {h.snippet}" for h in hits) Why recall comes first. search_memory
returns matching snippets, which suits "find the memory that mentioned
the Lisbon hotel". recall_memory calls
get_context, which returns the current facts
already reconciled. With both tools, the model uses recall to know the
user and search to find one exact past detail.
Attach them to an agent
Pass the decorated functions in the tools list of
Agent. The instructions string is the system
prompt, and it is where you tell the model to consult memory before it
answers. Run the agent with Runner.run_sync in a plain script,
or with await Runner.run(...) inside an async function. The
answer is in result.final_output.
from agents import Agent, Runnerfrom memory_tools import recall_memory, save_memory, search_memory
agent = Agent( name="Assistant", instructions=( "You are a helpful assistant with long-term memory. " "Call recall_memory before answering anything about the user. " "When the user tells you something durable, call save_memory." ), tools=[recall_memory, save_memory, search_memory],)
# --- Option A: synchronous (no event loop needed) ---result = Runner.run_sync(agent, "Where do I live now?")print(result.final_output)
Inside a server, a notebook, or anywhere an event loop is already running,
use the async runner instead of run_sync:
import asynciofrom agents import Agent, Runnerfrom memory_tools import recall_memory, save_memory, search_memory
agent = Agent( name="Assistant", instructions="You are a helpful assistant with long-term memory.", tools=[recall_memory, save_memory, search_memory],)
async def main() -> None: result = await Runner.run( agent, input="Where do I live now?", ) print(result.final_output)
asyncio.run(main()) run_sync and a running event loop. Runner.run_sync starts the async runner for you, which is
fine in a plain script. Do not call it from inside a running event loop
(a Jupyter cell, an async web handler). There, use
await Runner.run(agent, input=...). The first argument is
always the agent. The user message is the second argument, or
input=....
What comes back
Korely answers a read by looking things up: get_context
assembles facts and memories, and search compares meaning. No
language model runs on Korely's side when you read. Your OpenAI model does
all the reasoning over the block it receives. Reads are cheap for us, so
every plan allows at least 100 times more queries than writes per month
(see the pricing page). The terminal below is an
example, not your output.
$ python agent.py
# example output. Earlier sessions saved "User lives in Lisbon", then "User just moved to Berlin"
→ tool call recall_memory(query="where the user lives")
← _The facts below are a compact profile of the user; [...]_
## Known facts
- user lives_in Berlin (since 2026-06-16)
## Relevant memories
_A time in square brackets [...] is the day it was said._
- [2026-06-16] User just moved to Berlin
...
Agent › You live in Berlin now; you told me you just moved there. Facts arrive after the write. korely.add(...) returns when the memory is stored. The facts
are written by extraction,
usually about ten seconds later. A recall_memory right after
a save_memory in the same turn may not see the new fact. For
a chat agent that is usually fine, because the fact is there by the next
message. korely.events() tells you when a write has finished
processing. Do not poll get_context in a tight loop waiting
for the fact; let it land between turns.
One memory per end user
The tools above read and write the whole project. A read without a
user_id covers every end
user of the project. When each person who uses your product needs a
private memory, bind a user_id on your server and pass it on
every Korely call. Capture it in a closure, so the model can neither see nor
change it:
from agents import Agent, Runner, function_toolfrom korely_memory import Korely
korely = Korely() # reads KORELY_API_KEY from the environment
def build_agent(user_id: str) -> Agent: """user_id is bound here, server-side. The model never sees it."""
@function_tool def recall_memory(query: str) -> str: """Recall settled facts and memories about this user.""" return korely.get_context(query=query, user_id=user_id).context
@function_tool def save_memory(content: str) -> str: """Save something worth remembering about this user.""" memory = korely.add(content, user_id=user_id, agent_id="support-bot") return f"Saved (id={memory.id})."
return Agent( name="Support bot", instructions="Recall memory before answering. Save durable facts.", tools=[recall_memory, save_memory], )
# Per request, bound to the authenticated session:agent = build_agent(user_id=current_user.id) # your own login objectresult = Runner.run_sync(agent, user_message)print(result.final_output)
A read that carries a user_id returns only that user's data.
agent_id labels which
application wrote the memory, and
run_id can tag one
session.
Where to go next
- Memory as a tool: the end-to-end pattern of giving an agent recall, save and search, with tips for getting the model to call them.
- Get context: how the fact
block is built, what
token_budgetcontrols, and what eachsourcein the response means.
Something not working? Email
[email protected] with your
openai-agents and korely-memory versions and the
error output. We read every message.