OLAP-based memory engine for AI agents
Project description
EpisodicDB
An OLAP-based memory engine for AI agents.
Existing agent memory systems (Mem0, Zep, Letta) are designed as search systems. EpisodicDB treats agent memory as an analytics problem — aggregation, time-series patterns, causal tracing, temporal facts, and vector similarity all in a single query engine.
-- "Which tools failed most this week?"
SELECT tool_name, COUNT(*) AS failures
FROM tool_calls
WHERE outcome = 'failure'
AND called_at >= NOW() - INTERVAL '7 days'
GROUP BY tool_name
ORDER BY failures DESC;
-- "Find past episodes similar to this context that succeeded"
SELECT *, array_cosine_distance(context_embedding, ?) AS dist
FROM episodes
WHERE status = 'success'
ORDER BY dist ASC
LIMIT 5;
The Problem
| Query type | Example | Vector search? |
|---|---|---|
| Similarity | "Seen a similar error before?" | Yes |
| Aggregation | "How many tool failures this week?" | No |
| Time-series | "Do failures spike in the afternoon?" | No |
| Causal trace | "What tool ran right before failures?" | No |
| Comparison | "Worse than last week?" | No |
| Absence | "Tools that never succeeded?" | No |
| Temporal | "What was the user's timezone last Tuesday?" | No |
EpisodicDB answers all of them. Vector similarity is just another SQL operator.
Architecture
EpisodicDB
├── WriterMixin record_episode / record_tool_call / record_decision / record_fact
├── AnalyticsMixin 6 analytics methods + vector similarity
└── TemporalMixin facts_as_of / fact_history
Engine: DuckDB (OLAP) + VSS extension (HNSW vector index)
Schema: episodes + tool_calls + decisions + facts
Install
pip install episodicdb
Quick Start
Python SDK
from episodicdb import EpisodicDB
with EpisodicDB(agent_id="my-agent") as db:
# Record what happened
ep_id = db.record_episode(
status="failure",
task_type="file_edit",
context={"file": "auth.py", "error": "permission denied"},
)
db.record_tool_call(ep_id, "Edit", "failure",
duration_ms=120, error_message="permission denied")
db.record_tool_call(ep_id, "Bash", "success", duration_ms=50)
# Record temporal facts (auto-supersedes previous values)
db.record_fact("user_timezone", "Asia/Seoul", episode_id=ep_id)
db.record_fact("user_timezone", "America/New_York") # closes previous
# Analyze patterns
print(db.top_failing_tools(days=7))
# [{"tool_name": "Edit", "failures": 5}, ...]
print(db.before_failure_sequence("Edit"))
# [{"prev_tool": "Bash", "count": 4}, ...]
print(db.compare_periods("failure_rate", days=7))
# {"period_a": 0.32, "period_b": 0.18, "delta": 0.14}
# Time-travel query
from datetime import datetime
print(db.facts_as_of(datetime(2025, 3, 15)))
# [{"key": "user_timezone", "value": "Asia/Seoul", ...}]
MCP Server (Claude, OpenAI Agents SDK)
EpisodicDB ships an MCP server with 12 tools over stdio.
episodicdb-mcp --agent-id my-agent
episodicdb-mcp --agent-id my-agent --db ./memory.db
Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"episodicdb": {
"command": "episodicdb-mcp",
"args": ["--agent-id", "my-agent"]
}
}
}
Claude Code (.mcp.json):
{
"mcpServers": {
"episodicdb": {
"command": "episodicdb-mcp",
"args": ["--agent-id", "my-agent"]
}
}
}
OpenAI Agents SDK:
from agents import Agent
from agents.mcp import MCPServerStdio
agent = Agent(
name="my-agent",
instructions="You have access to episodic memory.",
mcp_servers=[MCPServerStdio(
command="episodicdb-mcp",
args=["--agent-id", "my-agent"],
)],
)
Auto-recording (recommended)
Add these rules to your CLAUDE.md (or system prompt) so the agent records episodes automatically:
## EpisodicDB — auto-recording rules
EpisodicDB MCP server is connected. Follow these rules to record work automatically.
### On session start
- Call `record_episode` (status: "partial", task_type: type of work)
- Remember the episode ID for subsequent tool call / decision records
### During work
- Record significant tool results with `record_tool_call` (success/failure, duration)
- Record important decisions with `record_decision` (rationale, alternatives)
- Record new user/project facts with `record_fact` (e.g. preferred_language, current_project)
### On session end
- Update the episode status to match the final outcome (success/failure/partial/aborted)
### Guidelines
- Don't interrupt the user's workflow to record — do it in the background
- Only record meaningful actions, not every small step
- Use english snake_case for fact keys (e.g. preferred_language, deploy_target)
API
Writer
db.record_episode(status, task_type=None, context=None,
embedding=None, tags=None,
started_at=None, ended_at=None) -> str # episode UUID
db.record_tool_call(episode_id, tool_name, outcome,
parameters=None, result=None,
duration_ms=None, error_message=None) -> str
db.record_decision(episode_id, rationale,
decision_type=None, alternatives=None,
outcome=None) -> str
db.record_fact(key, value, episode_id=None,
valid_from=None) -> str # auto-supersedes previous
Analytics
| Method | Description |
|---|---|
top_failing_tools(days, limit) |
Most-failed tools in the last N days |
hourly_failure_rate(days) |
Failure count by hour of day |
before_failure_sequence(tool_name, lookback) |
Tools that precede failures |
compare_periods(metric, days) |
Period-over-period comparison |
never_succeeded_tools() |
Tools with zero successful calls |
similar_episodes(embedding, status, limit) |
Vector similarity + SQL filter |
Temporal Facts
Facts are key-value pairs with automatic temporal validity. Recording a new value for the same key closes the previous one.
db.record_fact("preferred_model", "gpt-4o")
# later...
db.record_fact("preferred_model", "claude-sonnet") # supersedes gpt-4o
db.facts_as_of(some_datetime) # point-in-time snapshot
db.fact_history("preferred_model") # full change log
Persistence
EpisodicDB(agent_id="my-agent") # ~/.episodicdb/my-agent.db
EpisodicDB(agent_id="my-agent", path="./x.db") # explicit path
EpisodicDB(agent_id="my-agent", path=":memory:") # in-memory (testing)
Embeddings
Built-in helpers for popular providers (lazy imports, no hard dependencies):
pip install episodicdb[openai] # OpenAI
pip install episodicdb[voyage] # Voyage AI
pip install episodicdb[ollama] # Ollama (local)
pip install episodicdb[all] # all providers
from episodicdb import embeddings
# OpenAI
vec = embeddings.openai("what the agent was doing")
# Voyage AI
vec = embeddings.voyage("what the agent was doing")
# Local Ollama
vec = embeddings.ollama("what the agent was doing")
db.record_episode(status="success", embedding=vec)
db.similar_episodes(vec, status="failure", limit=5)
Or bring your own:
db.record_episode(status="success", embedding=your_1536_dim_list)
Development
git clone https://github.com/KsPsD/EpisodicDB
cd EpisodicDB
pip install -e ".[dev]"
pytest
Stack
- DuckDB — embedded OLAP engine
- DuckDB VSS — HNSW vector index
- MCP — Model Context Protocol server
- Python 3.11+
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file episodicdb-0.1.2.tar.gz.
File metadata
- Download URL: episodicdb-0.1.2.tar.gz
- Upload date:
- Size: 20.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d86388bf02981b4ebcef578c0e1d1e3e422b9e666d3f7ef22c6e164e3b7b76a1
|
|
| MD5 |
62d4f080b9cf27d396b2314d467d6329
|
|
| BLAKE2b-256 |
881d87e14b0c0d7685bc0866299da4422dd4c0f9b59925f5f2d4dd06febc6c98
|
Provenance
The following attestation bundles were made for episodicdb-0.1.2.tar.gz:
Publisher:
publish.yml on KsPsD/EpisodicDB
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
episodicdb-0.1.2.tar.gz -
Subject digest:
d86388bf02981b4ebcef578c0e1d1e3e422b9e666d3f7ef22c6e164e3b7b76a1 - Sigstore transparency entry: 1205641873
- Sigstore integration time:
-
Permalink:
KsPsD/EpisodicDB@108162a0ee37d7b5b49daa5eb9bdc95069005aaa -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/KsPsD
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@108162a0ee37d7b5b49daa5eb9bdc95069005aaa -
Trigger Event:
push
-
Statement type:
File details
Details for the file episodicdb-0.1.2-py3-none-any.whl.
File metadata
- Download URL: episodicdb-0.1.2-py3-none-any.whl
- Upload date:
- Size: 20.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8bfe3c5f45f30072c8b5bc661a1511dc15503e57b35c50bbfc2b40bae93bd6ef
|
|
| MD5 |
4253a573e0723b376b10a6d2aa4accd8
|
|
| BLAKE2b-256 |
b623b5fac0816a0c056990502fdcf2e324aa725c5e53c771e1e74cf25066a800
|
Provenance
The following attestation bundles were made for episodicdb-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on KsPsD/EpisodicDB
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
episodicdb-0.1.2-py3-none-any.whl -
Subject digest:
8bfe3c5f45f30072c8b5bc661a1511dc15503e57b35c50bbfc2b40bae93bd6ef - Sigstore transparency entry: 1205641874
- Sigstore integration time:
-
Permalink:
KsPsD/EpisodicDB@108162a0ee37d7b5b49daa5eb9bdc95069005aaa -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/KsPsD
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@108162a0ee37d7b5b49daa5eb9bdc95069005aaa -
Trigger Event:
push
-
Statement type: