Embeddings Spiegati ai Developer: Guida Pratica con Esempi
Perché gli Embeddings nel 2026
Nel 2026, chiunque voglia lavorare seriamente con l’AI non può ignorare il concetto di embedding. Non si tratta di una funzionalità avanzata riservata ai data scientist: è la tecnologia di base che alimenta la ricerca semantica, i sistemi RAG, i motori di raccomandazione e i chatbot contestuali. Se hai già sentito parlare di vettori, similarità coseno o database vettoriali, stai già ruotando attorno al mondo degli embeddings.
In parole semplici, un embedding è una rappresentazione numerica del significato. Prendi una frase come “Come posso ottimizzare una query SQL?”: un modello di embedding la trasforma in un array di numeri — tipicamente 768, 1536 o 3072 dimensioni — che cattura il significato semantico di quella frase. Frasi simili producono vettori simili. Questo è il cuore di tutto.
Questa guida è pensata per chi viene dal frontend o dal backend e vuole capire come funzionano gli embeddings dal punto di vista pratico: codice, API, tool reali. Alla fine saprai generare embeddings, confrontarli, salvarli in un database vettoriale e costruire la base per un sistema RAG. Proprio quello che vedrai in dettaglio nell’articolo su RAG con Claude API: Retrieval-Augmented Generation Step-by-Step.
Cosa è un Embedding: Il Concetto Matematico
Un embedding è fondamentalmente una funzione che mappa dati ad alta complessità in uno spazio vettoriale a dimensioni fisse. Il punto chiave: la funzione preserva le relazioni semantiche. “Re” e “Regina” avranno vettori più vicini tra loro rispetto a “Re” e “Automobile”.
La misura più comune per confrontare due vettori è la cosine similarity: misura l’angolo tra due vettori nello spazio n-dimensionale. Un valore di 1.0 significa identici, 0.0 completamente ortogonali (non correlati), -1.0 opposti. In pratica, per testi, valori sopra 0.8 indicano alta somiglianza semantica.
import numpy as np
# Calcola la similarita coseno tra due vettori
def cosine_similarity(vec_a: list[float], vec_b: list[float]) -> float:
a = np.array(vec_a)
b = np.array(vec_b)
dot_product = np.dot(a, b)
norm_a = np.linalg.norm(a)
norm_b = np.linalg.norm(b)
if norm_a == 0 or norm_b == 0:
return 0.0
return float(dot_product / (norm_a * norm_b))
# Esempio con vettori semplificati
vec_re = [0.8, 0.1, 0.6] # "re"
vec_regina = [0.75, 0.15, 0.65] # "regina" - simile
vec_auto = [0.1, 0.9, 0.2] # "automobile" - diverso
print(cosine_similarity(vec_re, vec_regina)) # ~0.99
print(cosine_similarity(vec_re, vec_auto)) # ~0.38Nella pratica, non costruiamo questi vettori a mano: li generiamo con modelli pre-addestrati. I più usati nel 2026 sono text-embedding-3-small e text-embedding-3-large di OpenAI, embed-multilingual-v3 di Cohere, e modelli locali come nomic-embed-text via Ollama.
Generare Embeddings con l’API OpenAI
Il punto di partenza più immediato per un developer è l’API di OpenAI. Il modello text-embedding-3-small produce vettori da 1536 dimensioni a un costo bassissimo — circa $0.02 per milione di token. Per la maggior parte dei progetti, questo è il punto di partenza ideale.
from openai import OpenAI
client = OpenAI(api_key="sk-...") # usa variabile d'ambiente in prod
# Genera un embedding per un singolo testo
def get_embedding(text: str, model: str = "text-embedding-3-small") -> list[float]:
text = text.replace("\n", " ") # best practice OpenAI
response = client.embeddings.create(input=[text], model=model)
return response.data[0].embedding
# Genera embeddings per piu testi in batch (piu efficiente)
def get_embeddings_batch(
texts: list[str],
model: str = "text-embedding-3-small"
) -> list[list[float]]:
texts_clean = [t.replace("\n", " ") for t in texts]
response = client.embeddings.create(input=texts_clean, model=model)
# Ordina per index per garantire l'ordine corretto
return [item.embedding for item in sorted(response.data, key=lambda x: x.index)]
# Uso pratico
domande = [
"Come funziona async/await in JavaScript?",
"Spiega il pattern async-await in JS",
"Come cucino la pasta al dente?",
]
embeddings = get_embeddings_batch(domande)
print(f"Dimensioni vettore: {len(embeddings[0])}") # 1536
# Le prime due domande saranno molto simili
sim_12 = cosine_similarity(embeddings[0], embeddings[1])
sim_13 = cosine_similarity(embeddings[0], embeddings[2])
print(f"Similarita dom1-dom2: {sim_12:.3f}") # ~0.92
print(f"Similarita dom1-dom3: {sim_13:.3f}") # ~0.15Nota l’uso del batch: passare più testi in una sola chiamata API è sempre preferibile a N chiamate separate. Riduce latenza, costi e rate-limit. Questo vale per qualsiasi provider di embedding.
Embeddings in JavaScript/TypeScript con l’SDK Ufficiale
Se il tuo stack è Node.js — magari stai costruendo un’API in TypeScript — la generazione di embeddings è ugualmente diretta. Utile se stai integrando la ricerca semantica in un’app Next.js o in una API tRPC type-safe end-to-end.
import OpenAI from "openai";
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
interface EmbeddingResult {
text: string;
embedding: number[];
tokens: number;
}
async function embedText(text: string): Promise<EmbeddingResult> {
const response = await openai.embeddings.create({
model: "text-embedding-3-small",
input: text.replace(/\n/g, " "),
});
return {
text,
embedding: response.data[0].embedding,
tokens: response.usage.total_tokens,
};
}
// Similarita coseno in TypeScript
function cosineSimilarity(a: number[], b: number[]): number {
const dot = a.reduce((sum, val, i) => sum + val * b[i], 0);
const normA = Math.sqrt(a.reduce((s, v) => s + v * v, 0));
const normB = Math.sqrt(b.reduce((s, v) => s + v * v, 0));
return normA && normB ? dot / (normA * normB) : 0;
}
async function main() {
const [r1, r2, r3] = await Promise.all([
embedText("Come ottimizzare query in Drizzle ORM?"),
embedText("Tecniche di query optimization in TypeScript"),
embedText("Ricette tradizionali italiane"),
]);
console.log(cosineSimilarity(r1.embedding, r2.embedding).toFixed(3)); // ~0.90
console.log(cosineSimilarity(r1.embedding, r3.embedding).toFixed(3)); // ~0.12
}
main();Se stai usando Drizzle ORM nel 2026, puoi combinarlo con il tipo vector di PostgreSQL tramite l’estensione pgvector per salvare gli embeddings direttamente nel tuo database relazionale senza aggiungere infrastruttura separata.
Salvare e Ricercare Embeddings con pgvector
In produzione devi salvare gli embeddings da qualche parte. Le opzioni principali nel 2026 sono: pgvector (estensione PostgreSQL), Pinecone, Weaviate, Qdrant e Chroma. Per la maggior parte dei progetti che già usano PostgreSQL, pgvector è la scelta più pragmatica: zero infrastruttura aggiuntiva.
Se stai usando Supabase nel 2026, hai pgvector già disponibile con un semplice enable: è la soluzione più rapida per un developer che vuole arrivare in produzione senza gestire infrastruttura.
-- Abilita l'estensione pgvector
CREATE EXTENSION IF NOT EXISTS vector;
-- Tabella documenti con embedding
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
content TEXT NOT NULL,
metadata JSONB DEFAULT '{}',
embedding vector(1536), -- dimensioni text-embedding-3-small
created_at TIMESTAMP DEFAULT NOW()
);
-- Indice HNSW per ricerca approssimativa veloce (ANN)
-- Ottimo per dataset grandi (>100k vettori)
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- Query: trova i 5 documenti piu simili a un embedding dato
SELECT
id,
content,
metadata,
1 - (embedding <=> $1) AS similarity -- <=> = distanza coseno
FROM documents
ORDER BY embedding <=> $1
LIMIT 5;L’operatore <=> in pgvector rappresenta la distanza coseno (1 — similarità). Più basso è il valore, più simili sono i vettori. L’indice HNSW (Hierarchical Navigable Small World) rende le ricerche su milioni di vettori praticamente istantanee a discapito di una piccola imprecisione — accettabilissima per casi d’uso reali.
Costruire un Sistema di Ricerca Semantica End-to-End
Mettiamo tutto insieme in un esempio completo: un sistema di ricerca semantica su una knowledge base. Questo è esattamente il pattern che sta alla base di qualsiasi sistema RAG. Se vuoi integrarlo con Claude per generare risposte in linguaggio naturale, l’articolo dedicato al RAG con Claude API mostra il passo successivo.
import os
import psycopg2
from openai import OpenAI
import json
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
DB_CONFIG = {
"host": os.getenv("DB_HOST", "localhost"),
"database": os.getenv("DB_NAME", "myapp"),
"user": os.getenv("DB_USER", "postgres"),
"password": os.getenv("DB_PASSWORD", ""),
}
def get_embedding(text: str) -> list[float]:
response = client.embeddings.create(
input=text.replace("\n", " "),
model="text-embedding-3-small"
)
return response.data[0].embedding
# Indicizza un documento con il suo embedding
def index_document(content: str, metadata: dict = None) -> int:
embedding = get_embedding(content)
emb_str = "[" + ",".join(map(str, embedding)) + "]"
conn = psycopg2.connect(**DB_CONFIG)
cur = conn.cursor()
cur.execute(
"INSERT INTO documents (content, metadata, embedding) "
"VALUES (%s, %s, %s::vector) RETURNING id",
(content, json.dumps(metadata or {}), emb_str)
)
doc_id = cur.fetchone()[0]
conn.commit(); cur.close(); conn.close()
return doc_id
# Ricerca semantica: trova i K documenti piu rilevanti
def semantic_search(query: str, top_k: int = 5) -> list[dict]:
emb_str = "[" + ",".join(map(str, get_embedding(query))) + "]"
conn = psycopg2.connect(**DB_CONFIG)
cur = conn.cursor()
cur.execute(
"SELECT id, content, metadata, "
"1 - (embedding <=> %s::vector) AS similarity "
"FROM documents ORDER BY embedding <=> %s::vector LIMIT %s",
(emb_str, emb_str, top_k)
)
rows = cur.fetchall()
cur.close(); conn.close()
return [{"id": r[0], "content": r[1], "metadata": r[2], "similarity": float(r[3])} for r in rows]
# --- Uso pratico ---
docs = [
("Gli embeddings sono vettori che rappresentano il significato semantico.", {"cat": "AI"}),
("pgvector estende PostgreSQL con supporto per vettori.", {"cat": "DB"}),
("Python e' il linguaggio principale per il machine learning.", {"cat": "DEV"}),
]
for content, meta in docs:
print(f"Indicizzato doc #{index_document(content, meta)}")
for r in semantic_search("Rappresentazioni vettoriali e significato?", top_k=2):
print(f"[{r['similarity']:.3f}] {r['content'][:60]}")Chunking: Come Dividere i Documenti in Modo Efficace
Un errore comune è indicizzare documenti interi. I modelli di embedding hanno limiti di token (tipicamente 8192 per text-embedding-3) e, soprattutto, embeddings di testi molto lunghi tendono a “mediare” troppi concetti, perdendo precisione semantica. La soluzione è il chunking: dividere i documenti in blocchi più piccoli.
- Chunk size: tipicamente 256—1024 token. Chunk più piccoli = maggiore precisione; chunk più grandi = più contesto.
- Overlap: sovrapponi 50—100 token tra chunk consecutivi per non perdere contesto alle giunzioni.
- Semantic chunking: divide in base ai cambi di argomento invece che a lunghezza fissa.
- Metadata: salva sempre source, posizione e titolo del chunk per ricostruire il contesto durante il retrieval.
import tiktoken
# Divide un testo in chunk con overlap (usa tiktoken per contare i token)
def chunk_text(
text: str,
chunk_size: int = 512,
overlap: int = 64
) -> list[dict]:
enc = tiktoken.get_encoding("cl100k_base")
tokens = enc.encode(text)
chunks = []
start = 0
while start < len(tokens):
end = min(start + chunk_size, len(tokens))
chunk_tokens = tokens[start:end]
chunks.append({
"text": enc.decode(chunk_tokens),
"start_token": start,
"end_token": end,
"token_count": len(chunk_tokens),
})
if end == len(tokens):
break
start = end - overlap # overlap tra chunk
return chunks
# Esempio
testo = """
Gli embeddings catturano il significato semantico dei testi.
Vengono generati da modelli neurali addestrati su grandi corpus.
Sono fondamentali per ricerca semantica e sistemi RAG.
""".strip()
for i, chunk in enumerate(chunk_text(testo, chunk_size=30, overlap=5)):
print(f"Chunk {i} ({chunk['token_count']} tok): {chunk['text'][:50]}")Per la scelta del chunk size ottimale non esiste una risposta universale. Un buon punto di partenza: 512 token con 64 di overlap per documentazione tecnica, 256 token con 32 di overlap per testo denso come paper scientifici. Testa sempre con i tuoi dati reali e misura la qualità del retrieval prima di ottimizzare.
Se stai automatizzando pipeline di processing documenti — magari con workflow in n8n con subflow ed error handling avanzato — puoi integrare il chunking come step intermedio prima dell’indicizzazione per costruire un sistema robusto e scalabile.
FAQ e Domande Frequenti
Qual è la differenza tra embedding e fine-tuning?
Sono approcci complementari, non alternativi. Il fine-tuning modifica i pesi del modello per adattarlo a un task specifico: richiede dati di training, tempo e costi significativi. Gli embeddings sono una tecnica di retrieval: il modello base rimane invariato e usi i vettori per trovare le informazioni rilevanti da passare come contesto. Il RAG (che usa embeddings) è quasi sempre la prima scelta, perché è più economico, aggiornabile in tempo reale e non richiede dati di training. Il fine-tuning è utile quando devi cambiare lo stile di risposta o il comportamento del modello, non per fornire nuove informazioni.
Quante dimensioni deve avere un embedding?
Dipende dal modello e dal trade-off che accetti. text-embedding-3-small produce 1536 dimensioni, text-embedding-3-large ne produce 3072. Più dimensioni significa maggiore capacità espressiva ma più storage e calcolo. OpenAI permette anche di ridurre le dimensioni tramite il parametro dimensions senza perdita significativa di qualità: puoi usare 512 o 256 dimensioni e ridurre i costi di storage del 60—80%. Per la maggior parte dei progetti, 1536 dimensioni sono più che sufficienti.
Gli embeddings funzionano bene in italiano?
Sì, i modelli moderni sono multilingua e funzionano bene anche in italiano. text-embedding-3-small di OpenAI gestisce decine di lingue con performance molto buone. Per use case specificamente italiani ad alta precisione, modelli come embed-multilingual-v3 di Cohere sono stati addestrati con corpus multilingua più bilanciati. In pratica, per la maggior parte delle applicazioni business italiane, text-embedding-3-small è sufficiente. Testa sempre con un campione dei tuoi dati reali prima di decidere il provider.
Posso generare embeddings in locale senza API esterne?
Assolutamente sì. Con Ollama puoi eseguire modelli di embedding in locale: nomic-embed-text (768 dimensioni) e mxbai-embed-large (1024 dimensioni) sono ottimi punti di partenza. Con sentence-transformers in Python hai accesso a centinaia di modelli open source, tra cui all-MiniLM-L6-v2 (384 dimensioni, velocissimo) e multilingual-e5-large (1024 dimensioni, eccellente per l’italiano). L’approccio locale è fondamentale quando hai vincoli di privacy o quando il volume di dati renderebbe le API troppo costose.
Conclusione
Gli embeddings sono la tecnologia fondamentale su cui si costruisce qualsiasi sistema AI contestuale. Padroneggiare come generarli, confrontarli e indicizzarli ti apre la porta a un’intera categoria di applicazioni: ricerca semantica, RAG, motori di raccomandazione, clustering di contenuti, rilevamento duplicati. Nel 2026, saper lavorare con i vettori è una skill che distingue un developer medio da uno che sa costruire prodotti AI reali, deployabili e scalabili.
Il passo successivo naturale dopo questa guida è costruire un sistema RAG completo: gli embeddings per il retrieval più un LLM come Claude per la generazione. Trovi tutto nel dettaglio nell’articolo su RAG con Claude API: Retrieval-Augmented Generation Step-by-Step. Se vuoi automatizzare la pipeline di indicizzazione, l’articolo su automatizzare workflow con n8n + Claude AI ti darà idee concrete su come orchestrare il tutto.
Suggerimenti e Risorse
🔧 Tool: Usa LangChain o LlamaIndex se vuoi un’astrazione di alto livello per gestire embeddings, chunking e retrieval. Per progetti custom, lavorare direttamente con le API ti dà più controllo e meno overhead.
💡 Pro tip: Prima di indicizzare tutto il dataset, testa la qualità del retrieval su 20—30 query rappresentative. Un retrieval di qualità scarsa non può essere compensato da un LLM migliore: garbage in, garbage out.
🎯 Next step: Dopo aver capito gli embeddings, leggi RAG con Claude API per costruire il tuo primo sistema di retrieval-augmented generation end-to-end con esempi di codice completi e pronti per la produzione.

