بناء "Lawyer GPT" لمدونتك - الجزء 3: فهم (العربية (Arabic))

بناء "Lawyer GPT" لمدونتك - الجزء 3: فهم

Wednesday, 12 November 2025

//

20 minute read

تحذير: هذه هي "الوظائف" التي "تُعنى".

ومن المرجح أن الكثير مما هو أسفل لن يعمل؛ أنا توليد هذه كما كيف إلى ل ME ومن ثم القيام بجميع الخطوات والحصول على تطبيق العينة العمل... كنت قد تم التسلل ورأيت لهم! أنها سوف تكون على الأرجح جاهزة منتصف كانون الأول/ديسمبر.

## أولاً

مرحباً بكم في الجزء الثالث من الجزء الثالثلدينا محفظة الـ GPU لدينا تعمل (الجزء الثانيو نحن نفهم البنية المعماريةالجزء الأول****الآن حان الوقت للغوص في السحر الذي يجعل البحث الدلالي ممكناً:****.

وقد عقد مؤتمراً بشأن

قواعد بيانات

ملاحظة: هذا جزء من تجاربي مع AI (مساعدة في الصياغة) + تحريري الخاص.

نفس الصوت، نفس العمليّة، مجرد أصابع أسرع.

هنا حيث تصبح الأمور مثيرة للاهتمام.

سنقوم بفهم كيفية تمثيل النص كأرقام بطريقة تلتقط المعنى، وليس مجرد الكلمات الرئيسية.

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

هو الفرق بين العثور على الوظائف التي تحتوي على كلمة "docker" vs العثور على الوظائف التي هي شكلية في مفاهيم الحاويات.

  • ما هي الاهميات ؟
  • التضمين هو تمثيل رقمي للنص (أو الصور، أو السمع، أو ما إلى ذلك) كمتجه - أساساً قائمة أرقام.
  • الحِتَمِيَّة
  • تخيل وضع كلمات في مساحة 2D استناداً إلى مدلولها:

**مجموعة من المفاهيم المتشابهة معاً:**الحيوانات (الأبيض/الأخضر) قريبة بعضها من بعض

وقد جُمعت المركبات (الأزرق) في مجموعات.

مجموعة المصطلحات التقنية

  • المفاهيم المنفصلة عن بعضها البعض
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

استخدام 384 إلى 1536 الأبعاد (ليس فقط 2!)، التقاط المزيد من التباين.

كيف تعمل**نموذج التضمين هو شبكة عصبية مدربة على رسم خريطة النص إلى المتجهات بحيث:**2 - دال - دال - دال - دال - دال - دال - دال - دال -

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.0التماثل
  • 0.0ن استخدام
  • -1.0التشابه

إلى قياس مدى قرب اثنين من المتجهات هي::

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)

التشابهات بين -1 و-1

(بدولارات الولايات المتحدة)

لا يُغيِم = لا يُغيِم

= المعنى العكسي (ناقص في الممارسة)

  • مثال مثال
  • لِمَاذَا مُسْتَمِلَاتُ ٱلْمَمَسِ ٱلْمُسْتَعْلِمَاتُ مِلِمَ ٱلْمُسْتَعْلِمَاتِنَا
  • تذكّرْ، نحن نَبْني a مُسَاعِد كتابَة.
  • عندما أبدأ الكتابة:

**"في هذا المنصب، سأظهر كيفية استخدام إطار الكيانين مع..."**وينبغي أن يجد النظام وظائف سابقة بشأن ما يلي:

أُكِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِِْْ

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. المواضيع الأساسية ذات الصلةليس فقط مباريات الكلمات الرئيسية!
  2. **يجب أن تفهم أن "الهجرات الأساسية" مرتبطة على الرغم من أنها لا تقول "إطار الكيان".**مُجُزّد مُحْث تقليدي
  3. اختيار نموذج مُزْزِجهناك العديد من النماذج المزدحمة.
  4. **نحن بحاجة إلى واحد:**تشغيلات محلياً

(الخدم + السرعة)

العمل مع OONNX عرض |-------|------------|------|---------|-------| | (حتى نتمكن من استخدام لدينا GPU) | 384 | 80MB | Good | Very Fast ⚡⚡⚡ | | جودة جودة جودة جودة الجودة | 768 | 420MB | Better | Fast ⚡⚡ | | (تفهـم تـد) | 384 | 133MB | Better | Very Fast ⚡⚡⚡ | | الحجم المُسسس | 768 | 436MB | Best | Fast ⚡⚡ | | (384-768 أبعاد هي توازن جيد) | 1536 | N/A (API) | Excellent | Slow (network) ⚡ |

الخيارات الشعبية: □ الأبعاد النموذجية □ حجم النوعية □ السرعة □

  • طراز MINIM-L6-v2
  • قاعدة الأساس - v2
  • ب ب ج - سمال - ع - ف
  • (ب)

نـص - مد - 002

التوصية رقم التوصية

(ب):

pip install optimum[exporters]

نموذج المصدر المفتوح:

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

أبعاد 768 7 (الرصيد الجيد)

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

عمل عظيم مع OnNX وقت العرض: الحرة والجارية محلياً

تحويل إلى هيئة ONNNX

معظم النماذج في تنسيق PyTorch.

نحن بحاجة إلى OONNX ل C#.

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

Install Ostititum (تُحوَّل مكتبة بايثون)

  • OnnxRuntime.Gpu
  • Microsoft.ML.Tokenizersهذا يُنشئ:

IF أنت لا تريد إلى تحويل

العديد من النماذج تم تحويلها مسبقاً ومتاحة على واجهة التهكم مع "onnx" في الاسم.

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);
    }
}

& مستخدم بوصة C#

  1. **دعونا نبني مولد دمج عملي باستخدام وقت التشغيل OnNX.**إنشاء مشروع[101, 8667, 2088, 102]

    • لماذا هذه الحزم؟
      • تشغيل نموذج على GPU[- حوّل نص النص إلى مع[الأول
  2. **قبل التضمين، يجب أن نرمز (نقل النص إلى الأرقام):**ما الذي يحدث هنا؟

      • "العالم"
  3. كل رقم هو رمز مع معرف رمزي من مفردات النموذجأرقام خاصة: 101 =

    • 1CLCS]، 102 =
    • 0[SP]
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

  • تُسِجِز النُمز
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. **إذا كان نص النص طويلاً:**المحتويات
  2. - يُخبرُ النموذجَ الذي الرموز هي حقيقية vs ading= هذه الدالة
  3. **(إنه يَزْدَدْدِدُ)**الفئة المصنفة
  4. **الآن كامل خط أنابيب التزكية:**الفصل
  5. ****- إنشاء جلسة OnNX جلسة مع دعم وحدة
  6. ****- محول نص إلى رمز

إنشاء المُحَسْسِس

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
  • نمط البيانات لـ نموذج إدخال

    • يدير الشبكة العصبية
  • المتوسط

- المتوسطات الرمزية المدمجات o الجملة الواحدة المدمجة

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
  • مما يجعل طول المتجه 1 (يسهل حساب التشابه)

هل يُوضَّح

نحن متوسطون بسبب:

كلّ رمز له خاصّه به يُرسّلُ

نحتاج لدمج واحد للجملة بأكملها

(أ) متوسط القيمة الإجمالية للمعنى:

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;
    }
}

****الناتج

جميل!

  • والمحتوى المتماثل من الناحية الدنامترية له تشابه كبير، والمحتوى غير المرتبط منخفض.
  • قواعد بيانات الفئة

الآن لدينا مُدمجات.

نحن بحاجة إلى تخزين الملايين منهم والبحث بكفاءة.

المشكلة

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

دعونا نفترض ان لدينا 1000 مدوّنة، كل منها مقسومة إلى 10 قطع = 10000 مدوّنة.:

  • & & & & & & & & & & & & & & & & & & & & &
  • & مجلد
  • هذا هو O(n)- نحن التحقق من دمج Every.

ببطء!

بالنسبة لـ 10000 تزكية x 768 أبعاد كل منها:

حوالي 30 مليون مليون من عمليات نقاط التفتيش

  1. ~50-100m حتى على المعالج السريع
  2. نحن بحاجة إلى شيء أسرع.
  3. الدالة مُجِرْفِر
  4. تستخدم قواعد بيانات المتجهات هياكل البيانات الذكية (مثل HNSW - الرسوم البيانية العالمية الصغيرة الرائدة الهرمية) للعثور على أقرب الجيران في وقت O(log n).

مقارنة بين السرعة |----------|------------|------------|-------------|---------| | البحث: 50-100ms لـ متجهات 10K | ✅ Excellent | Docker | Very Fast | Apache 2.0 | | (HNSW): 1-5م في حالة ناقلات 10K | ✅ (via Npgsql) | Postgres extension | Fast | PostgreSQL License | | أسرع بـ 10.50 أس أسرع! | ✅ Good | Docker | Very Fast | BSD-3 | | وهي تتفاوت: ملايين المتجهات لا تزال تأخذ فقط -10-20ms. | ⚠️ Limited | Docker/K8s | Very Fast | Apache 2.0 | | تُحدِّث قاعدة بيانات | ❌ Python-first | Docker | Fast | Apache 2.0 |

بالنسبة لمشروع C#، نحن بحاجة إلى:

  • جيّد جيّد ك_ مُحرّك المكتبةQdrant.Client)
  • سهل الوزع )الكركر(
  • الأداء الجيد
  • المصدر الحر/المفتوح
    • قاعدة بيانات * C# دعم / نشر أداء الأداء / الرخصة /

  • (ج) مُنْفِجِكَر
  • مُنَي مُنْي
  • 3/لف

خياري:& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & &

نشر نظام Ddockre

مُنشأ خاصّيّاً لـ متجه البحث (لا مُنْجِز على)

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

الوثائق الدرا الوثائق:

  • 6333التنمية النشطة
  • 6334البديل: pgvuct

**نحن بالفعل نستخدم PostgreSQL للمدونة!**يمكن أن يبقي كل شيء في قاعدة بيانات واحدة./qdrant_storageأقل أداً أقل بقليلاً ولكن أبسط معمارية

لهذه السلسلة، سأستخدم

dotnet add package Qdrant.Client --version 1.7.0

لأن الغرض من البناء و أسهل لفهم المفاهيم.

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إعدادات
  • Distanceنشر DOck
    • Distance.Cosineالموانئ
    • Distance.Euclid- أولاً - ألف - ألف - ألف - ألف - ألف - ألف -
    • Distance.Dot- GRPC API (أكفرستر، سوف نستخدم هذا)

الخزنة

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");
    }
}

بيانات مثبتة إلى::

  • (أ) أن تكون الجهة المضيفة
  • & أمر
  • إنشاء مجموعة

A جمع هو مثل a جدول هو يحمل متجهات a بُعد.:

  • البارات الرئيسية
    • يجب أن يطابق نموذجك المدمج (768 لـ bg-base)

- كيفية قياس التشابه:

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- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  • scoreThreshold- مُنتَج دوت
  • filterهل مُدرج

مُوضحاً

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...

يمكن تخزين أي شيء: عنوان الوظيفة، قطعة النص، التاريخ، الفئات

مُحَسَّب ومُرشَّح!

الإحضار

أسرع بكثير من واحد في واحد في واحد

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;
    }
}

Qdrant مناولة دفعات من 100 إلى 1000 بكفاءة

  • جاري البحث
    • ترجع أعلى K K أكثر مماثلة

- العائد فقط إذا كان التشابه يتجاوز العتبة

  • الترشيح بواسطة الوصفة الوصفية (مثلاً، الوظائف المحددة فقط في البحث)
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);
    }
}

الدالة TRUR LAP

الناتج

الكمال الكمال!

CREATE EXTENSION vector;

وجدت مواقع ذات صلة حول Docker وASP.NET الأساسية، على الرغم من أن العبارة الدقيقة لم تظهر.

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);

معدل الأداء

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();
}

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. ✅ What embeddings are and why they enable semantic search
  2. ✅ How to choose and convert an embedding model to ONNX
  3. ✅ Generating embeddings in C# with ONNX Runtime
  4. ✅ Vector database concepts (HNSW, similarity search)
  5. ✅ Using Qdrant for vector storage and search
  6. ✅ Alternative: pgvector in PostgreSQL
  7. ✅ Performance optimization (batching, caching)

التقدم المحرز في متابعة التقدم: تعليقات المستعملين

مُمَزِج

لا تُطوّر المُضمّنات من أجل المحتوى الغير مُتغيّر!****إذا كنت ترغب في الاحتفاظ بكل شيء في PostgresQL:

  • الثبات في حالة الثبات
  • إدراج من C
  • مع ابحث معالمشغل
  • :: مربع المسافة (الأدنى أكثر تشابهاً)
  • حوّل إلى مماثل درجة أعلى هو أفضل

أولاً - موجز

لقد غطينا:

الجزء 2: تركيبة الجيني وCUDA في C#الجزء 3: فهم قواعد البيانات!

Finding related posts...
logo

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