sentence-transformers/all-MiniLM-L6-v2
fine-tuned for searching wealth-management client files: an adviser types a short query
("evidence of residence", "where does the client's money come from", "pension") and should get
the matching documents (utility bills, bank statements, payslips, pension statements, ...).
General-purpose embedding models lack this domain knowledge: for "address proof" they rank a passport above a utility bill. This model was trained to close that gap while keeping general finance retrieval intact. Same size and output as the base model (6 layers, 384-dim, ~23M parameters), so it is a drop-in replacement and runs fast on CPU.
Built for the search API in nevis-search-api.
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("UmbrellaMalware/all-MiniLM-L6-v2-wealth")
docs = ["British Gas bill\nElectricity and gas charges for 14 Elm Grove, Manchester ...",
"Passport scan\nBiographical page of a British passport ..."]
query = model.encode("evidence of residence", normalize_embeddings=True)
scores = model.encode(docs, normalize_embeddings=True) @ query
Encode documents as "<title>\n<content>". Use cosine similarity (normalised embeddings).
gpt-4.1-mini generated 440 documents across
44 categories (KYC/onboarding, income and source of funds, tax, investments, pensions, estate
planning, insurance, plus non-advice distractors) and 8 adviser-style queries per document,
at least 2 of them one or two words ("address", "ID", "earnings"): 3,497 (query, document)
pairs. Documents read like the real thing and do not state their purpose. Fields are written
inline, never labelled with the concept they carry (no "Billing address:"), and the words
"address", "identity", "income", "wealth" and "residence" do not appear in document text, so
the model has to learn the concept instead of matching the word.MultipleNegativesRankingLoss (in-batch negatives), NO_DUPLICATES batches.68e3018b): 264 documents, 1,584 pairs, 3 epochs.Hand-labelled benchmark from nevis-search-api: 42 documents, 44 labelled queries in four groups, 5 noise queries. "Glossary" queries use KYC terms, "held-out" queries paraphrase the same concepts in other words, "other" queries cover general finance concepts, "short" queries are one or two words ("address", "residence", "identity", "earnings", ...). R@5 = share of relevant documents in the top 5; MRR = how high the first relevant document is (1.00 = always first).
| model | glossary R@5 / MRR | held-out R@5 / MRR | other R@5 / MRR | short R@5 / MRR | F1 at best threshold |
|---|---|---|---|---|---|
| all-MiniLM-L6-v2 | 0.45 / 0.62 | 0.26 / 0.43 | 0.92 / 0.87 | 0.52 / 0.65 | 0.45 @ 0.33 |
| v1 | 0.59 / 0.69 | 0.60 / 0.68 | 0.94 / 0.92 | 0.66 / 0.62 | 0.52 @ 0.18 |
| v2 (this revision) | 0.71 / 0.80 | 0.72 / 0.94 | 0.97 / 1.00 | 0.79 / 1.00 | 0.66 @ 0.22 |
Three training runs with different seeds gave the same picture (held-out MRR 0.94, short MRR 1.00 in all three).
Combined with a small domain glossary for query expansion (as nevis-search-api does), it reaches 0.82 / 1.00 on glossary queries and F1 0.66 at threshold 0.27.