Leçon 2 — Fondamentaux

Embeddings et similarité

1h

Un embedding est un vecteur v∈Rd\mathbf{v} \in \mathbb{R}^d qui encode le sens d'un texte. Deux textes proches sémantiquement ont des vecteurs proches. Le modèle text-embedding-005 de Vertex AI produit des vecteurs de dimension d=768d = 768.

La similarité cosinus

Pour comparer une question q\mathbf{q} et un passage p\mathbf{p}, on mesure l'angle entre leurs vecteurs :

cos⁡(θ)=q⋅p∥q∥ ∥p∥=∑i=1dqipi∑i=1dqi2 ∑i=1dpi2\cos(\theta) = \frac{\mathbf{q} \cdot \mathbf{p}}{\lVert \mathbf{q} \rVert \, \lVert \mathbf{p} \rVert} = \frac{\sum_{i=1}^{d} q_i p_i}{\sqrt{\sum_{i=1}^{d} q_i^2} \, \sqrt{\sum_{i=1}^{d} p_i^2}}

Le résultat est compris entre −1-1 et 11. Si les vecteurs sont normalisés (∥v∥=1\lVert \mathbf{v} \rVert = 1), la similarité cosinus se réduit au produit scalaire, ce qui accélère la recherche. La distance cosinus vaut alors 1−cos⁡(θ)1 - \cos(\theta).

Calculer des embeddings

embed.py
import numpy as np
from vertexai.language_models import TextEmbeddingInput, TextEmbeddingModel

model = TextEmbeddingModel.from_pretrained("text-embedding-005")


def embed(texts: list[str], task: str) -> np.ndarray:
    inputs = [TextEmbeddingInput(t, task_type=task) for t in texts]
    return np.array([e.values for e in model.get_embeddings(inputs)])


docs = embed(
    ["Cloud Run exécute des conteneurs sans serveur.",
     "BigQuery est un entrepôt de données analytique."],
    task="RETRIEVAL_DOCUMENT",
)
query = embed(["Où déployer mon conteneur ?"], task="RETRIEVAL_QUERY")[0]

scores = docs @ query / (np.linalg.norm(docs, axis=1) * np.linalg.norm(query))
print(scores)  # le premier document obtient le meilleur score

Le paramètre task_type compte : RETRIEVAL_DOCUMENT pour indexer, RETRIEVAL_QUERY pour interroger. Les deux espaces sont optimisés pour être comparés entre eux.

Le parcours d'une requête

sequenceDiagram
    autonumber
    participant U as Utilisateur
    participant A as Agent
    participant E as API Embeddings
    participant V as Index vectoriel
    participant G as Gemini
    U->>A: Question
    A->>E: embed(question, RETRIEVAL_QUERY)
    E-->>A: vecteur q (768 dimensions)
    A->>V: recherche top-k (distance cosinus)
    V-->>A: k passages + scores
    A->>G: prompt + passages
    G-->>A: réponse avec citations
    A-->>U: Réponse

Influence de la taille des chunks

Mesures indicatives sur un corpus de documentation technique (rappel@5 et fidélité de la réponse) :

xychart-beta
    title "Qualité selon la taille des chunks (tokens)"
    x-axis [128, 256, 512, 1024, 2048]
    y-axis "Score" 0 --> 100
    bar [58, 71, 84, 81, 66]
    line [62, 74, 86, 85, 72]

Des chunks trop petits perdent le contexte ; trop grands, ils diluent le signal. La zone 512 à 1024 tokens est un bon point de départ.

Où part le temps de réponse ?

pie showData
    title Répartition de la latence (ms)
    "Embedding de la question" : 40
    "Recherche vectorielle" : 25
    "Re-ranking" : 60
    "Génération Gemini" : 650

La génération domine : optimiser le retrieval sert surtout la qualité, pas la latence.

À retenir

  • Un embedding est un vecteur dense ; la similarité cosinus mesure l'angle entre deux vecteurs.
  • Utilisez le bon task_type à l'indexation et à la requête.
  • Réglez la taille des chunks en mesurant, pas au hasard.
Suivant