Ribot-135M (v0.2)
A small language model based on SmolLM2-135M ("the reader") with a second, thin network beside it, "the memory network" (11.8M parameters, a 23.6 MB file), that holds facts in its weights. You write facts into the memory network; writing never trains the reader, and to swap memories you load another file. A research release, not production software.
Paper: Ribot: A Language Model That Reads Memory Networks It Never Saw
Quickstart
First make a new Python environment (Python 3.12) and activate it:
python -m venv ribot-env(python3on Linux), then. ribot-env/bin/activate(Linux, macOS) orribot-env\Scripts\activate(Windows; use a short folder path such asC:\ribot).- On Linux without a GPU, first run
pip install torch --index-url https://download.pytorch.org/whl/cpu: the defaulttorchthere is the large CUDA build. On Windows the defaulttorchis the CPU build.
Then:
pip install huggingface_hub==0.36.0
python -c "from huggingface_hub import snapshot_download as d; d('aikronus-labs/ribot-135m', local_dir='ribot-135m', ignore_patterns=['reader-q/*', 'test-memories/*', 'research/*'])"
pip install -r ribot-135m/requirements.txt
python ribot-135m/smoke_test.py --repo ribot-135m
If you already have the folder, skip the download line. --repo takes the folder (--repo . from inside it).
Expected: exit code 0, and the last line starts with PASS. About 5 minutes on a CPU (8 threads), 35 s on a GPU
(RTX 3070); the download is about 400 MB. The table before the last line (CPU; "ranked" decides PASS):
check ranked starts with ranked threshold
1. no memory: answers match world_a 5% 0% <= 30% ok
2. memory world_a: answers match world_a 100% 80% >= 80% ok
3. swapped to world_b: match world_b 100% 95% >= 80% ok
swapped to world_b: still match world_a 0% 0% <= 15% ok
4. 5 new facts written, then asked 100% 100% >= 60% ok
5 old starter facts re-asked 100% 100% >= 60% ok
Write facts and ask
From inside ribot-135m/, with the same environment, in python (or as a file saved in that folder: a script
saved elsewhere can't import ribot):
from ribot import Ribot
rb = Ribot.from_pretrained(".") # this folder: the main reader with the starter memory (500 facts)
facts = [("Mira", "city", "Lisbon"), ("Mira", "pet", "turtle"), ("Jonas", "job", "baker"),
("Jonas", "favorite food", "pasta"), ("Lena", "hobby", "chess")]
rb.write(facts) # trains only the memory: about 3 minutes on a CPU, 10 s on a GPU
for entity, relation, value in facts:
print(entity, relation, "->", rb.ask(entity, relation))
print("memory off:", rb.ask("Mira", "city", memory=False))
rb.save_memory("mine.safetensors") # rb.write changes the memory in Python only; this keeps it
Expected output (on a CPU; the last line varies with the device, and it is never Lisbon):
Mira city -> Lisbon
Mira pet -> turtle
Jonas job -> baker
Jonas favorite food -> pasta
Lena hobby -> chess
memory off: The City of Miracles
Other calls: rb.load_memory("world_b") swaps the memory; Ribot.from_pretrained(".", memory="mine.safetensors")
loads your saved memory back; Ribot.from_pretrained(".", memory=None) loads no memory, and
rb.write(facts, from_scratch=True) then writes a new one; rb.rank(question(entity, relation), candidates) ranks
answers (from ribot import question). Each has a docstring.
Rules
Ask only in the trained form.
rb.ask(entity, relation)builds it:Q: What is Mira's city? A:Keep each memory's
.facts.jsonlbeside it (mine.safetensorsandmine.facts.jsonl). Writing replays the old facts from it; without it, a write erases them.
What it can't do
- It reads facts as
(entity, relation, value)with a short value, asked in the trained form. Natural questions are read at 14.0%. Not documents, instructions or behavior. - It always answers: there is no "I don't know". To see whether a fact is stored, look in the
.facts.jsonl. - It uses a fact poorly together with its own knowledge in one pass (the paper, Section 6).
- English only, one size, about 500 facts per memory. Writing is training: a live write is 30 steps, whatever the number of facts, and a whole memory from scratch takes hours on a CPU.
Results
The paper's main numbers. Exact match first (the answer, cut at the first line break or punctuation, equals the value), ranked in brackets (the value scores highest among a fixed pool of values).
| the main reader | % exact match (ranked) |
|---|---|
| reading 16 test memories it never saw (400 questions): a question | 94.0 (99.0) |
| the same memories: a single fact, new values in a sentence form never seen in training | 16.2 (49.0) |
| the same memories, with the memory removed | 0 |
500 facts in one memory (world_a): completing the fact / question |
79.8 (96.6) / 89.8 (97.8) |
- Writing 20 new facts live into the starter memory (30 steps, 9 s on an RTX 3070): 80% read exactly (16 of 20); the old facts went from 97.5% to 87.5%.
- The reader trained on made-up words reads a single fact, new values in a sentence form seen in training, at 92.4 (98.4).
The rest is in the paper, in research/README.md (the detailed tables and costs), and in results.json (every number with its source file and script).
If you see X, do Y
| you see | do |
|---|---|
loaded reader 'x2' |
nothing: x2 is this reader's name in the research logs |
| "Repository Not Found" (maybe also "Invalid username or password") | the folder given to --repo or from_pretrained does not exist, so it was read as a Hub id; give the right folder (no login is needed) |
hf download says "Ignoring --exclude since filenames have been explicitly set" |
your hf is newer than 0.36.0: give each pattern its own --exclude (--exclude "reader-q/*" --exclude "test-memories/*" --exclude "research/*"), or use the quickstart's download line, which works with both |
| "You are sending unauthenticated requests to the HF Hub" | nothing: no login is needed |
| PowerShell: "running scripts is disabled on this system" when activating | run Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass first, or use cmd |
| Ubuntu or Debian: "ensurepip is not available" | sudo apt install python3-venv, or make the environment another way (conda, uv) |
Windows: pip install torch fails under torch\include |
the folder path is too long for Windows: use a short path such as C:\ribot |
| every smoke check near 0% | install transformers==4.57.3 |
| low accuracy on natural questions | ask in the trained form: rb.ask("Mira", "city") |
new facts not read after rb.write |
ask them back; if they are wrong, write more steps: rb.write(facts, steps=60) |
| old facts forgotten after writing | the memory's .facts.jsonl was missing when it was loaded |
| out of GPU memory while writing | a live write needs about 3 GB of GPU memory; use device="cpu" (slow) |
Known issues
- Writing is not memory-efficient yet, and there is no KV cache, so answers are slow.
- Live writing wears down the old facts (97.5% to 87.5% above).
storage_newinrb.write's report is coarse with a few facts; check a write by asking.
Reproducing the paper
The full download (about 2.0 GB): hf download aikronus-labs/ribot-135m --local-dir ribot-135m.
test-memories/: the main reader's other 13 test memories;python test-memories/rerun_test.py --memory-off.reader-q/: the reader trained on made-up words and its 40 test memories;python reader-q/rerun_test.py --memory-off.research/: the research code and the result files behind the paper's numbers; seeresearch/README.md.
Citation
@misc{ribot2026,
title = {Ribot: A Language Model That Reads Memory Networks It Never Saw},
author = {Aikronus},
year = {2026},
publisher = {Zenodo},
doi = {10.5281/zenodo.24136794},
url = {https://doi.org/10.5281/zenodo.24136794}
}
This release replaces the first release of 21 Oct; the main reader was retrained with a line break after every answer.
- Downloads last month
- 430
Model tree for aikronus-labs/ribot-135m
Base model
HuggingFaceTB/SmolLM2-135M