Generare embeddings con OpenAI
Utilizzerà i modelli text-embedding-3-small e text-embedding-3-large per creare embeddings di frasi, paragrafi e documenti e ne confronterà i compromessi in termini di qualità e costo.
Generare embeddings con OpenAI è una lezione AI Engineering Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Engineering Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Engineering Academy include 4 lezioni in totale.
Panoramica dei modelli di embedding di OpenAI
OpenAI offre due modelli di embedding per la produzione: text-embedding-3-small e text-embedding-3-large. Il modello small produce vettori con 1536 dimensioni e ha un costo per token 5 volte inferiore, mentre il modello large produce vettori con 3072 dimensioni e offre una maggiore accuratezza nei benchmark. Per la maggior parte delle applicazioni RAG, il modello small è il punto di partenza più adatto.
Effettuare la prima chiamata per un embedding
Il metodo client.embeddings.create() accetta il nome di un model e una stringa o un elenco nel parametro input. Restituisce un oggetto di risposta con un elenco data, in cui ogni elemento dispone di un attributo embedding contenente il vettore come elenco Python di numeri floating-point.
Memorizzate sempre la chiave API in una variabile d’ambiente: non inseritela mai direttamente nei file sorgente.
import os
from openai import OpenAI
client = OpenAI(api_key=os.environ['OPENAI_API_KEY'])
response = client.embeddings.create(
model='text-embedding-3-small',
input='What is retrieval-augmented generation?'
)
vector = response.data[0].embedding
print(f'Vector length: {len(vector)}') # 1536
print(f'Type: {type(vector[0])}') # float
print(f'Sample values: {vector[:3]}') # [-0.02, 0.01, ...]Elaborare efficientemente i batch di embedding
Passare un elenco di stringhe a input consente di generare tutti gli embedding con un unico round trip verso l’API. È l’approccio consigliato per indicizzare un corpus di documenti. L’elenco data della risposta mantiene l’ordine originale, facilitando la creazione di un dizionario di ricerca.
I batch sono limitati a 2048 elementi per richiesta e il numero totale di token di tutti gli input deve rimanere entro il limite di token del modello.
from openai import OpenAI
client = OpenAI()
documents = [
'RAG stands for Retrieval-Augmented Generation.',
'Embeddings convert text to numerical vectors.',
'Pinecone is a managed vector database service.'
]
response = client.embeddings.create(
model='text-embedding-3-small',
input=documents
)
embeddings = [item.embedding for item in response.data]
print(f'Got {len(embeddings)} embeddings')
print(f'Each has {len(embeddings[0])} dimensions')Ridurre la dimensionalità degli embedding
Entrambi i modelli text-embedding-3 supportano un parametro dimensions che tronca il vettore di output. Ad esempio, impostando dimensions=256 si ottiene un vettore di 256 elementi anziché di 1536. I vettori più brevi richiedono meno spazio di archiviazione e meno risorse di calcolo, con un compromesso moderato in termini di accuratezza.
È utile quando volete sperimentare rapidamente o quando il costo dello spazio di archiviazione è più importante della qualità massima del recupero.
from openai import OpenAI
client = OpenAI()
response = client.embeddings.create(
model='text-embedding-3-small',
input='Shorter vectors save storage and query time.',
dimensions=256 # truncate from 1536 to 256
)
print(len(response.data[0].embedding)) # 256Formato di codifica: Base64 o elenco di numeri floating-point
Per impostazione predefinita, l’API restituisce gli embedding come array JSON di numeri floating-point (encoding_format='float'). Potete richiedere encoding_format='base64' per ricevere un blob binario compatto codificato in base64, che viene trasferito più rapidamente sulla rete per i batch di grandi dimensioni.
Quando usate base64, dovete decodificarlo con NumPy: np.frombuffer(base64.b64decode(b64_str), dtype='float32').
import base64
import numpy as np
from openai import OpenAI
client = OpenAI()
response = client.embeddings.create(
model='text-embedding-3-small',
input='Base64 encoding transfers faster.',
encoding_format='base64'
)
b64 = response.data[0].embedding
vector = np.frombuffer(base64.b64decode(b64), dtype='float32')
print(f'Decoded {len(vector)} floats')Monitorare l’uso dei token e i costi
Ogni risposta degli embedding include un oggetto usage con prompt_tokens. Il costo viene calcolato per token: text-embedding-3-small costa 0,02 $ per milione di token e text-embedding-3-large costa 0,13 $ per milione di token (a metà del 2024).
Monitorare l’utilizzo consente di stimare il costo dell’indicizzazione dell’intero corpus prima di avviare un’esecuzione in produzione.
from openai import OpenAI
client = OpenAI()
texts = ['Document one content here.', 'Document two content here.']
response = client.embeddings.create(
model='text-embedding-3-small',
input=texts
)
tokens_used = response.usage.prompt_tokens
cost_usd = tokens_used * 0.00000002 # $0.02 per 1M tokens
print(f'Tokens: {tokens_used}, Cost: ${cost_usd:.6f}')Confrontare text-embedding-3-small e large
La scelta tra small e large dipende dai requisiti di accuratezza e dal budget:
- text-embedding-3-small: 1536D, più veloce, 5 volte meno costoso, ideale per la maggior parte dei carichi di lavoro RAG
- text-embedding-3-large: 3072D, punteggi più elevati nel benchmark MTEB, migliore per contenuti multilingue e attività semantiche complesse
Un approccio comune consiste nel creare il prototipo e convalidare la qualità del recupero con il modello small, passando al modello large solo se le metriche di valutazione sono inferiori all’obiettivo.
Normalizzare gli embedding per la ricerca con prodotto scalare
OpenAI restituisce embedding normalizzati L2, il che significa che ogni vettore ha già magnitudine pari a 1. Potete quindi usare il prodotto scalare come approssimazione rapida della similarità coseno senza un passaggio aggiuntivo di normalizzazione. È importante quando cercate tra milioni di vettori, perché le micro-ottimizzazioni si sommano.
import numpy as np
from openai import OpenAI
client = OpenAI()
response = client.embeddings.create(
model='text-embedding-3-small',
input='Are OpenAI embeddings normalized?'
)
vec = np.array(response.data[0].embedding)
magnitude = np.linalg.norm(vec)
print(f'Vector magnitude: {magnitude:.6f}') # very close to 1.0Gestire documenti di grandi dimensioni
Il modello text-embedding-3-small accetta fino a 8191 token per input. Se il documento supera questo limite, l'API restituisce un errore. La soluzione standard consiste nel suddividere prima il documento in segmenti (in genere da 200 a 500 token ciascuno) e generare separatamente l'embedding di ogni segmento.
Questa suddivisione non è solo un requisito tecnico: migliora anche il recupero, perché l'embedding di parti più piccole e focalizzate restituisce risultati più precisi rispetto all'embedding di pagine intere.
import tiktoken
enc = tiktoken.encoding_for_model('text-embedding-3-small')
def count_tokens(text):
return len(enc.encode(text))
max_tokens = 8191
text = 'Very long document content...' # pretend this is huge
if count_tokens(text) > max_tokens:
print('Document too long — chunk before embedding')
else:
print(f'Safe to embed: {count_tokens(text)} tokens')Embedding asincrono per un throughput elevato
Quando indicizzate migliaia di documenti, utilizzate il client OpenAI asincrono con asyncio.gather per inviare più richieste di embedding simultaneamente. È molto più veloce delle chiamate sequenziali, perché il collo di bottiglia è la latenza di rete, non la CPU.
Quando eseguite richieste simultanee, aggiungete sempre la gestione dei limiti di frequenza con un backoff esponenziale, per evitare di raggiungere il limite di token al minuto.
import asyncio
from openai import AsyncOpenAI
async def embed_batch(texts):
client = AsyncOpenAI()
response = await client.embeddings.create(
model='text-embedding-3-small',
input=texts
)
return [item.embedding for item in response.data]
async def main():
batches = [['doc1', 'doc2'], ['doc3', 'doc4']]
results = await asyncio.gather(*[embed_batch(b) for b in batches])
all_embeddings = [emb for batch in results for emb in batch]
print(f'Embedded {len(all_embeddings)} documents')
asyncio.run(main())Memorizzare nella cache gli embedding per ridurre i costi
Generare più volte gli embedding dello stesso testo comporta uno spreco di denaro e tempo. Memorizzate gli embedding in un dizionario indicizzato dalla stringa di testo (o da un suo hash) e rendete persistente questa cache su disco tra una sessione e l'altra.
Nei sistemi di produzione, archiviate gli embedding in un database vettoriale che elimini i duplicati in base all'ID del documento. Generate nuovamente l'embedding di un documento solo quando il suo contenuto cambia effettivamente, non a ogni esecuzione dell'indicizzazione.
import json, hashlib, os
from openai import OpenAI
CACHE_FILE = '/tmp/embedding_cache.json'
client = OpenAI()
try:
cache = json.load(open(CACHE_FILE))
except FileNotFoundError:
cache = {}
def get_embedding(text):
key = hashlib.md5(text.encode()).hexdigest()
if key not in cache:
r = client.embeddings.create(model='text-embedding-3-small', input=text)
cache[key] = r.data[0].embedding
json.dump(cache, open(CACHE_FILE, 'w'))
return cache[key]
vec = get_embedding('Caching saves API costs.')
print(f'Retrieved {len(vec)}-dim embedding')Verifica rapida
Verificate la vostra comprensione dei concetti di AI Engineering trattati in questa lezione.
Riepilogo della lezione
In questa lezione avete imparato che text-embedding-3-small è la scelta più conveniente per la maggior parte dei carichi di lavoro RAG, che generare gli embedding di più testi in un'unica chiamata API è più efficiente delle chiamate sequenziali e che il parametro dimensions può ridurre le dimensioni dei vettori, scambiando una parte della precisione con un risparmio di spazio di archiviazione. Nella prossima lezione costruiremo un sistema di ricerca semantica utilizzando questi embedding e NumPy.
Domande Frequenti
La lezione «Generare embeddings con OpenAI» è gratuita?
Sì — il testo completo di «Generare embeddings con OpenAI» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Engineering Academy, passa a CoddyKit PRO. Il corso AI Engineering Academy include 4 lezioni in totale.
Cosa imparerò in «Generare embeddings con OpenAI»?
Utilizzerà i modelli text-embedding-3-small e text-embedding-3-large per creare embeddings di frasi, paragrafi e documenti e ne confronterà i compromessi in termini di qualità e costo. Eserciti AI Engineering Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare AI Engineering Academy?
Non è richiesta alcuna esperienza precedente. AI Engineering Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.
Quanto tempo richiede la lezione «Generare embeddings con OpenAI»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione AI Engineering Academy?
Sì. Ogni lezione AI Engineering Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Che cosa sono gli embeddings vettoriali?
- Generare embeddings con OpenAI
- Ricerca semantica con NumPy
- Clustering e visualizzazione degli embeddings