Skip to main content

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"],
    )],
)

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

EpisodicDB does not generate embeddings. Pass them in:

import openai

response = openai.embeddings.create(
    model="text-embedding-3-small",
    input="what the agent was doing"
)
embedding = response.data[0].embedding  # 1536 dims

db.record_episode(status="success", embedding=embedding)
db.similar_episodes(embedding, status="failure", limit=5)

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

episodicdb-0.1.0.tar.gz (13.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

episodicdb-0.1.0-py3-none-any.whl (13.5 kB view details)

Uploaded Python 3

File details

Details for the file episodicdb-0.1.0.tar.gz.

File metadata

  • Download URL: episodicdb-0.1.0.tar.gz
  • Upload date:
  • Size: 13.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.10

File hashes

Hashes for episodicdb-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f03ec45a7fe3cfb94de3df6f5782c2aec2bf87e09dbe084be4f57dadf883c045
MD5 39646e09375ecc478e998a32fdf4cbfc
BLAKE2b-256 335cfcdafa55005487132e66a04560b72e44bc50fda3975634e43e05243ab0f2

See more details on using hashes here.

File details

Details for the file episodicdb-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: episodicdb-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 13.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.10

File hashes

Hashes for episodicdb-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5a609945bcde82f144b7c4c55f45916a8a63fcc3069dbd9cb54aa8e2764835b3
MD5 4a866bbc2b39c2ad004f4e8c612d3a63
BLAKE2b-256 0c1cd264309776036d1a59b5c5d3d278853485880b8aef91285dcc11f049e89e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page