Add Persistent Memory to OpenAI Agents
Give OpenAI Agents SDK agents the ability to store and retrieve memories that persist across sessions.
Table of contents
Prerequisites
- A HippoDid account with an API key (
hd_key_...) - Python 3.10+
- A character created in HippoDid (see Quick Start)
Install
pip install hippodid openai-agents
How it works
The OpenAI Agents SDK lets you define function tools that agents can call during execution. You register search_memories and add_memory as tools, and the agent decides when to store or retrieve information based on the conversation.
Define the memory tools
Create two function tools that wrap HippoDid’s API.
from agents import function_tool
from hippodid import HippoDid
client = HippoDid(api_key="hd_key_...")
CHARACTER_ID = "YOUR_CHARACTER_ID"
@function_tool
def search_memories(query: str, limit: int = 5) -> str:
"""Search long-term memory for relevant facts, preferences, and past decisions.
Call this at the start of a conversation or when the user references something
from a previous session."""
results = client.search_memories(
character_id=CHARACTER_ID,
query=query,
limit=limit,
)
if not results["memories"]:
return "No relevant memories found."
lines = []
for mem in results["memories"]:
lines.append(f"[{mem['category']}] {mem['content']}")
return "\n".join(lines)
@function_tool
def add_memory(content: str) -> str:
"""Store an important fact, decision, or preference in long-term memory.
Call this when the user shares something worth remembering for future sessions,
such as preferences, project decisions, or personal details."""
result = client.add_memory(
character_id=CHARACTER_ID,
content=content,
)
return f"Stored: {result['id']} [{result.get('category', 'auto')}]"
Create an agent with memory
Wire the tools into an OpenAI agent.
from agents import Agent, Runner
agent = Agent(
name="Memory Assistant",
instructions=(
"You are a helpful assistant with long-term memory. "
"At the start of each conversation, search your memory for relevant context. "
"When the user shares preferences, decisions, or important facts, store them "
"in memory so you remember next time. "
"Always acknowledge when you recall something from a previous session."
),
tools=[search_memories, add_memory],
)
# Run the agent
result = Runner.run_sync(agent, "I just switched to a standing desk. Remember that.")
print(result.final_output)
The agent will call add_memory to store the standing desk preference. In a future session:
result = Runner.run_sync(agent, "What do you know about my workspace setup?")
print(result.final_output)
# The agent calls search_memories and recalls the standing desk.
Full working example
A complete script showing a multi-turn agent with persistent memory.
from agents import Agent, Runner
from hippodid import HippoDid
client = HippoDid(api_key="hd_key_...")
CHARACTER_ID = "YOUR_CHARACTER_ID"
@function_tool
def search_memories(query: str, limit: int = 5) -> str:
"""Search long-term memory for facts, preferences, and decisions from past sessions."""
results = client.search_memories(
character_id=CHARACTER_ID, query=query, limit=limit,
)
if not results["memories"]:
return "No relevant memories found."
return "\n".join(
f"[{m['category']}] {m['content']}" for m in results["memories"]
)
@function_tool
def add_memory(content: str) -> str:
"""Store a fact or preference in long-term memory for future sessions."""
result = client.add_memory(character_id=CHARACTER_ID, content=content)
return f"Stored: {result['id']}"
agent = Agent(
name="Personal Assistant",
instructions=(
"You are a personal assistant with persistent memory. "
"Search memory at the start of conversations for context. "
"Store new preferences and decisions when the user shares them. "
"Reference past memories naturally — do not list them mechanically."
),
tools=[search_memories, add_memory],
)
def chat(message: str) -> str:
result = Runner.run_sync(agent, message)
return result.final_output
# Session 1
print(chat("My favorite programming language is Rust. I'm working on a CLI tool."))
# Session 2 (could be days later)
print(chat("Can you suggest a good library for my project?"))
# Agent recalls Rust + CLI tool and suggests clap, serde, etc.
Per-customer agents
For multi-tenant applications, resolve the character ID dynamically based on the customer.
def get_agent_for_customer(customer_email: str) -> Agent:
# Resolve character by alias
character = client.resolve_character(alias=f"client:{customer_email}")
char_id = character["id"]
@function_tool
def search_memories(query: str, limit: int = 5) -> str:
"""Search this customer's memory."""
results = client.search_memories(character_id=char_id, query=query, limit=limit)
if not results["memories"]:
return "No relevant memories found."
return "\n".join(f"[{m['category']}] {m['content']}" for m in results["memories"])
@function_tool
def add_memory(content: str) -> str:
"""Store a fact about this customer."""
result = client.add_memory(character_id=char_id, content=content)
return f"Stored: {result['id']}"
return Agent(
name="Support Agent",
instructions="You are a support agent. Search memory for customer context before responding.",
tools=[search_memories, add_memory],
)
# Usage
agent = get_agent_for_customer("alice@example.com")
result = Runner.run_sync(agent, "I need help with my subscription.")
Using assemble_context for richer prompts
Instead of having the agent search during execution, you can pre-load context into the system prompt using assemble_context().
context = client.assemble_context(
character_id=CHARACTER_ID,
query="user asking about their project",
strategy="task_focused",
max_tokens=2000,
)
agent = Agent(
name="Project Assistant",
instructions=(
f"You are a project assistant. Here is what you know about the user:\n\n"
f"{context}\n\n"
f"Use this context to give informed, personalized responses."
),
tools=[add_memory], # still allow storing new facts
)
This approach is useful when you want the agent to have full context immediately without spending a tool call on search.
Tips
- Always include
search_memoriesin the tools list. The agent will learn to call it at the start of conversations when the instructions tell it to. - Write clear tool descriptions. The agent decides when to call tools based on the description string. Be specific about when each tool should be used.
- Use EXTRACTED mode (the default) so HippoDid’s AI categorizes and structures the memories automatically.
- Combine with assemble_context for the best of both worlds: pre-loaded context in the system prompt plus the ability to search for more during execution.
Next steps
- Assembly Strategies — all five context formatting strategies
- Memory Modes — EXTRACTED vs VERBATIM vs HYBRID
- LangChain Integration — if you also use LangChain
- API Reference — full endpoint documentation