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 :
| Model | Recall@10 |
|---|---|
| bge-m3 | 0.831 |
| multilingual-e5-small | 0.829 |
| USER2-small | 0.819 |
| strizh-ru-retriever | 0.751 |
| rubert-tiny2 | 0.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.