Model or dataset
neo4j-labs/agent-memory avatar
neo4j-labs/agent-memory

Neo4j Agent Memory: A Graph-Backed Memory Layer for Python and TypeScript Agents

A graph-native memory system for AI agents and context graphs. Store conversations, build knowledge graphs, and let your agents learn from their own reasoning — all backed by Neo4j.

552 stars104 forksPythonApache-2.0

At a glance

What is it?
Neo4j Labs ships a memory system that splits agent recall into short-term, long-term and reasoning memory on top of Neo4j, with a hosted backend or a bolt-connected self-hosted one. It is worth adopting when your agent's memory needs relationships, not just similarity, and worth skipping when a vector store already answers your retrieval questions.
Who is it for?
Adopt it if your agent needs to answer questions that depend on relationships between entities (who knows whom, which preference belongs to which person, which past reasoning step touched which entity), and you are willing to run Neo4j or pay for NAMS. Skip it if your retrieval problem is pure document similarity, or if you cannot accept an experimental, community-supported project in your dependency tree.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem: agent memory that cannot express relationships

Most agent memory implementations are a vector index with a session id attached. You embed a message, store it, and retrieve nearest neighbours at inference time. That works for "find me something similar to this question" and fails for "which of John's stated preferences conflict with what he said last week". The second question is a traversal, not a similarity search, and a flat index has no way to express it. Neo4j Agent Memory takes the position that memory should be a graph: nodes for entities, edges for relationships and provenance, and vectors attached to those nodes rather than replacing them. The README splits the model into three named layers. Short-term memory holds conversations and messages with per-session history plus vector and text search. Long-term memory holds entities, preferences and facts in a knowledge graph, with entity resolution and deduplication. Reasoning memory holds reasoning traces and tool usage, so the agent can retrieve similar past tasks. The intended user is an engineer building an agent that accumulates state across sessions and needs to query that state structurally, not just semantically.

POLE+O and the extraction pipeline behind the graph

The long-term layer is built on what the project calls the POLE+O model, which is the entity taxonomy the graph is organised around. The README links a separate explanation page for it rather than inlining the definition, so the exact type set is something to read on the docs site before you design your schema around it. What the README does describe is the pipeline that fills the graph: multi-stage entity extraction with a choice of spaCy, GLiNER or an LLM, relationship extraction via GLiREL, and background enrichment from Wikipedia and Diffbot. That is a meaningful design decision. Running spaCy or GLiNER locally keeps extraction cost predictable and offline, while the LLM path trades cost and latency for accuracy on ambiguous text. The enrichment stage is the part most teams will underestimate: pulling from Wikipedia and Diffbot means outbound calls and a dependency on those services' availability and terms, and the README does not state what happens when enrichment fails. The pipeline diagram is referenced as img/extraction-pipeline.png, which is the fastest way to see stage ordering without reading source.

Two backends, one MemoryClient API

The SDK exposes a single MemoryClient and lets the backend be selected by environment. Option A is NAMS, the hosted service at memory.neo4jlabs.com. You install with pip install "neo4j-agent-memory[nams]", export MEMORY_API_KEY with a nams_ prefixed key, and the backend auto-selects NAMS when that variable is set. No database to run. Option C is self-hosted Neo4j over bolt, which the README recommends when you already operate Neo4j or need write-Cypher, geospatial queries, or an air-gapped deployment. The API is identical either way, and the project maintains a dedicated Bolt vs NAMS explanation page for the trade-offs. One behavioural difference is called out explicitly and it matters for tests: on NAMS, entity extraction runs server-side and is asynchronous, so the README instructs you to await memory.long_term.wait_for_extraction(...) before asserting on freshly extracted entities. If you write integration tests against the hosted backend without that call, they will be flaky for reasons that have nothing to do with your code.

Getting it running: three concrete paths

The hosted path is the shortest. Install the extra, export the key, and the client picks up the backend: pip install "neo4j-agent-memory[nams]" then export MEMORY_API_KEY=nams_... The README's example then calls memory.short_term.add_message(session_id="user-123", role="user", content=...), memory.long_term.add_entity("John", "PERSON"), and memory.get_context("What restaurant should I recommend?", session_id="user-123"). The MCP path needs no application code at all. Run uvx "neo4j-agent-memory[mcp]" mcp serve --password <neo4j-password> and any MCP-compatible client (Claude Desktop, Claude Code, Cursor, VS Code Copilot) gets persistent memory. The README shows registration for Claude Code as claude mcp add neo4j-agent-memory -- uvx "neo4j-agent-memory[mcp]" mcp serve --password <neo4j-password>, and for Claude Desktop as a claude_desktop_config.json entry with command uvx, args ["neo4j-agent-memory[mcp]", "mcp", "serve", "--password", "your-password"], and an env block carrying OPENAI_API_KEY. Note that the MCP example passes the Neo4j password as a command-line argument, which means it lands in shell history and process listings; the config-file variant is the safer of the two shown. The MCP server exposes 16 tools according to the README.

Bring your own model, and the provider-string shorthand

MemorySettings.embedding and MemorySettings.llm accept either a provider-string shorthand such as "anthropic/claude-3-5-sonnet-latest" or "BAAI/bge-small-en-v1.5", or a Provider instance. Native adapters exist for OpenAI, Anthropic, Bedrock, Vertex AI and sentence-transformers, with a LiteLLM fallback the README says covers 100+ providers including Cohere, Voyage, Groq, Together, Mistral and Ollama. That fallback is the pragmatic part of the design: you are not locked to one vendor's embedding space. The constraint to internalise is in the README's own parenthetical. These settings configure the self-hosted backend only. On NAMS, embedding and extraction run server-side, so swapping MemorySettings.embedding has no effect on the hosted path. If your evaluation depends on a specific embedding model, that is an argument for the bolt backend, and the project points to a provider migration guide for teams moving between configurations.

Operational features and where the model breaks down

The README lists several features aimed at teams already running something. client.schema.adopt_existing_graph(...) lets you point the system at a Neo4j graph you already have rather than starting empty. Multi-tenant scoping is available through a user_identifier= argument. client.buffered.submit(...) provides fire-and-forget buffered writes. client.consolidation.dedupe_entities(...) exposes deduplication as a primitive you call, and client.eval.run(suite) runs an eval harness. Reasoning steps create explicit :TOUCHED audit edges to entities, which is the mechanism behind "learn from past decisions". The honest limitation is that dedupe_entities is a primitive, not a policy. The README does not describe when consolidation should run, what threshold triggers a merge, or how merges are reversed. Entity resolution errors are among the hardest memory bugs to diagnose because the graph stays internally consistent while being wrong about the world. Two other boundaries are stated plainly in the badges: the project is Experimental and Community Supported, not a production-supported Neo4j product. The README also does not document write throughput, latency, or how the buffered write path behaves when Neo4j is unreachable.

Where it sits against Mem0 and Zep

The obvious comparison is Mem0, which also stores agent memory across sessions but centres on a vector store with an LLM deciding what to add, update or delete, rather than on a property graph you can traverse with Cypher. Zep takes a third route, combining a temporal knowledge graph with summarisation over conversation history. The practical difference is what you can express in a query. With Neo4j Agent Memory, the self-hosted path gives you write-Cypher and geospatial queries against the memory store, so a question like "which entities did this reasoning trace touch" is a graph pattern, not a re-ranking heuristic. The cost of that expressiveness is that you are now operating a graph database, or paying a hosted service, and you have taken on a schema and an extraction pipeline. Mem0 and Zep both push more of that decision-making into the library. If your retrieval needs never leave the "find similar text" shape, the graph is overhead you will pay for and not use.

Versioning, licence and what to check before you pin it

The two SDKs are versioned and released independently. Python tags are python-v* and publish to PyPI as neo4j-agent-memory; TypeScript tags are typescript-v* and publish to npm as @neo4j-labs/agent-memory. Cross-language conformance is enforced by a separate repository, agent-memory-tck, which consumes both SDKs as external dependencies. That is a good sign for teams running mixed Python and TypeScript agents against the same memory, because it means the two SDKs are tested against a shared behavioural spec rather than drifting. The latest release listed is v0.4.0 from May 2026, and the repository's last push is September 2026, so the codebase is moving. The licence is Apache-2.0, which permits commercial use and modification and includes a patent grant; the repository carries no separate commercial terms that the README mentions, but the hosted NAMS service is a distinct product with its own terms, and the README does not state its pricing or data-handling policy. Read those before you send user conversations to memory.neo4jlabs.com. If you are evaluating this, the first concrete step is to run the MCP server against a throwaway Neo4j instance and inspect what the extraction pipeline actually produces on your own text, because the quality of the graph is the quality of every answer built on top of it.

Editorial conclusion

Adopt it if your agent needs to answer questions that depend on relationships between entities (who knows whom, which preference belongs to which person, which past reasoning step touched which entity), and you are willing to run Neo4j or pay for NAMS. Skip it if your retrieval problem is pure document similarity, or if you cannot accept an experimental, community-supported project in your dependency tree. Before committing, verify three things against your own setup: that the extraction pipeline's spaCy, GLiNER or LLM stage produces entities at the accuracy your prompts need, that the async server-side extraction on NAMS fits your request flow, and that the Python and TypeScript SDK versions you pin are compatible with the agent-memory-tck conformance suite.

Official sources

  1. License: Apache-2.0
  2. neo4j-labs/agent-memory on GitHub
  3. Project website
  4. README
  5. Releases
Community notes

Community notes