Gli embeddings sono la base tecnica di ogni soluzione di RAG (Retrieval-Augmented Generation) su Azure. Un modello di embedding trasforma testo (o immagini) in un vettore denso di numeri in virgola mobile, dove la posizione nello spazio cattura il significato semantico: frasi con contenuto simile finiscono vicine, anche se non condividono le stesse parole. Questa è la differenza chiave rispetto alla ricerca keyword/BM25, che confronta i token letterali.
Modelli di embedding e dimensione del vettore
Su Azure OpenAI (in Azure AI Foundry) i modelli tipici sono text-embedding-3-small e text-embedding-3-large. Le scelte architetturali principali:
- text-embedding-3-small: 1536 dimensioni di default, ottimo rapporto costo/qualità per gran parte degli scenari.
- text-embedding-3-large: 3072 dimensioni, recall più alto su corpus complessi/multilingua, ma più costoso in storage e latenza.
- I modelli v3 supportano il parametro dimensions per ridurre la lunghezza del vettore (es. large a 1024) mantenendo buona qualità: utile per contenere costo e footprint dell’indice.
Il punto architetturale da fissare: il numero di dimensioni del campo vettoriale nell’indice deve corrispondere esattamente all’output del modello scelto. Se generi gli embeddings con large a 3072 ma dichiari un campo a 1536, l’indicizzazione fallisce.
Similarità e metriche
La misura più usata è la cosine similarity, che confronta l’angolo tra due vettori ignorando la loro magnitudine: è la metrica corretta per gli embeddings OpenAI, già normalizzati. Alternative configurabili sono dotProduct (equivalente al coseno solo su vettori normalizzati) ed euclidean (L2). In pratica, per i modelli text-embedding-3 si lascia cosine.
Campi e vector profile in Azure AI Search
In Azure AI Search un campo vettoriale si definisce con:
- tipo Collection(Edm.Single),
- dimensions allineato al modello,
- un vectorSearchProfile che lega il campo a un algoritmo e a un eventuale vectorizer.
{ "name": "contentVector", "type": "Collection(Edm.Single)",
"dimensions": 1536, "vectorSearchProfile": "hnsw-profile" }
HNSW vs exhaustive KNN
La scelta dell’algoritmo è tipicamente d’esame:
- HNSW (Hierarchical Navigable Small World): indice a grafo, ricerca ANN (Approximate Nearest Neighbor). Bassa latenza su indici grandi, ma recall approssimato; si regola con parametri come m, efConstruction ed efSearch.
- exhaustiveKnn: scansione completa, recall esatto ma costo lineare col numero di documenti. Adatto a dataset piccoli o come ground truth per valutare il recall di HNSW.
Un pattern comune è indicizzare in HNSW ma consentire query exhaustive ad hoc. Per massimizzare la qualità si usa poi la hybrid search (vettoriale + keyword) con semantic ranker che riordina i risultati con un modello di re-ranking L2.
Chunking e integrated vectorization
I documenti lunghi vanno spezzati in chunk prima dell’embedding, perché i modelli hanno un limite di token e perché il retrieval deve restituire passaggi mirati, non interi PDF.
- Chunk troppo grandi: diluiscono il significato in un solo vettore, abbassano la precisione e rischiano di sforare la context window del modello generativo.
- Chunk troppo piccoli: frammentano il contesto, riducono il recall e costringono a recuperare più chunk per rispondere.
- Regola pratica: chunk di alcune centinaia di token con overlap (es. 10-25%) per non tagliare frasi a metà.
L’integrated vectorization automatizza la pipeline dentro Azure AI Search: uno skillset con SplitSkill (chunking) e AzureOpenAIEmbeddingSkill genera i vettori in fase di indexing, mentre un vectorizer nel profilo consente le query in testo naturale (Search converte a runtime la query in vettore usando lo stesso modello). Così eviti di scrivere codice di embedding lato client e garantisci coerenza modello indexing/query.
Trappole tipiche d’esame
- Scenario: hai cambiato modello da small a large ma le ricerche danno errore in indicizzazione → la dimensione del campo vettoriale deve corrispondere al modello (1536 vs 3072); ricrea l’indice con le dimensioni corrette.
- Scenario: milioni di documenti, serve bassa latenza tollerando lieve perdita di recall → scegli HNSW, non exhaustive KNN.
- Scenario: dataset piccolo dove serve recall esatto o baseline di valutazione → exhaustiveKnn.
- Scenario: le risposte RAG sono generiche o incomplete → probabile chunking errato; riduci la dimensione dei chunk e aggiungi overlap.
- Scenario: vuoi query in testo naturale senza generare embeddings nel codice → configura un vectorizer/integrated vectorization con lo stesso modello usato in indexing.