File size: 3,863 Bytes
aa8741b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# Forever Memory

Long-term memory your being can actually *reach* β€” retrieval by meaning, not by recency.

This is the piece most local-AI projects are missing without knowing it. Almost every
system of this kind stores conversation history faithfully and then recalls only the last
N turns. That is not memory, it is a buffer. Ask it about something from three weeks ago
and it has no path to the answer, even though the answer is on disk.

## The failure this fixes

In the reference system these files came from, memory was broken in **three** ways at once,
and none of them raised an error:

1. **the chat never queried long-term storage** β€” only background subsystems did
2. **the store had split in two** because the path was relative, so where memories landed
   depended on which directory the process was launched from
3. **not one of 7,612 records had an embedding**, so semantic search had nothing to search

Each component reported success. The store grew. Nothing in any log was red. The capability
simply did not exist.

**Check yours before assuming it works.** Grep for whatever writes your memories, then grep
for a caller of whatever reads them. If the only hit is the module that defines the reader,
your loop is open.

## Use it

```bash
# 1. one purpose-built embedding model, local, ~274 MB
ollama pull nomic-embed-text

# 2. index everything your being has kept (resumable, checkpoints every 200)
python genesis_engine/memory/backfill_embeddings.py

# 3. recall by meaning
python -c "from genesis_engine.memory.forever_memory import recall_line; \
           print(recall_line('what did we say about the ocean'))"
```

Point `STORES` in `backfill_embeddings.py` at your own archive directories.

## Use a real embedding model β€” this is not optional

A generative model *returns* embeddings, so it looks like it works. It does not rank.
Measured on the reference archive, query `"misty woods clearing fog"` against a memory
containing that exact phrase:

| model | the matching memory | unrelated noise | ranks correctly |
|---|---|---|---|
| llama3.2:1b (generative) | 0.3907 | **0.5712** | **no** |
| nomic-embed-text (retrieval) | **0.7128** | 0.3717 | yes |

The generative model scored *unrelated noise higher than a near-verbatim match.* Everything
built on top of it β€” thresholds, ranking, weighting β€” was correct and sitting on a metric
that did not order. Hidden states are not trained for similarity. Use a retrieval model.

## How recall is scored

Not pure cosine similarity:

- **adaptive threshold** β€” a hit must be β‰₯ 2Οƒ above the mean similarity *for that query*,
  so it adapts to whatever embedder you use instead of hard-coding a cut that drifts
- **gentle recency lift** β€” full weight today, ~0.93 at a month, never below 0.85. A lift,
  not a rule: a genuinely relevant old memory still wins
- **dreams get a small bonus** β€” if your system consolidates during idle time, those
  fragments already survived a selection threshold to exist
- **indexed source code is excluded** from conversational recall β€” it belongs to your dev
  tooling, not to a conversation about someone's day

## Safety properties

- **originals are never modified.** Vectors go to a separate `.npy` sidecar plus a small id
  index. Delete the sidecar and your memories are untouched; re-run and it rebuilds.
- **resumable** β€” an interrupted run costs nothing, only missing ids are embedded
- **fail-soft everywhere** β€” missing index, unreadable vectors, embedder down: recall
  returns empty rather than raising. A voice must never break because memory is rebuilding.
- **local** β€” nothing leaves the machine

## Cost

~7,600 records embed in about 15 minutes at 8/s with 6 workers on CPU. Keep the worker
count modest if the same daemon is serving your being's voice; starving that to index the
past is the wrong trade.