Let agents improve without silently regressing
mnesio is a Rust-native long-term memory layer for agents that turns real outcomes into improved, versioned policies: prompts, heuristics, and retrieval rules. Each candidate improvement is evaluated in shadow mode and can activate only after it clears a mechanically enforced safety gate.
Use mnesio when you need an agent system to:
- retain and retrieve durable, evolving knowledge;
- learn from outcomes without blindly rewriting its behavior;
- prove what it knew at a past moment, or erase data through crypto-shredding; and
- integrate through an HTTP service, MCP server, Python bindings, or Node SDK.
The core difference is procedural self-improvement: mnesio helps an agent get better at doing things over time, rather than only remembering more facts.
Two continuous loops operate over a single append-only event log:
- Procedural-memory compiler (the wedge) — turns batches of agent
Outcomes into improved, versionedPolicyArtifacts (system prompts, heuristics, retrieval rules) via a GEPA-style reflective loop: reflect → propose K candidates → shadow-evaluate → Pareto-select → gated commit. - Memory evolution (supporting) — when a memory is written, a bounded async worker retroactively re-tags and re-links related memories (A-MEM style), keeping the knowledge graph the compiler learns from adaptive.
Why the wedge matters
Hard Rule #1: Nothing procedural commits without passing
EvalReport::is_committable()— canaries 100%, safety probe passing, objective Δ ≥ 0. This is the regression guard LangMem omits. Mechanically enforced — setting every configurable gate threshold to its weakest value still cannot bypass the baseline. Held by a dedicated integration test on every commit.
🗺️ Map a codebase — one command, no server
curl -fsSL https://raw.githubusercontent.com/mnesio/mnesio/main/install.sh | sh
mnesio-code .
Needs a release built by the
binariesjob — v0.1.0 predates it and carries no binaries, so this line starts working at v0.1.1. Until then use the build from source below. The installer verifies a SHA-256, refuses to overwrite a different tool of the same name, and does not touch your shell profile.
git clone https://github.com/mnesio/mnesio.git && cd mnesio
cargo install --path crates/mnesio-code --features tree-sitter
mnesio-code .
--features tree-sitter compiles 28 grammars, so expect minutes on a cold
build. That cost is exactly why the prebuilt binaries exist.
Writes three files into the directory you pointed at:
No port, no running process, nothing fetched at view time — the page inlines its own data, so you can attach it to a review or mail it to someone.
Two things the map tells you that a prettier one would not.
What it could not read. A smaller map and a smaller codebase look identical unless the tool says which it is:
reading 18/54 files (33%) — skipped: .h (16), .cpp (12), .css (2), .frag (2)
Most of this repository is in a language this build cannot parse.
Which edges are guesses. Every edge is marked read — the callee is defined
in the caller's own file, so the name is right there — or inferred, meaning it
was bound to a unique same-named definition elsewhere with no type information
behind it. Inferred edges are drawn dashed. Measured with grammars, the share
that is read ranges from 99% on this repo to 73% on tare, so it is
reported per-graph rather than claimed once.
What it does not print is a token-saving multiple. Every number in the
artifacts describes the repository in front of it — symbols, resolved calls,
communities, and the share of call sites that bound to a definition, which is
how much of the call graph is missing. Retrieval quality is a benchmark
question with a benchmark answer, and it lives in
manifest/codeeval-v1-results.md
where you can re-run it.
The map colours symbols by whether retrieving them actually helped — but only once there are outcomes to colour with, which means running the MCP server below. On a fresh repository that section says it is empty instead of disappearing.
⚡ Quick start
Fastest — no Rust toolchain, one command (builds + runs in Docker, serves the
live dashboard on http://localhost:7777 with zero external downloads):
git clone https://github.com/mnesio/mnesio.git && cd mnesio
docker compose up --build
With Rust installed — one word:
git clone https://github.com/mnesio/mnesio.git && cd mnesio
make demo # instant demo: live dashboard, zero downloads (mock embedder)
# make run # real, persistent server (fastembed — downloads bge-small once)
# make test # the workspace test suite
# make mcp # install the MCP binary for Claude Desktop / Cursor / etc.
# make # list every target
Or the plain command the make demo target runs:
MNESIO_DEMO=1 MNESIO_PROCEDURAL=on cargo run -p mnesio-server
Then open:
- http://127.0.0.1:7777/ — live chat-style retrieval (hybrid vector + BM25 + extractive synthesis)
- http://127.0.0.1:7777/dashboard — real-time benchmarks: latency, BM25 tier distribution, memory evolution chains, procedural learning curve, an ingestion-intelligence panel (raw turns → ADD / UPDATE(contradiction) / NOOP, served by
/api/ingest/metrics), and a bi-temporal knowledge-graph panel (/api/graph), and a profile/persona panel (/api/profile)
In the demo, watch the PROCEDURAL section's learning curve climb from ~33% to 100% while the safety probe line stays glued at 100% — that's the Phase 2 "done when" criterion satisfied live.
Configuration
🏗️ Workspace architecture
Twelve crates, each with a focused responsibility. External dependencies sit behind traits (LlmClient, Embedder, EventLog, MaterializedView, Retriever, Synthesizer, PolicyExecutor, Judge) so providers are swappable.
📈 Eval-as-product (mnesio-bench)
The bench harness isn't a one-off demo — it's a CLI you can wire into your dev loop or your CI. Two subcommands, four output formats, exit codes that block PRs on regression.
Run mode — iterative improvement curve
# Iterate the procedural compiler against gsm8k-tiny and emit a
# self-contained HTML report you can attach to a PR.
cargo run -p mnesio-bench -- run \
--suite gsm8k \
--max-versions 6 \
--output html \
--out curve.html
The HTML is self-contained — inline SVG line chart of benchmark_score + safety_probe_pass_rate over versions, KPI strip, seed-vs-final prompt diff. No JS, no external assets, no Chart.js dep.
Compare mode — A vs B prompt evaluation
cargo run -p mnesio-bench -- compare \
--suite gsm8k \
--baseline "Answer the question." \
--candidate "Answer the question. Show your work step by step." \
--output markdown
Output (paste-into-PR-friendly):
| | benchmark | safety |
|---|---|---|
| baseline | 0.0% | 100.0% |
| candidate | 70.0% | 100.0% |
| **Δ** | **+70.0pp** | **+0.0pp** |
CI mode — block regressions
cargo run -p mnesio-bench -- run \
--suite gsm8k \
--max-versions 6 \
--regression-threshold 0.05 \
--output json --out bench.json
Exit code semantics:
0— benchmark held or improved within threshold; safety probe at 100% throughout.1— benchmark fell more than--regression-thresholdbelow v1.1(no threshold needed) — any safety probe regression. Alignment drift is the hard stop; you don't get to set a threshold for it.
Drop it in a GitHub Actions step:
- name: mnesio-bench gates
run: |
cargo run --release -p mnesio-bench -- run \
--suite gsm8k \
--regression-threshold 0.05 \
--output json --out bench.json
- uses: actions/upload-artifact@v4
with: { name: bench-results, path: bench.json }
A PR that regresses the bench fails the gate. The artifact is downloadable from the run page for inspection.
Suites
Two suites ship in-binary today — hand-curated, license-clean:
External suites land via a future --suite path/to/suite.json flag (JSON schema in crates/mnesio-bench/data/).
Memory recall — LOCOMO / LongMemEval
A third subcommand, memeval, benchmarks the memory layer itself (not the
procedural compiler): it ingests a haystack of memories through the real
FjallEventLog → VectorView + Bm25View → HybridRetriever path, then asks
questions and reports recall@k — does any top-k memory contain the gold
answer span? — overall and per category (single-hop / multi-hop / temporal /
knowledge-update / open-domain).
# Offline smoke (mock embedder, BM25-dominated):
cargo run -p mnesio-bench -- memeval --suite locomo --k 10
cargo run -p mnesio-bench -- memeval --suite longmemeval --k 10 --output json
# Real semantic number (downloads bge-small on first run):
cargo run -p mnesio-bench -- memeval --suite locomo --embedder fastembed
# CI floor — exit 1 if recall@k drops below the bar:
cargo run -p mnesio-bench -- memeval --suite locomo --min-recall 0.8
Two hand-curated, license-clean mini suites ship in-binary (locomo_mini,
longmemeval_mini). They're smoke-scale (≈12 memories) — under the mock
embedder recall is BM25-driven and HNSW tie-breaks make the borderline
question non-deterministic, so set CI floors with margin and quote published
numbers from --embedder fastembed against the full datasets.
🐍 Using mnesio from Python
pip install maturin
maturin develop --release --manifest-path crates/mnesio-py/Cargo.toml
maturin develop builds the Rust extension and drops a mnesio package into your active Python environment. Then:
import mnesio
client = mnesio.Client(data_dir="./mnesio-data", embedder="fastembed")
# Write a memory.
memory_id = client.write_memory(
content="My partner's coffee order is oat-milk flat white",
tenant="default",
tags=["coffee", "preference"],
)
# Hybrid retrieval with synthesized answer.
result = client.search(query="what coffee do I like?", k=5)
print(result.answer) # synthesized prose (or None)
for hit in result.hits: # ranked individual hits
print(hit.memory_id, hit.score, hit.content)
print(result.citations) # memory ids the synthesizer cited
# Record outcomes for the procedural compiler to learn from.
client.record_outcome(
artifacts_used=["01ABC..."], # ULID-string artifact ids
success=True,
scores={"accuracy": 0.95, "latency_ms": 1850.0},
)
Plugging into LangChain
mnesio works as a drop-in retriever inside any LangChain pipeline by wrapping client.search in a BaseRetriever:
from langchain_core.retrievers import BaseRetriever
from langchain_core.documents import Document
import mnesio
class MnesioRetriever(BaseRetriever):
client: mnesio.Client
tenant: str = "default"
k: int = 5
def _get_relevant_documents(self, query, *, run_manager):
result = self.client.search(query=query, tenant=self.tenant, k=self.k)
return [
Document(page_content=h.content, metadata={"memory_id": h.memory_id, "score": h.score})
for h in result.hits
]
retriever = MnesioRetriever(client=mnesio.Client("./mnesio-data"))
Async support
The current API is synchronous — each call blocks until complete. Agent-call latency is dominated by the LLM itself, so this is rarely the bottleneck. A future release will add a native AsyncClient using pyo3-asyncio.
🟦 Using mnesio from Node / TypeScript
sdk/node ships a tiny client over the HTTP surface — zero runtime
dependencies (it uses Node 18+ built-in fetch). Mirrors the DTOs from
mnesio-server 1:1 with full TypeScript types.
import { MnesioClient } from "@mnesio/sdk";
const mnesio = new MnesioClient({ baseUrl: "http://127.0.0.1:7777" });
// One round-trip: post-gate PolicyArtifacts + hybrid retrieval.
const { skills, hits } = await mnesio.retrieveWithSkills(
"what did our last call decide about pricing?",
5,
{ actor: "analyst" }, // optional — enforces inter-agent ACL
);
const system =
skills.map(s => s.injection).join("\n\n") +
"\n\nContext:\n" +
hits.map(h => `- ${h.content}`).join("\n");
Every returned skills[i] has cleared the mechanical safety gate
(canaries 100%, safety probe passing, objective Δ ≥ 0) — drop the
injection straight into your prompt.
The same client wraps cleanly into LangChain BaseRetriever,
LlamaIndex BaseRetriever, and CrewAI Tool. See sdk/node/README.md
for adapter sketches.
cd sdk/node
npm install
npm run build && npm test # 8 tests, no server required
🔌 Using mnesio from Claude Desktop (MCP)
The mnesio-mcp binary speaks the Model Context Protocol. Add it to your Claude Desktop config and three tools become available in any conversation. Other MCP agents (OpenClaw, Hermes, Cursor, …) connect the same way — see INTEGRATION.md for paste-ready configs + the write→search→record_outcome→gated-procedural loop, and examples/integrations/ for ready-to-edit files.
mnesio_write_memory(content, tenant?, tags?)— append a new memory. Embeds synchronously so it's searchable immediately.mnesio_search(query, tenant?, k?)— hybrid retrieval (vector + BM25) returning a synthesized answer plus excerpts and citations.mnesio_record_outcome(episode?, artifacts_used, success, scores?, error?)— record the outcome of an agent task. The procedural compiler consumes these to learn what prompt patterns lead to good outcomes.
Install
cargo install --path crates/mnesio-mcp
Configure Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"mnesio": {
"command": "mnesio-mcp",
"env": {
"MNESIO_DATA": "/Users/you/mnesio-data",
"MNESIO_EMBEDDER": "fastembed"
}
}
}
}
Restart Claude Desktop. The 🔌 icon in the input bar will show the three mnesio_* tools available.
Try it
> Remember: my partner's coffee order is oat-milk flat white, two shots.
[Claude calls mnesio_write_memory]
> What does my partner drink?
[Claude calls mnesio_search → finds + cites the memory]
Configuration
Transport
Newline-delimited JSON-RPC 2.0 over stdio. Three methods: initialize, tools/list, tools/call. Hand-rolled because the protocol is small enough that depending on an SDK adds more risk than it removes — crates/mnesio-mcp/src/protocol.rs is ~300 lines including doc comments and tests.
🔒 Hard rules (non-negotiable invariants)
These are enforced in code, not by convention. Each has a dedicated test that fails if the invariant breaks:
- Nothing procedural commits without passing
EvalReport::is_committable()— canaries + safety probe + non-negative objective delta. The configurableEvalGateslayer can only add rejection reasons on top of this baseline; it can never relax it. Test:loosening_configurable_gates_cannot_bypass_strict_baseline. - Never overwrite history — memory evolution invalidates the old version and writes a new bi-temporal version with a
parentpointer. Same for any fact update. The event log is append-only. - Scope is a security boundary — procedural learning + memory evolution never cross a
Scopewithout explicit aggregation. Every cross-entity read goes throughScope::contains. - The event log is the single system of record — every index (vector, BM25, graph, procedural) is a materialized view, fully reconstructible by replaying events. Tested end-to-end.
- The write path stays fast — embedding, evolution, and procedural compilation are async behind bounded queues. The write path target is < 5 ms; LLM calls never block it.
- Cascades are bounded —
EvolveConfigcaps cascade fan-out, per-memory cooldown, lifetime evolution count, and minimum structural delta. A-MEM has no convergence guarantee; these bounds replace it.
📊 Phase 2 "done when" — verified
Phase 2 is "done when" the system demonstrates a positive learning curve on an ALFWorld-style suite with no safety-probe regression.
Live demo output (MNESIO_PROCEDURAL=on):
v1: benchmark=33.33% safety=100%
v2: benchmark=66.67% safety=100%
v3: benchmark=100.00% safety=100%
v4+: benchmark=100.00% safety=100% (plateau — both improvement signals integrated)
The dashboard renders this as a dual-line chart with a safety 100% pill that flips red on any regression.
📊 Scale & real-data benchmarks
All numbers below are measured, not projected — produced by mnesio-bench
on a 2021 M1-class laptop (8 cores, 16 GB), release build. Reproduce with the
commands shown.
Real public benchmarks — SQuAD + HotpotQA (recall@10)
mnesio-bench fetch downloads a real dataset from the Hugging Face
datasets-server and runs it through the actual ingest → hybrid-retrieve path.
SQuAD (single-hop reading comprehension): each context → a memory
(deduplicated), each question/answer-span → a recall pair. HotpotQA
(multi-hop): each of a row's context paragraphs → a memory, the answer span
must be found across them (yes/no comparison answers are skipped — not
retrievable spans).
cargo run -p mnesio-bench --features fetch --release -- \
fetch --dataset squad --rows 2000 --k 10 --embedder fastembed
cargo run -p mnesio-bench --features fetch --release -- \
fetch --dataset hotpotqa --rows 1000 --k 10 --embedder fastembed
Real semantic embeddings lift recall over keyword-only on the same real questions — +4.2 pts on single-hop SQuAD, +5.3 pts on the harder multi-hop HotpotQA — the hybrid path earning its keep on non-synthetic data. The HotpotQA run is also a real-corpus scale check: 9k+ memories, 941 multi-hop questions, sub-18 ms/query.
Scale & load — synthetic corpus up to 105k memories
mnesio-bench scale ingests a deterministic synthetic corpus (labeled needles
salted among distractors, plus evolution chains + contradictions) through the
real storage→views→retriever path, and separates the two write phases so
the numbers reflect mnesio's architecture: the append path is the user-facing
write (Hard Rule #5, <5ms), while index build (HNSW + BM25) is what the
server does asynchronously off the write path. The index phase uses the bulk
replay-rebuild path (stage all docs, one BM25 commit), so its throughput is
HNSW-bound rather than dominated by per-document segment flushes.
cargo run -p mnesio-bench --release -- scale --sizes 1000,10000,50000,100000 --embedder mock
Read of the curve: append latency is flat (~0.0017 ms p50) across a 100×
size increase — the write path genuinely doesn't degrade with corpus size.
Index build is HNSW-bound and degrades gracefully (per-insert p50 0.13 ms →
0.75 ms as the graph deepens). Query latency grows sub-linearly (HNSW): p50
0.88 ms → 3.60 ms from 1k to 105k. Recall stays 100% on the exact-gold
needle set through 105k memories, confirming retrieval correctness holds at
scale. (The synthetic generator is deterministic — same --seed reproduces
the identical corpus.)
With a real semantic embedder (--embedder fastembed, 384-d) at 5,251
memories: append still 182,519/s, p50 0.00 ms (embedding is computed in a
separate pre-phase, off the write path — Hard Rule #5), index 1,345/s, query
p50 9.84 ms (per-query embedding dominates), recall@10 99.2%. The write path
stays fast whether the embedder is mock or a real model.
These recall floors are enforced in CI — the bench-gate job fails the build
if LOCOMO/LongMemEval mini-suite recall or synthetic-scale recall drops below
its floor (eval-as-product, the moat made into a regression gate).
Edge-case / adversarial stress
Throughput stress is only half of "ready". mnesio-bench edge drives the real
ingest→retrieve→replay path with hostile inputs and asserts the seven hard-rule
invariants hold — exiting non-zero (and gating CI) on any violation:
cargo run -p mnesio-bench -- edge
This suite found and fixed a real bug: an all-stopword query ("the of a")
or one with bare boolean operators ("a AND OR NOT b") used to surface a hard
tantivy parse error — i.e. a 500 on adversarial search input. The BM25 query
path now treats unparseable free-text as "no results for this tier" (graceful
empty), while still honoring valid explicit-operator queries like
revenue OR growth.
⚖️ How mnesio compares
📊 BENCHMARKS.md consolidates all the measured numbers in one place — substrate at 105k memories, real-data recall, live LLM-judged QA, and the GPU KV-cartridge speedups — with methodology + caveats.
cargo run -p mnesio-bench -- compete --k 10 --embedder fastembed
Two different metrics, kept separate. The capability matrix below is a structural comparison. The benchmark numbers further down mix cited competitor end-to-end QA accuracy with mnesio's measured retrieval recall@k — a retrieval-quality proxy, not the same metric. recall@k asks "was the gold answer in the retrieved set?"; QA accuracy asks "did the model produce the right answer?". We never present one as if it beat the other.
Capability matrix
✅ shipped · ◑ partial · — not in published design. Competitor cells reflect each system's published architecture and may evolve. mnesio is the only column with every row — the frontier features require the append-only + replayable + bi-temporal substrate behind a non-bypassable gate, which a storage-shaped system can't add without rebuilding its foundation.
Benchmark landscape (cited end-to-end QA — different metric from recall@k)
These are competitor/baseline numbers from the cited papers — not mnesio's. mnesio's measured numbers are retrieval recall@k: 98.1% on real SQuAD (fastembed, §Scale & real-data above) and 100% on the curated LOCOMO/ LongMemEval mini-suites. mnesio's differentiation is the capability matrix, not a single leaderboard cell.
mnesio — end-to-end LLM-judged QA accuracy (real, not recall)
mnesio also ships the same metric the papers above report — end-to-end
QA accuracy via mnesio-bench qaeval (retrieve → an LLM answers from the
retrieved context → an LLM judges the answer vs the gold reference):
cargo run -p mnesio-bench --features ollama --release -- \
qaeval --suite locomo --k 10 --embedder fastembed --llm ollama
Measured live against a local Ollama model — a real LLM in the loop for both
the answer and the judgement, not the offline stand-in. These are the curated
mini-suites (10 questions each), so 100% reflects a small set; the point is
that the harness produces a real QA-J number through the same ingest →
hybrid-retrieve path. Run the full LOCOMO/LongMemEval splits through qaeval
(any --llm ollama model) for a publishable headline number.
🗺️ Roadmap
- Phase 0 ✅ Foundation — event log, hybrid retrieval, dashboard
- Phase 1 ✅ Memory evolution — bounded A-MEM-style worker
- Phase 2 ✅ Procedural compiler — the wedge, with mechanically-enforced commit gate, ALFWorld-style bench harness
- Phase 3 ✅ Filtered HNSW — adaptive over-fetch on selective scopes, per-tenant partitioning (
TenantPartitionedVectorView), soft-delete observability (tombstone_ratio,live_count) - Phase 4 ✅ Bi-temporal property graph store on fjall — typed
Relationedges (Linked/EvolvedFrom/EvolvedTo/ContainedIn),as_oftime-travel, scope-filtered BFS + shortest-path, replay-rebuildable - Phase 5 ✅ Distribution — MCP server + Python (
pyo3) bindings, both reachable from any agent framework - Phase 6 ✅ Eval harness as a first-class product (the real moat) —
mnesio-benchrun/compare CLI, self-contained HTML reports, CI regression gates with exit-code semantics
Competitive layer (parity-plus-wedge)
- Phase 7 ✅ Ingestion intelligence — extract atomic facts → consolidate ADD / UPDATE(contradiction|refinement) / NOOP, importance admission + decay (
mnesio-extract) - Phase 8 ✅ Retrieval + personalization + privacy — graph/recency fusion + reranker, profile memory, multi-agent ACLs, PII redaction + crypto-shred forget (
mnesio-privacy) - Phase 9 ✅ Skill reuse + distribution — committed-artifact injection at query time, Node/TS SDK (
sdk/node)
Frontier layer (the bets no one else can ship)
- Phase 10 ✅ Causal memory — counterfactual contribution scoring + GC by measurement (
mnesio-causal) - Phase 11 ✅ Self-falsifying memory — acceptance probes + belief calibration; a refuted claim auto-supersedes (
mnesio-probe) - Phase 12 ✅ Gated KV cartridges — KV cache as a versioned, gated, erasable view of the log. Substrate + a real-tensor attention backend (
TensorKvBackend) + a real pretrained-weights backend (PretrainedKvBackend, featurepretrained-kv: loads GPT-2's real embeddings + layer-0c_attnQ/K/V) + a full 12-layer generative backend (GenerativeKvBackend, featuregenerative-kv) all done. In the generative backend the cartridge is GPT-2's key/value cache:compile_blobprefills the full forward over the context,answerrestores that cache and generates the continuation attending over it. Proven by a self-consistency oracle — generation from the cartridge is token-identical to processing the full prompt from scratch (KV caching is exact) — so the cartridge is a faithful, cheaper substitute, and a post-shred recompile can no longer generate the erased fact. Quantization is real, too: the cartridge blob is compact binary in both precisions, andQuant::Q8(per-row int8 + f32 scales) makes the cartridge 4.0× smaller (1,179,708 → 296,508 bytes on the live GPT-2 cache) while generating the same answer — closing thequantdimension ofCartridgeKey, which was a bare label before. And the cartridge path now runs on a modern 2024 model (qwen-kv: Qwen2.5-0.5B-Instruct — RMSNorm + RoPE + grouped-query attention + SwiGLU + bf16, hand-rolled in pure Rust so the cartridge owns the KV cache — the answer to "why GPT-2, not a more advanced model?"; an Ollama-style black-box text API can't back a cartridge because it never exposes the KV tensors). Live: the Qwen cartridge answers "capital of France" → "Paris", token-identical to the full prompt, and a shred-recompile drops the fact. And that same Qwen2 forward now runs on a real GPU backend (QwenCandleBackend, featurescandle-kv,metal) via candle on Apple Metal — identical code onDevice::CpuvsDevice::new_metal, so the speedup is like-for-like: 107× faster warm prefill (CPU 768.8 ms → Metal 7.2 ms on an M1 Pro; the first run pays a one-time ~100 ms Metal shader compile), the cartridge answers "Paris" token-identical to its own full-prompt path, and erasure still holds. The GPU backend is config-driven (architecture from the repo'sconfig.json, so Qwen2.5 0.5B / 1.5B / 3B / 7B load with no code change) and precision-selectable —F32,F16, orBF16. Half precision is real and the deep-model story is honest: f16's narrow exponent (max ≈ 65504) overflows on the 1.5B/28-layer model (garbage), so deep models use bf16 — half the memory of f32 with f32's exponent range (the model's native dtype) — and the 1.5B answers "Paris" correctly at bf16, verified live. The forward additionally accumulates the residual stream / RMSNorm / softmax / logits in f32 (mixed precision) for robustness, while weights + KV cache stay in the chosen half dtype. It surfaces live in the dashboard atGET /api/kv/metricsunder--features candle-kv+MNESIO_KV_GPU=1, with the model and precision selectable at runtime —MNESIO_KV_GPU_MODEL(any Qwen2 repo),MNESIO_KV_GPU_PRECISION(f32/f16/bf16),MNESIO_KV_GPU_CPU=0to skip the CPU baseline for large models. Verified live: the endpoint serves Qwen2.5-1.5B (28 layers) at bf16 on Metal, answering "Paris", with erasure-by-recompile holding (answerable_before → after=true → false). And the larger model amplifies the GPU win — measured 1.5B prefill: Metal bf16 3.34 ms vs CPU f32 5.27 s = ~1577× (this stacks GPU-vs-CPU and bf16-vs-f32, since candle's CPU backend has no bf16 matmul kernel so f32 is the honest CPU baseline; the clean same-precision figure is the 0.5B 107× above). With real GPT-2 + Qwen backends across CPU and GPU, multiple sizes, and two precisions, the open-weights tensor-backend lift Phase 12 was waiting on is delivered (mnesio-kv). The done-when is now closed end-to-end. A suite-level accuracy-parity eval (cargo run -p mnesio-bench -- kveval) shows the cartridge answers at least as accurately as per-query text-context retrieval — LOCOMO-mini 90% vs 80%, LongMemEval-mini 60% vs 60% — while answering ~167–180× faster (it compiles once and replays; the text-context baseline recompiles per query), with erasure-by-recompile holding, and it gates CI. Production polish landed alongside: theCartridgeStoretakes an LRU byte budget + bounded audit history for many-cartridge scale; the real backends raise actionable,HF_HUB_OFFLINE-aware weights errors; CI compile-checks every KV feature gate so they can't rot; and the candle backend now accepts Llama-family configs (optional QKV bias + arrayeos_token_id) — Qwen2 is the live-verified path, Llama is compile-/config-verified (untied-lm_head+ RoPE-scaling are documented TODOs) - Phase 13 ✅ Certified skill exchange — signed certificate; importer re-runs its own gate before activation (
mnesio-exchange) - Phase 14 ✅ Negative memory + dreaming — gated suppression rules + bounded offline prune-by-contribution & re-anchor (
mnesio-dream) - Phase 15 ✅ Regulator-grade provenance — time-travel reconstruction + provenance chains + verifiable erasure (
mnesio-provenance)
The frontier layer (10–15) is what a storage-shaped competitor (Mem0, Zep, Letta, Cognee, A-MEM) can't follow without rebuilding its foundation — each bet exploits the append-only + replayable + bi-temporal log behind the non-bypassable safety gate. See COMPETITIVE.md → "P3 — frontier bets".
🧪 Test counts
mnesio-core : 3 tests
mnesio-llm : 11 tests
mnesio-index : 83 tests
mnesio-evolve : 27 tests
mnesio-procedural : 112 tests
mnesio-causal : 18 tests
mnesio-probe : 14 tests
mnesio-kv : 15 tests (+1 `#[ignore]` under --features pretrained-kv; +1 q8 codec + 3 `#[ignore]` under --features generative-kv: GPT-2 12-layer forward + q8; +1 `#[ignore]` under --features qwen-kv: Qwen2.5-0.5B 24-layer forward; +1 Metal smoke + 3 `#[ignore]` under --features candle-kv,metal: GPU Qwen2 forward, f16, larger 1.5B model)
mnesio-exchange : 11 tests (+4 under --features ed25519: real signatures)
mnesio-dream : 10 tests
mnesio-provenance : 8 tests
mnesio-bench : 27 tests (+7 under --features fetch: SQuAD + HotpotQA loaders)
mnesio-mcp : 33 tests (unit + integration)
mnesio-py : 7 tests (Rust-side inner-client coverage)
mnesio-server : 27 tests
mnesio-store : 1 test
mnesio-graph : 27 tests
mnesio-extract : 33 tests
mnesio-privacy : 22 tests (+4 under --features aead: real ChaCha20-Poly1305)
sdk/node (TS) : 8 tests (offline, stub fetch)
──────────────────────────────
TOTAL : 489 Rust tests (486 on --no-default-features) + 8 SDK tests · all passing
(+7 with --features fetch on mnesio-bench, +4 aead, +4 ed25519)
🤝 Contributing
Contributions welcome. A few specific patterns the project enforces:
- The gate is sacred. Any change to
mnesio-procedural::gaterequires a corresponding test demonstrating that the property still holds. Loosening default thresholds requires a code review comment explaining the trade-off. - External dependencies behind traits. New backends (LLMs, embedders, judges, executors) go behind the existing trait surface; concrete implementations live in their own crate.
cargo fmt+cargo clippy -- -D warningsmust pass on both--no-default-featuresand the default config before any commit.- Tests live next to code in
#[cfg(test)] mod tests. Storage tests use a temp dir keyed by a fresh ULID and clean up after themselves. - Conventional commits —
feat:,fix:,refactor:,test:,docs:.
💜 Support mnesio
mnesio is independent, Apache-2.0, and built in the open. Funding goes straight into development time, eval compute (LOCOMO / LongMemEval runs aren't free), and keeping the project independent. If it's useful to you — or you want the frontier roadmap (causal memory, gated KV cartridges, certified skill exchange) to ship faster — consider sponsoring.
The Sponsor button at the top of the repo is wired through .github/FUNDING.yml.
📚 Background
Two design documents back this project:
- A comparative survey of agent memory systems (Mem0, Zep, Letta, A-MEM, etc.) and where each falls short.
- The Rust-native self-improving memory architecture + phased build plan.
Section numbers in code comments (e.g. "report §3") refer to document 2.
References embedded in the code:
- A-MEM: Lyu et al., Agentic Memory for LLM Agents, arXiv:2502.12110 (memory evolution model)
- GEPA: Du et al., General Evolutionary Prompt Adaptation, arXiv:2507.19457 (reflective-loop pattern)
- ACORN: Wu et al., ACORN: Performant Hybrid Search (filtered HNSW — informs the Phase 3 adaptive over-fetch + partitioning approach)
⚠️ Stability
This is 0.1.0 — the first usable release. The system is end-to-end working with 380 passing tests across all six build phases, but the public API surface will still move as the graph store and procedural compiler gain real-world mileage. Pin a specific version in your Cargo.toml; expect breaking changes between 0.x.y bumps.
📜 License
Apache License 2.0. See LICENSE.