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 (python3 on Linux), then . ribot-env/bin/activate (Linux, macOS) or ribot-env\Scripts\activate (Windows; use a short folder path such as C:\ribot).
  • On Linux without a GPU, first run pip install torch --index-url https://download.pytorch.org/whl/cpu: the default torch there is the large CUDA build. On Windows the default torch is 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

  1. Ask only in the trained form. rb.ask(entity, relation) builds it:

    Q: What is Mira's city?
    A:
    
  2. Keep each memory's .facts.jsonl beside it (mine.safetensors and mine.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_new in rb.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; see research/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
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for aikronus-labs/ribot-135m

Finetuned
(1,002)
this model