Astrix-Labs

Genesys

Community Astrix-Labs
Updated

Open-source causal graph memory for AI agents. 89.9% on LoCoMo. MCP server with ACT-R scoring, spreading activation, and active forgetting.

PyPIPyPI DownloadsCILicense: AGPL v3

Genesys

The intelligence layer for AI memory.

Genesys doesn't just remember what happened; it remembers why. A scoring engine + causal graph + lifecycle manager for AI agent memory. Speaks MCP natively.

What is this

Genesys is a scoring engine, causal graph, and lifecycle manager for AI memory. Memories are scored by a multiplicative formula (relevance × connectivity × reactivation), connected in a causal graph, and actively forgotten when they become irrelevant.

This package (genesys-memory) is the core library: an in-memory causal graph engine with optional JSON persistence, plus a stdio MCP server. It has no database dependency and no REST API. A hosted product built on top of this library — with Postgres, additional storage backends, and a REST/HTTP MCP API — is available separately at genesys-api.astrixlabs.ai; it is not part of this package.

Why

  • Flat memory doesn't scale. Dumping everything into a vector store gives you recall with zero understanding. The 500th memory buries the 5 that matter.
  • No forgetting = no intelligence. Real memory systems forget. Without active pruning, your AI drowns in stale context.
  • No causal reasoning. Vector similarity can't answer "why did I choose X?" — you need a graph.

Your AI remembers everything but understands nothing. Genesys fixes that.

Quick Start

Install the package. The base install has zero database dependencies — state lives in memory and is optionally persisted to a JSON file.

pip install genesys-memory

Optional extras:

pip install 'genesys-memory[openai]'      # OpenAI embeddings
pip install 'genesys-memory[local]'       # Local embeddings (sentence-transformers, no API key)
pip install 'genesys-memory[anthropic]'   # LLM-based causal inference (consolidation, contradiction detection)

Run the stdio MCP server directly:

python3 -m genesys_memory

From source

git clone https://github.com/Astrix-Labs/genesys.git
cd genesys
pip install -e '.[dev]'
pytest tests/

Connect to your AI

Claude Code

claude mcp add genesys -- python -m genesys_memory

Claude Desktop

Add to your claude_desktop_config.json:

{
  "mcpServers": {
    "genesys": {
      "command": "python",
      "args": ["-m", "genesys_memory"]
    }
  }
}

Reliability & retries

The stdio server is a single local process. Under load — or during a restart orredeploy of a hosted transport in front of it — a tool call can transiently failor the connection can briefly go unresponsive. Memory writes and reads are notworth crashing an agent turn over, so clients should degrade gracefully ratherthan treat a memory call as fatal:

  • The server degrades gracefully too: a tool exception (or a missingrequired argument) is returned as a structured{"error": "...", "retryable": bool} payload instead of a protocol-levelMCP failure, so a memory hiccup never crashes the transport. Theretryable flag encodes the guidance below — true only for read tools.
  • Retry idempotent reads (memory_recall, memory_search, memory_traverse,memory_explain, memory_stats) with a short bounded backoff (e.g. 2–3attempts). These have no side effects worth worrying about beyond reactivationbookkeeping.
  • Do not blindly retry memory_store / memory_amend on an ambiguous timeout— a silent success followed by a retry creates a duplicate node. Prefer tocontinue the turn and reconcile on the next memory_recall, or pass a stablesource_session so duplicates are easy to spot.
  • Treat memory as best-effort context, not a hard dependency. If a call fails,proceed with whatever context you already have and try again next turn ratherthan aborting. The graph is durable; a missed write is recoverable, a crashedagent turn is not.

MCP Tools

Tool Description
memory_store Store a new memory. Use related for writer-specified typed edges ({id, type}); related_to is legacy and always creates caused_by. Optional category. May return possible_conflicts (heuristic hints).
memory_amend Record a correction: creates a new memory that supersedes an existing one. The old memory is kept (decayed in recall), not deleted.
memory_recall Recall memories by natural language query (vector + keyword + graph spreading activation). Supports verbosity: "concise" for lightweight payloads.
memory_search Filtered vector search by status, category, date (since), last-active date (active_since), or entity. Pass an empty query to enumerate by recency instead (no embedder needed) — with since/active_since this answers "what's new since I last looked" without knowing what to query for.
memory_traverse Walk the causal graph from a node. Returns reachable nodes and the edges of the induced subgraph (source/target/type/weight/created_by) — a superset of the BFS tree, so paths can be reconstructed. Honors edge_types.
memory_explain Explain a memory's score. Includes a score_model block (formula + live per-force breakdown + staleness note) and removal_impact.
memory_stats Get memory system statistics
pin_memory Pin a memory so it's never forgotten
unpin_memory Unpin a previously pinned memory
delete_memory Permanently delete a memory
list_core_memories List core memories, optionally filtered by category
set_core_preferences Set user preferences for core memory categories
promote_to_org Promote a private memory to org visibility

Writer-specified edges & corrections

memory_store's related argument lets the writer set edge semantics instead ofguessing. Each entry is {"id": "<node-id>", "type": "<edge-type>"}, directednew_node --type--> target (so supersedes means the new node supersedes thetarget). Invalid types are rejected before the node is created — explicitwrites never half-succeed. related_to still exists but always createscaused_by; prefer related.

To correct a fact, use memory_amend(node_id, content, reason=...): it stores thenew version, links it SUPERSEDES → old, and keeps the old memory for audit.Recall automatically deprioritizes superseded hits and tags them withsuperseded_by.

When you memory_store something that lexically disagrees with an auto-linkcandidate (a changed number, a negation), the result may includepossible_conflicts — heuristic hints, not verified contradictions, and nevermaterialized as edges. Use them to decide whether to memory_amend.

Concise recall

memory_recall(query, verbosity="concise") skips the causal-chain enrichment andreturns only id / summary / status / score / activation / is_core (plussuperseded_by when set) per hit — much cheaper on tokens for high-frequencylookups. verbosity="full" (the default) is unchanged. Reactivation writes stilloccur in both modes (they are governed by read_only, not verbosity).

See docs/scoring.md for what activation / decay_scoreactually mean — in short, it is a retention weight that rises when a memory isrecalled, not a countdown to deletion.

How it works

Every memory is scored by three forces multiplied together:

decay_score = relevance × connectivity × reactivation
  • Relevance decays over time. Old memories fade unless reinforced.
  • Connectivity rewards memories with many causal links. Hub memories survive.
  • Reactivation boosts memories that keep getting recalled. Frequency matters.

Because the formula is multiplicative, a memory must score on all three axes to survive. A highly connected but never-accessed memory still decays. A frequently recalled but causally orphaned memory still fades.

decay_score (aliased activation on every hit) is a retention weight, not a deletion countdown — recalling a memory raises it, and a low score just means "resting," not "doomed." Deletion requires a low score and orphaned and unpinned and non-core and non-org, all at once. See docs/scoring.md for the full model and worked numbers.

STORE → ACTIVE → DORMANT → FADING → PRUNED
           ↑                    │
           └── reactivation ────┘
                                  (only if score=0, orphan, not pinned)

Memories can also be promoted to core status — structurally important memories that are auto-pinned and never pruned.

Benchmark Results

We've run internal evaluations against the LoCoMo long-conversation memory benchmark during development. These are self-reported, run with our own harness (category 5 — adversarial questions with disputed ground truth — excluded), and not independently reproduced, so treat them as directional rather than a verified claim. Reproduction scripts are in benchmarks/ if you want to run your own numbers.

Storage backend

This package ships one storage backend: an in-memory causal graph (storage/memory.py) with optional JSON persistence via GENESYS_PERSIST_PATH. No database is required.

Additional backends — Postgres/pgvector, FalkorDB, MongoDB, and an Obsidian vault adapter — along with a REST API, OAuth, and multi-user auth, are part of the hosted product at genesys-api.astrixlabs.ai and are not included in this repo.

Want a different storage backend for the open-source library? Implement the provider protocols in storage/base.py and bring your own.

Configuration

Copy .env.example to .env and set:

Variable Required Description
OPENAI_API_KEY Unless GENESYS_EMBEDDER=local Embeddings
ANTHROPIC_API_KEY No Enables LLM-based causal inference (consolidation, contradiction detection). Off by default — without it, causal edges only come from edges the caller explicitly declares in memory_store plus cosine-similarity linking.
GENESYS_EMBEDDER No openai (default) or local (sentence-transformers, no API key)
GENESYS_PERSIST_PATH No JSON file path to persist state across restarts (in-memory otherwise)
GENESYS_USER_ID No Default user ID for single-tenant mode

Auto-link tuning

Auto-linking connects a newly stored memory to semantically similar existingmemories. If it is too permissive you get a "hairball" — everything ends up ~2hops from everything, which destroys traversal scoping. Three knobs bound it:

Variable Default Description
GENESYS_AUTOLINK_MIN_SIMILARITY embedder-recommended Cosine floor to create an auto-link. Explicit value wins over the embedder default.
GENESYS_AUTOLINK_MAX_EDGES 3 Max auto-links a single memory_store may create. Caps fan-out.
GENESYS_AUTOLINK_MAX_NODE_DEGREE 10 Max auto_link edges any single node may accumulate as a target. Fan-out alone still lets a hub gain one edge per store forever; this caps the hub itself.

The floor is embedder-aware: an auto-link is permanent graph structure, so itsfloor sits above the transient recall floor. When GENESYS_AUTOLINK_MIN_SIMILARITYis unset, the effective floor is the embedder's recommendation — 0.6 for OpenAI(text-embedding-3-small, whose genuine matches cluster ~0.5+) and 0.45 forlocal sentence-transformers (whose genuine matches cluster ~0.2–0.4 but whosenoise pairs have been observed at ~0.44, so only near-duplicate contentauto-links locally). Any unknown embedder falls back to 0.45. Auto-linking alsode-dupes: if a pair is already connected by any edge (e.g. a user_explicitcaused_by), no parallel auto_link related_to is created.

The possible_conflicts hint on memory_store scans with its own, lowerfloor (GENESYS_CONFLICT_MIN_SIMILARITY, defaulting to the recall floor) overa wider window (GENESYS_CONFLICT_SCAN_K, default 8) — so tightening theauto-link floor never shrinks conflict detection.

Recall / relevance floors

The same embedder-aware pattern governs recall filtering:

Variable Default Description
GENESYS_RECALL_MIN_SIMILARITY embedder-recommended (OpenAI 0.5 / other 0.2) Cosine floor below which pure vector hits are dropped from memory_recall. Keyword hits bypass it.
GENESYS_CORE_INJECT_MIN_SIMILARITY embedder-recommended (OpenAI 0.45 / other 0.2) Floor for injecting auto-promoted core memories into recall results. Pinned memories are always injected.

Scoring knobs

The three-force scoring formula and its lifecycle thresholds are allenv-configurable (see engine/config.py anddocs/scoring.md): GENESYS_ACTR_DECAY,GENESYS_RELEVANCE_VECTOR_WEIGHT, GENESYS_RELEVANCE_KEYWORD_WEIGHT,GENESYS_MIN_CONNECTIVITY, GENESYS_FORGETTING_THRESHOLD, the GENESYS_DORMANCY_*transition thresholds, and the GENESYS_CORE_* promotion weights.

See .env.example for all options.

Built by

Genesys is built by Rishi Meka at Astrix Labs. It came out of frustration with re-explaining project context to Claude every session. The goal is the intelligence layer between your LLM and your memory — fully open source.

Contributing

See CONTRIBUTING.md.

License

AGPL-3.0-or-later

Note: Genesys releases prior to v0.3.6 were documented as Apache 2.0 in error. The LICENSE file has always contained the AGPLv3 text. From v0.3.6 onward, all documentation correctly references AGPL-3.0-or-later with a Contributor License Agreement.

MCP Server · Populars

MCP Server · New

    patsnap

    PatSnap MCP Servers

    MCP server for 200M+ patents, scientific literature, chemistry and pharma records. Search prior art and R&D intelligence powered by PatSnap's proprietary databases via Claude, Cursor and MCP-compatible clients.

    Community patsnap
    ghost-in-the-droid

    Ghost in the Droid

    Open-source Android device automation framework — control real phones via ADB with Python skills, a Vue dashboard, and LLM-powered tooling

    Community ghost-in-the-droid
    VoidNullable

    lific

    Local-first, lightweight issue tracker. Single Rust binary, SQLite-backed, MCP-native.

    Community VoidNullable
    ghost-in-the-droid

    Ghost in the Droid

    Open-source Android device automation framework — control real phones via ADB with Python skills, a Vue dashboard, and LLM-powered tooling

    Community ghost-in-the-droid
    squirrelscan

    squirrelscan

    The website QA tool for your coding agent. 270+ audit rules across SEO, performance, security, accessibility and agent experience.

    Community squirrelscan