Skip to content

Embeddings

Researcher Profiles can include pre-computed vector embeddings for semantic search (finding profiles by meaning rather than keywords). A profile with embeddings is called a “Searchable Profile.”

This document defines the embedding index format: the files in embeddings/, how they’re structured, and how consumers verify compatibility.


Vector search over HTTP means reading the entire vector set. SQLite is optimized for random row access, which is the wrong pattern here. Flat blobs are one contiguous read per model.

.cache/embeddings.sqlite is a derived local index at tier restricted. The servable form lives in embeddings/.


SurfaceLocationPurpose
Per-profile chunks<slug>/embeddings/One vector per text chunk
Collection centroidscollection/embeddings/One aggregate vector per profile

Both use the index.json and blob format below. They differ in these fields:

FieldPer-profile chunksCollection centroids
normalizedfalsetrue
row_keychunk_indexslug
rowsinteger chunk indexesprofile slugs
chunks file (section 5)presentabsent
proberequiredpresent when the contributing profiles supply one

FieldTypeDescription
backend_specstringModel identifier (e.g. st:all-MiniLM-L6-v2)
filestringRelative path to blob (e.g. st-all-minilm-l6-v2.bin)
dtypestringfloat32
byte_orderstringlittle
dimintegerVector dimensionality
countintegerNumber of rows
layoutstringrow_major
normalizedbooleanWhether rows are L2-normalized
metricstringcosine
row_keystringchunk_index or slug
rowsarrayRow manifest in blob order
sha256stringHex digest of blob
probeobject{text, vector} for model verification

The blob filename is a lowercased slug of backend_spec with : and / replaced by -. Consumers MUST resolve the blob by the file field rather than rebuilding the slug.

Every per-profile index MUST include a probe. A collection centroid index includes one when the contributing profiles provide it. A consumer embeds probe.text with its own model and computes cosine similarity against probe.vector:

  • Above 0.99: verified
  • 0.9 to 0.99: degraded (usable but flagged)
  • Below 0.9: incompatible

Raw, headerless binary:

  • Layout: row-major, contiguous
  • Size: exactly count * dim * 4 bytes
  • Type: IEEE 754 float32
  • Byte order: little-endian

No header, no padding, no magic bytes.


Per-profile indexes carry a chunks file next to the blob: the file path with .bin replaced by .chunks.json (for example st-all-minilm-l6-v2.chunks.json). It is a JSON array with one entry per row:

FieldTypeDescription
source_typestringexpertise, soul, paper_summary, paper_abstract, grant, cv, web
source_idstringDocument identifier
chunk_indexintegerZero-based index within source
sectionstring/nullSection heading
char_countintegerChunk text length

The file does not contain the chunk text; fetch the source document to display a hit.


Chunk tiers follow the derivation rule. A chunk from a restricted source is itself restricted and MUST NOT appear in a public blob. cv, web, and grant chunks resolve to restricted by their role default and are dropped from a public blob accordingly.

Collection centroids MAY be computed over all chunks (including restricted) because a single averaged vector cannot reconstruct individual inputs.


Consumers MUST compare backend_spec strings for exact equality before computing similarity. They MUST refuse to compute when the strings differ, because cosine similarity across models is meaningless.