Agent Memories
Introduction
Agent memories let agents store, retrieve, and share context, knowledge, and events. There are three practical classes of memory:
- SDK Context Store β namespaced key-value memory built into the agents SDK (
core.context), backed by local Redis, a global Redis, a shared Redis or an in-process store. It includes helpers for sessions, conversation history, working memory, tool state, checkpoints and more. - Agent Context Cache β in-process key-value working state whose updates are broadcast to NATS topics (
agent_context_cache, inlibs/agents-addons). - Memory Grid β FrameDB-based in-memory, storage and streaming backends (see the Memory Grid section).
For long-term episodic, semantic, procedural, reflective and reward memory, git-versioned agent files, and learning lessons from experience (mining, human review, applying lessons, feedback), see Advanced Agent Memories.
| Memory Type | Purpose / Use Case | Best for |
|---|---|---|
SDK Context Store (core.context.store.RedisStore) |
Namespaced get/set with TTLs and automatic serialization. Local, global or shared scope. Session-structured helpers (AgentSession, ConversationMemory, WorkingMemory, CheckpointManager, β¦). |
The agent's primary working memory: conversation history, active plan and steps, tool state, checkpoints. Use the global or shared store to share state between instances and agents. |
Agent Context Cache (AgentContextCache) |
Namespace-scoped in-process get/set. Every write is queued and broadcast over NATS to configured topics. |
Small, fast-changing state that other agents must be notified about (coordination flags, shared context updates). Data is not persisted. |
Memory Grid (memory_type="memory-grid") |
FrameDB-backed in-memory objects, durable storage and streams. | Large objects, durable results and event streams. |
Registering Memory in Subject Specification
Memories can be recorded in the subject's integrations.memory_systems list using MemoryItem. The agents SDK does not instantiate memories from these entries automatically. Read them from self.subject.integrations.memory_systems and create the store in your agent.
Data Class Reference
@dataclass
class MemoryItem:
memory_id: str
memory_type: str = ""
memory_backend: str = ""
memory_custom_config: Dict[str, Any] = field(default_factory=dict)
Example Configuration
{
"memory_id": "context-memory-01",
"memory_type": "agent-context",
"memory_backend": "redis-shared",
"memory_custom_config": {
"redis_url": "redis://redis.default.svc.cluster.local:6379/0",
"namespace": "agent-alpha"
}
}
Field Descriptions
| Field | Type | Description |
|---|---|---|
| memory_id | str |
Unique identifier for this memory entry. |
| memory_type | str |
Type of memory (e.g. "agent-context", "memory-grid"). |
| memory_backend | str |
Backend the agent should create (your own convention, e.g. "redis-local", "redis-global", "redis-shared", "in-memory"). |
| memory_custom_config | dict |
Backend settings (e.g. redis_url, namespace). |
SDK Context Store β Client Developer Documentation
Introduction
The agents SDK includes a key-value store at core.context.store.RedisStore, plus higher-level memory helpers in core.context.utils, core.context.io and core.context.workspace. Every helper is built on RedisStore namespaces, so the same code works against an in-process store or Redis.
Stores
from core.context.store import RedisStore
local = RedisStore.get_local_store() # redis://localhost:6379 (the instance's Redis sidecar)
mem = RedisStore.get_local_store(in_memory=True) # in-process store, no Redis needed (only if no local store exists yet)
glob = RedisStore.get_global_store() # Redis at REDIS_GLOBAL_STORE_HOST_URL
shared = RedisStore.get_shared_store("redis://redis.shared:6379/0")
| Store | Backend | Scope |
|---|---|---|
Local (get_local_store()) |
redis://localhost:6379, or an in-process dict when in_memory=True |
One agent instance |
Global (get_global_store()) |
Redis at REDIS_GLOBAL_STORE_HOST_URL (RuntimeError if unset) |
All agents configured with that URL |
Shared (get_shared_store(url)) |
Redis at url |
Anyone with the URL |
Each store is a process-wide singleton: one local store, one global store, and one shared store per URL. The first call to get_local_store decides whether the local store is in-memory or Redis.
Basic Operations
store = RedisStore.get_local_store().get_namespace("agent-alpha")
store.set("plan", ["fetch", "summarize"])
store.set("token", "abc", ex=3600) # expires in 1 hour
store.get("plan") # ['fetch', 'summarize']
store.get("missing", default={}) # {}
store.exists("plan") # True
store.pop("token") # get and delete
store.update({"a": 1, "b": 2.5}) # bulk set
store.keys() # keys in this namespace (and its sub-namespaces)
store.items(); store.values()
store.delete("a")
store.clear() # delete all keys under this namespace
store["mode"] = "auto"; "mode" in store; len(store); del store["mode"]
get_namespace(name)returns a child store whose keys are prefixedparent:name:. Namespaces nest.- Values keep their type:
None,bytes,str,bool,intandfloatare stored natively;dict/list/tupleas JSON (tuples come back as lists); anything else is pickled.
Memory Helpers
All helpers take a store (or a session) and keep their data in namespaces under it.
from core.context.utils import (
AgentSession, ConversationMemory, WorkingMemory, ToolState, ExecutionLock,
InterruptState, WorkflowState, MetricsState, CheckpointManager, PromptContextBuilder,
)
session = AgentSession(RedisStore.get_global_store(), agent_id="agent-alpha", session_id="sess-1")
AgentSession stores everything under agents:<agent_id>:sessions:<session_id>, with sub-namespaces meta, messages, working, tools, metrics, workflow, interrupts, checkpoints and locks. It records created and status (active) on first use, and provides status(), set_status(), touch(), reset(), export() and import_state().
| Helper | Main methods | Use for |
|---|---|---|
ConversationMemory(session) |
add(role, content), get_all(), get_recent(n), update(i, content), delete(i), summarize(text), get_summary(), clear() |
Conversation history and a running summary |
WorkingMemory(session) |
set/get/delete, set_goal/get_goal, set_plan/get_plan, set_step, complete_step, state() |
Goal, plan and step tracking |
ToolState(session) |
set_pending, resolve, set_history, history(), pending(), last_result() |
Pending tool calls and tool history |
ExecutionLock(session) |
acquire(owner, ttl=60), release(owner), owner(), is_locked(), force_release() |
Ensuring one worker processes a session at a time |
InterruptState(session) |
wait_for(event), resolve(data), waiting(), result() |
Waiting for human or external input |
WorkflowState(session) |
set_current, complete_node, completed(), is_complete(), mark_complete() |
Tracking workflow progress |
MetricsState(session) |
inc, record_latency, record_tokens, avg_latency(), stats() |
Per-session counters |
CheckpointManager(session) |
snapshot() β id, restore(id), list(), delete(id) |
Saving and restoring messages, working memory, tools, workflow and metrics |
PromptContextBuilder(session) |
build(max_messages=20), build_flat_text() |
Building LLM prompt context from session memory |
convo = ConversationMemory(session)
convo.add("user", "Summarize the contract.")
convo.add("assistant", "Here is the summary...")
work = WorkingMemory(session)
work.set_goal("Review contract")
work.set_plan(["extract", "analyze", "report"])
cid = CheckpointManager(session).snapshot()
prompt_ctx = PromptContextBuilder(session).build(max_messages=10)
Other helpers built on a store:
| Helper | Module | Description |
|---|---|---|
IOChannel(store, name) |
core.context.io |
Request/response channel: submit(input) β id, fetch_input(), set_output(id, output), wait_for_output(id, timeout=60), get_status(id), cleanup(id) |
TaskQueue(store, name="default") |
core.context.workspace |
Simple queue: enqueue(task) β id, dequeue(), retry(id) |
SharedWorkspace(store, name="global") |
core.context.workspace |
publish(key, value) (keeps an event history), subscribe(key) |
StreamingBuffer(store, name) |
core.context.workspace |
Token streaming: start(), append(token), complete(), get_partial() |
These helpers do read-modify-write on whole values (for example, the message list), so concurrent writers to the same session can overwrite each other. Use
ExecutionLockwhen several workers may touch one session.
Agent Context Cache Memory
The Agent Context Cache (agent_context_cache, in libs/agents-addons) keeps namespaced key-value data in process memory and broadcasts every write to NATS topics.
from agent_context_cache.core.agent_context import AgentContextCache
ctx = AgentContextCache()
ctx.set("user_profile", {"name": "Alice", "age": 30})
ctx.get("user_profile") # {'name': 'Alice', 'age': 30}
ctx.set("session_token", "abc123", namespace="auth")
ctx.list_keys(namespace="auth")
ctx.list_namespaces()
ctx.clear_namespace("auth")
ctx.set_topics_list(["agent.context.updates"]) # broadcast topics (NATS at nats://localhost:4222)
Each write is published to every configured topic as:
{
"event_type": "context_update",
"sender_subject_id": "<SUBJECT_ID>",
"event_data": { "operation": "set", "namespace": "default", "key": "user_profile", "value": { "name": "Alice", "age": 30 } }
}
Data is not persisted. enable_backup() and set_backup_settings() only store configuration; no backup is performed. The package currently has import and shutdown issues. See Agent Context Cache for the full reference and known issues.
Memory Grid β Client Developer Documentation
Introduction
The Memory Grid is the integration layer between an agent and distributed FrameDB instances. It allows the agent to maintain and access different kinds of memory through a single, consistent API.
Memory Grid supports three backend modes:
- in-memory β Fast, volatile storage for ephemeral data.
- storage β Persistent object storage for long-term retention.
- streaming β Ordered queues for real-time event processing.
All modes use the same FrameDBClient interface for storing, retrieving, streaming, and backing up data.
A typical workflow is:
- Declare a Memory Grid entry in the agentβs subject specification.
- Initialize the FrameDB client with cluster and routing URLs.
- Perform operations according to the backend type.
- Optionally back up or restore memory between instances or external storage.
Registering Memory Grid in Subject Specification
A Memory Grid is declared in the subject specification using the MemoryItem structure.
The memory_type must be "memory-grid", and the memory_backend specifies which mode to use.
Data Class Reference
from dataclasses import dataclass, field
from typing import Dict, Any
@dataclass
class MemoryItem:
memory_id: str
memory_type: str = "memory-grid" # Fixed value
memory_backend: str = "" # "in-memory", "storage", or "streaming"
memory_custom_config: Dict[str, Any] = field(default_factory=dict)
Minimal Required Configuration
{
"memory_id": "memory-grid-01",
"memory_type": "memory-grid",
"memory_backend": "in-memory",
"memory_custom_config": {
"framedb_id": "framedb-inmem-1",
"cluster_url": "http://framedb-cluster",
"routing_url": "http://routing-service"
}
}
Field Descriptions
| Field | Location | Type | Description |
|---|---|---|---|
| memory_id | root | str |
Unique identifier for this memory entry in the subject spec. |
| memory_type | root | str |
Must be "memory-grid" for Memory Grid integration. |
| memory_backend | root | str |
Memory mode β "in-memory", "storage", or "streaming". |
| memory_custom_config.framedb_id | config | str |
Target FrameDB instance ID. |
| memory_custom_config.cluster_url | config | str |
Base API URL for the FrameDB cluster. |
| memory_custom_config.routing_url | config | str |
API URL for the FrameDB routing service. |
| memory_custom_config.extra_params | config | dict |
Optional backend-specific settings (e.g., cache size, credentials). |
Import and Setup
from framedb_sdk import FrameDBClient
client = FrameDBClient(
cluster_url="http://framedb-cluster",
routing_url="http://routing-service"
)
In-Memory Backend (memory_backend="in-memory")
Purpose: Fast, volatile memory backed by Redis for temporary state, shared context, or scratchpad data.
Example:
client.set_object({
"key": "session:123",
"data": b"ephemeral binary data",
"framedb_id": "framedb-inmem-1",
"type": "in-memory"
})
result = client.get_object("session:123")
print(result["object"]["data"])
Notes:
- Data is not persisted after restart.
- Use
create_backup(...)to persist important data.
Storage Backend (memory_backend="storage")
Purpose: Persistent object storage backed by TiDB or another SQL-compatible DB.
Example:
client.set_pythonic_object(
key="model_snapshot_v1",
obj={"weights": [1, 2, 3]},
framedb_id="framedb-storage-1",
framedb_type="storage"
)
res = client.get_pythonic_object("model_snapshot_v1")
print(res["object"]["python_object"])
Notes:
- Supports long-term retention and backups to object stores.
Streaming Backend (memory_backend="streaming")
Purpose: Ordered queues backed by Redis Streams for event-driven workflows.
Example β Continuous Listener:
for msg in client.listen_for_stream_data("agent-events", "framedb-stream-1"):
print("New event:", msg)
Example β Bulk Pull:
for msg in client.pull_all_stream_data("agent-events", "framedb-stream-1"):
print("Event:", msg)
Notes:
- Maintains strict ordering.
- Suitable for live ingestion or inter-agent communication.
Backup & Restore
# Backup a single key
client.create_backup("session:123", "framedb-backup-1")
# Restore from backup
client.restore_from_backup(
keys=["session:123"],
framedb_id="framedb-inmem-1",
framedb_type="in-memory",
s3_credentials_dict={
"bucket": "my-bucket",
"access_key": "...",
"secret_key": "...",
"region": "ap-south-1"
}
)