Skip to main content

inspeximus — long-term memory for AI agents that stays correct

Python agent memory in one zero-dependency file, plus an MCP server for Claude Code and Cursor. When a fact is corrected, inspeximus serves the new value and stops the old one from resurfacing — deterministically, with no LLM in the loop.

PyPI Downloads CI Claims audit Python Zero dependencies Tests License DOI

pip install inspeximus
After you correct a fact, how often does the old value come back? inspeximus 0%, Graphiti 0.x 13.3%, mem0 2.0.11 46.7%, and inspeximus with its guard disabled 100% — n=30 per system, each on its own native configuration.

The 30 seconds that matter

Every memory library can store and retrieve. The question nobody answers is what happens when a stored fact turns out to be wrong.

from inspeximus import Inspeximus

m = Inspeximus("memory.json")

m.remember("The staging database is db-3.internal", key="staging-db")
m.remember("The staging database is db-7.internal", key="staging-db")   # a correction

m.recall("which staging database")[0]["text"]
# 'The staging database is db-7.internal'          <- the correction wins, every time

m.revert("staging-db")                              # and it is reversible
m.recall("which staging database")[0]["text"]
# 'The staging database is db-3.internal'

No embedding drift, no "the LLM usually picks the newer one". The old value is retired by key, and the retirement is a record you can audit, revert, and prove.


Why another memory library

Because we measured the one thing the others do not publish: how often a corrected fact comes back.

Each system was run on its own native configuration, same task, same 30 trials:

system keeps the correction resurrects the old value
inspeximus 100% 0%
Graphiti 0.x (Neo4j + OpenAI) 86.7% 13.3%  95% CI [3.3, 26.7]
mem0 2.0.11 (OpenAI native) 53.3% 46.7%  95% CI [30.0, 63.3]
inspeximus, guard disabled 0% the control: this is what the guard is doing

n = 30 per system. mem0 measured at 2.0.11 (2026-07); mem0 is now on 2.0.18 and we have not re-run it — the version is stamped rather than the claim being restated as current. Full method, raw arrays and the re-runnable harness: RAMR · echo_resistance_backends_result.json

Read the Graphiti row correctly — its echo defense did not fail. Our own raw output records echo_attributable_flips: 0 out of 26 corrections that were extracted correctly before the echo ran. Graphiti's bi-temporal invalidation held every one of them. The 13.3% above is four pre-echo extraction misses — the correction never made it into the graph — which is a different failure from the one this table is about. Stated as the mechanism rather than the headline: on echo-attributable resurrection, Graphiti scores 0%, the same as us, by keeping the supersession link at write time. That is the real finding here: what separates these systems is whether the link is recorded, not who recorded it.

The bottom row is the point. Turn our guard off and we score zero — so the number is the mechanism, not the benchmark being kind to us.


Use it in Claude Code (one line)

inspeximus install --ide claude     # also: cursor, windsurf, codex, cline

That wires an MCP server with 68 tools and three hooks. From the next session on, your agent starts knowing what the last one decided — no CLAUDE.md editing, no re-explaining:

  • SessionStart injects the decisions still in force
  • PostToolUse captures what actually happened, keyed by file
  • PreToolUse surfaces the decision that bears on the action before it runs

What you get

Correction as a first-class operation. remember(key=...) retires the previous value for that key. revert(key) restores it. history(key) shows the chain. All deterministic, all auditable.

Erasure that can be proven. forget_subject() hard-deletes every memory attributable to a subject — including summaries that inherited it through lineage — and leaves a signed, content-free tombstone, so a later audit can tell deliberately erased from tampered with.

Provenance you can check, not just store. check_sources() re-reads each record's origin and returns FRESH / DRIFTED / ORPHANED / UNCHECKABLE, plus four coverage numbers that are deliberately kept apart — because a source field that is 98.3% populated and 0.01% re-fetchable is a schema, not a guarantee. (Those two numbers are ours, measured on our own production store.)

Current-state applicability. evaluate_applicability() answers a different question from "is this memory true": may it drive an action here, now? Historical evidence can be perfectly valid and no longer authorized — the branch moved, the policy changed, the tenant differs, the window expired. Implements the vendor-neutral CML contract; two independent implementations agree on its frozen fixture.

Multi-tenant isolation. for_tenant("acme") gives a scoped view over one shared store, with the tenant bound into the signed message so a record cannot be moved between tenants and still verify.

Zero dependencies. One file. Semantic recall is optional (embed=your_model); the lexical fallback needs nothing. The MCP server, encryption and framework adapters are all opt-in extras.


Works with

langchain · langgraph-store · llamaindex · haystack · autogen · pydantic-ai · google-adk · memoryagentbench

10 of 13 verified against current upstream, 3 recorded brokencrewai, langgraph-checkpointer and openai-agents, named rather than quietly dropped from the list. The counts are read from docs/integration_conformance.json by the claims audit, so this line cannot drift from what the runner last measured.

A "works with" list that only names successes is a logo wall. This one tells you which adapter will break before you build on it.


How this is tested

2,600+ tests, and a mutation gate that is the reason to believe them: 175 seeded defects, 175 killed, 0 survived. A test suite that passes is not evidence; a suite that catches every deliberate break is.

Every number on this page is registered in docs/CLAIMS.md, with the exact command that recomputes it. If one disagrees with your run, that is a bug report we want.


Documentation

Project site → the guided tour: the benchmark, the MCP surface, the governance story
Measured vs mem0 & Graphiti the resurrection table in full, with the control and the honest scope
Claude Code setup the one-line MCP install, and what each of the three hooks does
The long version every mechanism, every measurement, and the ones that failed
Full API every method, with the failure it exists to prevent
Erasure & GDPR right-to-erasure across derived summaries, with receipts
EU AI Act evidence Article 12 logging, mapped to what the store already keeps
MCP tools all 68, and what each is for
Claims ledger every published number, and the command that recomputes it
Changelog what changed and why, including what we got wrong

Who this is for

You are building an agent that runs for weeks, not minutes. It will learn something, and then that thing will change — a config value, a policy, a person's preference, a fact. The failure that will cost you is not the agent forgetting. It is the agent confidently remembering the old answer.

That is the failure this library is built around, and the only one we benchmark ourselves on.


Citing

Archived on Zenodo with a version-independent DOI — 10.5281/zenodo.21708778. Machine-readable metadata is in CITATION.cff, so GitHub's "Cite this repository" button gives you BibTeX and APA directly.


MIT licensed. Built by Agora, an autonomous research organisation that publishes its failed replications next to its successful ones.

mcp-name: io.github.DanceNitra/inspeximus

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

inspeximus-2.8.1.tar.gz (973.7 kB view details)

Uploaded Source

Built Distribution

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

inspeximus-2.8.1-py3-none-any.whl (444.8 kB view details)

Uploaded Python 3

File details

Details for the file inspeximus-2.8.1.tar.gz.

File metadata

  • Download URL: inspeximus-2.8.1.tar.gz
  • Upload date:
  • Size: 973.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for inspeximus-2.8.1.tar.gz
Algorithm Hash digest
SHA256 1ac0e84679cddcf785ba6ec66efb21937ecafe494b52c3c46159c28bdb9f2b0d
MD5 4aa6e6aceab512f9ac5438483d9d121a
BLAKE2b-256 843f8b48c3b68976eb536982a2a0fe33f59c79b4467235b0aa4ab611df0cf5e7

See more details on using hashes here.

Provenance

The following attestation bundles were made for inspeximus-2.8.1.tar.gz:

Publisher: release.yml on DanceNitra/inspeximus

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file inspeximus-2.8.1-py3-none-any.whl.

File metadata

  • Download URL: inspeximus-2.8.1-py3-none-any.whl
  • Upload date:
  • Size: 444.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for inspeximus-2.8.1-py3-none-any.whl
Algorithm Hash digest
SHA256 37c4dee75ab14ad0878c7cb030a60a85afb47ff9622da083300c4e215a216c30
MD5 e8c97b3d543816110755cfd1c2a39979
BLAKE2b-256 4f72152c7a3f7d6b809b937e982ebb1a833b66ae7e6f3f199f43ba71201ffd7b

See more details on using hashes here.

Provenance

The following attestation bundles were made for inspeximus-2.8.1-py3-none-any.whl:

Publisher: release.yml on DanceNitra/inspeximus

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

Supported by

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