Skip to main content

Episodic Memory for Multi-Step Agents

A stateless agent that restarts a task from scratch every session wastes context. A long-running agent that can write its observations and decisions to a graph database — and read them back on resume — can pick up where it left off, avoid repeating work, and build compound reasoning over time.

This tutorial shows the episodic memory pattern: one GOAL record per task, linked STEP records capturing each intermediate action, and linked OBSERVATION records for tool outputs and discoveries.


Graph shape​

LabelWhat it represents
GOALThe agent's top-level objective for a session
STEPA single action the agent took (tool call, reasoning step, decision)
OBSERVATIONThe result or output of a step (tool output, search result, partial answer)

Step 1: Create a goal record when a session starts​

from rushdb import RushDB
import os
from datetime import datetime, timezone

db = RushDB(os.environ["RUSHDB_API_KEY"], base_url="https://api.rushdb.com/api/v1")


def start_session(task: str, agent_id: str):
goal = db.records.create("GOAL", {
"task": task,
"agentId": agent_id,
"status": "in_progress",
"startedAt": datetime.now(timezone.utc).isoformat()
})
return goal


goal = start_session(
"Research RushDB graph query patterns and summarize the top 5",
"agent-001"
)
print("Session goal ID:", goal.id)

Step 2: Record each step as the agent executes it​

Each agent action — tool call, web search, or LLM reasoning step — becomes a STEP record linked to the GOAL.

def record_step(goal_id: str, step_type: str, action: str, index: int):
step = db.records.create("STEP", {
"type": step_type,
"action": action,
"index": index,
"status": "executing",
"startedAt": datetime.now(timezone.utc).isoformat()
})

goal_result = db.records.find({"labels": ["GOAL"], "where": {"$id": goal_id}})
db.records.attach(
goal_result.data[0].id,
step.id,
{"type": "HAS_STEP", "direction": "out"}
)
return step


step1 = record_step(goal.id, "tool_call", "Search docs: graph traversal patterns", 1)

Step 3: Store observations from tool outputs​

When a tool returns results, store the output as an OBSERVATION linked to the STEP that produced it.

def record_observation(step_id: str, content: str, obs_type: str):
obs = db.records.create("OBSERVATION", {
"content": content,
"type": obs_type,
"recordedAt": datetime.now(timezone.utc).isoformat()
})

step_result = db.records.find({"labels": ["STEP"], "where": {"$id": step_id}})
db.records.attach(
step_result.data[0].id,
obs.id,
{"type": "PRODUCED", "direction": "out"}
)

db.records.update(step_id, {"status": "completed"})
return obs


obs1 = record_observation(
step1.id,
"Found 3 relevant tutorials: thinking-in-graphs, modeling-hierarchies, temporal-graphs",
"search_result"
)

Step 4: Resume a session and retrieve accumulated context​

When the agent resumes — or when a new session needs to continue where a prior one left off — retrieve the full context for the GOAL.

def resume_session(goal_id: str) -> dict:
goal_result = db.records.find({"labels": ["GOAL"], "where": {"$id": goal_id}})
goal_rec = goal_result.data[0]

steps = db.records.find({
"labels": ["STEP"],
"where": {
"GOAL": {
"$relation": {"type": "HAS_STEP", "direction": "in"},
"$id": goal_id
}
},
"orderBy": {"index": "asc"}
})

observations = db.records.find({
"labels": ["OBSERVATION"],
"where": {
"STEP": {
"$relation": {"type": "PRODUCED", "direction": "in"},
"GOAL": {
"$relation": {"type": "HAS_STEP", "direction": "in"},
"$id": goal_id
}
}
}
})

completed = sum(1 for s in steps.data if s.data.get("status") == "completed")
return {
"goal": {"task": goal_rec.data.get("task"), "status": goal_rec.data.get("status")},
"completedSteps": completed,
"totalSteps": steps.total,
"observations": [o.data.get("content") for o in observations.data]
}


context = resume_session(goal.id)
print(context)

Step 5: Mark the goal complete​

When the task finishes, update the GOAL record with its outcome.

from datetime import datetime, timezone

db.records.update(goal.id, {
"status": "completed",
"completedAt": datetime.now(timezone.utc).isoformat(),
"summary": "Identified 5 top query patterns"
})

When to use episodic memory vs. team memory​

PatternUse case
Episodic memory (this tutorial)Per-session agent context: goals, steps, tool outputs
Team memory (Building Team Memory)Shared persistent knowledge: tickets, docs, decisions
Fact memory (RushDB as a Memory Layer)Long-lived facts about entities across all sessions

These patterns compose: an agent can write episodic steps to its own GOAL graph while also reading from shared team memory for background knowledge.


Production caveat​

Episodic records accumulate quickly during active agent use. Define a cleanup policy: archive GOAL records after N days, compress older OBSERVATION content (store summaries instead of full tool outputs), or use a separate RushDB project for ephemeral agent memory versus persistent team knowledge.


Next steps​