Skip to main content

memlife

Memory that degrades gracefully. Not another pile that grows forever.

PyPI Python License

Current version: 0.4.4

What

memlife is a four-tier lifecycle memory system for AI agents. Instead of treating memory as a monotonically growing database, every entry has a lifecycle — facts decay, journal entries retire, superseded data is pruned, and nothing accumulates forever.

The four tiers:

  • Episodes — raw events (what happened)
  • Facts — durable truths (what I know)
  • Journal — reflected beliefs (what I believe)
  • Decay/Prune — confidence fades, stale entries retire, GC cleans up

Why

Every other memory system accumulates. Facts never expire. Confidence never decays. Stale conventions become unquestioned truths. Recall quality degrades over time.

memlife solves this. Memory should be like human memory — it fades, it gets revised, it gets pruned. Not a database that grows until it breaks.

Install

pip install memlife

With adapters (optional):

pip install memlife[ollama]       # Ollama embedder + chat
pip install memlife[openai]       # OpenAI embedder + chat
pip install memlife[sentence-transformers]  # Local embeddings
pip install memlife[mcp]          # MCP server

Quickstart (30 seconds, zero dependencies)

import asyncio
from memlife import MemoryStore, MemoryConfig, DummyEmbedder

async def main():
    store = MemoryStore(
        config=MemoryConfig(db_path="./memlife.db", embedding_model="dummy"),
        embedder=DummyEmbedder(),
    )

    # Store an episode (something happened)
    store.remember(task="User asked about deployment", outcome="success")

    # Store a fact (durable truth)
    await store.store_fact("User deploys via GitHub Actions", confidence=0.8)

    # Store an entity relationship (fact-like but structured)
    store.store_triple("User", "deploys_via", "GitHub Actions", confidence=0.8)

    # Retrieve relevant memories (unified scoring across all layers)
    context = await store.retrieve("deployment")
    print(context)

    store.close()

asyncio.run(main())

No Ollama, no OpenAI, no API key. The DummyEmbedder uses bag-of-words vectors — similar sentences get positive cosine similarity. The full lifecycle — store, retrieve, decay, GC, and entity graph — works without any LLM. Only structured extraction and reflection need a model.

The Lifecycle

┌───────────┐     reflection      ┌───────────┐
│  EPISODE  │ ──────────────────▶│  JOURNAL  │
│  (event)  │   LLM synthesises   │ (belief)  │
└─────┬─────┘   observations &   └─────┬─────┘
│  extract triples   │
      │                                 │
      │ store_fact() / store_triple()  │ confidence decay
      ▼                                 │ (configurable)
┌───────────┐    recall bumps    ┌─────▼─────┐
│   FACT    │ ◀────────────────  │  RETIRE   │
│  (truth)  │   confidence +0.05 │ (floor)   │
└─────┬─────┘                    └─────┬─────┘
│    │
│    │ entity graph (triples)
│    ▼
┌───────────────┐
│ TRIPLE / GRAPH│
│(subject-pred- │
│ object + prov)│
└───────┬───────┘
      │
      │ revise / supersede             │ GC prunes
      ▼                                ▼
┌───────────┐                   ┌───────────┐
│ SUPERSEDED│  configurable     │  PRUNED   │
│ (replaced)│ ──────────────────▶│ (deleted) │
└───────────┘   retention       └───────────┘

UNIFIED SCORE = relevance × confidence × recency
Applied across ALL layers before every response.

NO-LLM MODE: store + retrieve + decay + GC + entity graph work
without any model. Only reflection and structured extraction need an LLM.

No-LLM Mode

The store, retrieval, decay, GC, entity graph, and embedding versioning all work without any LLM. Only the reflection loop and structured extraction need a model.

from memlife import MemoryStore, MemoryConfig

store = MemoryStore(config=MemoryConfig(db_path="./memlife.db"))
store.remember(task="something happened", outcome="success")

# retrieve() is async — use SyncMemoryStore or asyncio.run():
import asyncio
context = asyncio.run(store.retrieve("something"))
store.close()

With an Embedder

import asyncio
from memlife import MemoryStore, MemoryConfig
from memlife.adapters.ollama import OllamaEmbedder

async def main():
    store = MemoryStore(
        config=MemoryConfig(db_path="./memlife.db", embedding_model="mxbai-embed-large:latest"),
        embedder=OllamaEmbedder(model="mxbai-embed-large:latest"),
    )
    await store.store_fact("User prefers dark mode", confidence=0.9)
    context = await store.retrieve("dark mode")
    store.close()

asyncio.run(main())

Also available: OpenAIEmbedder (pip install memlife[openai]) and STEmbedder for local Sentence Transformers (pip install memlife[sentence-transformers]).

With Reflection

import asyncio
from memlife import MemoryStore, MemoryConfig, Reflector, DummyEmbedder, DummyChat

async def main():
    store = MemoryStore(
        config=MemoryConfig(db_path="./memlife.db", embedding_model="dummy"),
        embedder=DummyEmbedder(),
    )
    reflector = Reflector(
        memory=store,
        model_chat=DummyChat(),
        critic=False,
    )
    result = await reflector.reflect()
    store.close()

asyncio.run(main())

For real LLMs, use an adapter:

from memlife.adapters.ollama import OllamaChat

# Provide your own model name — memlife doesn't ship deployment-specific defaults.
chat = OllamaChat(model="your-model-name")
reflector = Reflector(memory=store, model_chat=chat, agent_name="my-agent")

Sync API

For non-async codebases:

from memlife import SyncMemoryStore, MemoryConfig, DummyEmbedder

store = SyncMemoryStore(
    config=MemoryConfig(db_path="./memlife.db", embedding_model="dummy"),
    embedder=DummyEmbedder(),
)
store.remember(task="hello", outcome="success")
fact_id = store.store_fact("Test fact", confidence=0.7)
context = store.retrieve("test")

MCP Server

Expose memlife to any MCP-compatible agent (Claude Desktop, Cursor, etc.):

memlife-mcp-server --db ./memlife.db --embedder ollama --embedding-model mxbai-embed-large:latest

Claude Desktop config:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

Linux: ~/.config/Claude/claude_desktop_config.json

Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "memlife": {
      "command": "memlife-mcp-server",
      "args": ["--db", "/path/to/memlife.db", "--embedder", "ollama", "--embedding-model", "mxbai-embed-large:latest"]
    }
  }
}

Tools exposed:

Tool Description
memory_store Store a durable fact
memory_search Search facts by query
memory_search_journal Search journal entries
memory_search_episodes Search episodes by keyword or tool name
memory_store_triple Store an entity relationship
memory_search_triples Search triples connected to an entity
memory_entity_neighbors Traverse the entity graph
memory_revise Revise an existing fact
memory_expire Mark a fact as expired
memory_retrieve Unified cross-layer retrieval
memory_gc Run garbage collection

Resources:

Resource Description
memlife://stats Memory statistics
memlife://health Embedding health report
memlife://contradictions Detected contradictions

Features

  • Four-tier lifecycle: Episode → Fact → Journal → Decay/Prune
  • Entity graph: normalized entities, aliases, and temporal triples with provenance
  • Graph traversal: BFS entity neighbors exposed via MCP, no external graph DB
  • Triple lifecycle: closed triples and orphan entities/aliases are GC'd like everything else
  • Confidence decay: facts decay with a configurable halflife; triples inherit the same decay
  • Unified scoring: relevance × confidence × recency across all layers
  • Confidence ceiling (0.99): facts are never immutable
  • GC with configurable retention: superseded facts, episodes, runs, metrics, and closed triples
  • Embedding versioning: detect stale vectors when the model changes, backfill automatically
  • Episode tool index: search "have I used this tool before?"
  • Incremental contradiction detection: O(new × n), not O(n²)
  • Reflection loop: LLM synthesises observations, hypotheses, and revisions with a critic gate
  • Structured extraction: optional MEMORIA extraction turns reflection output into attributable triples
  • JSONL import/export: backup and migration
  • MCP server: plug into Claude, Cursor, or any MCP client
  • Adapters: Ollama, OpenAI, Sentence Transformers
  • Sync wrapper: for non-async codebases
  • SQLite-backed: single file, zero external services
  • Zero dependencies: works out of the box with DummyEmbedder + DummyChat

Comparison

memlife Mem0 MemPalace Graphiti
Lifecycle/decay Yes — core feature No No No
Confidence erosion Yes (configurable halflife) No No No
GC + pruning Yes (configurable, includes triples) No No No
Reflection loop Yes (LLM + critic) No No No
Embedding versioning Yes No No No
Entity graph / triples Yes (SQLite-native) No No Yes
Graph lifecycle Yes (decay + GC) No No No
Zero-dependency mode Yes (DummyEmbedder) No No No
MCP server Yes No No No
Backend SQLite (single file) Various SQLite Neo4j
Multi-user Namespaces (isolated DBs) Yes Yes (by wing) Yes
Self-hosted/local Yes Yes Yes Requires Neo4j

memlife wins on lifecycle, decay, and zero-dependency quickstart. It doesn't pretend to beat everyone at everything — Mem0 has multi-user, Graphiti has deep graph analytics. If you want memory that degrades gracefully instead of accumulating forever, memlife is the one.

Status

v0.4.3. The API may change before v1.0. Not recommended for production yet.

License

MIT

Metadata

Release files for memlife 0.4.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for memlife 0.4.4
File Size Uploaded
memlife-0.4.4.tar.gz 101.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for memlife 0.4.4
File Interpreter ABI Platform
memlife-0.4.4-py3-none-any.whl Python 3 none any Details

Total release size: 193.6 kB

Release files / memlife-0.4.4.tar.gz

Download URL memlife-0.4.4.tar.gz
Size 101.7 kB
Tags Source
SHA-256 checksum
How to use checksums
69994bdb932a107da125bd65cc00cdab8ecd890402a217dc4797f2da2fd80574
BLAKE2b-256 checksum
How to use checksums
28fb61281673d33a8a12b026e88be1c83c65a7c0ababb39d2c722535aa2ad685
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.6

Release files / memlife-0.4.4-py3-none-any.whl

Download URL memlife-0.4.4-py3-none-any.whl
Size 91.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f45f8f67662333fd5dd736a1e950f80b2029aefd721ac42f077ddf045a06213a
BLAKE2b-256 checksum
How to use checksums
7fefb9ab956362c68ce696b9f659c842a1d9aa88e4f48e0cf58726ac4453219f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.6
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page