|
| 1 | +# Multilingual Embedding Benchmark |
| 2 | + |
| 3 | +This benchmark supports the evidence-first model selection tracked by |
| 4 | +[#1372](https://github.com/basicmachines-co/basic-memory/issues/1372). It does not change Basic |
| 5 | +Memory's default embedding model. |
| 6 | + |
| 7 | +## Corpus and measurements |
| 8 | + |
| 9 | +`multilingual-retrieval-v1` contains 17 notes and 23 judged queries covering English, Chinese, |
| 10 | +Japanese, Korean, Arabic, Russian, Spanish, Thai, and mixed-language text. The query set includes |
| 11 | +same-language retrieval, English-to-non-English retrieval, mixed-language notes, a long-note |
| 12 | +chunk-boundary case, and four negative queries. |
| 13 | + |
| 14 | +Every run uses the production FastEmbed provider and one production storage pairing: |
| 15 | +SQLite/sqlite-vec, PostgreSQL/pgvector, or PostgreSQL/Milvus. Milvus runs use the first-party |
| 16 | +adapter against an isolated Milvus Lite database, while PostgreSQL continues to own search rows, |
| 17 | +the vector manifest, and FTS. Ranking quality is measured without a similarity cutoff. The same |
| 18 | +query is then repeated with the configured production cutoff to measure empty results and negative |
| 19 | +false positives. JSONL output includes: |
| 20 | + |
| 21 | +- recall@5, MRR@10, wrong-top-result rate, empty-result rate, and negative false-positive rate; |
| 22 | +- ranking and thresholded-query p50/p95 latency; |
| 23 | +- cold model load, indexing throughput, current/peak RSS, unique model-cache bytes, and vector |
| 24 | + storage bytes; |
| 25 | +- model identity, dimensions, prefixes, license, corpus version, FastEmbed/Python versions, and |
| 26 | + host information. |
| 27 | + |
| 28 | +Each model runs in a separate pytest process. This matters because loading multiple ONNX models in |
| 29 | +one process contaminates cold-load and resident-memory measurements. |
| 30 | + |
| 31 | +## Running the benchmark |
| 32 | + |
| 33 | +Python 3.12 is the authoritative environment for these measurements. |
| 34 | + |
| 35 | +```bash |
| 36 | +# One model/backend pair. Artifacts go to .benchmarks/. |
| 37 | +just benchmark-multilingual bge-small-en sqlite vector 0.55 |
| 38 | +just benchmark-multilingual multilingual-minilm postgres vector 0.55 |
| 39 | +just benchmark-multilingual multilingual-minilm milvus vector 0.55 |
| 40 | + |
| 41 | +# Run and compare the current baseline and the first 384-dimensional candidate. |
| 42 | +just benchmark-multilingual-compare sqlite vector 0.55 |
| 43 | +just benchmark-multilingual-compare postgres vector 0.55 |
| 44 | +just benchmark-multilingual-compare milvus vector 0.55 |
| 45 | +``` |
| 46 | + |
| 47 | +The recipe installs the locked `milvus` optional extra as needed. The `milvus` backend label means |
| 48 | +PostgreSQL metadata/FTS plus Milvus vector storage; it is not a replacement SQL database. |
| 49 | + |
| 50 | +Supported model keys are `bge-small-en`, `multilingual-minilm`, `multilingual-mpnet`, |
| 51 | +`multilingual-e5-large`, and `jina-embeddings-v3`. E5's required `passage: ` and `query: ` prefixes |
| 52 | +are part of its benchmark contract. A model key being available to the harness does not mean it is |
| 53 | +approved for Cloud redistribution. |
| 54 | + |
| 55 | +## Initial screening results |
| 56 | + |
| 57 | +These results were collected on 2026-08-30 with Python 3.12.12, FastEmbed 0.8.0, and an Apple |
| 58 | +Silicon machine with 8 logical CPUs and 16 GiB RAM. PostgreSQL used the repository's |
| 59 | +`pgvector/pgvector:pg16` testcontainer. Milvus used PyMilvus 3.0.0 and Milvus Lite 3.1.0 through |
| 60 | +the production adapter. The small corpus proves provider/repository parity and gives directional |
| 61 | +model evidence; it is not a Cloud capacity test or a meaningful HNSW scale test. |
| 62 | + |
| 63 | +### SQLite vector quality at the current 0.55 cutoff |
| 64 | + |
| 65 | +| Slice | Metric | BGE small English | Multilingual MiniLM | |
| 66 | +| --- | --- | ---: | ---: | |
| 67 | +| Overall | recall@5 | 0.9474 | 1.0000 | |
| 68 | +| Overall | MRR@10 | 0.8474 | 1.0000 | |
| 69 | +| Overall | wrong top | 0.2105 | 0.0000 | |
| 70 | +| Overall | accepted empty | 0.0000 | 0.1579 | |
| 71 | +| Negative queries | false positive | 0.7500 | 0.0000 | |
| 72 | +| Same-language | MRR@10 | 0.9048 | 1.0000 | |
| 73 | +| Cross-language | recall@5 | 0.8571 | 1.0000 | |
| 74 | +| Cross-language | MRR@10 | 0.7762 | 1.0000 | |
| 75 | +| English baseline | MRR@10 | 1.0000 | 1.0000 | |
| 76 | + |
| 77 | +The PostgreSQL/pgvector and PostgreSQL/Milvus runs produced the same vector quality values for both |
| 78 | +models. That confirms the model comparison survives both hosted vector-storage paths. |
| 79 | + |
| 80 | +Hybrid retrieval remains backend-sensitive because SQLite FTS5 and PostgreSQL `tsvector` contribute |
| 81 | +their own ranks before reciprocal-rank fusion. The candidate still improved the complete hybrid |
| 82 | +path on both backends: |
| 83 | + |
| 84 | +| Backend | Model | recall@5 | MRR@10 | Wrong top | Negative false positive | |
| 85 | +| --- | --- | ---: | ---: | ---: | ---: | |
| 86 | +| SQLite hybrid | BGE small English | 0.8947 | 0.7531 | 0.3684 | 0.7500 | |
| 87 | +| SQLite hybrid | Multilingual MiniLM | 1.0000 | 0.8947 | 0.2105 | 0.0000 | |
| 88 | +| PostgreSQL hybrid | BGE small English | 0.8947 | 0.8070 | 0.2632 | 0.7500 | |
| 89 | +| PostgreSQL hybrid | Multilingual MiniLM | 1.0000 | 0.9211 | 0.1579 | 0.0000 | |
| 90 | +| PostgreSQL/Milvus hybrid | BGE small English | 0.8947 | 0.8070 | 0.2632 | 0.7500 | |
| 91 | +| PostgreSQL/Milvus hybrid | Multilingual MiniLM | 1.0000 | 0.9211 | 0.1579 | 0.0000 | |
| 92 | + |
| 93 | +Cross-language hybrid recall@5 increased from 0.7143 to 1.0000 on both backends. Hybrid MRR does |
| 94 | +not reach vector-only MRR because a lexical rank can still move the correct semantic result below |
| 95 | +an FTS result; that is fusion behavior, not a disagreement between sqlite-vec and pgvector. |
| 96 | + |
| 97 | +In vector-only retrieval, MiniLM ranks every positive query first and rejects every negative query |
| 98 | +at 0.55, but that cutoff also hides three correctly ranked positive results: Japanese watcher |
| 99 | +reconciliation, English to Spanish sourdough retrieval, and the Japanese mixed-language runbook |
| 100 | +query. At 0.50, the cross-language miss is recovered without introducing a negative-query false |
| 101 | +positive; the Japanese same-language and mixed-language queries remain below the cutoff. A model |
| 102 | +switch therefore needs an explicit similarity-threshold decision rather than inheriting 0.55 |
| 103 | +without measurement. |
| 104 | + |
| 105 | +### Directional local runtime measurements |
| 106 | + |
| 107 | +| Backend | Model | Cold load | Index 17 notes | Notes/sec | Model RSS delta | Cache bytes | Vector storage | |
| 108 | +| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | |
| 109 | +| SQLite | BGE small English | 0.69 s | 1.86 s | 9.13 | 214,073,344 | 67,179,926 | 2,019,328 | |
| 110 | +| SQLite | Multilingual MiniLM | 1.59 s | 1.53 s | 11.14 | 436,699,136 | 252,141,023 | 2,019,328 | |
| 111 | +| PostgreSQL | BGE small English | 0.50 s | 8.21 s | 2.07 | 227,213,312 | 67,179,926 | 262,144 | |
| 112 | +| PostgreSQL | Multilingual MiniLM | 4.50 s | 11.53 s | 1.47 | 297,451,520 | 252,141,023 | 262,144 | |
| 113 | +| PostgreSQL/Milvus Lite | BGE small English | 0.25 s | 7.02 s | 2.42 | 260,882,432 | 67,179,926 | 40,930 | |
| 114 | +| PostgreSQL/Milvus Lite | Multilingual MiniLM | 1.01 s | 4.57 s | 3.72 | 684,883,968 | 252,141,023 | 40,930 | |
| 115 | + |
| 116 | +Local PostgreSQL query latency is dominated by testcontainer and `NullPool` connection setup and |
| 117 | +varied substantially between individual queries. Milvus Lite also opens short-lived local clients |
| 118 | +through the production repository boundary. The raw artifact preserves those samples, but neither |
| 119 | +path should be used to size Cloud workers or set a latency SLO. |
| 120 | + |
| 121 | +FastEmbed 0.8.0 reports that multilingual MiniLM now uses mean pooling instead of the CLS pooling |
| 122 | +used by older FastEmbed releases. Any rollout decision must therefore pin and record the tested |
| 123 | +FastEmbed/model combination; these results should not be treated as portable across provider |
| 124 | +version changes without rerunning the corpus. |
| 125 | + |
| 126 | +## Current decision |
| 127 | + |
| 128 | +Multilingual MiniLM advances as the first Cloud finalist because it materially improves every |
| 129 | +ranking slice, preserves the English baseline, remains at 384 dimensions, and has a permissive |
| 130 | +Apache-2.0 catalog license. It is not selected as the new default yet: its approximately 2x model |
| 131 | +RSS and 3.75x cache footprint need validation in the shared Cloud image and tenant-worker process, |
| 132 | +and the similarity cutoff needs calibration on a larger judged set. |
| 133 | + |
| 134 | +Do not benchmark the larger candidates by default. Advance MPNet or E5 only if MiniLM fails the |
| 135 | +Cloud resource/reindex test or a reviewed corpus exposes a material quality gap. The FastEmbed |
| 136 | +catalog labels Jina embeddings v3 as CC-BY-NC-4.0, so it is not a Cloud finalist without explicit |
| 137 | +license clearance. |
| 138 | + |
| 139 | +## Cloud follow-up |
| 140 | + |
| 141 | +The next bounded task is tracked in |
| 142 | +[`basic-memory-cloud#1898`](https://github.com/basicmachines-co/basic-memory-cloud/issues/1898): |
| 143 | + |
| 144 | +1. Bake MiniLM into the shared API/worker image and configure the same model, dimensions, and |
| 145 | + prefixes in both process types. |
| 146 | +2. Run a representative tenant reindex on Cloud-equivalent worker hardware while measuring peak |
| 147 | + RSS, duration, failure/retry behavior, image growth, vector-index size, and query latency. Run |
| 148 | + the same corpus against Cloud's configured pgvector or Milvus service before comparing results. |
| 149 | +3. Calibrate the similarity threshold with reviewed multilingual judgments. |
| 150 | +4. If the evidence still favors MiniLM, deploy the new model identity and use the existing |
| 151 | + revision-deduplicated fleet reindex. Accept temporary semantic incompleteness while FTS remains |
| 152 | + available; do not add blue/green vector storage unless the measured rebuild window requires it. |
0 commit comments