← All models

feature-extraction Β· text Β· WASM Β· ~34 MB

E5-small-v2 β€” the embedder with a query:/passage: ritual

E5 (from Microsoft) turns a sentence into 384 numbers β€” but with a twist no other small embedder shares: you must prefix every input. A search gets query:, a document gets passage:. E5 was contrastively trained on query–passage pairs, so the two sides play different roles and the prefix tells the model which one this is. Skip it and retrieval gets worse. Type some lines to build a live similarity matrix, run asymmetric search, watch the prefix move the geometry, and see four embedders disagree on the same pairs. Nothing leaves your device.

At a glance

ModelXenova/e5-small-v2 (ONNX of intfloat/e5-small-v2)
Task / pipelinefeature-extraction
ArchitectureBERT-style encoder (~33M params) β†’ 384-dimensional sentence embedding
Poolingmask-aware mean pooling, then L2-normalize
Prefixesrequired: query: for searches, passage: for documents (contrast GTE = none, BGE = one query instruction)
Quantizationq8 (8-bit) ONNX
BackendWebAssembly (runs anywhere; no WebGPU needed)
Download~34 MB q8, cached after first load
LicenseMIT (intfloat E5)
Web APIsWebAssembly, Web Workers β€” all Baseline widely available

Run it

Put one sentence per line, then embed. For a symmetric similarity matrix E5 recommends the query: prefix on every line. The matrix shows cosine similarity between every pair (1.00 = identical direction); the scatter is a 2D PCA projection where near = similar. Then run a search below, where the query and documents get different prefixes.

Cosine similarity matrix

2D map (PCA)

The query is embedded with query:; the lines above are embedded with passage: β€” exactly as E5 is meant to be used for retrieval.

See inside β€” the vector, the prefix, and the model choice

The encoder emits one 384-number vector per token. To get a single vector per sentence E5 mean-pools those and L2-normalizes, so every sentence becomes a unit-length arrow and cosine similarity is a plain dot product. Pick a sentence to see its real vector and pre-normalization magnitude (embedded here with query:).

Embed some sentences above to inspect a vector.

The prefix is not decoration

Same query, same document β€” embedded three ways. Correct uses query: on the question and passage: on the document (how E5 is meant to run). No prefix embeds both raw. Swapped deliberately mislabels them. Watch how the cosine to the right document changes when you get the prefix wrong.


E5 vs MiniLM vs BGE vs GTE on the same pairs

Four embedders, four training recipes: E5 (mean pool, query/passage prefix), MiniLM (mean pool, no prefix), BGE (CLS pool, query instruction) and GTE (mean pool, no prefix). They do not agree on how similar two sentences are β€” that disagreement is exactly why the model you pick changes your search results. Loads MiniLM (~25 MB), BGE (~34 MB) and GTE (~34 MB) on demand, embeds each pair with all four (E5 with its query: prefix), and shows the real cosines.

Why it matters

Embeddings are the quiet workhorse behind search, recommendations, deduplication, clustering, and retrieval-augmented generation. E5's pitch is retrieval quality from the prefix trick: telling the model whether a string is a question or an answer lets one small 33M-param encoder handle both sides of a search well β€” and it runs in a tab, private, because the text never leaves the device.

Use cases

Basics

Do two lines mean the same thing? One cosine β€” and a toggle showing the prefix change the answer.

Practical

Asymmetric search and classify-by-nearest-label over your own text, prefixes applied correctly.

Wild

Cluster a mixed pile of text into themes with k-means over E5 vectors, then see them on a 2D map.

Multi-model

E5 retrieves a shortlist by meaning, then a cross-encoder reranker re-scores the top hits.

How the API works

Feature-extraction plus the prefix discipline is the whole recipe β€” the prefix is the part people forget:

import { pipeline } from "@huggingface/transformers";

const extract = await pipeline(
  "feature-extraction",
  "Xenova/e5-small-v2",
); // downloads + caches ~34 MB once

// E5 REQUIRES prefixes: "query: " on searches, "passage: " on documents.
const out = await extract(
  ["query: how do I sign in?", "passage: the login page rejects my password"],
  { pooling: "mean", normalize: true },
);
const [q, d] = out.tolist();

// unit-length vectors β†’ cosine similarity is just the dot product:
const sim = q.reduce((s, x, i) => s + x * d[i], 0); // high β€” same meaning, correct prefixes

This page asks the worker for the un-normalized pooled vectors so "See inside" can show the real pre-normalization magnitude, then normalizes them in JS. Inference runs in a Web Worker, so the matrix, projection and comparison never jank the page.

References