# अपने ब्लॉग के लिए "Lawyer GPT" का निर्माण करना | - | भाग |3: | एम्बेडिंगों को समझना

<!--category-- AI, LLM, Embeddings, Vector Database, C#, AI-Article, mostlylucid.blogllm -->
<datetime class="hidden">2025-11-12T22:45</datetime>

## परिचय

भाग में आपका स्वागत है 3! हम ' हमारे GPU थाक काम कर रहा है[भाग 2](/blog/building-a-lawyer-gpt-for-your-blog-part2)और हम वास्तुकला को समझते हैं[भाग 1](/blog/building-a-lawyer-gpt-for-your-blog-part1)अब यह समय है कि मैं उस जादू में घुस जाऊँ जो सेमेटिक खोज को संभव बनाता है **[सम्मिलन](https://www.sbert.net/)** और **भेक्टर डाटाबेस**.

> नोट: यह AI के साथ मेरे प्रयोगों का एक हिस्सा है।

यह है जहां चीजें दिलचस्प हो जाती हैं।

[TOC]

## एम्बेडिंग क्या हैं

एक सम्मिलन पाठ का संख्यात्मक प्रतिनिधित्व है।

### अंतर्ज्ञान

एक 2D स्थान में शब्दों को उनके अर्थ के आधार पर प्लॉट करने की कल्पना करें

```mermaid
graph TD
    subgraph "2D Embedding Space (simplified)"
        A[cat: 0.8, 0.2]
        B[kitten: 0.7, 0.3]
        C[dog: 0.6, 0.1]
        D[puppy: 0.5, 0.2]
        E[car: -0.5, 0.8]
        F[vehicle: -0.6, 0.7]
        G[database: 0.1, -0.7]
        H[SQL: 0.2, -0.8]
    end

    class A,B cats
    class C,D dogs
    class E,F vehicles
    class G,H tech

    classDef cats stroke:#333
    classDef dogs stroke:#333
    classDef vehicles stroke:#333
    classDef tech stroke:#333
```

समान अवधारणाओं को एक साथ समूहबद्ध करें

- जानवर (redM SK1green) एक-दूसरे के पास हैं
- वाहन (blueM SK1 समूहबद्ध किए गए हैं
- तकनीकी शब्द (yellow) क्लस्टर
- असंबंधित अवधारणाएं अलग-अलग हैं।

**वास्तविक एम्बेडिंग** उपयोग 384 से | 1536 | आयामों को |( | न केवल \2!), | अधिक बारीकण प्राप्त करने के लिए

### यह कैसे काम करता है

एक एम्बेडिंग मॉडल, एक तंत्रिका नेटवर्क है जो पाठ को वेक्टरों में मानचित्रित करने के लिए प्रशिक्षित किया जाता है कि

- समान अर्थ → निकट सदिश
- भिन्न अर्थ → दूरवर्ती भेक्टर

```mermaid
graph LR
    A[Text: 'Docker container'] --> B[Embedding Model]
    B --> C[Vector: 384 floats]

    D[Text: 'containerization'] --> B
    B --> E[Vector: 384 floats]

    C -.Similar.-> E

    F[Text: 'chocolate cake'] --> B
    B --> G[Vector: 384 floats]

    C -.Very Different.-> G

    class B model
    class C,E similar
    class G different

    classDef model stroke:#333,stroke-width:4px
    classDef similar stroke:#333
    classDef different stroke:#333
```

### समानता मापन करें

हम उपयोग करते हैं **कोसाइन समानता** मापने के लिए कैसे करीब दो सदिश हैं

```csharp
public static float CosineSimilarity(float[] vectorA, float[] vectorB)
{
    if (vectorA.Length != vectorB.Length)
        throw new ArgumentException("Vectors must have same length");

    // Dot product: sum of element-wise multiplication
    float dotProduct = 0;
    for (int i = 0; i < vectorA.Length; i++)
    {
        dotProduct += vectorA[i] * vectorB[i];
    }

    // Magnitude of each vector: sqrt(sum of squares)
    float magnitudeA = 0;
    float magnitudeB = 0;
    for (int i = 0; i < vectorA.Length; i++)
    {
        magnitudeA += vectorA[i] * vectorA[i];
        magnitudeB += vectorB[i] * vectorB[i];
    }
    magnitudeA = MathF.Sqrt(magnitudeA);
    magnitudeB = MathF.Sqrt(magnitudeB);

    // Cosine similarity: dot product / (magnitude_a * magnitude_b)
    return dotProduct / (magnitudeA * magnitudeB);
}
```

**कोसाइन समानता -1 से 1 तक होती है**:

- `1.0` = समान अर्थ
- `0.0` = असंबंधित
- `-1.0` = विपरीत अर्थ

**उदाहरण**:

```csharp
var dockerEmbed = new float[] { 0.5f, 0.3f, -0.2f, 0.8f };  // "Docker container"
var containerEmbed = new float[] { 0.45f, 0.35f, -0.18f, 0.75f };  // "containerization"
var cakeEmbed = new float[] { -0.7f, 0.1f, 0.9f, -0.3f };  // "chocolate cake"

Console.WriteLine(CosineSimilarity(dockerEmbed, containerEmbed));  // ~0.95 (very similar!)
Console.WriteLine(CosineSimilarity(dockerEmbed, cakeEmbed));  // ~0.15 (unrelated)
```

## हमारे उपयोग के लिए क्यों एम्बेडिंग पूर्ण हैं

याद रखें हम एक लेखन सहायक बना रहे हैं जब मैं लिखना शुरू करता हूँ

> "इस पोस्ट मेंM SK1 मैं'के साथ अस्तित्व फ्रेमवर्क का उपयोग कैसे दिखाएँ

प्रणाली के बारे में पिछली पोस्टें ढूंढनी चाहिए

- इकाई फ्रेमवर्क
- डाटाबेस अभिगम पैटर्न
- ओआरएम कॉन्फ़िगरेशन
- संबंधित ASP.NET कोर विषय

**सिर्फ शब्दकुञ्जी मेल नहीं** यह समझना चाहिए कि "EF कोर माइग्रेशनों" संबंधित है यद्यपि यह नहीं कहता है

### एम्बेडिंग व पारंपरिक खोज

```mermaid
graph TB
    subgraph "Traditional Keyword Search"
        A1[Query: 'Docker setup'] --> B1[Find: 'Docker' OR 'setup']
        B1 --> C1[❌ Misses: 'containerization guide']
        B1 --> D1[❌ Misses: 'running containers']
        B1 --> E1[✅ Finds: 'Docker setup tutorial']
    end

    subgraph "Embedding-Based Semantic Search"
        A2[Query: 'Docker setup'] --> B2[Generate embedding]
        B2 --> C2[Find similar embeddings]
        C2 --> D2[✅ Finds: 'containerization guide']
        C2 --> E2[✅ Finds: 'running containers']
        C2 --> F2[✅ Finds: 'Docker setup tutorial']
    end

    class B2,C2 semantic

    classDef semantic stroke:#333,stroke-width:2px
```

## एक सम्मिलित मॉडल चुनना

कई सम्मिलित मॉडल हैं

1. **स्थानीय रूप से चलाता है** Privacy speed
2. **ऑनिक्स रनटाइम के साथ काम करता है** हम अपने GPU का उपयोग कर सकते हैं
3. **अच्छी गुणवत्ता** (शुद्ध अर्थबोध
4. **दाहिने आकार** आयाम अच्छा संतुलन है

### लोकप्रिय विकल्प

मॉडल | आयाम | | | आकार || | गुणवत्ता
|-------|------------|------|---------|-------|
| **सभी-MiniLM** | | 384 | || | МSK3 | MB | | | अच्छा | एमSK5 | बहुत तेजी से |
| **सभी-mpnet** | | 768 | || | МSK3 | MB | {\displaystyle \mathcal {MSC
| **bge-small** अधिक अच्छी | बहुत तेजी से
| **bge-base** सबसे अच्छा | तेजी से
| **ओपनएआई पाठ** Excellent | Slow (network

**मेरी सिफारिश**: **bge-base**

- स्टेट-of-the-art open source model
- आयाम (good balance
- ऑनिक्स रनटाइम के साथ बहुत अच्छा काम करता है
- निःशुल्क और स्थानीय रूप से चलाया जाता है

### ऑनिक्स प्रारूप में परिवर्तित कर रहा है

अधिकांश मॉडल पिटोर्च प्रारूप में हैं

**अनुकूल (परिवर्तन के लिए Python लाइब्रेरी संस्थापित करें**:

```bash
pip install optimum[exporters]
```

**BGE मॉडल बदलें**:

```bash
optimum-cli export onnx --model BAAI/bge-base-en-v1.5 --task feature-extraction bge-base-en-onnx/
```

यह creates:

```
bge-base-en-onnx/
    model.onnx           # The neural network
    tokenizer.json       # Text → tokens converter
    tokenizer_config.json
    special_tokens_map.json
    config.json
```

**डाउनलोड करें यदि आप 'convert नहीं करना चाहते**:
कई मॉडल पूर्व -परिवर्तित हैं और हग्गिंग फेस पर उपलब्ध है जिसका नाम ‘.’ है।

## C# में सम्मिलन का उपयोग

एक व्यावहारिक एम्बेडिंग जेनेरेटर बनाने के लिए ONNX रनटाइम का उपयोग करें

### परियोजना सेटअप

```bash
mkdir EmbeddingTest
cd EmbeddingTest
dotnet new console
dotnet add package Microsoft.ML.OnnxRuntime.Gpu --version 1.16.3
dotnet add package Microsoft.ML.Tokenizers --version 0.1.0-preview.23511.1
```

**इन पैकेजों का कारण**

- `OnnxRuntime.Gpu` - GPU पर मॉडल चलाएँ
- `Microsoft.ML.Tokenizers` पाठ को टोकन आईडी में बदलें

### टोकनीकरण पहले

सम्मिलित करने से पहले हमें , को टोकनाइज करना होगा

```csharp
using Microsoft.ML.Tokenizers;
using System;
using System.Linq;

public class SimpleTokenizer
{
    private readonly Tokenizer _tokenizer;

    public SimpleTokenizer(string tokenizerPath)
    {
        // Load the tokenizer.json file
        _tokenizer = Tokenizer.CreateTokenizer(tokenizerPath);
    }

    public (long[] InputIds, long[] AttentionMask) Tokenize(string text, int maxLength = 512)
    {
        // Tokenize the text
        var encoding = _tokenizer.Encode(text);

        // Get token IDs
        var ids = encoding.Ids.Select(i => (long)i).ToArray();

        // Pad or truncate to maxLength
        var inputIds = new long[maxLength];
        var attentionMask = new long[maxLength];

        int length = Math.Min(ids.Length, maxLength);

        // Copy actual tokens
        Array.Copy(ids, inputIds, length);

        // Set attention mask (1 = real token, 0 = padding)
        for (int i = 0; i < length; i++)
        {
            attentionMask[i] = 1;
        }

        return (inputIds, attentionMask);
    }
}
```

**क्या हो रहा है यहाँ**

1. **एनकोडिंग** à¤ ̧à¥à¤°à¥à¤ `[101, 8667, 2088, 102]`
   
   - प्रत्येक संख्या एक टोकन आईडी है मॉडल's शब्दावली से
   - विशेष टोकन [सीएलएस [एसईपी

2. **पैडिंग** - मॉडल स्थिर लंबाई इनपुट की आशा करते हैं
   
   - यदि पाठ छोटा है
   - यदि पाठ लंबा है

3. **ध्यान मास्क** - मॉडल को बताता है कि कौन सी टोकन वास्तविक हैं पैडिंग के विपरीत
   
   - `1` इस टोकन को = प्रक्रिया करें
   - `0` अनदेखा करें

```mermaid
graph LR
    A["Text: 'Docker setup'"] --> B[Tokenizer]
    B --> C[Token IDs:<br/>101, 12849, 12229, 102]
    C --> D[Pad to 512]
    D --> E[Input IDs:<br/>101, 12849, 12229, 102, 0, 0,...]
    D --> F[Attention Mask:<br/>1, 1, 1, 1, 0, 0,...]

    E --> G[Feed to Model]
    F --> G

    class B,G process

    classDef process stroke:#333,stroke-width:2px
```

### एम्बेडिंग जेनेरेटर वर्ग

अब पूरी सम्मिलित पाइपलाइन

```csharp
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
using System;
using System.Collections.Generic;
using System.Linq;

public class EmbeddingGenerator : IDisposable
{
    private readonly InferenceSession _session;
    private readonly SimpleTokenizer _tokenizer;
    private readonly int _embeddingDimension;

    public EmbeddingGenerator(string modelPath, string tokenizerPath, bool useGpu = true)
    {
        // Setup session options
        var options = new SessionOptions();
        if (useGpu)
        {
            options.AppendExecutionProvider_CUDA(0);
        }

        // Load model
        _session = new InferenceSession(modelPath, options);

        // Load tokenizer
        _tokenizer = new SimpleTokenizer(tokenizerPath);

        // Get embedding dimension from model output shape
        var outputMetadata = _session.OutputMetadata["last_hidden_state"];
        _embeddingDimension = outputMetadata.Dimensions[2]; // Usually 768 for base models
    }

    public float[] GenerateEmbedding(string text)
    {
        // Step 1: Tokenize
        var (inputIds, attentionMask) = _tokenizer.Tokenize(text);

        // Step 2: Create input tensors
        var inputIdsTensor = new DenseTensor<long>(inputIds, new[] { 1, inputIds.Length });
        var attentionMaskTensor = new DenseTensor<long>(attentionMask, new[] { 1, attentionMask.Length });

        var inputs = new List<NamedOnnxValue>
        {
            NamedOnnxValue.CreateFromTensor("input_ids", inputIdsTensor),
            NamedOnnxValue.CreateFromTensor("attention_mask", attentionMaskTensor)
        };

        // Step 3: Run inference
        using var results = _session.Run(inputs);

        // Step 4: Extract embeddings from output
        var outputTensor = results.First().AsTensor<float>();

        // Output shape is [batch_size, sequence_length, embedding_dim]
        // We want [batch_size, embedding_dim] by mean pooling

        return MeanPooling(outputTensor, attentionMask);
    }

    private float[] MeanPooling(Tensor<float> outputTensor, long[] attentionMask)
    {
        int seqLength = outputTensor.Dimensions[1];
        int embeddingDim = outputTensor.Dimensions[2];

        var embedding = new float[embeddingDim];
        int tokenCount = 0;

        // Average across all non-padded tokens
        for (int seq = 0; seq < seqLength; seq++)
        {
            if (attentionMask[seq] == 0) continue; // Skip padding

            tokenCount++;
            for (int dim = 0; dim < embeddingDim; dim++)
            {
                embedding[dim] += outputTensor[0, seq, dim];
            }
        }

        // Divide by count to get mean
        for (int dim = 0; dim < embeddingDim; dim++)
        {
            embedding[dim] /= tokenCount;
        }

        // Normalize to unit length (common practice)
        return Normalize(embedding);
    }

    private float[] Normalize(float[] vector)
    {
        float magnitude = 0;
        foreach (var val in vector)
        {
            magnitude += val * val;
        }
        magnitude = MathF.Sqrt(magnitude);

        var normalized = new float[vector.Length];
        for (int i = 0; i < vector.Length; i++)
        {
            normalized[i] = vector[i] / magnitude;
        }

        return normalized;
    }

    public void Dispose()
    {
        _session?.Dispose();
    }
}
```

**कोड विच्छेदन**:

1. **मॉडल लोडिंग** - GPU समर्थन के साथ ONNX सत्र बनाता है
2. **टोकनीकरण** - पाठ को टोकन आईडी में बदलता है
3. **टेनसोर निर्माण** - मॉडल इनपुट के लिए आकार डेटा
4. **उग्रता** - तंत्रिका नेटवर्क चलाता है
5. **औसत पूलिंग** - औसत टोकन अंतःस्थापित करता है
6. **सामान्यीकरण** - लेक्टर लंबाई बनाता है

### औसत पूलिंग समझा गया

```mermaid
graph TB
    A[Model Output:<br/>Token Embeddings] --> B["Token 0 (CLS):<br/>(0.1, 0.5, -0.3, ...)"]
    A --> C["Token 1 (Docker):<br/>(0.4, 0.2, -0.1, ...)"]
    A --> D["Token 2 (setup):<br/>(0.3, 0.6, -0.2, ...)"]
    A --> E["Token 3 (SEP):<br/>(0.2, 0.3, -0.4, ...)"]

    B --> F[Average]
    C --> F
    D --> F
    E --> F

    F --> G["Sentence Embedding:<br/>(0.25, 0.4, -0.25, ...)"]

    class A input
    class F process
    class G output

    classDef input stroke:#333
    classDef process stroke:#333,stroke-width:2px
    classDef output stroke:#333,stroke-width:2px
```

हम औसत है क्योंकि

- प्रत्येक टोकन के पास अपना एम्बेडिंग है
- हम पूरे वाक्य के लिए एक सम्मिलित करने की जरूरत है
- औसत समग्र अर्थ को समाहित करता है

### उपयोग उदाहरण

```csharp
using System;

class Program
{
    static void Main(string[] args)
    {
        using var embedder = new EmbeddingGenerator(
            modelPath: "bge-base-en-onnx/model.onnx",
            tokenizerPath: "bge-base-en-onnx/tokenizer.json",
            useGpu: true
        );

        // Generate embeddings
        var embedding1 = embedder.GenerateEmbedding("Docker containerization tutorial");
        var embedding2 = embedder.GenerateEmbedding("Setting up containers with Docker");
        var embedding3 = embedder.GenerateEmbedding("Baking a chocolate cake");

        Console.WriteLine($"Embedding dimension: {embedding1.Length}");
        Console.WriteLine($"First 5 values: {string.Join(", ", embedding1.Take(5).Select(f => f.ToString("F4")))}");

        // Calculate similarities
        float sim12 = CosineSimilarity(embedding1, embedding2);
        float sim13 = CosineSimilarity(embedding1, embedding3);

        Console.WriteLine($"\nSimilarity (Docker vs Containers): {sim12:F4}");  // ~0.85
        Console.WriteLine($"Similarity (Docker vs Cake): {sim13:F4}");  // ~0.10
    }

    static float CosineSimilarity(float[] a, float[] b)
    {
        // Since vectors are normalized, dot product = cosine similarity
        float dot = 0;
        for (int i = 0; i < a.Length; i++)
        {
            dot += a[i] * b[i];
        }
        return dot;
    }
}
```

**आउटपुट**:

```
Embedding dimension: 768
First 5 values: 0.0123, -0.0456, 0.0789, -0.0234, 0.0567

Similarity (Docker vs Containers): 0.8542
Similarity (Docker vs Cake): 0.1023
```

सुंदर! Semantically similar content has high similarity

## भेक्टर डाटाबेस

अब हम एम्बेडिंग है

### समस्या

हम कहते हैं कि हमारे पास 1000 ब्लॉग पोस्ट्स है

**सहज खोज**:

```csharp
float bestSimilarity = -1;
int bestIndex = -1;

for (int i = 0; i < 10000; i++)
{
    float sim = CosineSimilarity(queryEmbedding, storedEmbeddings[i]);
    if (sim > bestSimilarity)
    {
        bestSimilarity = sim;
        bestIndex = i;
    }
}
```

**समस्या**यह O है

के लिए 10,000 अंतःकरणों के लिए प्रत्येक आयाम

- ~30 मिलियन उत्प्लावन बिंदु ऑपरेशन
- तीव्र सीपीयू पर भी ~50-100ms

हमें कुछ अधिक तेजी की जरूरत है

### भेक्टर डाटाबेस समाधान

भेक्टर डाटाबेसों में HNSW की तरह clever डेटा संरचनाओं का उपयोग करता है (Hierarchical Navigable Small World graphs

```mermaid
graph TB
    A[Query Embedding] --> B[Vector Database]
    B --> C{HNSW Index}

    C --> D[Layer 2:<br/>Coarse Search]
    D --> E[Layer 1:<br/>Refined Search]
    E --> F[Layer 0:<br/>Exact Search]

    F --> G[Top K Results]

    H[10,000 vectors] -.Indexed.-> C

    class B db
    class C index
    class G results

    classDef db stroke:#333,stroke-width:4px
    classDef index stroke:#333,stroke-width:2px
    classDef results stroke:#333,stroke-width:2px
```

**गति तुलना**:

- 10K सदिशों के लिए सहज खोज
- 10K सदिशों के लिए भेक्टर डीबी (HNSW): \1-5ms
- **अधिक तेजी से**

और यह पैमाने पर : मिलियन सदिशों अभी भी सिर्फ लेता है

### एक भेक्टर डाटाबेस चुनना

हमारे C# परियोजना के लिए हमें , की जरूरत है

1. अच्छा C# क्लाइंट लाइब्रेरी
2. विस्थापित करने के लिए आसान (Docker
3. अच्छा निष्पादन
4. मुक्त स्रोत /

डेटाबेस
|----------|------------|------------|-------------|---------|
| **क्वारंटCity name (optional, probably does not need a translation)** Excellent | Docker | Very Fast
| **पीजीवेक्टर** एनपीजीएसक्यूएल के माध्यम से | \✅ | ( via Npgsql
| **वियत करना** good | Docker | Very Fast
| **मृग** सीमित | Docker/KM SK4s | बहुत तेजी से || Apache |
| **क्रोमा** à¤ ̧à¥à¤°à¥à¤

**मेरी पसंद: Qdrant**

- उत्कृष्ट C# क्लाइंट`Qdrant.Client`)
- सरल डॉकर डेप्लोमेंट
- भेक्टर खोज के लिए विशेष रूप से बनाया गया (not bolted on)
- महान दस्तावेज़ीकरण
- सक्रिय विकास

**वैकल्पिक: pgvector**

- हम पहले से ही ब्लॉग के लिए PostgreSQL का उपयोग कर रहे हैं
- सब कुछ एक डाटाबेस में रख सकता है
- कुछ कम निष्पादक, लेकिन सरल वास्तुकला

इस श्रृंखला के लिए मैं उपयोग करेंगे **क्वारंटCity name (optional, probably does not need a translation)** क्योंकि यह निर्मित और अवधारणाओं को समझने के लिए आसान है

## Qdrant सेटअप कर रहा है

### डॉकर विनियोजन

```bash
docker run -p 6333:6333 -p 6334:6334 \
    -v $(pwd)/qdrant_storage:/qdrant/storage \
    qdrant/qdrant
```

**बंदरगाह**:

- `6333` - रेस्ट API
- `6334` जीआरपीसी API

**भंडारण**: डेटा को स्थायी बनाता है `./qdrant_storage` होस्ट पर

### क्लाइंट सेटअप

```bash
dotnet add package Qdrant.Client --version 1.7.0
```

### एक संग्रह बना रहा है

एक संग्रह एक तालिका की तरह है - यह एक विशिष्ट आयाम के सदिशों को धारण करता है

```csharp
using Qdrant.Client;
using Qdrant.Client.Grpc;

public class QdrantSetup
{
    private readonly QdrantClient _client;

    public QdrantSetup(string host = "localhost", int port = 6334)
    {
        _client = new QdrantClient(host, port);
    }

    public async Task CreateCollectionAsync(string collectionName, ulong vectorSize)
    {
        // Check if collection exists
        var collections = await _client.ListCollectionsAsync();
        if (collections.Any(c => c.Name == collectionName))
        {
            Console.WriteLine($"Collection '{collectionName}' already exists");
            return;
        }

        // Create collection
        await _client.CreateCollectionAsync(
            collectionName: collectionName,
            vectorsConfig: new VectorParams
            {
                Size = vectorSize,  // 768 for bge-base
                Distance = Distance.Cosine  // Cosine similarity
            }
        );

        Console.WriteLine($"Created collection '{collectionName}' with {vectorSize} dimensions");
    }
}
```

**कुंजी पैरामीटर**:

- `Size` - आपके एम्बेडिंग मॉडल से मिलना चाहिए bge के लिए 768
- `Distance` - समानता को कैसे मापना है
  - `Distance.Cosine` कोसाइन समानता
  - `Distance.Euclid` - यूक्लिड दूरी
  - `Distance.Dot` - Dot उत्पाद

### भेक्टरों को घुसा रहा है

```csharp
using Qdrant.Client.Grpc;
using System.Collections.Generic;

public class QdrantInserter
{
    private readonly QdrantClient _client;

    public QdrantInserter(QdrantClient client)
    {
        _client = client;
    }

    public async Task InsertBlogChunkAsync(
        string collectionName,
        ulong id,
        float[] embedding,
        string blogPostSlug,
        string chunkText,
        int chunkIndex)
    {
        var point = new PointStruct
        {
            Id = id,
            Vectors = embedding,
            Payload =
            {
                ["blog_post_slug"] = blogPostSlug,
                ["chunk_text"] = chunkText,
                ["chunk_index"] = chunkIndex,
                ["timestamp"] = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
            }
        };

        await _client.UpsertAsync(collectionName, new[] { point });
    }

    public async Task InsertBatchAsync(
        string collectionName,
        List<(ulong id, float[] embedding, Dictionary<string, object> payload)> points)
    {
        var qdrantPoints = points.Select(p => new PointStruct
        {
            Id = p.id,
            Vectors = p.embedding,
            Payload = { p.payload }
        }).ToList();

        // Batch insert for efficiency
        await _client.UpsertAsync(collectionName, qdrantPoints);

        Console.WriteLine($"Inserted {points.Count} points");
    }
}
```

**पेलोड का वर्णन**:

- प्रत्येक सदिश में संलग्न मेटाडेटा की तरह
- किसी भी वस्तु को भंडारित कर सकता है
- खोज योग्य और फिल्टर योग्य!

**बैच प्रवेश**:

- एक से अधिक तेजी
- Qdrant 100-1000 की बैचों को कुशलता से संभालता है

### भेक्टर खोज रहा है

```csharp
public class QdrantSearcher
{
    private readonly QdrantClient _client;

    public QdrantSearcher(QdrantClient client)
    {
        _client = client;
    }

    public async Task<List<SearchResult>> SearchAsync(
        string collectionName,
        float[] queryEmbedding,
        int topK = 10)
    {
        var searchResult = await _client.SearchAsync(
            collectionName: collectionName,
            vector: queryEmbedding,
            limit: (ulong)topK,
            scoreThreshold: 0.7f  // Only return if similarity > 0.7
        );

        return searchResult.Select(r => new SearchResult
        {
            Id = r.Id.Num,
            Score = r.Score,
            BlogPostSlug = r.Payload["blog_post_slug"].StringValue,
            ChunkText = r.Payload["chunk_text"].StringValue,
            ChunkIndex = (int)r.Payload["chunk_index"].IntegerValue
        }).ToList();
    }

    public async Task<List<SearchResult>> SearchWithFilterAsync(
        string collectionName,
        float[] queryEmbedding,
        string blogPostSlug,  // Only search within this post
        int topK = 5)
    {
        var filter = new Filter
        {
            Must =
            {
                new Condition
                {
                    Field = new FieldCondition
                    {
                        Key = "blog_post_slug",
                        Match = new Match { Keyword = blogPostSlug }
                    }
                }
            }
        };

        var searchResult = await _client.SearchAsync(
            collectionName: collectionName,
            vector: queryEmbedding,
            filter: filter,
            limit: (ulong)topK
        );

        return searchResult.Select(r => new SearchResult
        {
            Id = r.Id.Num,
            Score = r.Score,
            BlogPostSlug = r.Payload["blog_post_slug"].StringValue,
            ChunkText = r.Payload["chunk_text"].StringValue,
            ChunkIndex = (int)r.Payload["chunk_index"].IntegerValue
        }).ToList();
    }
}

public class SearchResult
{
    public ulong Id { get; set; }
    public float Score { get; set; }
    public string BlogPostSlug { get; set; }
    public string ChunkText { get; set; }
    public int ChunkIndex { get; set; }
}
```

**खोज विशेषताएँ**:

- `limit` - वापस ऊपर K सबसे समान
- `scoreThreshold` - केवल लौटाएँ यदि समानता थ्रेसहोल्ड से अधिक है
- `filter` मेटाडेटा द्वारा फ़िल्टर करें

### पूर्ण उदाहरण: खोज पाइपलाइन

```csharp
using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        // Setup
        var embedder = new EmbeddingGenerator(
            "bge-base-en-onnx/model.onnx",
            "bge-base-en-onnx/tokenizer.json",
            useGpu: true
        );

        var client = new QdrantClient("localhost", 6334);
        var searcher = new QdrantSearcher(client);

        // User query
        string query = "How do I set up Docker with ASP.NET Core?";

        // Generate query embedding
        Console.WriteLine($"Searching for: {query}");
        var queryEmbedding = embedder.GenerateEmbedding(query);

        // Search
        var results = await searcher.SearchAsync(
            collectionName: "blog_embeddings",
            queryEmbedding: queryEmbedding,
            topK: 5
        );

        // Display results
        Console.WriteLine($"\nFound {results.Count} results:\n");

        foreach (var result in results)
        {
            Console.WriteLine($"Score: {result.Score:F4}");
            Console.WriteLine($"Post: {result.BlogPostSlug}");
            Console.WriteLine($"Chunk: {result.ChunkText.Substring(0, Math.Min(100, result.ChunkText.Length))}...");
            Console.WriteLine();
        }
    }
}
```

**आउटपुट**:

```
Searching for: How do I set up Docker with ASP.NET Core?

Found 5 results:

Score: 0.8923
Post: dockercomposedevdeps
Chunk: In this post, I'll show you how to set up a development environment using Docker Compose. This is p...

Score: 0.8654
Post: dockercompose
Chunk: Docker Compose is a tool for defining and running multi-container Docker applications. With Compose...

Score: 0.8102
Post: addingentityframeworkforblogpostspt1
Chunk: You can set it up either as a windows service or using Docker as I presented in a previous post on...

Score: 0.7891
Post: imagesharpwithdocker
Chunk: When running ASP.NET Core applications in Docker containers, you may encounter issues with ImageSha...

Score: 0.7654
Post: selfhostingseq
Chunk: I use Docker Compose to run all my services. Here's the relevant part of my docker-compose.yml file...
```

Docker और ASP के बारे में प्रासंगिक पोस्टें मिलीं

## निष्पादन अनुकूलन

### बैच सम्मिलन उत्पादन

एक embeddings उत्पन्न नहीं करें

```csharp
public class BatchEmbeddingGenerator
{
    private readonly EmbeddingGenerator _embedder;

    public BatchEmbeddingGenerator(EmbeddingGenerator embedder)
    {
        _embedder = embedder;
    }

    public List<float[]> GenerateBatch(List<string> texts, int batchSize = 32)
    {
        var embeddings = new List<float[]>();

        for (int i = 0; i < texts.Count; i += batchSize)
        {
            var batch = texts.Skip(i).Take(batchSize).ToList();

            foreach (var text in batch)
            {
                embeddings.Add(_embedder.GenerateEmbedding(text));
            }

            Console.WriteLine($"Processed {Math.Min(i + batchSize, texts.Count)} / {texts.Count}");
        }

        return embeddings;
    }
}
```

**बैचिंग क्यों करता है**

- GPU उपयोग: GPU व्यस्त रखता है
- स्मृति दक्षता: बफर का पुनः उपयोग करता है
- प्रगति ट्रैकिंग: उपयोक्ता प्रतिक्रिया

### कैशिंग एम्बेडिंग्स

अपरिवर्तित सामग्री के लिए एम्बेडिंग को पुनः उत्पन्न नहीं करें

```csharp
using System.Security.Cryptography;
using System.Text;

public class EmbeddingCache
{
    private readonly Dictionary<string, float[]> _cache = new();

    public float[] GetOrGenerate(string text, Func<string, float[]> generator)
    {
        string hash = ComputeHash(text);

        if (_cache.TryGetValue(hash, out var cached))
        {
            return cached;
        }

        var embedding = generator(text);
        _cache[hash] = embedding;

        return embedding;
    }

    private string ComputeHash(string text)
    {
        using var sha256 = SHA256.Create();
        var bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(text));
        return Convert.ToBase64String(bytes);
    }
}
```

## वैकल्पिक

अगर आप सब कुछ PostgreSQL में रखना चाहते हैं

### pgvector एक्सटेंशन संस्थापित करें

```sql
CREATE EXTENSION vector;
```

### तालिका बनाएँ

```sql
CREATE TABLE blog_embeddings (
    id SERIAL PRIMARY KEY,
    blog_post_slug VARCHAR(255),
    chunk_text TEXT,
    chunk_index INT,
    embedding VECTOR(768)  -- 768 dimensions
);

-- Create HNSW index for fast search
CREATE INDEX ON blog_embeddings USING hnsw (embedding vector_cosine_ops);
```

### से घुसाएँ C#

```csharp
using Npgsql;
using Pgvector;

public async Task InsertEmbeddingAsync(
    string slug,
    string chunkText,
    int chunkIndex,
    float[] embedding)
{
    await using var conn = new NpgsqlConnection(connectionString);
    await conn.OpenAsync();

    await using var cmd = new NpgsqlCommand(
        "INSERT INTO blog_embeddings (blog_post_slug, chunk_text, chunk_index, embedding) VALUES ($1, $2, $3, $4)",
        conn
    )
    {
        Parameters =
        {
            new() { Value = slug },
            new() { Value = chunkText },
            new() { Value = chunkIndex },
            new() { Value = new Vector(embedding) }
        }
    };

    await cmd.ExecuteNonQueryAsync();
}
```

### pgvector के साथ खोजें

```csharp
public async Task<List<SearchResult>> SearchAsync(float[] queryEmbedding, int topK = 10)
{
    await using var conn = new NpgsqlConnection(connectionString);
    await conn.OpenAsync();

    await using var cmd = new NpgsqlCommand(
        @"SELECT blog_post_slug, chunk_text, chunk_index,
                 1 - (embedding <=> $1) as similarity
          FROM blog_embeddings
          ORDER BY embedding <=> $1
          LIMIT $2",
        conn
    )
    {
        Parameters =
        {
            new() { Value = new Vector(queryEmbedding) },
            new() { Value = topK }
        }
    };

    var results = new List<SearchResult>();

    await using var reader = await cmd.ExecuteReaderAsync();
    while (await reader.ReadAsync())
    {
        results.Add(new SearchResult
        {
            BlogPostSlug = reader.GetString(0),
            ChunkText = reader.GetString(1),
            ChunkIndex = reader.GetInt32(2),
            Score = reader.GetFloat(3)
        });
    }

    return results;
}
```

**`<=>` प्रचालक**: कोसाइन दूरी
**`1 - distance`**समानता प्राप्ताङ्क में बदलें

## सारांश

हमने शामिल किया है

1. ✅ एम्बेडिंग क्या हैं और वे क्या सेमेटिक खोज सक्षम करते हैं
2. ✅ एक सम्मिलित मॉडल को ओनएनक्स में चुनने और रूपांतरित करने के लिए कैसे
3. ✅ ऑनएनक्स रनटाइम के साथ C में एम्बेडिंग उत्पन्न करना
4. ✅ भेक्टर डाटाबेस संकल्पनाएँ
5. ✅ वेक्टर भंडारण और खोज के लिए Qdrant का उपयोग
6. ✅ PostgreSQL में वैकल्पिक: pgvector
7. निष्पादन अनुकूलन

हम अब सेमेटिक रूप से समान ब्लॉग सामग्री ढूंढने के लिए कोर प्रौद्योगिकी है

## क्या है

में **[भाग 4: इनग्रास पाइपलाइन का निर्माण](/blog/building-a-lawyer-gpt-for-your-blog-part4)**हम इंजेक्शन पाइपलाइन बनाएंगे

- मार्कडाउन फ़ाइलों को पढ़ने और विश्लेषण (हम पहले से ही ब्लॉग के लिए ऐसा करते हैं
- बुद्धिमान खंडन रणनीति
- सभी सामग्री के लिए एम्बेडिंग उत्पन्न किया जा रहा है
- बल्क में घुसा रहा है [क्वारंटCity name (optional, probably does not need a translation)](https://qdrant.tech/)
- अद्यतन और मिटाने को संभालना
- प्रगति पर निगरानी

हम अपना पूरा ब्लॉग कोर्स प्रक्रमित करेंगे और एक खोज योग्य ज्ञान आधार रखेंगे

## श्रेणी नेविगेशन

- [भाग 1: परिचय](/blog/building-a-lawyer-gpt-for-your-blog-part1)
- [भाग 2: GPU सेटअप](/blog/building-a-lawyer-gpt-for-your-blog-part2)
- **भाग 3: सम्मिश्रणों को समझना | & | भेक्टर डाटाबेस** यह पोस्ट
- [भाग 4: इनग्रास पाइपलाइन का निर्माण](/blog/building-a-lawyer-gpt-for-your-blog-part4)
- [भाग 5: विंडोज क्लाइंट](/blog/building-a-lawyer-gpt-for-your-blog-part5)
- [भाग 6: स्थानीय LLM एकीकृत](/blog/building-a-lawyer-gpt-for-your-blog-part6)
- [भाग 7: सामग्री उत्पादन & त्वरित इंजीनियरिंग](/blog/building-a-lawyer-gpt-for-your-blog-part7)
- [भाग 8: उन्नत विशेषताएँ & उत्पादन विनियोजन](/blog/building-a-lawyer-gpt-for-your-blog-part8)

## संसाधन

- [वाक्य ट्रांसफार्मर्स](https://www.sbert.net/)
- [क्वान्ट दस्तावेज़ीकरण](https://qdrant.tech/documentation/)
- [pgvector GitHub](https://github.com/pgvector/pgvector)
- [ऑनिक्स रनटाइम](https://onnxruntime.ai/)
- [माइक्रोसॉफ्ट](https://www.nuget.org/packages/Microsoft.ML.Tokenizers/)

आप में मिलेंगे [भाग 4](/blog/building-a-lawyer-gpt-for-your-blog-part4)!