Memory that degrades gracefully — four-tier lifecycle memory for AI agents
Project description
memlife
Memory that degrades gracefully. Not another pile that grows forever.
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 --pre
With adapters (optional):
pip install memlife[ollama] --pre # Ollama embedder + chat
pip install memlife[openai] --pre # OpenAI embedder + chat
pip install memlife[sentence-transformers] --pre # Local embeddings
pip install memlife[mcp] --pre # MCP server
Quickstart (30 seconds, zero dependencies)
import asyncio
from memlife import MemoryStore, MemoryConfig, DummyEmbedder
async def main():
store = MemoryStore(
config=MemoryConfig(db_path="./mem.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)
# 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 hash-based vectors. The full lifecycle — store, retrieve, decay, GC — works without any LLM.
The Lifecycle
┌───────────┐ reflection ┌───────────┐
│ EPISODE │ ──────────────────▶│ JOURNAL │
│ (event) │ LLM synthesises │ (belief) │
└─────┬─────┘ observations & └─────┬─────┘
│ hypotheses │
│ │
│ store_fact() │ confidence decay
▼ │ (30d halflife)
┌───────────┐ recall bumps ┌─────▼─────┐
│ FACT │ ◀──────────────── │ RETIRE │
│ (truth) │ confidence +0.05 │ (floor) │
└─────┬─────┘ └─────┬─────┘
│ │
│ revise / supersede │ GC prunes
▼ ▼
┌───────────┐ ┌───────────┐
│ SUPERSEDED│ 90 days retention │ PRUNED │
│ (replaced)│ ──────────────────▶│ (deleted) │
└───────────┘ └───────────┘
UNIFIED SCORE = relevance × confidence × recency
Applied across ALL layers before every response.
NO-LLM MODE: store + retrieve + decay + GC work
without any model. Only reflection needs an LLM.
No-LLM Mode
The store, retrieval, decay, GC, and embedding versioning all work without any LLM. Only the reflection loop needs a model.
from memlife import MemoryStore, MemoryConfig
store = MemoryStore(config=MemoryConfig(db_path="./mem.db"))
store.remember(task="something happened", outcome="success")
context = await store.retrieve("something")
With an Embedder
from memlife import MemoryStore, MemoryConfig
from memlife.adapters.ollama import OllamaEmbedder
store = MemoryStore(
config=MemoryConfig(db_path="./mem.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")
Also available: OpenAIEmbedder (pip install memlife[openai]) and STEmbedder for local Sentence Transformers (pip install memlife[sentence-transformers]).
With Reflection
from memlife import MemoryStore, MemoryConfig, Reflector, DummyEmbedder, DummyChat
store = MemoryStore(
config=MemoryConfig(db_path="./mem.db", embedding_model="dummy"),
embedder=DummyEmbedder(),
)
reflector = Reflector(
memory=store,
model_chat=DummyChat(),
critic=False,
)
result = await reflector.reflect()
For real LLMs, use an adapter:
from memlife.adapters.ollama import OllamaChat
chat = OllamaChat(model="qwen3.5:cloud")
reflector = Reflector(memory=store, model_chat=chat)
Sync API
For non-async codebases:
from memlife import SyncMemoryStore, MemoryConfig, DummyEmbedder
store = SyncMemoryStore(
config=MemoryConfig(db_path="./mem.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 ./mem.db --embedder ollama --embedding-model mxbai-embed-large:latest
Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"memlife": {
"command": "memlife-mcp-server",
"args": ["--db", "/path/to/mem.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_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
- Unified scoring: relevance × confidence × recency across all layers
- Confidence ceiling (0.99): facts are never immutable
- Confidence decay: 30-day halflife, floored at 0.15 — journal entries fade
- GC with configurable retention: 90 days for superseded facts, 60 for runs, 30 for metrics
- 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
- 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 (30d halflife) | No | No | No |
| GC + pruning | Yes (configurable) | No | No | No |
| Reflection loop | Yes (LLM + critic) | No | No | No |
| Embedding versioning | Yes | 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 | No (single-agent) | Yes | No | Yes |
| Graph reasoning | No | No | No | 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 graph reasoning. If you want memory that degrades gracefully instead of accumulating forever, memlife is the one.
Status
v0.3.0-beta. The API may change before v1.0. Not recommended for production yet.
License
MIT
Project details
Release history Release notifications | RSS feed
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 memlife-0.3.0b0.tar.gz.
File metadata
- Download URL: memlife-0.3.0b0.tar.gz
- Upload date:
- Size: 55.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1f4c419729e51c20404ba7a063298e970dd6fde28d65079eb848304111797101
|
|
| MD5 |
b8c37a5af768aa80fbdc005c231fd9f2
|
|
| BLAKE2b-256 |
39c397b10264056fff22053baef0116bec866889d6b52c83bf116807719a3b74
|
File details
Details for the file memlife-0.3.0b0-py3-none-any.whl.
File metadata
- Download URL: memlife-0.3.0b0-py3-none-any.whl
- Upload date:
- Size: 53.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c56078ceeb77b9964570670216e7e06e95e826b58ae62509ee2315bf58bdb4e1
|
|
| MD5 |
1d8b463529cee14879ad4012712e6593
|
|
| BLAKE2b-256 |
00919cf806cbcb901efbcc4c7ab591bd1e80a6fa8c6e5fba63db3f910f5ec4d8
|