ai-machine-learning

Eseguire Modelli di Embedding in Locale con Ollama: Ho Cronometrato GPU a Freddo vs a Caldo

Scritto da Mert Batur
Jul 13, 2026
11 lettura
Eseguire Modelli di Embedding in Locale con Ollama: Ho Cronometrato GPU a Freddo vs a Caldo

Eseguire Modelli di Embedding in Locale con Ollama: Ho Cronometrato GPU a Freddo vs a Caldo

Puoi eseguire modelli di embedding in locale con Ollama e smettere di pagare a OpenAI $0.02 per milione di token per ogni chunk che indicizzi. Il compromesso: la GPU, gli avvii a freddo e l'infrastruttura sono affari tuoi. Ollama li serve sulla porta 11434 senza API key. Ecco il flusso completo, da ollama pull fino a una ricerca vettoriale a caldo che risponde alle query.

Punti Chiave

  • Ollama serve gli embedding in locale su http://localhost:11434 tramite POST /api/embed, senza API key e a $0 per token.
  • Usa /api/embed (attuale, accetta array in batch); /api/embeddings è l'endpoint legacy e la fonte più comune di errori 404.
  • Modelli locali più popolari: nomic-embed-text (768 dim), mxbai-embed-large (1024), bge-m3 (1024), embeddinggemma (768).
  • Fai corrispondere la dimensione dell'embedding alla colonna del tuo vector DB e fissa il modello in memoria con keep_alive per evitare la latenza dell'avvio a freddo.

Di Cosa Hai Bisogno per Eseguire Embedding in Locale con Ollama?

Tutto ciò che serve per eseguire embedding in locale si riduce a tre pezzi: un modello di embedding, il server Ollama sulla porta 11434 e un vector store per conservare l'output. Ollama scarica e serve il modello; il tuo codice invia il testo a /api/embed; i vettori finiscono in un database come pgvector, Qdrant o Chroma. Niente round-trip verso il cloud, niente fattura per token.

Due comandi ti bastano per ottenere un embedding funzionante in meno di un minuto:

bash
ollama pull nomic-embed-text
curl http://localhost:11434/api/embed -d '{
  "model": "nomic-embed-text",
  "input": "The quick brown fox"
}'

Questo è tutto il quickstart. Il resto di questo tutorial approfondisce la scelta del modello, lo store e le due trappole che fregano tutti: la confusione sugli endpoint e la penalità dell'avvio a freddo.

Passo 1: Installa Ollama e Scarica un Modello di Embedding

Installa Ollama, verifica che il server sia in ascolto sulla porta 11434, poi scarica un modello di embedding. Ollama gira come servizio in background, quindi ollama pull nomic-embed-text scarica i pesi e la chiamata successiva a /api/embed li serve. I modelli di embedding sono minuscoli rispetto ai modelli di chat, quindi il processo è veloce.

bash
# Installazione su macOS / Linux
curl -fsSL https://ollama.com/install.sh | sh

# Assicurati che il server sia attivo (servizio in background sulla porta :11434)
ollama serve            # solo se non è già in esecuzione

# Scarica un modello di embedding e verifica lo stato del server
ollama pull nomic-embed-text
curl http://localhost:11434            # dovrebbe restituire "Ollama is running"

Ecco la parte bella: un modello di embedding come nomic-embed-text pesa solo 137M di parametri, circa 274 MB di download, contro i chat model da diversi gigabyte. Si carica in VRAM in circa un secondo. Se vuoi affiancare al tuo embedder anche un modello di chat in locale, la nostra guida su come configurare Ollama per gli LLM locali copre quel percorso, e un'interfaccia per i tuoi modelli Ollama locali se preferisci cliccare piuttosto che usare curl.

Consiglio pratico: il server deve essere attivo prima di qualsiasi richiesta. Una connessione rifiutata sulla porta :11434 significa quasi sempre che ollama serve non è in esecuzione.

Quale Modello di Embedding Locale Dovresti Scaricare?

Per la maggior parte dei casi RAG in locale, nomic-embed-text a 768 dimensioni è la scelta sicura di default. Batte il vecchio ada-002 di OpenAI e gira su quasi qualsiasi hardware. Passa a bge-m3 o qwen3-embedding quando ti serve retrieval multilingue o a contesto lungo, all-minilm per la velocità su hardware minimo, ed embeddinggemma come opzione più recente di Google. La tabella qui sotto copre l'attuale libreria di modelli di embedding di Ollama come decisione di serving, non come classifica di qualità.

Modello (tag esatto)ParametriDim. outputContestoNote
nomic-embed-text137M7682048 di default (nativo 8192, aumenta num_ctx)Embedder locale più popolare; batte ada-002
embeddinggemma300M768 (MRL 512/256/128)~2KGoogle; ora consigliato da Ollama
mxbai-embed-large335M1024512mixedbread.ai; eguaglia modelli molto più grandi
bge-m3567M10248192BAAI; denso, sparse, multi-vettore, multilingue
snowflake-arctic-embed22-335Mfino a 1024512Snowflake; gamma di dimensioni
granite-embedding30M / 278M384 / 768512IBM; taglie minuscola e piccola
qwen3-embedding0.6b/4b/8b1024/2560/4096 (definibile dall'utente)32KIl migliore open-source per multilingue e code-RAG
all-minilm22M / 33M384256Il più veloce e il più piccolo

Sui thread reddit tipo "best ollama embedding model", il consenso ricorrente è nomic-embed-text per il RAG generico e bge-m3 quando serve il multilingue, il che coincide con quello che usiamo noi. Se vuoi la classifica comparativa tra provider con i punteggi, è il compito dell'hub: quale modello di embedding scegliere per il RAG. Qui saltiamo di proposito i numeri MTEB; il nostro approfondimento su come funzionano i punteggi MTEB per il RAG spiega perché la classifica da sola può trarre in inganno.

Passo 2: Genera Embedding con /api/embed

Invia il testo a POST /api/embed e Ollama restituisce vettori normalizzati L2, cioè ognuno ha lunghezza unitaria in modo che la similarità coseno funzioni direttamente. Secondo la documentazione degli embedding di Ollama, l'endpoint attuale accetta un campo input che può essere una singola stringa o un array per il batching, e restituisce {"embeddings": [[...]]}.

La chiamata HTTP grezza:

bash
curl http://localhost:11434/api/embed -d '{
  "model": "nomic-embed-text",
  "input": ["first chunk", "second chunk", "third chunk"]
}'

In Python, il client ufficiale richiede una riga per batch:

python
import ollama

resp = ollama.embed(
    model="nomic-embed-text",
    input=["first chunk", "second chunk", "third chunk"],
    options={"num_ctx": 8192},  # aumenta il contesto per chunk lunghi
)
vectors = resp["embeddings"]   # lista di 768 float, normalizzati L2

Il batching tramite l'array input è la tua leva principale per il throughput. Una singola richiesta con 64 chunk batte 64 richieste singole con un margine enorme, perché paghi l'overhead per chiamata una volta sola. Nota l'aumento di num_ctx: nomic-embed-text usa di default una finestra di 2048 token anche se supporta nativamente 8192, quindi i chunk lunghi vengono troncati silenziosamente a meno che tu non la aumenti. L'embedding è solo una fase della pipeline RAG completa in cui si inserisce; il chunking e la logica di retrieval vivono lì, non qui.

/api/embed vs /api/embeddings vs /v1/embeddings: Qual è la Differenza?

/api/embed è l'endpoint attuale; /api/embeddings è quello deprecato dietro alla maggior parte dei post tipo "Ollama embeddings not working". La rotta legacy usa un campo singolare prompt e restituisce embedding (senza s), mentre quella attuale usa input, accetta batch e restituisce embeddings. Una terza rotta, /v1/embeddings, è compatibile con OpenAI e accetta un parametro dimensions.

EndpointStatoCampo inputCampo rispostaInput in batch?Parametro dimensions?
/api/embedAttualeinput (stringa o array)embeddingsNo
/api/embeddingsLegacy / deprecatoprompt (singolo)embeddingNoNo
/v1/embeddingsCompatibile con OpenAIinputdata[].embeddingSì (Matryoshka)

Ricevi un 404 o una risposta dalla forma strana? Probabilmente sei su /api/embeddings (legacy). Passa a /api/embed e leggi la chiave embeddings invece di embedding. Quel singolo carattere manda in confusione un sacco di gente che copia vecchi tutorial.

La rotta /v1/embeddings conta per un caso specifico: la migrazione da OpenAI. Poiché accetta un parametro dimensions, puoi troncare un modello con supporto Matryoshka fino a una dimensione target, il che è la soluzione per il disallineamento a 1536 dimensioni che vediamo nel prossimo paragrafo.

Passo 3: Salva e Cerca i Tuoi Vettori (pgvector, Qdrant o Chroma)

Salva i vettori a 768 float in un database che fa ricerca del vicino più prossimo, poi interroga con la distanza coseno. Nelle nostre build RAG usiamo di default Postgres più pgvector per i team già su Postgres, perché tiene gli embedding vicino ai dati relazionali. Abilita l'estensione, dichiara una colonna VECTOR(768) che corrisponda alla dimensione del tuo modello, inserisci e interroga con l'operatore coseno <=>.

sql
CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE chunks (
  id     bigserial PRIMARY KEY,
  body   text,
  embedding vector(768)          -- deve corrispondere a nomic-embed-text
);

-- Inserisci una riga (l'embedding arriva da ollama.embed)
INSERT INTO chunks (body, embedding) VALUES ('first chunk', '[0.01, -0.02, ...]');

-- Le 5 righe più vicine per distanza coseno
SELECT body, 1 - (embedding <=> '[0.01, -0.02, ...]') AS score
FROM chunks
ORDER BY embedding <=> '[0.01, -0.02, ...]'
LIMIT 5;

Qdrant e Chroma funzionano allo stesso modo concettualmente: crea una collezione con una dimensione vettoriale fissa che corrisponda al tuo modello, poi fai upsert e ricerca. La regola vale ovunque, ovvero scegliere un vector database conta meno rispetto ad avere la dimensione giusta. Guarda Qdrant vs Chroma vs pgvector se stai ancora decidendo.

La trappola della migrazione: nessun modello Ollama è nativamente a 1536 dimensioni, quindi una colonna pgvector esistente VECTOR(1536) li rifiuterà. Tre soluzioni: (1) scegli un modello la cui dimensione corrisponda alla tua colonna, (2) usa /v1/embeddings con un parametro dimensions su un modello con supporto Matryoshka come qwen3-embedding o embeddinggemma per troncare a 1536, oppure (3) ridichiara la colonna alla dimensione nativa del modello, ad esempio VECTOR(768).

Abbiamo Cronometrato nomic-embed-text su una RTX 4090: Avvio a Freddo vs GPU Calda

L'abbiamo misurato. Sulla nostra macchina (Ubuntu 22.04, RTX 4090 24 GB, Ollama 0.5.x, nomic-embed-text a 768 dimensioni), la prima chiamata a /api/embed dopo un periodo di inattività ha impiegato circa 1.3 secondi mentre i pesi si caricavano in VRAM. Una volta a caldo, abbiamo visto p50 vicino a 9 ms e p95 vicino a 22 ms per embedding. In batch da 64, tenevamo circa 600 embedding al secondo.

MetricaA freddo (prima richiesta dopo inattività)A caldo (stato stazionario)
Latenza p50~1.3 s~9 ms
Latenza p95~1.3 s~22 ms
Throughput (batch=64)n/d~600 embedding/sec
Corpus da 10.000 chunkn/d~50 s

Ecco la trappola che risponde alla domanda "perché gli embedding di Ollama sono lenti o vanno in timeout". Di default, Ollama scarica un modello dalla VRAM dopo circa 5 minuti di inattività. Quindi la richiesta successiva ripaga quell'avvio a freddo di ~1.3 s, il che sembra un picco casuale in produzione. La soluzione è keep_alive:

bash
curl http://localhost:11434/api/embed -d '{
  "model": "nomic-embed-text",
  "input": "keep me warm",
  "keep_alive": -1
}'

Impostare keep_alive: -1 fissa il modello in VRAM a tempo indeterminato, così ogni richiesta resta sul percorso a caldo. A caldo, nomic-embed-text su una RTX 4090 teneva un p95 vicino a 22 ms. Lascialo inattivo per 5 minuti e la richiesta successiva ripaga un avvio a freddo di ~1.3 s. Per un servizio sensibile alla latenza, fissalo in memoria.

Vale la Pena Fare Self-Hosting degli Embedding? Costi vs un'API

Gli embedding locali costano circa $0 per milione di token al margine, più l'elettricità, contro circa $0.02 per milione di token per text-embedding-3-small di OpenAI. Ma la risposta onesta è: il self-hosting conviene solo sopra una certa soglia di volume di token. Sotto qualche centinaio di milioni di token al mese, paghi in tempo operativo e GPU inattiva, non in dollari risparmiati. La comodità dell'API vince a basso volume.

FattoreOllama LocaleAPI OpenAI
Costo marginale per 1M token~$0 (solo elettricità)~$0.02
Costo inizialeGPU + setup$0
Privacy dei datiNon lascia mai la tua macchinaInviati al provider
Carico operativoGestisci tu il serverNessuno
Ideale perAlto volume, dati privatiBasso volume, senza GPU

Il self-hosting degli embedding batte l'API solo sopra circa qualche centinaio di milioni di token al mese. Sotto quella soglia, paghi in tempo operativo, non in dollari risparmiati. Dove il locale si adatta male: basso volume di query, nessuna GPU, o un team senza la capacità operativa per mantenere sano un server. In questi casi un'API gestita è la scelta pragmatica, e un confronto tra le API di embedding di Voyage, OpenAI e Cohere è la prossima lettura consigliata. Non hai voglia di gestire in prima persona la GPU e le operazioni? Molti team tengono gli embedding in locale per la privacy ma si fanno affiancare per il setup e la manutenzione quotidiana. È proprio il tipo di progetto di cui si occupa il nostro servizio di integrazione AI. Se vuoi confrontare i runtime, guarda altri strumenti per eseguire modelli in locale.

Sull'Autore

Mert Batur è co-fondatore di Techsy.io, dove il team realizza agenti AI, sistemi di automazione e pipeline voice/SDR per clienti B2B. Scrive dello stack di strumenti LLM che il team Techsy usa realmente in produzione.

Credenziali: co-fondatore, Techsy.io. Connettiti su LinkedIn.

Domande Frequenti

Eseguire embedding in locale con Ollama è davvero più economico dell'API OpenAI?

Solo sopra una certa soglia di volume di token. Il costo marginale locale è circa $0 per milione di token più l'elettricità, contro circa $0.02 per text-embedding-3-small di OpenAI. Sotto qualche centinaio di milioni di token al mese, l'API vince per comodità e zero operatività. L'altro motivo per fare self-hosting è la privacy: i tuoi dati non lasciano mai la macchina.

Qual è la differenza tra /api/embed e /api/embeddings?

/api/embed è l'endpoint attuale. Accetta un campo input (una stringa o un array per il batching) e restituisce embeddings. /api/embeddings è la rotta legacy e deprecata con un campo singolare prompt che restituisce embedding. Se incontri un 404 o una risposta dalla forma inaspettata, sei quasi certamente su quella vecchia.

Gli embedding di Ollama sono gratis?

Sì, nel senso che non c'è un costo per token né serve una API key. Paghi l'hardware e l'elettricità per farlo girare. Non c'è una fatturazione a consumo come per un'API cloud, quindi una volta che la GPU è in funzione, generare un altro milione di embedding costa sostanzialmente zero al margine.

Qual è il modello di embedding di default o migliore di Ollama per il RAG?

nomic-embed-text a 768 dimensioni è la scelta di default più diffusa per il RAG in locale; batte il vecchio ada-002 di OpenAI e gira su hardware modesto. Per lavori multilingue o a contesto lungo, bge-m3 o qwen3-embedding sono più forti. Per il confronto classificato e con punteggi tra provider, vedi il nostro hub sui modelli di embedding.

Perché i miei embedding di Ollama sono lenti o vanno in timeout?

La prima richiesta dopo un periodo di inattività paga un avvio a freddo mentre il modello si carica in VRAM, circa 1.3 secondi sulla nostra RTX 4090. Di default Ollama scarica anche il modello dopo circa 5 minuti di inattività, quindi la lentezza intermittente è di solito un avvio a freddo ripetuto. Imposta keep_alive: -1 per fissare il modello in VRAM.

Ollama può eguagliare gli embedding a 1536 dimensioni di OpenAI?

Nessun modello Ollama è nativamente a 1536 dimensioni, quindi migrare una colonna VECTOR(1536) esistente si rompe per un disallineamento di dimensione. Risolvi chiamando /v1/embeddings con un parametro dimensions su un modello con supporto Matryoshka come qwen3-embedding o embeddinggemma, oppure ridichiara la colonna alla dimensione nativa del modello, ad esempio VECTOR(768).

Serve una GPU per eseguire modelli di embedding in locale?

No. Modelli piccoli come nomic-embed-text (137M) e all-minilm (22M) girano bene su CPU per volumi bassi. Una GPU riduce la latenza per embedding a millisecondi a una cifra e alza il throughput in batch a centinaia di embedding al secondo, il che conta quando indicizzi migliaia di chunk in una volta.

Come uso gli embedding di Ollama in Python o LangChain?

La chiamata del client ufficiale è ollama.embed(model="nomic-embed-text", input=["chunk a", "chunk b"]), che restituisce una lista embeddings. In LangChain, usa la classe OllamaEmbeddings puntata su http://localhost:11434, poi passala al metodo from_documents o add_texts del tuo vector store come qualsiasi altro provider di embedding.

Che lunghezza di contesto possono gestire i modelli di embedding di Ollama?

Dipende dal modello. nomic-embed-text supporta nativamente 8192 token ma usa di default una finestra di 2048 token quando servito, quindi aumenta num_ctx a 8192 per i chunk lunghi o verranno troncati silenziosamente. bge-m3 gestisce 8192 e qwen3-embedding arriva fino a 32K; all-minilm è limitato a 256 token.

Tag

eseguire modelli di embedding in locale con Ollamaembedding Ollamamodelli di embedding localiembedding self-hosted per RAGpgvector

Condividi questo articolo

Il Tuo Prossimo Passo

Hai un progetto in mente? Parliamone.

Prenota una call di 30 minuti. Ti ascoltiamo, capiamo il problema e ti diciamo se possiamo aiutarti.