←back to thread

198 points alexmrv | 1 comments | | HN request time: 0s | source

Hey HN! I built a proof-of-concept for AI memory using Git instead of vector databases.

The insight: Git already solved versioned document management. Why are we building complex vector stores when we could just use markdown files with Git's built-in diff/blame/history?

How it works:

Memories stored as markdown files in a Git repo Each conversation = one commit git diff shows how understanding evolves over time BM25 for search (no embeddings needed) LLMs generate search queries from conversation context Example: Ask "how has my project evolved?" and it uses git diff to show actual changes in understanding, not just similarity scores.

This is very much a PoC - rough edges everywhere, not production ready. But it's been working surprisingly well for personal use. The entire index for a year of conversations fits in ~100MB RAM with sub-second retrieval.

The cool part: You can git checkout to any point in time and see exactly what the AI knew then. Perfect reproducibility, human-readable storage, and you can manually edit memories if needed.

GitHub: https://github.com/Growth-Kinetics/DiffMem

Stack: Python, GitPython, rank-bm25, OpenRouter for LLM orchestration. MIT licensed.

Would love feedback on the approach. Is this crazy or clever? What am I missing that will bite me later?

Show context
BenoitP ◴[] No.44970042[source]
I'm failing to grasp how it solves/replaces what vector db were created for in the first place (high-dimensional neighborhood searching, where the space to be searched grows by distance^dimension)
replies(4): >>44970063 #>>44970064 #>>44970076 #>>44970631 #
alexmrv ◴[] No.44970063[source]
Super simplistic example, but say i mention my Daughter, who is 9.

Then mention she is 10,

a few years later she is 12 but now i call her by her name.

I have struggled to get any of the RAG approaches to handle this effectively. It is also 3 entries, but 2 of them are no longer useful, they are nothing but noise in the system.

replies(5): >>44970126 #>>44970254 #>>44970511 #>>44970533 #>>44974081 #
visarga ◴[] No.44970254[source]
> I have struggled to get any of the RAG approaches to handle this effectively.

You need to annotate your text chunks. For example you can use a LLM to look over the chunks and their dates and generate metadata like summary or entities. When you run embedding the combination data+metadata will work better than data alone.

The problem with RAG is that it only sees the surface level, for example "10+10" will not embed close to "20" because RAG does not execute the meaning of the text, it only represents the surface form. Thus using LLM to extract that meaning prior to embedding is a good move.

Make the implicit explicit. Circulate information across chunks prior to embedding. Treat text like code, embed <text inputs + LLM outputs> not text alone. The LLM is how you "execute" text to get its implicit meaning.

replies(1): >>44971506 #
ffsm8 ◴[] No.44971506[source]
Hmm, I'm trying to contextualize your insight with the example that was given.

That approach sounds great for a lot of usecases, but wouldn't it still struggle with the given example of the age changing over the years?

How old is x? -> 10

Two years later:

How old is x? -> 12

replies(1): >>44976108 #
sprobertson ◴[] No.44976108[source]
As a simplified example:

(memory) [2023-07-01] My daughter is 10

(memory) [2024-05-30] My daughter turned 11 today!

System prompt: Today is 2025-08-21

User prompt: How old is my daughter?

The vector DB does the work of fetching the daughter-age-related memories, your system decides (perhaps with another LLM) if the question needs time-based sorting or something else.

replies(1): >>44986224 #
potsandpans ◴[] No.44986224[source]
Still new to the rag space, but there op had an additional callout, "and [later] i call her by her name"

Is it capable to go from `[name] -> [my daughter as a concept] -> age` ?

replies(2): >>44995123 #>>45020341 #
1. sprobertson ◴[] No.45020341[source]
Yeah good call, I missed that. I don't think there's a correct answer here, but it could be another step of the read or write. Either it would do another lookup of "my daughter" -> Name on read, or do a lookup on write if you already have a "my daughter is Name" memory. Whatever's less expensive in the long run. The graph memory someone else mentioned also seems like a good option there.