Report

A Russian retrieval embedder in 24M parameters — and the one number that nearly ruined it

The story behind strizh-ru-retriever: layer-pruning RuModernBERT-small into a four-layer encoder, the distillation shortcut that produced a beautiful loss and dead retrieval, what the compression kept — and the leftover config value that silently truncated every input.

lab_single_run internal research August 21, 2026 · Funding: Self-funded internal research

strizh-ru-retriever is the lab’s compact Russian sentence encoder: 24.4M parameters, four layers, one 384-dimensional embedding per text , no query/document prefixes, published under Apache-2.0 with GGUF weights alongside. The full story of how it was made — including the mistake this page is named after — is a Russian long-read on Habr, and that article is the primary source for every figure quoted here. This is the English companion: the same story, condensed, with the numbers marked as quotes from the article rather than registry claims.

Why shrink an embedder that already fits?

Not for memory. The base model is small enough to live anywhere; the thing that ran out was compute. In a self-hosted RAG box the embedder shares silicon with the generator, and every query it embeds — and every corpus it reindexes — is stolen from the LLM’s decode budget. As the article puts it, the memory was not running out; GPU time was. The design target was a first-stage retriever cheap enough to sit next to a serving LLM without being felt, and simple enough that wrappers cannot misuse it — which is why it takes bare text, with none of the query/document prefixes that every integration forgets to add.

What failed before anything worked

The base was deepvk/RuModernBERT-small: 12 transformer layers, about 35M parameters . The obvious shortcut — regress the small model’s embeddings onto a large teacher’s with MSE — produced the article’s best cautionary tale. The loss settled around 0.00062, and retrieval was dead on arrival: Recall@10 of 0.029 . A proxy objective converged perfectly to something that was not the task. The recipe that did work is contrastive end to end: train the 12-layer donor as a retriever first, keep layers [0, 5, 9, 11] as a warm start for the student , then re-train the four-layer model on a 220-thousand-pair mixed Russian/English set with hard negatives mined by a larger retriever.

What did the compression keep?

The article’s quality benchmark is MIRACL-ru dev, on a filtered subset of 758 of 1000 queries ; Recall@10 as published there :

ModelRecall@10
bge-m30.831
multilingual-e5-small0.829
USER2-small0.819
strizh-ru-retriever0.751
rubert-tiny20.633

On the unfiltered set it reads 0.800 . So no, the small model does not beat the large ones, and the article does not pretend it does: it gives up a measured slice of recall against bge-m3 and buys back an order of magnitude elsewhere. On a Strix Halo box under llama.cpp/Vulkan at concurrency 16 it embedded about four thousand short queries per second — 3,614 to 4,450 across runs — versus about 448 for bge-m3 and 1,837 for multilingual-e5-small . Run online next to a serving 35B-class MoE at 200 embedding QPS, it cost the LLM about 2% of generation throughput where bge-m3 cost 7% ; under a full reindex the co-resident LLM held 14.0 transactions per minute next to STRIZH against 6.0 next to bge-m3 . The GGUF files are 52 MB at F16, 29 MB at Q8_0, 26 MB at Q4_0 . That is the trade, stated with its price tag.

The one wrong number

The near-miss is the part most worth retelling. The released tokenizer_config.json carried model_max_length: 256 — a leftover from contrastive training, which used 256-token sequences on an architecture that accepts 8,192 . Every standard wrapper reads that field and silently truncates. Nothing crashes, nothing warns; the model simply never sees most of a long passage. It surfaced during dogfooding on the lab’s internal RAG corpus — 163 generated questions over its own documents — where the same checkpoint scored Recall@10 of 0.589 with the bug fixed and 0.448 with it present: a drop of 14.1 percentage points , down into rubert-tiny2 territory. One digit in a config file nearly cancelled the entire training effort, and no benchmark run on short queries would ever have caught it.

That is this lab’s founding observation wearing a different hat: a quality number detached from its configuration is a config in disguise. The checkpoint had one Recall@10; the checkpoint-plus-shipped-config had another, and only the second one is real for users. The fix — and a release checklist item that now diffs effective tokenizer settings against the architecture — shipped before the public release did.

Where everything lives, and what this page is not

The primary source is the Habr long-read (Russian), with the full training iterations, evaluation methodology and caveats. The model and its GGUF builds are on Hugging Face, and the artifact’s identity is on the artifacts page. What this page is not: none of the numbers above are lab registry claims. They are quotes from the article — single-lab work, evaluated by its own author, with speed figures from one box — and they keep the article’s own caveats. What our registry-grade protocol would demand of them is a separate report.

Cite this report

AGmind Systems Lab (2026-08-21). A Russian retrieval embedder in 24M parameters — and the one number that nearly ruined it. Evidence level: lab_single_run. https://agmind.ai/reports/russian-embedder-24m-params/
← Reports