Microsoft MVP
8 min read
AgentMemory for .NET: A native sibling to Neo4j Agent Memory #
Graph-native agent memory for the .NET world — built from scratch, and verified against Neo4j’s own Test Compatibility Kit.
Authors:
José L. Latorre · independent open-source community project
Zaid Zaim, Developer Advocate, Neo4j
If you build AI agents on Neo4j, you already know how graph-native memory is structured
The three layers, as one connected graph in Neo4j:
- three connected memory types in one graph,
- POLE+O ontology for the entities,
- reasoning traces you can actually traverse.
Vector stores give you recall; the graph gives you understanding.
Here’s what’s new. That model now has a native .NET implementation — AgentMemory for .NET — built from scratch, and verified 178 out of 178 against Neo4j’s own Test Compatibility Kit (the TCK).
Why this exists #
Python developers are spoiled for agent tooling. .NET developers, less so. Microsoft Agent Framework (MAF) | Neo4j Integration gives us a foundation for agentic apps, but the moment you move past demos into durable memory, the native library shelf is nearly empty.
Neo4j Agent Memory already had Python and TypeScript SDKs, a self-hosted path, and the hosted Neo4j Agent Memory Service (NAMS) — and it documented a MAF integration, but through the Python package.
The concepts were all there. The native .NET runtime wasn’t. So I built it.
AgentMemory for .NET is not a code port and shares no implementation code with the Python project.
Because the Neo4j driver integration, extraction pipeline, embeddings, DI setup, the MAF and Semantic Kernel adapters, the MCP surface, security policies, and the .NET domain model are all .NET native. It targets .NET 8, 9, and 10, and gives agents the same three first-class layers you already know:
- Short-term — conversations and messages.
- Long-term — entities, facts, preferences, and relationships, classified with POLE+O (people, objects, locations, events, organizations).
- Reasoning — steps and tool calls: how the agent got there, not just what it said.
Retrieval offers vector, full-text, hybrid, and graph traversal, with optional GraphRAG.
And the unglamorous parts are treated as real architecture: owner and application isolation, bitemporal knowledge, provenance, non-destructive supersession, access auditing, decay, and OpenTelemetry.
The whole picture — the packages, the surfaces you build against, and the systems behind them:
Compatibility as evidence, not a claim
An independent reimplementation can quietly drift for months and still call itself “compatible.” I wanted a harder bar than that.
In conversations with Zaid Zaim and Michael Hunger — including an unscheduled meeting at AgentCon Berlin that I did not see coming — I learned about the Neo4j Agent Memory TCK. That changed how I approached the entire project. Compatibility checks two ways now:
- Static schema-parity analysis compares the .NET schema against a pinned upstream reference and reports every divergence, intended or not.
- An out-of-process TCK bridge lets Neo4j’s own upstream runner drive the .NET implementation over HTTP, through the same contract used for every other implementation.
The result today: 178/178 Bronze, Silver, and Gold scenarios passing — short-term, long-term, reasoning, and cross-memory behavior.
Platinum covers hosted-service operations and is intentionally out of scope for the current self-hosted backend. That boundary is also a roadmap hint.
To be clear: this is an independent community project. Not an official Neo4j product, and no commercial SLA. “Compatible” here means evidence I regenerate continuously, not a marketing adjective.
And since you already know the Python original, here is the honest side-by-side:
Memory belongs wired into the agent, not just offered as a tool #
One opinion I’ll stand behind: if your framework lets you wire memory in natively, do that — don’t reach for an MCP server as the primary path. The moment remembering becomes a tool the model chooses to call, you’ve made it optional. It can skip the call, fire it too late, pick the wrong operation, or call it twice — and you find out when a user tells you the agent forgot something that mattered.
MAF’s context-provider lifecycle solves this cleanly – like hooks in other frameworks: ProvideAIContextAsync before the turn, StoreAIContextAsync after a successful one. The host guarantees recall and persistence deterministically, while MCP stays available for explicit search, inspection, and admin. It complements the lifecycle. It doesn’t own it.
The part I’m most excited about: the eval loop #
This project didn’t start with memory. It started with evaluation. AgentMemory for .NET is one half of a loop; the other half is AgentEval, a .NET evaluation toolkit I’ve been building — tool-call assertions, stochastic runs, model comparison, red-team probes, and a memory suite covering retention, reach-back, temporal reasoning, noise resistance, and abstention, plus LongMemEval (ICLR 2025) reimplemented in .NET with paper-comparable scoring.
So the two push on each other.
- Change the retrieval policy? Run the benchmark.
- Swap the embedding provider? Compare to the baseline.
- Add compaction? Measure exactly what you lost.
That’s healthier than shipping a memory demo and judging it by whether one conversation felt impressive.
Honest status: AgentMemory for .NET went public less than a week ago, so stabilization comes first, and rigorous evaluation of the engine itself is just beginning. The loop runs. Filling it with real numbers is the work happening now.
A very small taste #
The direct-Neo4j package registers through the normal .NET dependency-injection model:
dotnet add package AgentMemoryservices.AddNeo4jAgentMemory(memory => { },neo4j => { neo4j.Uri = "bolt://localhost:7687"; neo4j.Username = "neo4j"; neo4j.Password = configuration["NEO4J_PASSWORD"];}, configureLlm: null);var memory = provider.GetRequiredService<IMemoryService>();await memory.AddMessageAsync( "session-01", "conversation-01", "user","I prefer dark mode.");
What’s next #
Two directions. First, NAMS. Today AgentMemory for .NET connects directly to Neo4j over Bolt.
The next backend is NAMS — additive, not a replacement — so a single deterministic .NET integration gives you two deployment choices: self-hosted Neo4j for maximum control, or hosted NAMS when you’d rather Neo4j operate the memory backend. That work is underway now.
Second, two memory tiers I want to propose — still on paper, not yet in the repo. Identity Continuity: an agent’s values, its decisions under pressure, and its self-model, so you can detect drift across sessions. And Experiential Continuity: how the agent relates to a specific person over time. The goal of both is behavior that’s consistent and predictable with the person in front of it, the way a good colleague is — the same values on Friday as on Monday, no re-briefing every morning. And because a consistent agent with bad values is worse than an inconsistent one, none of it ships without evaluation attached.
Try it, break it, tell me where it falls short #
If you build agents in .NET, this is a chance to shape the missing memory layer instead of waiting for it. If you work with Neo4j, it’s a bridge into MAF, Semantic Kernel, and the wider enterprise .NET world. Are you building on .NET? How would you use a native graph-memory layer in your agents? I’d really like to know.
Resources
AgentMemory for .NET
AgentEval — agenteval.dev
Neo4j Agent Memory & NAMS (Documentation)
The full story (longer read):
Thanks to Zaid Zaim for the early encouragement, and to Michael Hunger for surfacing the TCK.
*Independent community project by *José L. Latorre
AgentMemory for .NET: A native sibling to Neo4j Agent Memory was originally published in Neo4j Developer Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.