L'API Embedding ne génère pas de texte : elle projette un texte en une suite de nombres. Recherche sémantique, RAG, clustering, déduplication et recommandation reposent sur cette suite. L'intégration elle-même n'est pas difficile ; les pièges sont les dimensions, les lots, la normalisation et l'indexation. Une erreur à ce niveau dégrade silencieusement la recherche.
Cet article prend l'interface compatible OpenAI comme exemple et détaille requête et réponse, appels Python et curl, stratégie de lots, stockage vectoriel, enchaînement RAG, erreurs courantes et vérifications de migration.
Remarque : OpenAI, PostgreSQL et les autres marques citées appartiennent à leurs détenteurs respectifs. Cet article est purement technique et n'implique aucune affiliation avec les fournisseurs ; interfaces, modèles et prix de référence figurent dans la documentation et sur les pages du site.
1. Interface et authentification
L'interface embedding compatible OpenAI est :
POST https://ai.zhongshangqihang.com/v1/embeddings
Authorization: Bearer sk-votre-cle
Content-Type: application/json
L'URL de base s'arrête à /v1 ; ne concaténez pas /embeddings vous-même avant de confier l'URL au SDK. Le SDK ajoute le chemin selon la méthode ; une double concaténation produit un 404.
Le corps de requête ne nécessite que deux champs au minimum :
{
"model": "text-embedding-3-small",
"input": ["Qui supporte les frais de retour", "Qui paie les frais logistiques du retour"]
}
input accepte une chaîne unique ou un tableau de chaînes. Un tableau renvoie plusieurs vecteurs dans l'ordre, adapté au traitement par lots. Les chaînes vides sont généralement rejetées ; filtrez-les lors du nettoyage.
2. Champs de requête et de réponse
| Champ | Position | Description |
|---|---|---|
model |
Requête | Obligatoire ; le nom de référence figure dans la documentation de la plateforme et les tarifs des modèles |
input |
Requête | Chaîne ou tableau de chaînes ; chaque élément ne doit pas dépasser la limite du modèle |
dimensions |
Requête | Pris en charge par certains modèles seulement, pour réduire la dimension de sortie |
encoding_format |
Requête | Généralement float, base64 possible pour réduire le volume transféré |
data[].embedding |
Réponse | Tableau de vecteurs, dans l'ordre des entrées |
data[].index |
Réponse | Position de l'entrée dans le tableau |
usage |
Réponse | prompt_tokens et total_tokens, pour le rapprochement |
Le champ index de la réponse est important. En cas de retry après échec d'un lot ou de fusion concurrente, ne supposez pas que l'ordre de retour correspond toujours à l'ordre d'entrée ; alignez sur index.
3. Appel Python et similarité
Après installation du SDK, exemple minimal :
import os
from math import sqrt
from openai import OpenAI
client = OpenAI(
api_key=os.environ["ZSQH_API_KEY"],
base_url="https://ai.zhongshangqihang.com/v1",
)
resp = client.embeddings.create(
model="text-embedding-3-small",
input=["Qui supporte les frais de retour", "Qui paie les frais logistiques du retour"],
)
a, b = resp.data[0].embedding, resp.data[1].embedding
cos = sum(x * y for x, y in zip(a, b)) / (
sqrt(sum(x * x for x in a)) * sqrt(sum(y * y for y in b))
)
print(len(a), round(cos, 4), resp.usage.total_tokens)
Ce code valide trois choses : la clé fonctionne, la dimension est conforme, la similarité entre deux textes proches est plausible. En production, utilisez NumPy ou la fonction de distance de la base vectorielle ; n'exécutez pas du Python pur sur de gros volumes.
4. Dimensions, normalisation et cohérence de modèle
Dimensions des modèles embedding OpenAI courants :
text-embedding-3-small: 1536 dimensions.text-embedding-3-large: 3072 dimensions.text-embedding-ada-002: 1536 dimensions.
Le paramètre dimensions peut raccourcir la sortie de la série text-embedding-3, par exemple de 3072 à 1024, pour économiser stockage et bande passante. Le raccourcissement perd une partie de l'information ; validez le compromis sur votre jeu d'évaluation, pas sur le seul nombre de dimensions.
La documentation d'OpenAI indique que ses embeddings sont normalisés à une longueur de 1 ; la similarité cosinus peut donc se calculer par produit scalaire. Mais avec un modèle auto-hébergé, un modèle tiers ou une troncature manuelle des dimensions, refaites une normalisation L2. La règle la plus sûre : utiliser la même logique de normalisation à l'écriture et à la requête, et la documenter dans le code.
Contrainte forte : tous les vecteurs d'une base doivent provenir du même modèle, de la même dimension et de la même normalisation. Après changement de modèle, les anciens vecteurs ne peuvent pas être mélangés, sinon la similarité n'a plus de sens.
5. Découper les requêtes par lots
Le traitement par lots réduit les coûts HTTP, mais une requête plus grosse n'est pas toujours meilleure. La documentation actuelle d'OpenAI indique qu'une entrée ne doit pas dépasser environ 8K tokens et que le total des entrées d'une requête ne doit pas dépasser 300 000 tokens ; les limites exactes sont celles de votre fournisseur.
En pratique, découpez selon un budget de tokens, pas selon un nombre fixe d'éléments :
- Nettoyer le texte : espaces, doublons et paragraphes trop longs.
- Estimer les tokens par élément avec un tokenizer ; découper les textes trop longs.
- Grouper selon un budget total, par exemple quelques dizaines de milliers de tokens par lot, avec une marge pour les retries.
- Utiliser un backoff exponentiel sur 429 et 5xx ; ne pas retenter aveuglément une erreur de paramètre 4xx.
- Journaliser modèle, dimension, tokens et durée de chaque lot, pour le rapprochement et la reproduction.
Les retries doivent être idempotents. Utilisez une clé de cache « hash du texte + nom du modèle + dimension » pour éviter de facturer deux fois le même texte. Après échec d'un lot, identifiez les index déjà réussis et ne complétez que les manquants.
6. Stocker dans une base vectorielle
Avec pgvector de PostgreSQL, le schéma peut se simplifier ainsi :
CREATE TABLE chunks (
id bigserial PRIMARY KEY,
content text NOT NULL,
source text,
embedding vector(1536)
);
CREATE INDEX ON chunks USING hnsw (embedding vector_cosine_ops);
Requête par distance cosinus :
SELECT id, content
FROM chunks
ORDER BY embedding <=> $1
LIMIT 5;
Outre le vecteur, stockez au minimum : texte original, source, locataire ou périmètre de droits, position du bloc, nom du modèle et dimension. Sans ces métadonnées, la réponse ne peut pas citer sa source ni appliquer de filtre de droits. En production, prévoyez aussi le type d'index, les paramètres de rappel, la sauvegarde et la mise à jour incrémentale, sans attendre que le volume explose.
7. Ordre d'enchaînement RAG
Un flux RAG fonctionnel suit généralement cet ordre :
- Analyser et nettoyer les documents : retirer en-têtes, pieds de page et doublons.
- Découper par titre ou par paragraphe, avec un léger chevauchement entre blocs.
- Appeler l'embedding pour chaque bloc et écrire par lots dans la base vectorielle.
- À la question utilisateur, vectoriser la question avec le même modèle.
- Rappeler le Top-K par similarité cosinus, puis filtrer par droits et par source.
- Si nécessaire, ajouter une recherche par mots-clés en hybride, ou un modèle de reranking.
- Envoyer les résultats et la question au modèle de chat, en demandant une réponse avec citations.
La taille des blocs n'a pas de réponse universelle. Pour le chinois, un point de départ courant est de 300 à 800 caractères, à ajuster selon la qualité des réponses. Trop grand, la recherche manque de précision ; trop petit, le contexte est incomplet. Plutôt que de débattre des paramètres, construisez un petit jeu d'évaluation avec questions et réponses de référence, et exécutez-le à chaque modification du flux.
8. Erreurs courantes et diagnostic
401 Unauthorized: clé erronée, supprimée, Bearer absent ou variable d'environnement vide.404 Not Found: URL de base sans/v1, ou chemin concaténé deux fois.model not found: nom de modèle incorrect ou compte sans droit sur ce modèle.400entrée trop longue : un élément dépasse la limite du modèle ; découper avant l'appel.400entrée vide : le tableau contient une chaîne vide ; la filtrer au nettoyage.429: débit ou concurrence dépassés ; réduire la concurrence et appliquer un backoff exponentiel.- Dimension incohérente : écriture et requête utilisent un modèle ou un
dimensionsdifférent. - Similarités toutes à 0 ou anormalement proches : vérifier la normalisation, le champ lu et l'absence de vecteur nul dans la base.
Lors du diagnostic, imprimez d'abord le nom du modèle, le nombre d'entrées, la longueur de chaque entrée et la dimension retournée. La plupart des problèmes ne viennent pas d'un « modèle de mauvaise qualité », mais d'une incohérence entre requête et stockage.
9. Migration et vérifications avant mise en production
Pour migrer d'une API du fournisseur ou d'une autre passerelle vers une passerelle compatible OpenAI, on ne change généralement que deux choses : base_url et api_key. Mais l'embedding a un coût supplémentaire : tout changement de modèle ou de dimension impose de reconstruire les vecteurs historiques.
À vérifier avant mise en production :
- la nouvelle plateforme prend-elle en charge le nom de modèle et la dimension utilisés ;
- faut-il re-vectoriser toute la base, et la fenêtre de reconstruction et son coût sont-ils acceptables ;
- la requête et l'écriture basculent-elles en même temps, pour éviter de mélanger anciens et nouveaux vecteurs ;
- existe-t-il un déploiement progressif permettant de comparer le rappel sur une partie du trafic ;
- la supervision couvre-t-elle taux d'erreur, latence, consommation de tokens et taux de résultats vides ;
- existe-t-il un retour arrière, avec conservation de l'ancienne clé et de l'ancien index pendant un certain temps.
Si seul l'URL de base change, à modèle et dimension identiques, la migration est beaucoup plus simple ; si le modèle change aussi, traitez-la comme un projet de reconstruction de données à planifier. Modèles et prix de référence : tarifs des modèles.
Questions fréquentes
Quelle différence entre l'interface Embedding et Chat Completions ?
Embedding renvoie un vecteur de longueur fixe pour la recherche, le clustering et le calcul de similarité ; Chat Completions renvoie du texte généré. L'authentification est souvent identique, mais corps de requête et structure de réponse sont totalement différents.
Combien de textes peut-on envoyer par appel ?
L'interface accepte un tableau de chaînes, mais la vraie limite est le total de tokens et la longueur par élément. La documentation actuelle d'OpenAI indique 300 000 tokens par requête et environ 8K tokens par élément. En pratique, découpez selon un budget de tokens, sans frôler la limite.
Faut-il normaliser les vecteurs soi-même ?
Si la plateforme indique explicitement que les vecteurs sont normalisés, la similarité cosinus peut se calculer par produit scalaire. Avec un modèle auto-hébergé, une troncature manuelle ou une origine incertaine, appliquez la même normalisation L2 à l'écriture et à la requête.
Après changement de modèle embedding, les anciens vecteurs restent-ils utilisables ?
Pas en mélange direct. Les espaces vectoriels de modèles différents ne sont pas comparables ; une dimension identique ne suffit pas à assurer un alignement sémantique. Après changement, reconstruisez l'index et enregistrez le nom du modèle et la dimension dans les métadonnées.
Faut-il segmenter le texte chinois avant l'embedding ?
En général non. Les modèles embedding modernes traitent le texte brut ; une segmentation peut au contraire faire perdre du contexte. Ce qu'il faut traiter, c'est le nettoyage, le découpage, la déduplication et le contrôle de longueur.
Étapes suivantes
Pour valider une requête, créez une clé à faible quota depuis la page d'inscription et remplacez le texte des exemples curl ou Python par le vôtre. Pour un choix de production, consultez les tarifs des modèles et comparez le rappel sur un petit lot de textes métier réels. Prix et modèles évoluent ; référez-vous à l'affichage actuel du site.
Lectures associées :
