::
lurgy.nl

documentation, notes, images and assorted //things//

[status: public]
.::last update::. 2026-07-14
https://lurgy.nl/
lol
lurgy.nl / projects / m8-memory.md
Files
listing for /projects
.. parent directory -
autocalsniffer.md add autocalsniffer project 22 days ago
graph-memory.md content file 5 days ago
INDEX.md add mauraders map project 22 days ago
m8-memory.md content file 5 days ago
m8-memory.md~ content file 5 days ago
marauders-map.md add mauraders map project 22 days ago
Graph Memory in m8

Graph Memory in m8

I have been building a harness called m8. It is my attempt at an agent system where subagents are first-class, where parent and child agents can collaborate, and where memory is not an afterthought bolted onto a chat log.

The thing I am most proud of is the memory system. It is built on RDF triples. A real graph. Subject, predicate, object. Large language models seem to bloody love SPARQL. They really do, m8.

Why a graph

Most LLM memory systems I have seen fall into one of two traps. Either they stuff every previous message into the context window and hope the model sorts it out, or they dump everything into a vector database and retrieve the most similar-looking chunks. Both work, up to a point, but both feel like you are asking the model to remember things by rereading a diary.

A graph is different. A graph says: here are the things that exist, and here is how they relate. A user, project, preference, outcome, contradiction or a time. When the model needs to know something, it does not have to scan a wall of text. It can query and ramble the graph.

LLMs are weirdly good at this. Once they understand the shape of the graph they're able to interact with it with SPARQL queries really effectively.

How it works

In m8, every completed conversation turn gets fed to a background subagent called the MaintainerRound. Its job is to read the transcript and emit facts as RDF triples.

Subject Predicate Object
user prefersEditor nano
refactor caused buildFailure
deployment occurredAt 2026-06-15T14:00:00Z

The maintainer harvests aggressively. Entities, relationships, decisions, outcomes, preferences, absences, contradictions. It also adds meta triples about the session and the round, so the graph knows when things happened and in what context.

If an embedding backend is configured, the maintainer also generates vectors for labeled concepts. This is not the primary way we retrieve things, the graph is - but it is a nice starting point for fuzzy matching.

The subconscious

While the maintainer writes to the graph, another background agent called the SubconsciousRound reads from it.

The subconscious observes the active conversation. Every so often it gets a batch of recent input — what the user said, what the main agent reasoned, what tools returned then asks itself: is there anything in the graph that matters here?

It has two tools: sparql_query and fuzzy_search. It can look up concepts by label, walk the graph from a node, or search semantically if it is not sure where to start. Then it emits short, plain memories back to the main agent.

No "I think you might recall that..." hedging. Just:

User prefers vim. ECS refactor reverted in April. HyperLane has no entries.

The main agent sees these as system-prepended context and can use them or ignore them. The subconscious is rate-limited so it does not chatter. One or two memories per round, max.

Why this feels right

I was trying to model it after how humans actually think, which is not "I have a perfect transcript of everything that ever happened." Human memory is a mess of associations, half-remembered facts, and sudden realisations that something you just said is completely wrong.

I do this all the time. I will confidently state something, then halfway through the sentence my brain will hand me a contradictory fact and I have to course-correct on the spot. "No wait, it wasn't March, it was April. And it wasn't a build failure, it was a revert." That is not context-window lookup. That is a graph update.

The maintainer/subconscious split mirrors this. The maintainer is the slow consolidation that happens after a conversation, the bit that files things away and fixes the labels. The subconscious is the thing that taps you on the shoulder mid-sentence and says "you are wrong about that."

Because the graph stores contradictions explicitly, the system can do the same thing. A new triple does not have to fight with old text. It can just sit there as a newer, stronger fact, and the subconscious can surface it when it matters. The model does not have to "remember" the correction. It can be told.

What is still janky

The in-memory graph store is fine for testing but will not survive serious use. Oxigraph works but needs babysitting. The embedding pipeline is optional and sometimes fails silently. The subconscious occasionally retrieves something hilariously irrelevant and I have to tighten its prompt.

Also, the maintainer can get overeager and emit fifty triples about a conversation that was mostly "hello, can you refactor this function?" There is tuning to do.

The point

I think when we use LLMs we forget that they run on inference engines and are the product of inference. Graph data is designed for inference, and inferring facts from other facts, and LLMs are good at reasoning over explicit structure if you give it to them. A triple store plus SPARQL plus a couple of background agents is, for me, the most promising memory shape I have tried.

To throw out an AIism: It is not RAG. It is not a long context window. It is a knowledge graph that the system maintains and queries on its own. Which sounds wanky when I say it out loud, but the practical upshot is that you stop pretending the model remembers things and just let it look them up instead.