# Embeddings for a catalogue: 111,985 vectors to maintain

Indexing a catalogue with embeddings is cheap once and expensive when the model changes. Do the maths first: input tokens, 656 MiB of vectors, an in-memory index.

> Page: https://ui.eneawork.it/blog/ai-embedding-catalogo
> Markdown: https://ui.eneawork.it/blog/ai-embedding-catalogo.md

*2026-11-01*

Un catalogo si indicizza con gli embedding quando la ricerca deve capire la richiesta invece di copiare una parola: 111.985 componenti diventano 111.985 vettori da tenere allineati.

/api/stats, letto il 1 novembre 2026, dichiara 111.985 componenti, 1.556 blocchi, 12.808 icone e 1.472 librerie. La guida OpenAI sugli embedding, letta lo stesso giorno, dichiara 1.536 dimensioni e 8.192 token di input per text-embedding-3-small, a 0,02 $ per milione di token. I conteggi sono misurati, l'aritmetica è nostra: 111.985 × 1.536 × 4 byte fa 688.035.840 byte, circa 656 MiB. Su npm, sempre il 1 novembre 2026, pgvector 0.3.0 conta 39 file per 52.767 byte, sqlite-vec 0.1.9 conta 5 file per 4.004 byte e @huggingface/transformers 4.3.2 conta 1.372 file per 9.990.867 byte.

## Cosa si vettorizza

import OpenAI from 'openai'; const openai = new OpenAI(); async function embedBatch(items: { id: string; text: string }[]) { const res = await openai.embeddings.create({ model: 'text-embedding-3-small', input: items.map((i) => i.text.slice(0, 2000)), encoding_format: 'float', }); return res.data.map((d, i) => ({ id: items[i].id, embedding: d.embedding })); }

## Il conto si fa prima

Il costo di una passata è il numero di token in ingresso diviso un milione per 0,02 $. Il costo che sorprende è il secondo: cambiare modello significa rigenerare ogni vettore, quindi la dimensione va salvata accanto ai dati e il prezzo del nuovo modello va letto prima di migrare.

curl -s "https://ui.eneawork.it/api/stats" | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{const j=JSON.parse(s);console.log(j.components*1536*4,'bytes')})"

Il comando legge il catalogo e moltiplica per la dimensione dichiarata: serve a rifare il conto quando il catalogo cresce, senza fidarsi del numero scritto qui.

## Aggiornare, non ricostruire

Un catalogo cambia ogni giorno: si vettorizzano le righe nuove, si cancellano quelle rimosse e si rigenera solo ciò che è cambiato. L'indice si ricostruisce quando il recall peggiora, non a ogni inserimento.

## La limitazione

656 MiB sono solo l'inizio: l'indice HNSW aggiunge strutture in memoria e va dimensionato. E non abbiamo una misura di qualità su questo catalogo, perché servirebbe un set di query con la risposta attesa che non abbiamo pubblicato.

## Dove continuare

La scelta della ricerca è in ricerca semantica nel sito, il database in SQLite o Postgres, la struttura del catalogo in che cos'è un registry. I record: 111.985 componenti, 1.556 blocchi, Aceternity UI.

## FAQ

### Quanti vettori servono per un catalogo di 111.985 elementi?

Uno per elemento, quindi 111.985. Se si vettorizzano anche blocchi e icone il numero cresce: 1.556 blocchi e 12.808 icone portano il totale a 126.349 vettori.

### Quanto spazio occupano i vettori?

A 1.536 dimensioni in float32, 6.144 byte per vettore: circa 656 MiB per 111.985 elementi. Un indice HNSW aggiunge memoria e va dimensionato a parte.

### Meglio embedding locali o API?

Locali quando il volume è alto e il testo non esce, API quando vuoi evitare di scaricare un modello da 9.990.867 byte come @huggingface/transformers 4.3.2. La differenza vera è chi paga la CPU.

### Ogni quanto va rigenerato l'indice?

Quando cambia il modello o quando il recall scende. Con lo stesso modello bastano aggiornamenti incrementali sulle righe modificate, più una ricostruzione periodica pianificata.

### Gli embedding sostituiscono la ricerca per parola chiave?

No, la completano. Il filtro esatto su categoria e libreria resta, la similarità ordina i risultati dentro quel sottoinsieme.
