Back to "RAG pour les implémentateurs: CPU-Suite sémantique conviviale avec ONNX et Qdrant"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

AI-Article ASP.NET Machine Learning Multimodal ONNX Qdrant RAG Semantic Search Vector Search

RAG pour les implémentateurs: CPU-Suite sémantique conviviale avec ONNX et Qdrant

Thursday, 05 November 2026

Vous recherchez “ déployer une application dans un conteneurM SK1 L'article dont vous avez besoin est appelé “Getting Started with Docker”. Un système de recherche utile devrait relier ces idéesMSC4 même si les mots ne correspondent pas exactement à la définition de '

REMARQUE: Mise à jour M SK1 Novembre 2026. Remanié pour .NET M10 avec correcte tokenisation et mise en commun de l’information, passageMSC6 indexation de niveauMNK7 contexte sourceMRK8 récupération filtrée et mises à jour d’index plus sûresMMK9 Une nouvelle section montre comment EmbeddingGemma 2 étend l’architecture aux imagesMST11 audio et vidéoMTSK12

Cet article établit que la base de récupération dans C#: exploite localement un petit modèle d'embedding avec ONNX Runtime , stocke ses vecteurs dans Qdrant, et retrieve des postes connexesM SK3 Il n'a pas besoin de langageMSC4 service de génération ni GPUMska5 Il consomme encore le CPUMSKA6 mémoireM Ska7 effort opérationnel et de stockage Mska8 selfMská9 l'hébergement supprime les frais par embedding API requisMsaka11 non le coût du logiciel d'exploitationMaskan12

Il s'agit de Partie 4a de la série RAG:

Vous devriez être à l’aise avec les méthodes asynchrones et l’injection de dépendance du C#, . J’expliquerai le modèle et la terminologie des bases de données au fur et à mesure que nous y assisterons

Ce que nous construisons

Aux termes de la Loi sur les droits de l'homme, intégration est une série de chiffres produite par un modèle formé.

ANNEXE I est un format pour représenter une machine Le temps de fonctionnement d'ONNX exécute ce modèle. Qdrant stocke des vecteurs et trouve les candidats voisins, à côté de champs ordinaires qui nous permettent d’identifier et de filtrer les résultatsM SK1

Le chemin d'indexation commence avec le matériau source, avant que tout ne parvienne au modèleM SK1

flowchart LR
    D[Source document] --> E[Extract body and structure]
    E --> S[Passages with headings and source ranges]
    S --> T[MiniLM tokenizer and ONNX encoder]
    T --> P[Pool and normalize each passage]
    P --> V[Qdrant passage vectors and provenance]
    Q[Search query] --> T
    V --> F[Filter and rank passages]
    F --> R[Best passage per post]
    R --> B[Load current post details]

L’indexation stocke un vecteur pour chaque passage. La recherche intègre la demande avec le le même modèle de pipeline, filtre les passages admissibles et retourne la meilleure correspondance pour chaque posteM SK1 Connexe- le retrait du poste utilise un vecteur de sommaire distinct , formé à partir de ce posteVektor des passages de '

RAG signifie recherche-génération accrue: rechercher des éléments de preuveM SK2 puis le fournir à un modèle qui génère une réponse . Nous mettons en œuvre la recherche iciMSC4 Un résultat de recherche est utile par lui-mêmeMNK5 et rien dans ce passage n'génére une réponse ou établit qu'une réponse est factuellement correcte MNK6

Choisissez la représentation avant de choisir la base de données

Nous utilisons sentence-transformers/all-MiniLM-L6-v2, une phrase anglaise -un modèle d'incorporation qui produit 384-dimensionnelle vecteurs. Sa petite taille fait de ce modèle une base pratique pour le traitement par ordinateur.

Le contrat important est plus grand que le nom de fichier modèle:

Component Qu’est-ce qui doit être convenu entre indexation et recherche ?
Modèle MSC1 Les mêmes poids et exportation SMC2
Tokenizer Vocabulaire
Préparation des intrants Quel texte est incorporé et combien de temps les intrants sont divisés
Établissement d'un bassin Comment les sorties detoken par - deviennent un vecteur unique
Normalisation Comment l'amplitude du vecteur est gérée
Système de base de données Dimensions

Deux modèles peuvent produire à la fois des floats 384 tout en plaçant ces floats dans des espaces incompatibles.

La source vit dans Mostlylucid.SemanticSearch. Le corridor fait appel à ses interfaces publiques ; les passages internes plus courts ci-dessous expliquent comment il fonctionne sans reproduire des classes de services entières.

Extraction et segmentation déterminent ce qui peut être trouvé

Supposons qu’une page contient un guide de déploiement, un menu de navigation , trois pages connexesM SK2 des panneaux d’affichage et un avis sur les cookies. L’intégration de l’ensemble de la réponse HTML fait que toutes ces choses sont candidates à la rechercheMSC4 La répétition de la même navigation sur chaque page peut rendre les pages non liées semblables artificiellement.

Extraction sélectionne le contenu source utile. Segmentation Diviser ce contenu en unités récupérables. Extraction du contexte conserve les renseignements nécessaires à l’interprétation de chaque unité: son titreM SK1 la position des sections , l’emplacement de la source et, le cas échéant, la structure environnante

StyloExtract aborde l'extraction HTML par le partage, segmentation de blocsM SK1 classification et rendering. Il préserve les titres , liens M SK4 listes МSK5 tableaux et code tout en identifiant le mobilier de la page modèle d'induction article explique comment la structure du site apprenté peut soutenir l’extraction subséquente. Mettre ce travail précédemment Cette ligne de conduite embedding: plus propre donne au modèle une meilleure entrée.

Pour cette bibliothèque, BlogPostDocument.Content devrait déjà contenir un texte extrait ou un marquage clair. La bibliothèque ne crawle pas les pages ni n’enlève la navigation HTMLM SK2 Si les titres sont importants, conserver des titres de marquages plutôt que d’éliminer toute structure pour en faire un texte simple .

L'inclusion ContentSegmenter est une petite ligne de base: jusqu’à M SK1 caractères par passage, 100 caractère du chevauchementMSC4 limites d’un paragraphe où il y a pratiqueMNK5 et l’en-tête précédente de marquage de démarrageMMK6 Il enregistre le début exact et la longueur dans le contenu fourniMRK7 Ces sont : Offsets de chaîne UTF-16, les positions de bytes dans le HTML original ne sont pas prises en compte

Chaque passage est incorporé avec son titre de poste et sa position.. Ces champs contextuels influent sur le vecteur, mais ne sont pas rattachés au texte source ci-dessus.

La segmentation des caractères est une héuristique de lecture, non pas un budget ponctuel. L'encoder gère séparément la limite réelle du symbole . Cette ligne de base peut séparer une clôture ou un tableau de code M SK2 l'extrateur de production devrait émettre des blocs de code intacts , des rangées de tableau avec les titresM SK4 et des voies de section où ces structures sont importantes MSC5 Pipeline RAG de DocSummarizer' élabore une extraction de segments plus riche et des métadonnées pour cette étape suivante. Son API est une autre couche d’ingestion, plutôt qu’une dépendance supplémentaire requise pour exécuter cet exemple

La qualité de l’extraction est mesurable. Gardez des exemples avec une navigation répétée, positionnements encadrésM SK2 blocs et tableaux de code longsMSC3 Vérifier si la source pertinente surviendraMNK4 que son contexte sera maintenuMMK5 et qu’un passage extrait peut être tracé vers sa source avant de juger du modèle d’incorporationMRK6

Faire tout d'abord un petit exemple

Démarrer Qdrant localement

De la source du répertoire:

docker compose -f semantic-search-docker-compose.yml up -d
curl --fail http://localhost:6333/readyz

Le fichier Compose autonome utilise un volume persistant et se lie à loopback:

services:
  qdrant:
    image: qdrant/qdrant:v1.20.0
    restart: unless-stopped
    ports:
      - "127.0.0.1:6333:6333"
      - "127.0.0.1:6334:6334"
    volumes:
      - qdrant_storage:/qdrant/storage

volumes:
  qdrant_storage:

6333 est HTTP L'article 5 de la Loi sur l'immigration et le statut des réfugiés Client Qdrant .NET utilise gRPC. Configure http://localhost:6334, même si la demande de disponibilité et le tableau de bord sont utilisés 6333. Un navigateur Web qui montre un tableau de travail sain ne prouve pas que votre application

L’image est fixée pour la reproductibilité. Examiner et tester une mise à niveau explicite plutôt que de traiter un mouvement latest identifier comme stratégie de déploiement. L'exemple local n'a pas d'authentification ; il est lié à votre machineM SK2 Pour les déploiements à distance, configurer le réseau privéMSC4 assurer l'authenticisation et la protection du transport en utilisant Directives de sécurité du Qdrant'.

Télécharger le modèle et le vocabulaire correspondant

Le répertoire comprend les scripts Bash et PowerShell:

bash Mostlylucid.SemanticSearch/download-models.sh
./Mostlylucid.SemanticSearch/download-models.ps1

Dans les deux cas, all-MiniLM-L6-v2.onnx et vocab.txt dans Mostlylucid/mlmodels. Ils reçoivent la même révision du modèle couplée que le service . Les fichiers sont téléchargés à des frères et sœurs temporaires et ne sont renommés qu’après un téléchargement réussi M SK2 de sorte qu’un transfert interrompu ne laisse pas un modèle partiel masqué comme un modèle complet .

Le service peut aussi récupérer les actifs manquants lorsqu'il est initialisé pour la première fois. Pour le déploiement, En les téléchargeant pendant la préparation, vous avez l'occasion de vérifier et de conserver les asset avant de servir le trafic . Gardez les fichiers modèles dans un répertoire persistant à l'extérieur de la source WebM SK3 Pour une approvisionnement plus forteMSC4 Contrôle des chaînesMNK5 conservez les contrôles lors du lancement et vérifiez-les avant le déploiement~.

Le changement de la révision configurée ne remplace pas les fichiers déjà présents sur le même chemin.

Créer un hôte de console

À partir de la racine du répertoire, créer un petit hôte qui renvoie à la mise en œuvre:

dotnet new console -n SemanticSearchDemo -f net10.0
dotnet add SemanticSearchDemo/SemanticSearchDemo.csproj reference Mostlylucid.SemanticSearch/Mostlylucid.SemanticSearch.csproj

Remplacer son Program.cs avec:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Mostlylucid.SemanticSearch.Extensions;
using Mostlylucid.SemanticSearch.Models;
using Mostlylucid.SemanticSearch.Services;

var configuration = new ConfigurationBuilder()
    .AddInMemoryCollection(new Dictionary<string, string?>
    {
        ["SemanticSearch:Enabled"] = "true",
        ["SemanticSearch:QdrantUrl"] =
            Environment.GetEnvironmentVariable("QDRANT_DEMO_URL") ?? "http://localhost:6334",
        ["SemanticSearch:ReadApiKey"] = Environment.GetEnvironmentVariable("QDRANT_DEMO_READ_KEY"),
        ["SemanticSearch:WriteApiKey"] = Environment.GetEnvironmentVariable("QDRANT_DEMO_WRITE_KEY"),
        ["SemanticSearch:CollectionName"] = "blog_posts_demo_v1",
        ["SemanticSearch:EmbeddingModelPath"] =
            "Mostlylucid/mlmodels/all-MiniLM-L6-v2.onnx",
        ["SemanticSearch:VocabPath"] = "Mostlylucid/mlmodels/vocab.txt",
        ["SemanticSearch:MinimumSimilarityScore"] = "-1"
    })
    .Build();

var services = new ServiceCollection();
services.AddLogging(logging => logging.AddConsole());
services.AddSemanticSearch(configuration);
using var provider = services.BuildServiceProvider();
var search = provider.GetRequiredService<ISemanticSearchService>();
await search.InitializeAsync();

await search.IndexPostsAsync(new[]
{
    new BlogPostDocument
    {
        Id = "docker-deployment",
        Slug = "docker-deployment",
        Title = "Deploying with Docker",
        Content = "Package a web application in a container image and run it with Docker Compose.",
        Languages = new[] { "en" }
    },
    new BlogPostDocument
    {
        Id = "chocolate-cake",
        Slug = "chocolate-cake",
        Title = "Baking a chocolate cake",
        Content = "Mix flour, butter and chocolate, then bake the cake in an oven.",
        Languages = new[] { "en" }
    }
});

var results = await search.SearchAsync(
    "How do I deploy an application in a container?", limit: 2);

foreach (var result in results)
    Console.WriteLine($"{result.Score:F3}  {result.Slug}");

Exécuter à partir de la racine du répertoire afin que les voies relatives des modèles soient résolues conformément:

dotnet run --project SemanticSearchDemo/SemanticSearchDemo.csproj

Le poste Docker devrait être classé au-dessus du poste de gâteau. L'exemple utilise délibérément un seuil de -1 pour afficher les deux cotes, y compris les matches faiblesM SK1 C'est une configuration diagnostique pour ce petit corpus , pas une politique de pertinence à copier dans la production.

En exécutant de nouveau le code d’indexation, on met à jour les mêmes points. Un document inchangé est passé avant l’inférenceM SK1 Aucun nouvel identifiant aléatoire n’est produit sur chaque exécution.

Faire en sorte que le pipeline d'implantation soit correct

Les tokens doivent correspondre au modèle formé

A Le tableau ci-dessous montre les résultats obtenus en fonction de l’échantillonnage. est une unité d’entrée modèle. Il peut s’agir d’une parole entière , d’un sous- mot ou de la punctuation. MiniLM utilise un tokenizer de type BERTMSC3 WordPiece tokeniserM SK4 une word absent from the wholeMska5 le vocabulaire des mots peut encore être représenté par plusieurs pièces connuesMka6

Une séparation du regex suivie d’une recherche de vocabulaire est insuffisante. Il est pire d’insérer une mot inconnu dans un index du vocabulaire: il attribue à la word le sens d’un token non relié à celui-ci.

La mise en œuvre utilise BertTokenizer de Microsoft.ML.Tokenizers:

var tokenizer = BertTokenizer.Create(vocabPath, new BertOptions
{
    LowerCaseBeforeTokenization = true,
    ApplyBasicTokenization = true
});

var ids = tokenizer.EncodeToIds(text, addSpecialTokens: false);

Nous ajoutons le symbole de classification. [CLS] et séparateur [SEP] après chaque fenêtre de modèle. Ils occupent deux des positions disponibles M SK1 , quittent 254 tokens de contenu par fenêtre.

Ce vocabulaire-adaptateur basé sur ce modèle est spécifique à cette famille de modèlesM SK1 Un modèle différent peut nécessiter un tokenizer différent, noms d’entréesMSC3 préfixe de la demande ou règle de regroupement de données . Remplacement du .onnx le fichier seul n’est pas une migration de modèle.

Passages et fenêtres modèles résolvent différents problèmes

Un passage est l’unité que nous voulons récupérer.

Deux mille caractères ne sont pas une limite de token. Code, URLsM SK2 la ponctuation et les différentes langues peuvent produire différents nombres de tokens pour le même nombre de caractères . Même un passage MSC4 de caractère peut nécessiter plusieurs fenêtres MiniLM une fois que son titre et sa position figurent dans l’encadré.

L’encoder tokenise l’entrée complète fournie, puis passe par les fenêtres avec un chevauchement configurable des 32 tokens de contenuM SK2 Chaque fenêtre inclut ses propres tokens spéciaux et demeure dans les positions 256

flowchart TB
    A[Passage plus title and heading] --> B[WordPiece token IDs]
    B --> C[First window]
    B --> D[Overlapping windows through the tail]
    C --> E[Masked pooling and normalization]
    D --> E
    E --> F[Average window vectors and normalize]
    F --> G[One vector for this passage]
    G --> H[Store source range and context]

La comparaison des vecteurs de plusieurs fenêtres dilue encore les détails fins et donne une influence égale à celle d’une fenêtre finale courte. Maintenir des passages raisonnablement concentrés réduit ce problème . Cela signifie également qu’une recherche peut retourner le paragraphe sur TLS sans exiger que ce détail domine un article entierM SK2s vector.

Le résumé distinct du document permet de moyenner les vecteurs de passage pour un large éventail de sujets liés.

ONNX envoie les vecteurs de token d'abord

Le modèle consomme trois tenseurs entiers:

Input Forme d'une fenêtre MSC2 Meaning MSC3
input_ids [1, tokenCount] IDs vocabulaires
attention_mask [1, tokenCount] 1 pour les tokens réels, 0 pour le revêtement
token_type_ids [1, tokenCount] Tous zéro pour cette seule entrée de texte -

A Tensor est un tableau multidimensionnel. Là, la dimension du lot est d’une dimension.

La déclaration d'exportation sélectionnée last_hidden_stateEn forme de , [1, tokenCount, 384]: un vecteur par tokenM SK1 Flattement que produit le tensor tokenCount × 384 n’incluant pas une seule phrase.

Nous validons les noms d’entrée et de sortie lors du chargement du modèle, sélectionnez le résultat par nom, et rejetons des formes imprévues . Cela empêche un autre exportateur de changer silencieusement ce qui est indexéM SK3

Pool, puis normaliser

Répartition moyenne Moyenne des vecteurs de cote. La masque d'attention exclut le padding à la fois de la somme et du comptageM SK1

windowVector[d] = sum(tokenVector[t, d] where mask[t] = 1)
                  / number of included tokens

Les tokens spéciaux demeurent inclus, correspondant à la moyenne masquéeM SK1 approche de partage utilisée par ce pipeline. Pour la mise en œuvre et son essai de régression d'additionnage, voir OnnxEmbeddingService. Le Transformers de la Sentence Orientation ONNX Explique également que l’exportation de transformateurs peut nécessiter la mise en commun et la normalisation hors du modèle.

L2 normalisation Diviser le vecteur par sa longueur euclidienne. Un résultat non zéro a alors une longueur de 1M SK1 Nous normalisons chaque vectore de fenêtre et la moyenne finale. Zéro -magnitude et nonMSC4les vecteurs définitifs sont des erreursMNK5 ne sont pas des substituts valables d'une invocation du modèle défectueuseMRK6

La similitude est un signal de classement

La similitude cosine compare les directions de deux vecteurs:

cosine(a, b) = dot(a, b) / (length(a) × length(b))

Pour l’unité-vecteurs de longueurM SK1 cela simplifie leur produit ponctuel . Les cotes peuvent varier entre -1 à 1. Résultat obtenu 0.72 Il s'agit d'une affaire qui n'a pas été réglée par la Cour. non pas signifie qu'il a une probabilité de 72% d'être pertinente

La métrique cosine de Qdrant' normalise également les vecteurs sur upload, comme il est décrit dans son Documentation par vecteur. Nous normalisons également le service d’embedding pour que ses produits se comportent de façon cohérente dans les essais et les comparaisons à l’extérieur du Qdrant.

Un seuil qui fonctionne pour les suggestions correspondantes-post peut supprimer des résultats utiles pour de courtes demandes de rechercheM SK1 Tune it on examples from your own corpus . Le modèle, la préparation du document et le corpus affectent tous la répartition des notesMSC4

Entreposer un index de recherche versionné

A Qdrant collecte contient des points de recherche. A point de vue a un vecteur ID, et un charge de travail, signifie des champs ordinaires entreposés le long du vecteurM SK1

La collecte MiniLM par défaut utilise les dimensions 384 avec distance de cosine . L'initialisation vérifie une collection existante's schéma vecteur au lieu d'espérer que le nom correspondant implique la compatibilitéM SK3

Sa charge de travail contient:

Secteur But
slug Trouver le poste canonique dans la base de données d'application
content_hash Détecter les intrants d'indexation modifiés
embedding_version Garder les vecteurs incompatibles hors de la même recherche
published_date Unix timbre disponible pour le filtrage des dates
languages Traductions disponibles du post
categories Champs disponibles pour le filtrage
record_type Différencier un résumé de document d’un passage
source_text, source_start, source_length Recouvrer le passage source correspondant
heading, source_uri Expliquez son contexte et le lien vers sa source
modality TexteM SK1 image, éligibilité audio ou vidéo
page, start_seconds, end_seconds Localisations médiatiques facultatives fournies par ingestion

Afficher les titres, Les détails de la publication et les articles rendus sont chargés à partir de PostgreSQL . Qdrant conserve également le texte extrait du passage pour la recherche des éléments de preuve , Aussi appliquer les mêmes règles de conservation et d’accès à cet index que ses sources M SK3 Cela maintient Qdran comme un indice de recherche renouvelable plutôt qu’une deuxième base de données indépendante d’édition.

Le service crée des index de mots clés sur le slug, versionM SK1 langues, catégories , type d'enregistrementMNK4 modalité et contenu hashMSC5 plus un index entier à la date de publicationMRK6 Les indices de chargement rendent les filtres correspondants utilisables efficacementMBK7 ajouter un champ de chargements seul ne fait pasMGK8 ne crée pas un indexMDK9

Publier une génération complète de passages

Un résumé de document comporte une ID déterministe non signée 64-bit dérivée de l'ID du document en utilisant xxHash64. Les ID des passages incluent également la version pipelineM SK2 les empreintes digitales et le numéro de segment du contenu . l'indexation répétée est idempotente M SK4 la modification du contenu crée une nouvelle génération de passages . Ces haches ne sont pasMSC6 un schéma d'identité cryptographiqueMNK7 utiliser un mapping persistant ou un schéma UUID approprié si le risque de collision n'est pas acceptableMRK8

L’ordonnance de mise à jour est importante:

  1. Accélérer la nouvelle génération' les passages dans des lots limités, attendre que chaque écriture soit terminée
  2. Inscrire le résumé du document, dont l' empreinte digitale sert de manifeste pour identifier la génération complétée.
  3. Supprimer les passages obsolètes pour ce poste.

Les lecteurs comparent un passage candidat' son empreinte digitale avec le manifeste parent de celui-ci . Un téléchargement annulé ne peut exposer la moitié- génération écriteM SK3 Jusqu’à ce que les modifications du manifeste se produisentMska4 les lecteurs peuvent utiliser la génération précédente Mska5 Le nettoyage manqué peut laisser des points inutilisés qui prennent de l’espaceM Ska6 mais ils ne sont pasM ska7 devenus des résultats admissiblesMSKA8 une réconciliation périodique devrait les supprimerMaskan9

Il s’agit d’un protocole de publication d’une application, pas une transaction entre Qdrant et PostgreSQL ou un snapshot couvrant plusieurs demandes de recherche. Le blocage écrivain couvre une seule instance de service . Coordonner des écrivains multiples pour le même posteM SK3 ou indexer la route par l’intermédiaire d'un seul travailleurMska4 Pour les migrations importantesM Ska5 construire une autre collection reste plus simple à expliquer.

Les vecteurs de passage sont actuellement conservés pour un document à la fois avant le téléchargement. Les lots d’écriture sont limitésM SK1 le plus grand document individuel détermine toujours le point culminant de l’indice de mémoire. Pour les livres ou enregistrements très importantsMSC3 les segments d’écoulement par une génération de staging en lieu et place

Recupérer les passages sans remplir la page avec un seul post

Dix fragments d’un article hautement classés ne devraient pas conduire à dix résultats de recherche. MaxSearchCandidates candidats, défaut M SK1 Un corpus très répétitif peut donc révéler moins que le nombre de postes requis ; groupement et l’exhaustivité budgétaire du candidat pour les travaux limités .

Utiliser la méthode plus riche lorsque vous avez besoin des preuves:

var hits = await search.SearchPassagesAsync(
    "configure HTTPS behind a reverse proxy",
    new RetrievalFilter(Language: "en", Modality: "text"),
    limit: 5);

foreach (var hit in hits)
    Console.WriteLine($"{hit.Slug}: {hit.Segment.Heading} — {hit.Segment.Text}");

Langue, catégorieM SK1 modalité et publication facultative - les filtres de date sont appliqués à l'intérieur de la demande Qdrant, avant le classement et l’agglomération de groupements . Il s’agit des règles d’admissibilitéM SK2 non postérieures-décorations de traitementMSC4 Plus simple SearchAsync utilisés antérieurement, retourne seulement les boucles et les points alors qu'on utilise ce chemin de recherche du passage sous.

Une empreinte digitale pour chaque entrée importante

Les empreintes digitales indexées comprennent l’ID, slug , titre, contenu intégralMSC3 date de publicationM SK4 langues et catégories distinctes triéesMNK5 version pipeline efficaceMRK6 titre

La version efficace inclut l'étiquette d'algorithme configurée, révision du modèle en pinsM SK1 réglage de la ségrégation des chevauchements et des passages des tokens. Changer l'étagème d'Algorithm lors de la préparation du document , les changements dans le coefficient de tokenisation ou le regroupement des donnéesMSC4 Les dimensions correspondantes ne sont pas suffisantes pour démontrer que deux versions sont comparables.

Recherches et liens connexes-filtre de recherche d'affichage sur cette version. Les points anciens ne sont pas traités silencieusement comme des embeddings nouveaux , et un point antérieurM SK3version n'est pas satisfaisantMSC4 à la recherche par hash qui décide si une réindexation est nécessaireMNK5

Pour une migration en direct, créer une nouvelle collection , la reconstruire, vérifier la pertinence et l’exhaustivité de celle-ciM SK3 et changer les lecteurs après qu’elle est prêteMSC4 Maintenir la collection antérieure provisoirement fournit un chemin de roulpageMNK5 La reconstitution en place peut laisser moins de postes à rechercher pendant la transitionMRK6

Postes connexes nécessitent un vecteur pour comparer

La recherche des champs de charge de paye n’obtient pas nécessairement les vecteurs.

La question pertinente est:

var related = await readClient.QueryAsync(
    collectionName: collectionName,
    query: sourceVector,
    filter: filterWithVersionAndSourceExclusion,
    limit: (ulong)limit,
    scoreThreshold: minimumScore,
    payloadSelector: true,
    cancellationToken: cancellationToken);

C'est là. filterWithVersionAndSourceExclusion est le filtre construit dans le vecteur-service de stockage, et sourceVector est le vecteur téléchargé. La méthode complète se trouve dans QdrantVectorStoreService.

Connectez-le à ASP.NET Core

Inscrire les services une fois par l’intermédiaire de la prolongation:

builder.Services.AddSemanticSearch(builder.Configuration);

Le service embedding, vector-store service and orchestrator are singletonsM SK2 La session de modèle est coûteuse à construire et est réutilisée . Les téléchargements de modèles utilisent un client HTTP dénomméMSC4 et l'hôte dispose des services lorsqu'il s'arrête.

La section de configuration est:

{
  "SemanticSearch": {
    "Enabled": true,
    "QdrantUrl": "http://localhost:6334",
    "CollectionName": "blog_posts",
    "EmbeddingModelPath": "mlmodels/all-MiniLM-L6-v2.onnx",
    "VocabPath": "mlmodels/vocab.txt",
    "VectorSize": 384,
    "EmbeddingVersion": "minilm-segments-v2",
    "IntraOpNumThreads": 2,
    "MaxConcurrentEmbeddings": 1,
    "ChunkOverlapTokens": 32,
    "SegmentMaxCharacters": 800,
    "SegmentOverlapCharacters": 100,
    "MaxSearchCandidates": 4096,
    "IndexBatchSize": 16,
    "MinimumSimilarityScore": 0.5,
    "SearchResultsCount": 10,
    "RelatedPostsCount": 5
  }
}

ModelRevision Défaults à l'affichage SemanticSearchConfig. La bibliothèque valide des paramètres tels que les dimensions, les limites positives et les chevauchements plus petits que la fenêtre de contenu.

Initialiser lors du démarrage ou d’un processus de mise à niveau géré-up processM SK1 Si la recherche est facultative, conserver le reste du site disponible lorsque l’initialisation se révèle insuffisante , en signalant honnêtement la non-disponibilité de la rechercheMSC4 Un point final HTTP devrait distinguer une recherche réussie sans correspondance d’une recherche manquante de service de recherche.

Le point final de recherche dédié valide maintenant la longueur de la demande et les limites des résultats, passe le token d'annulation de la requête , et retourne 503 pour les défaillances opérationnellesM SK3 La bibliothèque permet aux exceptions et à l'annulation d'atteindre l'appelantMSC4 elle ne modifie pas une mise en place manquante dans zéro ou affirme qu'une écriture manquée a été indexée avec succèsMNK6

Connexes- le cache post varie selon la langue requise, , limite des résultats et format de réponse HTMXM SK3 Lorsque l’on intègre un autre endroit, les clés du cache doivent inclure toutes les saisies qui modifient la réponseMSC5 plus la version index si votre cache peut survivre à une mise en place de modèleMST6

Ce que signifie l’appui linguistique ici

Le blog indexe le contenu source canonique et enregistre ses traductions disponibles sur les points de sommaire et de passage. A languages entrée de charge signifie qu’une traduction existe; il ne l’est pas't signifie que le modèle d’embedding comprend les demandes aussi bien dans cette langue .

L'interface affiche les détails des postes actuels pour la langue requise. L'Interface correspondante-post peut toujours retourner moins de postes après la langueM SK2filtration d'affichage spécifiqueMSC3 La recherche du passage appuie un préfilter linguistique par RetrievalFilter; fils qui introduisent une préférence d'application fiable dans votre point de recherche lorsque cela est requis . Une balise linguistique décrit la disponibilité

Évaluer un modèle multilingue ou une demande documentée-Path de traduction pour la recherche plurilingueM SK1 Chacun des deux choix modifie le pipeline et nécessite une migration d’indices.

Sécurité et limites opérationnelles

Les deux ReadApiKey et WriteApiKey appartiennent au serveur. Un point final de recherche publique ne rend pas public une clé de base de données. Configurer les credentials réels par l'entremise des variables environnementales ou de votre stockage secret de déploiement .

L'entrepôt de vecteurs construit des clients distincts. Lorsque les deux clés sont configurées, les recherches utilisent le lecteur et les mutations l'utilisent pour écrireM SK2 Un service configuré avec une seule clé de lecture rejette les écritures . Si seulement une clef d'écriture est configuréeMSC4 les lectures doivent nécessairement la utiliser aussi

Un processus Web combiné a toujours accès à ses credentials d'indexation. Pour une isolation plus forte, les collections de dispositions s'effectuent séparément et l'indexage est exécuté dans un travailleur avec accès à la saisie de donnéesM SK2 le service de demande reste luMSC3seulement . Une limite du processus et une base de données≥-les permissions forcées fournissent la séparation

Pour les contenus privés ou multifonctionnels, l’autorisation doit aussi restreindre la récupération.

Veuillez ne pas enregistrer par défaut les demandes complètes ou les fragments de document: ceux-ci peuvent contenir des renseignements sensiblesM SK1 Noms d'opérations de surveillance , Cotes, durées et catégories d'erreurs au lieu de celles-ciMSC4

Que se passe-t-il lorsque l'indexation échoue?

Qdrant et la base de données sur les demandes ne partagent pas une transaction' . Si vous publiez un post et que Qdran n’est pas disponible, la publication peut réussir alors que l’indice de recherche demeure inexistant

La bibliothèque propage maintenant les défaillances de façon à ce qu’un appelant puisse y réessayer. Un flux de travail robuste d’édition devrait encadrer un emploi d’indexation après que la base de données ait été commitée, conserver le travail manquant et l’essayer de nouveau idempotentlyM SK2 Une rapprochement périodique peut comparer le corpus canonique avec l’indice , réparer les points manquants et supprimer les points pour les postes supprimésMSC4 ConnexesMST5 les caches postaux doivent être invalidés après des changements réussis aussiMSP6

L’indexation initiale et l’intégration des observateurs du blog sont des points d’entrée utiles, mais ils ne sont pas durables.

Les mises à jour linguistiques méritent une attention similaire. La bibliothèque serialise simultanément AddLanguageAsync appels à l’intérieur d’une instance de service, mais cela ne bloque pas les autres processusM SK1t lock other processes. Dans un déploiement avec plusieurs écrivains , écrire la liste linguistique complète autorisée de la base de données par le biais d’un travail d’indexation coordonné

Performance du CPU sans promesses inventées

ONNX Runtime peut exécuter des opérations concurrentes sur une session pour les configurations supportées; la runtime's session API documents thread-safe runs. Un sémaphore est donc une ressource - la décision de contrôle ici M SK2 un choix concernant la capacité et l’admission du CPU .

MaxConcurrentEmbeddings Limites admises opérations d'incorporation. IntraOpNumThreads Limite le parallèle de la CPU à l’intérieur d’une opération. Le soulèvement des deux peut rendre un petit VPS plus lent en épuisant sa répartition de CPU Guide de threading du temps d'exécution ONNX Explique les contrôles; mesure avec les limites CPU réelles de votre hôte ou contenant.

Aux termes de la Loi sur les droits de l'homme, async method doesn't make CPU inference free. Cette mise en oeuvre prévoit le travail sur la base de threads et vérifie l’annulation entre les fenêtres du modèle . Une demande annulée pendant un essai d’inférence native peut encore consommer du CPU jusqu’à ce que cet essai reçoive son retourM SK3 Under sustained trafficMSC4 add bounded admission and reject or defer excess work rather than allowing an unlimited waiting queue

La méthode d’embedding pluriel traite actuellement les documents séquentiellement; il ne s’agit pas d’un battage tensor. Les batches de téléchargement Qdrant et les batches d’inférence ONNX sont différentes optimisations . Le battage d’incidence réel combiendrait plusieurs intrants dans un seul appel de modèle et les ramènerait à une longueur communeM SK3

Mémoire et stockage

Un vecteur 384-dimensionnel flottant32occupe le Vektor

384 × 4 bytes = 1,536 bytes = 1.5 KiB
100,000 raw vectors ≈ 146.5 MiB

Il s’agit uniquement des données vectrices.

Qdrant utilise habituellement le HNSW, un indice voisin approximatif de la plus procheM SK1 , pour effectuer une recherche efficace sur des collections plus grandes. Moyenne approximative selon laquelle l’ensemble de résultats peut échanger le rappel par rapport à la vitesseMSC4 Les petites collections ou certains filtres particuliers peuvent utiliser d’autres stratégies de recherche.

Extend the same design to multimodal retrieval

MiniLM embeds text. Une caption d'image peut entrer cette pipeline de texte, mais elle représente ce que la caption dit. embeddings multimodaux laisser une demande de texte récupérer un image, segment audio ou clip vidéo dans un espace d'apprentissage compatible.

EmbeddingGemma 2 est un chemin d'actualisation concrète Carte modèle décrive un espace dimensionnel partagé 768- pour le texteM SK1 code, imagesMSC3 audio et vidéoMNK4 avec un contexte de 8,192-tokenMRK6 Il supporte également les représentations dimensionnelles abrégées SSK7 256- et S128- par l'entremise du Matryoshka Représentation LearningMMK10 Une représentation plus courte nécessite une rénormalisation et une évaluation sur votre propre matérielMEK11 Ces propriétés sont : EmbeddingGemma 2; ne supposent pas que les modèles désignés de façon similaire ont le même contrat d’entrée ou de sortie

Remplacer délibérément le contrat de codeur

Pour l’exploration, Google's Guide du développeur Utilise Transformers de Sentence 6.1 ou plus tard . Les demandes de texte et les documents utilisent différents prompts de tâches; les entrées médiatiques utilisent des dictionaries de modalité sans ces prompts

pip install -U "sentence-transformers[image,audio,video]>=6.1.0" transformers
import torch
from sentence_transformers import SentenceTransformer

model = SentenceTransformer(
    "google/embeddinggemma-2",
    device="cpu",
    model_kwargs={"torch_dtype": torch.float32},
)

query = model.encode(
    "a diagram showing a reverse proxy in front of a web application",
    prompt_name="SearchQuery",
    normalize_embeddings=True,
)
passage = model.encode(
    "A reverse proxy forwards incoming requests to the application server.",
    prompt_name="Document",
    normalize_embeddings=True,
)
diagram = model.encode(
    {"image": "reverse-proxy.png"}, normalize_embeddings=True,
)
clip = model.encode(
    {"video": "deployment-demo.mp4"}, normalize_embeddings=True,
)

assert query.shape == passage.shape == diagram.shape == clip.shape == (768,)

C'est un exemple d'extension utilisant les fichiers de média local, plutôt qu'un remplacement. Program.cs pour la démonstration MiniLM. Indiquer les dépendances de Python et la révision du modèle lorsqu'on transforme l'expérience en un travailleur déployé. orientation de précision spécifie le float32 ou bfloat 16 et exclut explicitement les vecteurs non valides ou dégradés qui peuvent être utilisés dans l’exemple.

L'article 5 de la Loi sur l'immigration et le statut des réfugiés Guide multimodal explique le chargement sélectif: désactive les encodateurs de vision ou d'audio non utilisés par config_kwargs. Le chargement seulement des codeurs requis par un travailleur de demande peut réduire son empreinte, alors que les travailleurs d’ingestion traitent les médias plus riches.

Carry provenance across the boundary

La couche de stockage accepte déjà les vecteurs précomputés par ISegmentedVectorStore.IndexSegmentsAsync. EmbeddedSegment Les paires d'un float[] avec un ContentSegment; cette dernière contient une source URIM SK1 modalité, page et début facultatifs optionnels / temps de fin ainsi que le texte sourceMSC4 C'est la limite entre un médiaMNK5 travailleur de traitement et QdrantMRK6

Par exemple, l’ingestion de , peut produire ces enregistrements.

Unité de source Input vectoriel Données conservées avec le point
Section d'articles TitreM SK2 titre et passage champ de caractères extraits SSK4
Diagramme de l'architecture Image ou image avec explication environnante SSK2 Asset original URIM SK3 titre et section parentale
Page manuelle balayée Image de page Rendue Numéro de la page URI du document et extrait séparément de l'OCR
Clip de démonstration Un clip vidéo limité URI d'enregistrement original et démarrage/heures de fin ≥
Explication prononcée Un clip audio limité Enregistrement URI, Timmes de temps et transcriptions produites séparément

Un emplacement ne produit pas de transcription OCR ou ne vérifie pas une caption. Intelligence de l'image Explore la façon de garder les signaux OCR et visuels distincts; AudioSummarizer démontre pourquoi les étiquettes de temps et les preuves audio récupérables sont importantes. Captions , Les OCR et les transcriptions peuvent aussi alimenter la recherche lexiqueM SK2 complétant un vecteur médiatique indigène.

Pour relier cet exemple à C#,, créez un nouveau 768-Collection de cosine dimensionnelle et configuration d'une identité distincte de pipeline contenant la révision du modèle réel , Règles de prétraitement et de promptage . Envoyer les tableaux flottants et les métadonnées source de l'employé à IndexSegmentsAsync; envoyer les demandes produites par le modèle correspondant à SearchSegmentsAsync. L'entrepôt valide les dimensions de vecteurs génériquesM SK1 et ses exercices d'essai d'intégration 768-enregistrements médiatiques dimensionnels et filtres de modalité.

Par défaut OnnxEmbeddingService reste MiniLM-spécifique et rejette une configuration non -384 . Un chemin de demande EmbeddingGemma en direct nécessite donc un chemin séparé IEmbeddingService adapter, ou un travailleurM SK1 paire de clients qui expose explicitement l'encodage du document et des demandes . Donn't drop different weights into the BERT adapter or mix MiniLM queries with EmbeddingGemma pointsMSC4 L'exemple Python suit la API publiée.

Un espace vectoriel partagé permet une classification modique croisée-,non bien calibrée automatiquementM SK2Évaluer le texte -àMSC4imageMNK5texteMRK6à+-audio et texteMMK8à=-questions vidéo séparémentMBK10y compris les distracteursMGK11 Si vous utilisez des encoders spécialisés non liésMZK12 conserver des collections distinctes ou des espaces vecteurs nommés et fusionner leurs classements de résultats au lieu de comparer des notes brutes incompatiblesMDK13

Qualité et correction de la recherche d'essai

L'article 5 de la Loi sur l'immigration et le statut des réfugiés semantic-projet d'essai de recherche couvre la tokenisation, le pooling , les limites de fenêtres, les vecteurs invalidesM SK3 l'annulationMSC4 les téléchargements atmosphériquesMska5 les empreintes digitales indexéesMske6 les plages de sourcesM Ska7 la recherche des passages filtrésMSKA8 la visibilité de la génération et les uploads limités Mska9

dotnet test Mostlylucid.SemanticSearch.Test/Mostlylucid.SemanticSearch.Test.csproj

Les essais en direct sont optiques-in. Pointez-les sur une instance de Qdrant isolée et les actifs téléchargésM SK2

MINILM_TEST_MODEL=/absolute/path/all-MiniLM-L6-v2.onnx \
MINILM_TEST_VOCAB=/absolute/path/vocab.txt \
QDRANT_TEST_URL=http://localhost:6334 \
dotnet test Mostlylucid.SemanticSearch.Test/Mostlylucid.SemanticSearch.Test.csproj

Pour une instance d'essai authentifiée, également établi QDRANT_TEST_READ_KEY et QDRANT_TEST_WRITE_KEY. Les essais créent et éliminent leur propre collection uniquement désignée . Ne pointez jamais les tests d’intégration à une collecte contenant vos données de production M SK2

Un essai réussi sur la fumée devrait démontrer plus de “pas d’exceptionM SK1

  • Les emplacements ont des valeurs limites 384 et une longueur d'unitéM SK1
  • Les textes connexes sont classés au-dessus d’un exemple clairement non lié.
  • Le contenu au-delà de la première fenêtre du modèle affecte son emplacement.
  • Les plages des sources de passage reproduisent exactement le texte fourni.
  • Filtres sélectionnent les candidats admissibles avant le regroupement des documents.
  • Les générations de passage non engagées restent invisibles, et la réindexation supprime les morceaux obsolètes .
  • Une écriture complète est immédiatement visible à la lecture suivante.
  • Les résultats connexes excluent leur source post.
  • Les vecteurs de pipeline anciens- sont exclus même lorsqu'ils ont la même dimensionM SK1
  • Le changement d’un titre ou de métadonnées pertinents modifie les empreintes digitales indexées.
  • Une dépendance manquante peut être distinguée d'un résultat réel vide.

Ensuite, assemblez des questions représentatives avec les articles que vous jugez pertinents. Inclure des synonymes, identificateurs exacts , messages d'erreursM SK3 demandes courtes , demande et réponse ambiguës sans réponse utileMSC5 Enregistrez la fréquence à laquelle les articles pertinents apparaissent dans les dix premiers résultats et le degré élevé du premier résultat utile.

Pour une recherche approximative, comparer un échantillon à la recherche exacte aussi bien. Ce qui sépare les problèmes de représentation embedding des candidats manqués par l’indice de recherche .

Où la recherche sémantique a besoin d'aide

Les vecteurs denses sont utiles pour les paraphrases. Codes d'erreur exacts, les noms de paquets rares et les chaînes de version bénéficient souvent aussi de la recherche lexiqueM SK2 Recherche hybride reçoit les deux types de candidats et combine leur classement. La fusion réciproque des rangs combine les postes de rang sans présumer qu’une note lexique et une note cosine signifient la même chose .

A re-ranker peut ensuite examiner chaque demande–parité de candidats afin d’ordonner plus soigneusement la mise en place d’un petit candidat . Il ajoute une latence et ne peut récupérer un passage que la recherche initiale n’a jamais trouvé

Partie 4b couvre l'interface et les hybrides Partie 5 suit l’intégration à la publication et à l’indexation. Pour un aperçu plus approfondi des options de stockage et de déploiement de Qdrant', voir Self-Databases de vecteurs hébergées avec Qdrant.

Un système de recherche utile préserve les preuves qu’il a trouvées: le passage , le contexte qui en fait une significationM SK2 et la source que le lecteur peut vérifier. Les mises à niveau du modèle et des médias plus riches peuvent ensuite améliorer ce qu’ils trouvent sans rendre plus difficile l’inspection de ces preuves.

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.