Content
# memlife
Memory that degrades gracefully. Not another pile that grows forever.
[](https://pypi.org/project/memlife/)
[](https://pypi.org/project/memlife/)
[](https://github.com/EzyRider/memlife/blob/main/LICENSE)
**Current version: 0.5.4**
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 memlife
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
```bash
pip install memlife
```
With adapters (optional):
```bash
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)
```python
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.
```python
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()
```
This makes memlife useful for testing, offline agents, and lightweight deployments where you don't want to manage model access.
## Core Concepts
### Episodes
Episodes are raw events. They're cheap to store and useful for recall, but the system doesn't treat them as durable truth. Over time, reflection can promote observations from episodes into facts or journal entries.
```python
store.remember(
task="User asked about deployment",
outcome="success",
confidence=0.9,
)
```
### Facts
Facts are durable truths with explicit confidence. They decay on disuse, get bumped on recall, and can be revised or superseded.
```python
await store.store_fact("User deploys via GitHub Actions", confidence=0.8)
```
### Entity Graph (Triples)
Structured relationships between entities. Triples behave like facts but participate in graph traversal.
```python
store.store_triple("User", "deploys_via", "GitHub Actions", confidence=0.8)
```
### Journal
The journal holds reflected beliefs — higher-level observations the system synthesises from episodes and facts during reflection.
```python
await store.reflect() # or schedule it
```
### Decay, Retirement, and GC
Confidence decays over time. Entries that fall below a floor retire. Superseded entries age out. Garbage collection prunes them according to retention policy. Everything is configurable.
## Retrieval
`retrieve()` scores across all four tiers using a unified function:
```
score = relevance × confidence × recency
```
This means a highly relevant but low-confidence memory won't drown out a moderately relevant, high-confidence, recent one. The result is a blended context string ready to feed into a prompt.
```python
context = await store.retrieve("deployment")
```
## Configuration
```python
from memlife import MemoryConfig
config = MemoryConfig(
db_path="./memlife.db",
embedding_model="dummy", # or "nomic-embed-text", etc.
vector_backend="json", # json | binary | sqlite_vec
reflection_interval=3600, # seconds
decay_half_life_days=30,
confidence_floor=0.3,
namespace="default",
)
```
See `MemoryConfig` for the full set of options.
## MCP Server
memlife ships with an MCP server so any MCP-compatible client can use it:
```bash
memlife-mcp-server --db-path ./memlife.db --vector-backend binary
```
Tools exposed include `memory_store`, `memory_search`, `memory_retrieve`, `memory_reflect`, `memory_contradictions`, `memory_revise`, `memory_expire`, `memory_recall`, and more.
## What's new in 0.5.4
- **MCP reflection timeout controls.** `memlife-mcp-server` now exposes
`--reflection-timeout` and `--reflection-total-timeout` so reflection stays
within the MCP client's window on slower LLM endpoints.
- **README tool list refreshed.** The MCP server tool list now matches the
actually implemented tools, including `memory_recall`, `memory_revise`, and
`memory_expire`.
- **Memorias and polyphonic feature flags.** `--memorias-extraction` and
`--polyphonic-recall` can now be enabled from the MCP server CLI.
- **Episode search by tool name.** `memory_search_episodes` now accepts a
`tool_name` filter, making it easier to audit how a specific tool was used.
- **Vector backend default cleanup.** `--vector-backend` now defaults to `json`
explicitly, with clearer precedence when legacy flags are also supplied.
## What's new in 0.5.3
- **Config validation regression fixed.** `MemoryConfig.validate()` now runs all
invariant checks even when `vector_backend` is left at its default auto value.
Previously the default of `None` caused validation to return early, skipping
decay and threshold checks.
- **Binary backend no longer loses JSON-stored vectors.** `BinaryVectorBackend`
deserialisation now falls back to JSON format for rows stored before the
backend was switched, so changing from `json` to `binary` on an existing store
does not make old vectors invisible to search.
## What's new in 0.5.2
- **Binary vector backend is now used for retrieval.** Previously the backend
was selectable but recall still used inline cosine. Fact, episode, and journal
vector recall now route through the configured backend, so `binary` uses
Hamming distance search and `sqlite_vec` uses sqlite-vec KNN.
- **Vector backend precedence is centralised.** `MemoryConfig.resolved_vector_backend()`
gives a clear order: explicit `vector_backend` > `use_binary_vectors` >
`use_sqlite_vec` > `json` default. Both legacy flags at once now resolve to
`binary` with a warning, instead of silently depending on if/else order.
- **MCP server shutdown is clean.** The shutdown path now closes the store,
embedder, chat adapter, and reflector, and SIGTERM exits the process after
cleanup instead of hanging.
- **Ollama adapters no longer create sessions before a loop exists.** Sessions
are created lazily on first async call, avoiding `RuntimeError` when the
embedder/chat is instantiated outside an event loop.
## What's new in 0.5.1
- `memlife-mcp-server` now accepts `--vector-backend {json,sqlite_vec,binary}`.
- **Pluggable vector backends.** Swap between JSON, binary, and `sqlite-vec`
backends with a single config change. The new binary backend stores embeddings
as bit-packed vectors for ~32x smaller storage, while sqlite-vec provides
fast approximate nearest neighbours when the extension is available.
- **Public `Metrics` snapshot.** `store.metrics()` returns a structured
snapshot of counts, embedding coverage, reflection aggregates, recall
counters, DB metadata, and schema migration health. Exposed via the
`memlife://stats` MCP resource.
- **Schema migration status.** `store.migration_status()` checks whether the
database has all expected tables and columns, flags missing items, and
reports SQLite version and page stats. Surfaces in `Metrics` so operators
can detect a stale schema before it breaks.
- **pysqlite3 fallback for sqlite-vec.** On interpreters whose stdlib `sqlite3`
is compiled without extension loading, memlife transparently falls back to
`pysqlite3` so sqlite-vec can still load.
- **Reflection audit trail.** Reflection passes are recorded with proposed,
kept, and dropped items, plus timing and model metadata. Paginated audit
queries help debug what the reflection loop is actually doing.
- **Namespace default and validation hardening.** The default namespace is now
`_default`, names are validated/normalized before any DB is opened, and the
config fails fast on unknown vector backends or unsafe paths.
## License
MIT
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available