Praxworx - Subjects lab

On August 27th I started planning out a new AI research project that I now call the "Subjects lab". The purpose was to build a better memory subsystem for today's LLM-powered conversational agents (CAs).

One essential premise was that the best memory a CA can have is all of its earlier conversations with the current user or many of them within an organization. Since today's LLMs have limited context windows (active memory), they need a way to be aware of their whole conversation history without having to load all of it into context.

The heart of this mechanism is an associative memory that indexes conversation history on subjects the CA identifies and manages. As a conversation develops, one extra LLM is mining it for a list of subjects that the user covers. The process creates something analogous to an old subject-centric library card catalogue. So as the user engages with the CA via prompts, those subjects get recognized very quickly along with shorthand summaries of relevant past conversations. The CA can then decide whether to delve deeper into those conversations to further the current conversation.

Modern AI application engineers will recognize this as being similar to RAG. But it's not quite the same. RAG tends to be oriented toward trying to provide the agent with all the textual content it needs to do its work. This mechanism instead builds on that idea by providing fast paths to the relevant conversation history and the tools for the agent to do its own research against that full history.

Prior art

As I developed my concept a bit I did a little research into what else is available. This has been a hot topic in recent years. I wanted to make sure I was contributing something novel and not simply reinventing the wheel. I didn't install and directly evaluate any of the following systems. I had ChatGPT help me develop these comparisons.

Finding related material

SYNAPSE spreads activation through a graph that connects concepts with episodes. HippoRAG and HippoRAG 2 use graph relationships to find passages that may be useful together. My catalogue starts with a recurring subject and shows the agent where it came up. The agent can then choose which conversations to open.

Organizing what gets remembered

A-MEM builds and revises linked notes, much like a Zettelkasten. EverMemOS groups episodes by theme and uses those groups to assemble context. Zep and Graphiti track how facts and relationships change over time. Mem0 extracts useful details for later recall, and SimpleMem compresses and combines related material. In my lab, a catalogue entry can point to an old disagreement or a tentative idea. The agent can go back and see what was actually said when that detail matters.

Putting memory to work

MemGPT, which grew into Letta, gives agents tools to manage what goes into their limited working context. Generative Agents uses remembered experiences and reflections to guide simulated characters. I was interested in a narrower question. When a familiar subject comes up in conversation, can the agent recognize it and decide whether the history is worth a look?

ChatGPT's memory

I have actually done quite a bit of experimental probing of and exploring how ChatGPT "remembers" things. I'm not talking about GPT models like 5.6 Sol. Those don't have memory per se. Everything those models know comes from their training and the total prompt you hand them, starting from system instructions and through the last thing the user said and their own evolving replies.

ChatGPT is an application of GPT models, not a trivial interface to GPT. But right now the heart of ChatGPT's memory is what has become essential for coding agents and other large-context applications of LLMs: self-generated summaries. ChatGPT mines your conversations for useful facts and produces pared-down versions. I've proved repeatedly that ChatGPT currently does not have the ability to read its own chat history outside of the current conversation. The main method is by seeing when it is unable, even with explicit prompting and direction, to recover certain things one discussed in the past, even when it can recover other things from those same conversations. That ChatGPT apparently can't access its own conversation history is surprising. But I assume it will very soon be able to. The problem is not reading part of a long conversation. It's finding what is worth reading in the first place.

Which is where this Subjects lab comes in. The essential difference is that I'm not relying on detailed summaries being sufficiently compact representations of the original conversations. I'm instead relying on associating the current user-driven conversation with that user's entire conversation history so there is no need to pre-guess what information might become useful later on. The whole history is maximally useful.

This is actually my main critique of most of the other techniques I'm aware of right now. Almost all of them appear oriented toward spoon-feeding the agent information in digests of one form or another. While I believe that LLMs are very, very good at following clues and finding what information is useful for doing practical work. So the best available information is all of it, not a digest of it. And it's best if the agent is handed suggestions about past conversations that clearly relate to the present subject matter. If you want a good analogy, consider coding agents. They deal best when they can read all of the code in a project rather than rely on summaries of that code. No matter how big the codebase is.

Experimental design

I took a few weeks to develop this lab project. I was able to run through many iterations, experimental designs, and experimental runs largely because I was able to orchestrate the whole process. I used Cursor as my primary dev environment. Its coding agent wrote most of the code. Although I defined the data structures and algorithms and very closely orchestrated the coding and testing process.

The most important data input for this project was my entire ChatGPT conversation history. You can request your own full history as a download. Although you currently have to wait up to a week to get it. But mine is a trove, with several years of rich conversation history already. The way I used it was to process every one of those conversations in (proximal) time order, one at a time. That simulated what would happen if the subject indexing process happened in realtime as those conversations took place. The resulting multi-part index turned out to be surprisingly potent.

In one set of experiments I tested the resulting index by picking one of the conversations to process as though it was a brand new one. I made it so all of the subject index from after the time the experimental conversation started was invisible, as though it had never been indexed. Then I would ask a new agent to respond to each prompt along the way. I was effectively playing back the user's prompts but not revealing ChatGPT's actual replies.

The conversations were sometimes stilted by prompts not perfectly fitting the CA's replies. But overall the CA showed a clear benefit by being able to draw on earlier conversations in a way ChatGPT at the time was unable to. I used another LLM to compare ChatGPT's responses to my custom CA's. And despite me choosing GPT models that were much less capable than what ChatGPT defaults to and having all of its optimizations, my custom CA demonstrated superior recall and contextual replies quite often.

I experimented in a variety of other ways, but this was the main thrust of it. One very simple method was a "taste test" where I simply had conversations with my conversational agent, both with and without the associative memory subsystem enabled. Sometimes just casually. Sometimes expressly to probe its memory and see how well it could pick up on very subtle hints. The results were very promising.

Final outcome

IMO this was a very successful lab project. I set it aside knowing that when I am ready to apply it to a later lab or production system, I would want to rewrite it for that purpose. But the existing code provides a clear blueprint for all the essentials.

Thus far I have not employed it in my many other labs for one simple reason. In those labs I want every experimental run to start in a naive state for clean, repeatable testing. But I know this would be a natural thing to include in a production version of every one of those labs too.

Comments

Popular posts from this blog

Coherence and ambiguities in problem solving

Neural network in C# with multicore parallelization / MNIST digits demo

Discovering English syntax