Back to "DocSummarizer भाग 3 - उन्नत संकल्पनाएँ: मैं बहुत दूर गया था | 🤦" | गहरी डुब्बी"

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

.NET AI BERT C# Embeddings LLM ONNX RAG

DocSummarizer भाग 3 - उन्नत संकल्पनाएँ: मैं बहुत दूर गया था | 🤦" | गहरी डुब्बी

Sunday, 21 December 2025

परिचय

यह है भाग 3 DocSummarizer श्रृंखला के

  1. भाग 1: RAG के साथ एक दस्तावेज सारणी तैयार करना - वास्तुकला और क्यों पाइपलाइन दृष्टिकोण naive LLM कॉलों से आगे जाता है
  2. भाग 2: उपकरण का उपयोग करते हुए सीएलआई के लिए आरंभ निर्देशिका
  3. भाग 3: उन्नत संकल्पना (इस अनुच्छेद) - मैं बहुत दूर गया था
  4. भाग 4: निर्माण RAG पाइपलाइन्स - अपना आरएजी अनुप्रयोग बनाने के लिए न्यूगेट लाइब्रेरी का उपयोग करें

यह मेरे दृष्टिकोण का एक हिस्सा है।

DocSummarizer कैसे आप के एक प्रदर्शन के रूप में शुरू होना चाहिए LLMs के साथ दस्तावेज़ संक्षेपकर्ता बनाना - पाइपलाइन दृष्टिकोण जो मैं भाग में रेखांकित किया था | 1. | अधिकतर शिक्षण आपको दिखाते हैं कि कैसे एक LLM में पाठ को shove करने और सर्वोत्तम के लिए आशा करता है |. | मैं उचित वास्तुकला दिखाना चाहते थे

लेकिन जैसा कि मैं हमेशा करता हूँ-,-मैं समस्या के क्षेत्र में दिलचस्पी ले गया।

मैंने इन दृष्टिकोणों के संस्करणों को लागू किया।

उचित चेतावनीयह है "मैं बहुत दूर गया। क्यों यह काम करता है और कैसे टुकड़े एक साथ मिलते हैं

इस लेख में शामिल है

  • वाक्य सम्मिलन: ट्रांसफार्मर मॉडलों को कैसे रूपांतरित करें
  • ऑनिक्स रनटाइम: पाइथोन या क्लाउड APIs के बिना स्थानीय रूप से मैल मॉडल चलाना
  • RAG (Retrieval-Augmented GenerationM SK2: स्रोत सामग्री में ग्रेडिंग एलएलएम आउटपुट
  • हाइब्रिड खोज: आरआरएफ के साथ सेमेटिक और लेक्सिकल पुनर्प्राप्ति का संयोजन

एक नज़र में वास्तुकला

विनिर्दिष्टताओं में डुबोने से पहले

flowchart TB
    subgraph Input["Document Input"]
        DOC[/"Document<br/>(PDF, MD, URL)"/]
    end

    subgraph Parse["Parsing Layer"]
        DOCLING["Docling<br/>(PDF/DOCX)"]
        MARKDIG["Markdig<br/>(Markdown)"]
    end

    subgraph Extract["Extraction Layer"]
        CHUNK["Document Chunker"]
        SEGMENT["Segment Extractor"]
    end

    subgraph Embed["Embedding Layer"]
        ONNX["ONNX Runtime<br/>(Sentence Transformers)"]
        OLLAMA_EMB["Ollama<br/>(Optional)"]
    end

    subgraph Store["Vector Storage"]
        QDRANT["Qdrant<br/>(Vector DB)"]
        MEMORY["In-Memory<br/>(Small Docs)"]
    end

    subgraph Retrieve["Retrieval Layer"]
        DENSE["Dense Search<br/>(Semantic)"]
        BM25["BM25<br/>(Lexical)"]
        RRF["RRF Fusion"]
    end

    subgraph Synthesize["Synthesis Layer"]
        OLLAMA["Ollama LLM<br/>(Local)"]
        TEMPLATES["Summary Templates"]
    end

    subgraph Output["Output"]
        SUMMARY[/"Summary with<br/>Citations [chunk-N]"/]
    end

    DOC --> DOCLING & MARKDIG
    DOCLING & MARKDIG --> CHUNK & SEGMENT
    CHUNK --> ONNX & OLLAMA_EMB
    SEGMENT --> ONNX
    ONNX & OLLAMA_EMB --> QDRANT & MEMORY
    QDRANT & MEMORY --> DENSE
    CHUNK --> BM25
    DENSE & BM25 --> RRF
    RRF --> OLLAMA
    OLLAMA --> TEMPLATES
    TEMPLATES --> SUMMARY

एम्बेडिंग को समझना

समस्या: आप बिना शब्दकुञ्जी के सुसंगत सामग्री कैसे ढूंढ सकते हैं

जब एक 500-पृष्ठ मैनुअल को संक्षेपित करता है

  • उपयोक्ता पूछता है "मैं युक्ति को कैसे रिसेट करता हूँ
  • मैनुअल कहता है "फॉक्टरी सेटिंग्स को बहाल करने के लिए
  • Keyword search misses it (no shared words)

आपको ज़रूरत है अर्थिक खोज अर्थ के अनुसार मिलान

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

इनमेडिंग इस बात को हल करता है कि पाठ को घने सदिशों में बदलकर।

यहाँ ''' अंतर्ज्ञान है, ':' एक dimensional space को कल्पना करें जहां प्रत्येक पाठ के टुकड़े का एक स्थान होता है,'.' समान अर्थों वाले पाठ एक साथ गुच्छ होते हैं

graph LR
    subgraph "Embedding Space (simplified to 2D)"
        A["🚗 car"]
        B["🚙 automobile"]
        C["🏎️ vehicle"]
        D["🍎 apple"]
        E["🍊 orange"]
        F["🍌 fruit"]
    end
    
    A -.->|"close"| B
    B -.->|"close"| C
    A -.->|"close"| C
    
    D -.->|"close"| E
    E -.->|"close"| F
    D -.->|"close"| F
    
    A -.-|"far"| D

क्यों वाक्य ट्रांसफार्मर्स (रॉ BERT नहीं है

समस्या: मुझे एम्बेडिंग की जरूरत थी जो सेमेटिक समानता के लिए काम करता है

समाधानउपयोग वाक्य रूपांतरक - मॉडल विशिष्ट रूप से अनुरूपता कार्यों पर व्यतिरेकात्मक सीखने का प्रयोग करते हुए प्रशिक्षित BERT वास्तुकला लेकिन अच्छा-अभिन्न रूप से टाउन

जैसे मॉडल all-MiniLM-L6-v2 और bge-small-en-v1.5 अरबों के पाठ जोड़े जैसे”:” पर प्रशिक्षित थे

  • "उपकरण को पुनःस्थापित करने के लिए कैसे करें
  • "प्रयुक्ति को पुनः सेट करने के लिए कैसे करें

प्रशिक्षण उन्हें सिखाता है

संबद्ध: अगर आप समझना चाहते हैं कि कैसे ट्रांसफार्मर मॉडल एक गहरे स्तर पर काम करते हैं - जिसमें ध्यान के तंत्र शामिल है तंत्रिका मशीन अनुवाद कैसे काम करता हैयह अनुवाद की दृष्टि से एक ही ट्रांसफार्मर अवधारणाओं को शामिल करता है

क्रियान्वयन: हम मॉडल को ले लेते हैं ' आउटपुट परत और लागू करें इकट्ठा करना सभी टोकन के अंतःकरणों को औसत करके संपूर्ण पाठ के लिए एक एकल सदिश प्राप्त करना

flowchart LR
    subgraph Input
        TEXT["The quick brown fox"]
    end
    
    subgraph Tokenization
        CLS["[CLS]"]
        T1["the"]
        T2["quick"]
        T3["brown"]
        T4["fox"]
        SEP["[SEP]"]
    end
    
    subgraph "BERT Encoder"
        direction TB
        L1["Layer 1: Self-Attention"]
        L2["Layer 2: Self-Attention"]
        L3["..."]
        L6["Layer 6: Self-Attention"]
    end
    
    subgraph Output
        E1["E[CLS]"]
        E2["E[the]"]
        E3["E[quick]"]
        E4["E[brown]"]
        E5["E[fox]"]
        E6["E[SEP]"]
    end
    
    subgraph Pooling
        MEAN["Mean Pool<br/>(with attention mask)"]
        VEC["384-dim Vector"]
    end
    
    TEXT --> CLS & T1 & T2 & T3 & T4 & SEP
    CLS & T1 & T2 & T3 & T4 & SEP --> L1
    L1 --> L2 --> L3 --> L6
    L6 --> E1 & E2 & E3 & E4 & E5 & E6
    E1 & E2 & E3 & E4 & E5 & E6 --> MEAN
    MEAN --> VEC

स्थानीय रूप से ML मॉडल चला रहे हैं

समस्या: पाइथोन निर्भरता नरक

मैं embeddings करने के लिए चाहते थे

  1. पाइथन + PyTorch + ट्रांसफार्मर्स संस्थापित करें
  2. मॉडलों को हस्तचालित डाउनलोड करें
  3. आशा संस्करण द्वंद्व नहीं हैं-'-सब कुछ तोड़ता है

यह शून्य है docsummarizer -f doc.pdfचरण सेटअप गाइड नहीं है

क्यों ONNX?

ONNX (Open Neural Network Exchange)ML मॉडलों के लिए एक खुला प्रारूप है पाइथन के बिना रनटाइम निष्कर्ष.

What I get with ONNX Runtime

  1. शून्य बाहरी निर्भरताएँ - कोई पाइथन नहीं
  2. स्वतः मॉडल डाउनलोड करें सबसे पहले HuggingFace से डाउनलोड चलाएँ
  3. शुद्ध .NET कहीं भी काम करता है
  4. सीपीयू निष्कर्ष - कोई GPU आवश्यक नहीं है

व्यापार-off: GPU PyTorch से थोड़ा धीमा है, लेकिन उपयोगकर्ताओं से पाइथन को स्थापित करने के लिए पूछने की अपेक्षा बहुत तेजी से

मॉडल रजिस्ट्री

DocSummarizer में कई सम्मिलित मॉडल शामिल हैं

माडल आयाम अधिकतम टोकन МSK3 आकार ( क्वांटाइज एमएसके5 मएसके6 उपयोगी केस एमएसके7 अनुदेश की आवश्यकता है
AllMiniLmL6V2 384
BgeSmallEnV15 तकनीकी डक के लिए बेहतर
GteSmall सामान्य प्रयोजन
MultiQaMiniLm Q के लिए अनुकूलित

नोट: सभी रजिस्ट्री प्रविष्टियाँ WordPiece को इंगित करती हैं vocab.txtयूनिग्राम मॉडल अभी तक समर्थित नहीं हैं

बीजीई अनुदेश प्रारूपकुछ मॉडलों (जैसे BGE)से बेहतर निष्पादन के लिए उपसर्गों की आवश्यकता होती है

// Query embedding (what the user asks)
var queryText = "Represent this sentence for searching relevant passages: " + userQuery;
var queryEmbedding = await EmbedAsync(queryText);

// Passage embedding (document chunks)
// Some BGE variants prefix passages, others don't - check model documentation
var passageEmbedding = await EmbedAsync(chunkText);

रजिस्ट्री ट्रैक करता है कि मॉडलों के लिए निर्देशों के माध्यम से RequiresInstruction और QueryInstruction क्षेत्रों. निर्देश के साथ काम करते समय हमेशा मानक पुनःप्राप्ति गुणवत्ता

यहाँ यह है कि मॉडल रजिस्ट्री कैसे काम करता है

public static class OnnxModelRegistry
{
    public static EmbeddingModelInfo GetEmbeddingModel(OnnxEmbeddingModel model, bool quantized = true)
    {
        return model switch
        {
            OnnxEmbeddingModel.AllMiniLmL6V2 => new EmbeddingModelInfo
            {
                Name = "all-MiniLM-L6-v2",
                HuggingFaceRepo = "Xenova/all-MiniLM-L6-v2",
                ModelFile = quantized ? "onnx/model_quantized.onnx" : "onnx/model.onnx",
                VocabFile = "vocab.txt",
                EmbeddingDimension = 384,
                MaxSequenceLength = 256,
                SizeBytes = quantized ? 23_000_000 : 90_000_000,
                RequiresInstruction = false
            },
            // ... other models
        };
    }
}

टोकनीकरण: WordPiece vs BPE

विभिन्न मॉडल भिन्न टोकनाइजर का उपयोग करते हैं all-MiniLM-L6-v2 मॉडल WordPiece टोकनीकरण का उपयोग करता है ( BERT के समान ), जो अज्ञात शब्दों को उपशब्द टोकन में विभाजित करता है

महत्वपूर्ण: मॉडल टोकनाइजर प्रशिक्षण टोकनizer के साथ मिलना चाहिए. हमारी रजिस्ट्री ट्रैक जो टोकनiza हर मॉडल की आवश्यकता है वर्तमान में कार्यान्वित (via vocab.txtयूनिग्राम समर्थन के माध्यम से tokenizer.json योजनाबद्ध है लेकिन अभी तक लागू नहीं किया गया - अभी तक रजिस्ट्री में WordPiece मॉडलों पर चिपकाएँ

public class BertTokenizer
{
    private readonly Dictionary<string, int> _vocab;
    private const int ClsTokenId = 101;  // [CLS] - start of sequence
    private const int SepTokenId = 102;  // [SEP] - end of sequence
    private const int PadTokenId = 0;    // [PAD] - padding
    private const int UnkTokenId = 100;  // [UNK] - unknown token

    public BertTokenizer(string vocabPath)
    {
        // Load vocabulary: word -> token ID
        _vocab = File.ReadAllLines(vocabPath)
            .Select((word, index) => (word, index))
            .ToDictionary(x => x.word, x => x.index);
    }

    public (long[] InputIds, long[] AttentionMask, long[] TokenTypeIds) 
        Encode(string text, int maxLength)
    {
        // Split text into words, then apply WordPiece to each word
        var words = text.ToLowerInvariant()
            .Split(new[] { ' ', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
        var tokens = words.SelectMany(WordPieceTokenize).ToList();
        
        // Truncate to fit [CLS] and [SEP] tokens
        if (tokens.Count > maxLength - 2)
            tokens = tokens.Take(maxLength - 2).ToList();

        // Build input: [CLS] + tokens + [SEP] + [PAD]...
        var inputIds = new List<long> { ClsTokenId };
        inputIds.AddRange(tokens.Select(t => (long)GetTokenId(t)));
        inputIds.Add(SepTokenId);

        // Pad to maxLength
        var padCount = maxLength - inputIds.Count;
        inputIds.AddRange(Enumerable.Repeat((long)PadTokenId, padCount));

        // Attention mask: 1 for real tokens, 0 for padding
        var attentionMask = inputIds.Select(id => id != PadTokenId ? 1L : 0L).ToArray();
        
        // Token type IDs: all zeros for single sentence
        var tokenTypeIds = new long[maxLength];

        return (inputIds.ToArray(), attentionMask, tokenTypeIds);
    }

    private IEnumerable<string> WordPieceTokenize(string word)
    {
        // If the whole word is in vocabulary, return it
        if (_vocab.ContainsKey(word))
        {
            yield return word;
            yield break;
        }

        // Otherwise, split into subwords with "##" prefix
        int start = 0;
        while (start < word.Length)
        {
            int end = word.Length;
            string? curSubstr = null;

            while (start < end)
            {
                var substr = word[start..end];
                if (start > 0) substr = "##" + substr;  // Continuation marker

                if (_vocab.ContainsKey(substr))
                {
                    curSubstr = substr;
                    break;
                }
                end--;
            }

            if (curSubstr == null)
            {
                yield return "[UNK]";
                yield break;
            }

            yield return curSubstr;
            start = end;
        }
    }
}

उदाहरण टोकनीकरण:

इनपुट

------- --------
"DocSummarizer" ["doc", "##su", "##mm", "##ari", "##zer"]
"the quick brown" ["the", "quick", "brown"]

ध्यान मास्क के साथ औसत पूलिंग

जब BERT टोकन को प्रक्रमित करता है, तो हम प्रत्येक टोकन के लिए एक छुपी स्थिति प्राप्त करते हैं।

private static float[] MeanPool(Tensor<float> hiddenStates, long[] attentionMask, int hiddenSize)
{
    // Assumes last_hidden_state shape: [batch=1, seq_len, hidden_size]
    // Note: Many sentence-transformer models export a pooled output directly,
    // but we use mean pooling for consistency across all ONNX exports.
    var result = new float[hiddenSize];
    var dims = hiddenStates.Dimensions.ToArray();
    var seqLen = (int)dims[1];
    
    // Count real tokens (not padding)
    float maskSum = attentionMask.Count(x => x == 1);
    if (maskSum == 0) maskSum = 1; // Avoid division by zero

    // Average each dimension, weighted by attention mask
    for (int h = 0; h < hiddenSize; h++)
    {
        float sum = 0;
        for (int s = 0; s < seqLen; s++)
        {
            if (attentionMask[s] == 1)
                sum += hiddenStates[0, s, h];
        }
        result[h] = sum / maskSum;
    }

    // L2 normalize for cosine similarity
    float norm = MathF.Sqrt(result.Sum(x => x * x));
    if (norm > 0)
    {
        for (int i = 0; i < result.Length; i++)
            result[i] /= norm;
    }

    return result;
}

पूर्ण सम्मिलन पाइपलाइन

यहाँ पाठ से अंतःस्थापित करने के लिए पूरा प्रवाह है

public class OnnxEmbeddingService : IEmbeddingService, IDisposable
{
    private InferenceSession? _session;
    private BertTokenizer? _tokenizer;

    public async Task<float[]> EmbedAsync(string text, CancellationToken ct = default)
    {
        await InitializeAsync(ct);  // Downloads model if needed
        
        // Prepend instruction for models that need it (like BGE)
        if (_modelInfo.RequiresInstruction)
            text = _modelInfo.QueryInstruction + text;

        // Tokenize
        var (inputIds, attentionMask, tokenTypeIds) = 
            _tokenizer.Encode(text, _maxSequenceLength);

        // Create ONNX tensors
        var inputIdsTensor = new DenseTensor<long>(inputIds, new[] { 1, inputIds.Length });
        var attentionMaskTensor = new DenseTensor<long>(attentionMask, new[] { 1, attentionMask.Length });
        var tokenTypeIdsTensor = new DenseTensor<long>(tokenTypeIds, new[] { 1, tokenTypeIds.Length });

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

        // Run inference
        using var results = _session.Run(inputs);
        
        // Get hidden states output
        var output = results.First(r => r.Name == "last_hidden_state");
        var outputTensor = output.AsTensor<float>();

        // Mean pooling with attention mask
        return MeanPool(outputTensor, attentionMask, _modelInfo.EmbeddingDimension);
    }
}

RAG: पुनःप्राप्ति-वर्धित जनन

समस्या: LLMs Can't Read M SK2Page Documents

naiv approach fails

var text = File.ReadAllText("500-page-manual.txt"); // 2MB of text
var summary = await llm.GenerateAsync($"Summarize: {text}"); // ❌ Doesn't fit in context

128K संदर्भ विंडों के साथ भी, आप केवल विशाल दस्तावेज़ों को डुम्प नहीं कर सकते

  • संकुचनकेवल पहले 100 पृष्ठ फिट हैं
  • भ्रम: एलएलएम रिक्तियों को भरने के लिए खोजता है
  • लागतप्रसंस्करण 2MB पाठ लागत प्रति क्वेरी
  • गुणवत्ताLLMs बड़े संदर्भ के साथ भ्रमित हो जाते हैं

समाधान:RAG (Retrieval

सब कुछ भेजने के बजाय केवल क्या भेजें

  1. चाइंगिंग: दस्तावेज को खंडों में विभाजित करें
  2. सम्मिलित: खंडों को वेक्टर में बदलें
  3. पुनःप्राप्तिक्वेरी के लिए : सबसे प्रासंगिक खंडों को ढूंढें
  4. संश्लेषण: LLM केवल उन खंडों का सारांश करता है

यह काम क्यों करता है एलएलएम अधिकतर अप्रत्यक्ष पाठ के 2MB की बजाय उच्च प्रासंगिक सामग्री के \10\KB को देखता है

flowchart LR
    subgraph "Without RAG"
        DOC1[/"500-page PDF"/]
        LLM1["LLM<br/>(32K context)"]
        OUT1["❌ Truncated or<br/>Hallucinated"]
    end
    
    subgraph "With RAG"
        DOC2[/"500-page PDF"/]
        CHUNKS["100 Chunks"]
        VDB["Vector DB"]
        QUERY["Query"]
        TOP["Top 10 Chunks"]
        LLM2["LLM"]
        OUT2["✅ Grounded<br/>Summary"]
    end
    
    DOC1 --> LLM1 --> OUT1
    
    DOC2 --> CHUNKS --> VDB
    QUERY --> VDB --> TOP --> LLM2 --> OUT2

दस्तावेज़ निर्माण कार्यनीतियां

DocSummarizer दस्तावेज संरचना पर आधारित बहुल खंडन रणनीतियों को समर्थन करता है:

public class DocumentChunker
{
    public List<DocumentChunk> ChunkByHeadings(string markdown, int maxHeadingLevel = 2)
    {
        var chunks = new List<DocumentChunk>();
        var lines = markdown.Split('\n');
        var currentChunk = new StringBuilder();
        var currentHeading = "";
        var headingLevel = 0;
        var order = 0;

        foreach (var line in lines)
        {
            // Detect heading (# to ######)
            var headingMatch = Regex.Match(line, @"^(#{1,6})\s+(.+)$");
            
            if (headingMatch.Success && 
                headingMatch.Groups[1].Length <= maxHeadingLevel)
            {
                // Flush current chunk
                if (currentChunk.Length > 0)
                {
                    chunks.Add(new DocumentChunk(
                        Order: order++,
                        Heading: currentHeading,
                        HeadingLevel: headingLevel,
                        Content: currentChunk.ToString().Trim(),
                        Hash: ComputeHash(currentChunk.ToString())
                    ));
                }
                
                // Start new chunk
                currentHeading = headingMatch.Groups[2].Value;
                headingLevel = headingMatch.Groups[1].Length;
                currentChunk.Clear();
            }
            else
            {
                currentChunk.AppendLine(line);
            }
        }
        
        // Don't forget the last chunk
        if (currentChunk.Length > 0)
        {
            chunks.Add(new DocumentChunk(
                Order: order,
                Heading: currentHeading,
                HeadingLevel: headingLevel,
                Content: currentChunk.ToString().Trim(),
                Hash: ComputeHash(currentChunk.ToString())
            ));
        }
        
        return chunks;
    }
}

Fine-Grained पुनःप्राप्ति के लिए खण्ड निष्कर्षण

लम्बे दस्तावेज़ों के लिए, DocSummarizer व्यक्तिगत खंडों को निकालता है।

public class SegmentExtractor
{
    public async Task<ExtractionResult> ExtractAsync(string docId, string markdown)
    {
        // 1. Parse into typed segments
        var segments = ParseToSegments(docId, markdown);
        
        // 2. Generate embeddings
        await GenerateEmbeddingsAsync(segments);
        
        // 3. Calculate document centroid (average embedding)
        var centroid = CalculateCentroid(segments);
        
        // 4. Score by salience using MMR (Maximal Marginal Relevance)
        ComputeSalienceScores(segments, centroid);
        
        return new ExtractionResult
        {
            AllSegments = segments,
            TopBySalience = segments.OrderByDescending(s => s.SalienceScore).Take(50).ToList(),
            Centroid = centroid
        };
    }
}

समस्या: सेमेटिक खोज परिणाम प्रतिलिपि

एमएमआर बिनाके लिए , पुनःप्राप्ति

  1. " कैचिंग ओवरव्यू
  2. कैशिंग के आरंभ में
  3. कैशिंग क्या है
  4. "कैश क्रियान्वयन विवरण

सबसे ऊपर 3 परिणाम सभी एक ही बात कहते हैं

समाधान: अधिकतम सीमान्त प्रासंगिकता

एमएमआर संतुलन प्रासंगिकता क्वेरी से (समानता विविधता चुने हुए मदों के प्रति समानता

सूत्र à¤a्रें_चयनित में

क्या करता है पहले से ही समान उम्मीदवारों को दंडित करता है

flowchart TB
    subgraph "MMR Selection"
        S1["Segment 1<br/>Score: 0.95"]
        S2["Segment 2<br/>Score: 0.90"]
        S3["Segment 3<br/>Score: 0.88"]
        S4["Segment 4<br/>Score: 0.85"]
    end
    
    subgraph "Selected"
        SEL1["✓ Seg 1<br/>(highest)"]
        SEL2["✓ Seg 3<br/>(most diverse)"]
        SEL3["✓ Seg 4"]
    end
    
    S1 -->|"Select"| SEL1
    S2 -->|"Skip - too similar to Seg 1"| X["❌"]
    S3 -->|"Select"| SEL2
    S4 -->|"Select"| SEL3

सूत्र

$$MMR_चुने गए में

private List<Segment> SelectSentencesMMR(
    List<Segment> segments,
    float[] centroid,
    int targetCount)
{
    var selected = new List<Segment>();
    var candidates = new HashSet<Segment>(segments.Where(s => s.Embedding != null));
    
    // Pre-calculate centroid similarities
    foreach (var segment in candidates)
    {
        segment.Score = CosineSimilarity(segment.Embedding!, centroid) 
                      * segment.PositionWeight;
    }
    
    while (selected.Count < targetCount && candidates.Count > 0)
    {
        Segment? best = null;
        double bestScore = double.MinValue;
        
        foreach (var candidate in candidates)
        {
            // Relevance: similarity to centroid
            var relevance = candidate.Score;
            
            // Diversity: max similarity to already selected
            double maxSimToSelected = 0;
            foreach (var sel in selected)
            {
                var sim = CosineSimilarity(candidate.Embedding!, sel.Embedding!);
                maxSimToSelected = Math.Max(maxSimToSelected, sim);
            }
            
            // MMR score: balance relevance and diversity
            var mmrScore = _config.Lambda * relevance 
                         - (1 - _config.Lambda) * maxSimToSelected;
            
            if (mmrScore > bestScore)
            {
                bestScore = mmrScore;
                best = candidate;
            }
        }
        
        if (best != null)
        {
            selected.Add(best);
            candidates.Remove(best);
        }
    }
    
    return selected;
}

आरआरएफ के साथ हाइब्रिड खोज

समस्या: अर्थ खोज में सही मिलान नहीं है

मैने यह जाँच करने के दौरान सामना किया

क्वेरीप्रमाणीकरण के लिए API अंतबिंदु क्या है

सेमेटिक खोज लौटा

  1. "प्रयोक्ता लॉगिन फ्लो आव्यू
  2. "सुरक्षा के सर्वोत्तम प्रथाएं" (उच्च समानता
  3. "स्थिति प्रबंधन

क्या भूल गया?: वास्तविक API अंतबिंदु को कोड उदाहरणों में गाड़ दिया गया है POST /api/v1/auth/login

क्यों: एम्बेडिंग मॉडल प्राकृतिक भाषा पर प्रशिक्षित किए जाते हैं POST /api/v1/auth/login semantically match नहीं है "authentication endpoint

समाधान: हाइब्रिड खोज | ( | Semantic |+ | Lexical

दो पुनर्प्राप्ति विधियों को संयोजित करने के साथ अनुपूरक शक्तियाँ

खोज प्रकार |-------------|-----------|------------| | dense (Embedding) अर्थ समझना | Sparse (BMM SK1 | सही शब्दकुञ्जी मिलान

हाइब्रिड खोज दोनों का उपयोग Reciprocal Rank Fusion (RRF

flowchart TB
    QUERY["Query: 'authentication security'"]
    
    subgraph Dense["Dense Search (Semantic)"]
        D1["1. OAuth 2.0 implementation"]
        D2["2. User login flow"]
        D3["3. Password hashing"]
    end
    
    subgraph Sparse["BM25 Search (Lexical)"]
        S1["1. Authentication middleware"]
        S2["2. Security headers"]
        S3["3. OAuth 2.0 implementation"]
    end
    
    subgraph RRF["RRF Fusion (Illustrative)"]
        R1["OAuth 2.0 implementation<br/>RRF = 1/(60+1) + 1/(60+3) ≈ 0.032"]
        R2["Authentication middleware<br/>RRF = (not in dense) + 1/(60+1) ≈ 0.016"]
        R3["User login flow<br/>RRF = 1/(60+2) + (not in BM25) ≈ 0.016"]
    end
    
    QUERY --> Dense & Sparse
    Dense --> RRF
    Sparse --> RRF

नोट: आरआरएफ प्राप्तियों को दिखाया गया आरेखात्मक है।

आरआरएफ कार्यान्वयन

public static class HybridRRF
{
    /// <summary>
    /// Reciprocal Rank Fusion: combine multiple rankings into one.
    /// 
    /// Formula: RRF(d) = Σ 1/(k + rank_i(d))
    /// 
    /// Where k = 60 (standard constant to prevent division by small numbers)
    /// </summary>

    public static List<Segment> Fuse(
        List<Segment> segments,
        string query,
        BM25Scorer bm25,
        int k = 60,
        int topK = 20)
    {
        // Rank by dense similarity
        var byDense = segments
            .Where(s => s.Embedding != null)
            .OrderByDescending(s => s.QuerySimilarity)
            .ToList();
        
        // Rank by BM25 (scorer is built over the same ordered segment list)
        var bm25Scores = segments
            .Select((s, i) => (segment: s, score: bm25.Score(i, query)))
            .OrderByDescending(x => x.score)
            .Select(x => x.segment)
            .ToList();
        
        // Rank by salience (pre-computed importance)
        var bySalience = segments
            .OrderByDescending(s => s.SalienceScore)
            .ToList();
        
        // Compute RRF scores
        var rrfScores = new Dictionary<Segment, double>();
        
        void AddRRFScore(List<Segment> ranking)
        {
            for (int i = 0; i < ranking.Count; i++)
            {
                var segment = ranking[i];
                var rrfContribution = 1.0 / (k + i + 1);  // 1-based rank
                
                if (!rrfScores.TryAdd(segment, rrfContribution))
                    rrfScores[segment] += rrfContribution;
            }
        }
        
        AddRRFScore(byDense);
        AddRRFScore(bm25Scores);
        AddRRFScore(bySalience);
        
        // Return top-K by fused score
        return rrfScores
            .OrderByDescending(kv => kv.Value)
            .Take(topK)
            .Select(kv => kv.Key)
            .ToList();
    }
}

BM25: Sparse Retrieval Workhorse

BM25 M SK1Best Matching 25) एक क्लासिक सूचना पुनःप्राप्ति एल्गोरिथ्म है।

public class BM25Scorer
{
    private const double K1 = 1.5;  // Term frequency saturation
    private const double B = 0.75;  // Length normalization factor
    
    public double Score(int docIndex, string query)
    {
        var queryTerms = Tokenize(query);
        var docTermFreq = _docTermFreqs[docIndex];
        var docLength = _docLengths[docIndex];
        
        double score = 0;
        
        foreach (var term in queryTerms.Distinct())
        {
            if (!docTermFreq.TryGetValue(term, out var tf)) continue;
            if (!_docFreqs.TryGetValue(term, out var df)) continue;
            
            // IDF with smoothing
            var idf = Math.Log((_corpusSize - df + 0.5) / (df + 0.5) + 1);
            
            // BM25 TF component with length normalization
            var tfNorm = (tf * (K1 + 1)) / 
                (tf + K1 * (1 - B + B * docLength / _avgDocLength));
            
            score += idf * tfNorm;
        }
        
        return score;
    }
}

सामग्री केंद्रितता के लिए TF-IDF

समस्या: आप कोर सामग्री को ट्रिविया से कैसे अलग करते हैं

एक उपन्यास को सारांश करने के लिए मैं परिणाम प्राप्त करता हूँ जैसे

पात्र ने एक नीली कोट पहना था

ये सही निष्कर्ष हैं, लेकिन वे रंग (scene कोर प्लोट बिन्दु.

चुनौतीएमएसके0 के बीच आप क्या अंतर बता सकते हैं

  • मुख्य सामग्रीदस्तावेज़ में दिखाई देता है
  • समर्थन विवरणकुछ खंडों में दिखाई देता है
  • रंग: दुर्लभ

केंद्रीयता वर्गीकरण के लिए समाधान

TF-IDF क्या एक शब्द दस्तावेज के लिए केंद्रीय हैइसका सत्य मूल्य नहीं है

तार्किक:

  • उच्च DF chunks के (>50%: "Sherlock
  • मध्यम DF (20-50%): "Baker StreetM SK2 | | "|investigation | 4 | 5 | सहायक विवरण
  • कम DF (<20%): "नीला कोट", | | " | अक्ष फर्नीचर |

यह सत्य के बारे में नहीं है। दस्तावेज के लिए केंद्रीयता.

flowchart LR
    subgraph "TF-IDF Classification"
        CLAIM["Claim text"]
        TERMS["Extract terms"]
        TFIDF["Compute TF-IDF"]
        CLASS["Classify"]
    end
    
    subgraph "Term Types"
        COMMON["High DF (>50%)<br/>→ Core content"]
        MODERATE["Medium DF (20-50%)<br/>→ Supporting detail"]
        RARE["Low DF (<20%)<br/>→ Incidental colour"]
    end
    
    CLAIM --> TERMS --> TFIDF --> CLASS
    CLASS --> COMMON & MODERATE & RARE
public class TextAnalysisService
{
    private readonly Dictionary<string, int> _documentFrequency = new();
    private int _totalDocuments;

    public void BuildTfIdfIndex(IEnumerable<string> documents)
    {
        _documentFrequency.Clear();
        _totalDocuments = 0;
        
        foreach (var doc in documents)
        {
            _totalDocuments++;
            var terms = Tokenize(doc).Distinct();
            
            foreach (var term in terms)
            {
                _documentFrequency.TryGetValue(term, out var count);
                _documentFrequency[term] = count + 1;
            }
        }
    }

    /// <summary>
    /// Classify term centrality (not epistemic truth):
    /// - High DF (>50%): appears across most chunks = core content
    /// - Medium DF (20-50%): supporting detail
    /// - Low DF (<20%): rare = likely incidental ("colour")
    /// 
    /// Note: This estimates centrality, not factuality. A repeated 
    /// claim can be false; a rare fact can be true.
    /// </summary>

    public ClaimType ClassifyTermImportance(string term)
    {
        var df = _documentFrequency.GetValueOrDefault(term.ToLowerInvariant(), 0);
        
        if (_totalDocuments == 0 || df == 0)
            return ClaimType.Colour;
        
        var documentRatio = (double)df / _totalDocuments;
        
        // High centrality = appears widely
        if (documentRatio > 0.5)
            return ClaimType.Core;
        
        // Medium centrality = supporting themes
        if (documentRatio > 0.2)
            return ClaimType.Supporting;
        
        // Low centrality = incidental detail
        return ClaimType.Colour;
    }
}

पूर्ण बर्टरेग पाइपलाइन

DocSummarizer's उत्पादन पाइपलाइनBertRagSummarizerसभी इन अवधारणाओं को जोड़ता है

public class BertRagSummarizer
{
    /// <summary>
    /// Full pipeline: Extract → Retrieve → Synthesize
    /// 
    /// Key properties:
    /// - LLM only at synthesis (no LLM-in-the-loop evaluation)
    /// - Deterministic extraction (reproducible, debuggable)
    /// - Validated citations (every claim traceable to source segment)
    /// - Scales to any document size
    /// - Cost-optimal (cheap CPU work first, expensive LLM last)
    /// </summary>

    public async Task<DocumentSummary> SummarizeAsync(
        string docId,
        string markdown,
        string? focusQuery = null)
    {
        // === Phase 1: Extract ===
        // Parse document → segments with embeddings + salience scores
        var extraction = await _extractor.ExtractAsync(docId, markdown);
        
        // === Phase 2: Retrieve ===
        // Hybrid search: Dense + BM25 + Salience via RRF
        var retrieved = await RetrieveAsync(extraction, focusQuery);
        
        // === Phase 3: Synthesize ===
        // LLM generates fluent summary from retrieved segments
        var summary = await SynthesizeAsync(docId, retrieved, extraction, focusQuery);
        
        return summary;
    }
}

सामान्य असफल मोड

जब निर्माण और DocSummarizer का उपयोग करते हैं, मैं' इन समस्याओं को छुआ है M SK2और आप भी करेंगे

  1. टोकनाइजर असमेल → बेवकूफ एम्बेडिंगBPE के लिए एक WordPiece vocab लोड करना-प्रशिक्षित मॉडल वैध बनाता है।

  2. एकल में अधिमानता-स्थानिक पक्षपात-सेंट्रोइड स्कोरिंगएक दस्तावेज के केन्द्रीकृत को व्यवस्थित रूप से नीचे उपयोग करते हुए - अल्पसंख्यक विषयों का दर्जा करता है

  3. BM25 दुर्लभ शर्तों पर घन खोज को पार करता है: यदि आपके क्वेरी में तकनीकी शब्दावली या सही नाम नहीं है, तो यह अच्छी तरह से प्रस्तुत होता है।

  4. स्कैन किए गए पीडीएफ में ओसीआर कचराडॉकलिंग अच्छा है, लेकिन ओसीआर त्रुटियों का संयोजन होता है।

  5. Low-coverage सारांश भाषा को hedge करना चाहिएयदि आप एक दस्तावेज़ में केवल 3% को देख रहे हैं, तो आपको यह पता चलेगा कि वाक्यांश जैसे " अंतिम रूप से | " या |" | अंत में |

  6. उद्धरण भ्रम: छोटे LLMs [chunk-N], सत्यापन N स्रोत टुकड़ों में मौजूद है, और फ्लैगिंग या मरम्मत दावे जो अनुपस्थित टुकड़े को उद्धृत करता है [chunk-999] के लिए 10-chunk दस्तावेज़

ये bugs नहीं हैं, वे डिज़ाइन जगह में अंतर्निहित तनाव है

व्यावहारिक विचार

कवरेज और नमूनों का सम्मान

जब बहुत बड़े दस्तावेजों को प्रक्रमित किया जाता है, तो DocSummarizer सब कुछ सम्मिलित करने की कोशिश नहीं करता। इसका अर्थ है कि सारांश नमूना पर आधारित है

सिस्टम इसे पारदर्शी रूप से संभालता है

// If coverage is low (<5%), prepend disclaimer and use cautious language
if (coverage < 0.05)
{
    var disclaimer = $"WARNING: Summary (sampled ~{coverage:P1} of document)";
    summary = $"{disclaimer}\n\n{CleanAndHedge(summary)}";
}

// Append coverage footer to every summary
var footer = $"\n\n---\nCoverage: {coverage:P1} ({scope})\nConfidence: {confidence}";

महत्वपूर्ण: यह एक है प्राप्त साक्ष्य का सारांशजब हम कहते हैं "सम्पलेट 3%", कि यह ठीक है कि क्या हुआ ', तब सिस्टम ने document को देखा और संक्षेपित किया कि

नमूनाकरण अनियमित नहीं है 0 यह अर्थशास्त्रीयहम बहुआधार समूहन का उपयोग अल्पसंख्यक विषयों को सुनिश्चित करने के लिए करते हैं।

बहुविध विषय एङ्कर के साथ अनुकूली नमूनाकरण:\ The pre\ -\ filter uses multiple anchors\ (\ k\ -\ means\ mk4\ style clustering of a stratified sample\ mksq5\ to ensure minority topics are\ msq6\ not systematically excluded\ m sq7\ This prevents the "\dominant theme bias\ mshq9\ where a single centroid down\ msc10\ ranks important\ mcsq11\ but\ m sc12\ rare content like constraints

से SegmentExtractor.cs:

// Multi-anchor approach prevents single-centroid bias
var topicAnchors = ComputeTopicAnchors(embeddedSample, k: 5);

// Score by max similarity to ANY anchor (catches minority topics)
var score = topicAnchors.Max(anchor => CosineSimilarity(segment.Embedding, anchor));

यह अनुसंधान है।

क्यों सिर्फ सब कुछ सम्मिलित नहीं करें

पृष्ठ दस्तावेज़ के लिए 500- खंडों को embedding everything would work but isn’t optimal

  • लागतएम्बेडिंग रनटाइम पर प्रभावी होती है।
  • गुणवत्ताEverything Embedding increases noise in the retrieval pool
  • व्यवहारिकतामेमोरी और लैटेंसी अवरोध महत्वपूर्ण हैं।

बहुआधार नमूनाकरण आपको व्यावहारिक संगणन के साथ दोनों का सर्वोत्तम उपयोग करता है

क्या मैं,', ऊपर वर्णन किया है यह नहीं है, ' केवल ", पुनःप्राप्ति-", "-", यह एक विशिष्ट पैटर्न मैं कहता हूँ प्रतिबंधित अस्पष्ट संदर्भ खींचना (सीएफसीडीज्ञान

अधिकतर संक्षेपकर्ता संदर्भ जोड़ रहे हैं आगे खींचता है केवल deterministic चयन से बचता है तो मॉडल को उन सीमाओं के भीतर fluently लिखने देता है

यहाँ DocSummarizer पाइपलाइन CFCD में कैसे मानचित्रित करता है

सीएफसीडी संकल्पना |--------------|------------------------------| | सूक्ष्मता पहचान (fuzzyM SK1 | Embeddings, centroid similarityMSC4 TF | निर्णायक संवर्धन |MMR,BM25,RF फ्यूजन,top-K चयन| | Anchor ledger प्रतिलिपि आईडी के साथ प्राप्त खंड सेट | अवरोधित जनन निष्कर्षित साक्ष्य से bound संश्लेषण प्रवर्धन

यह क्यों महत्वपूर्ण है: नमूना नहीं करता है-' क्या तय करने के लिए ' क्या प्रासंगिक है |- पुनर्प्राप्ति पाइपलाइन करता है .\ नमूने केवल हम निर्धारित सीमाओं के भीतर प्रवाहित रूप से उत्पन्न करता है।

सैद्धांतिक रूप से यह दिखता है

{
  "coverage": "3.2% semantic sample",
  "anchors": [
    { "id": "chunk-12", "text": "Reset requires holding button 10s", "salience": 0.92 },
    { "id": "chunk-45", "text": "Factory reset clears all settings", "salience": 0.88 }
  ],
  "constraints": {
    "terms": { "factory reset": "restore factory settings" },
    "hedging": "sampled 3% - avoid definitive conclusions"
  }
}

फिर संश्लेषण में prompt:

  • प्रत्येक दावा एक सम्मिलित टुकड़ा आईडी को उद्धृत करना चाहिए
  • "यदि कवरेज हैसियत भाषा
  • "इस शर्तों का निरंतर प्रयोग करें

इसीलिए

  • लंबा संदर्भ विंडो एक लाल खरगोश हैं समस्या यह है कि क्या जीवित रहने का हकदार है निर्णय करना
  • पुनरावर्ती संक्षेपण असफल यह मध्यवर्ती आउटपुट को पाठ के रूप में व्यवहार करता है
  • एलएलएम द्वारा संवर्द्धित अवरोधों को स्मरण किया जा सकता है - वे ' र संरचना \ , \ prose यह से drift नहीं कर सकता

सीएफसीडी एक ही दार्शनिक विभाजन है जैसा कि अवरोधित अस्पष्टता, प्रतिबंधित अस्पष्ट मोएम, और छवि सारणी संभावना प्रस्तावित करता है

क्वांटीकरण

ONNX मॉडलों को क्वांटाईकृत किया जा सकता है (छोटे साइज और तेजी से निष्कर्ष के लिए कम सूक्ष्मता | ) |

मॉडल | पूर्ण सूक्ष्मता |-------|---------------|-----------|-------------------| सभी -MiniLM 0 bge 1 small 2 en 3 v 4 5 6 7 8 9 10 11

बैच प्रक्रमण और प्रतियोग

बड़े दस्तावेज़ों के लिए बैच एम्बेडिंग निष्पादन के लिए महत्वपूर्ण है InferenceSession सामान्यतः थ्रेडों में सुरक्षित रूप से साझा किया जा सकता है, लेकिन निष्पादन सत्र कॉन्फ़िगरेशन पर निर्भर करता है

public async Task<float[][]> EmbedBatchAsync(IEnumerable<string> texts, CancellationToken ct)
{
    var textList = texts.ToList();
    var results = new float[textList.Count][];
    
    // InferenceSession is safe to share for inference in most cases
    // Tune SessionOptions.IntraOpNumThreads and InterOpNumThreads for your workload
    var maxParallel = Math.Min(Environment.ProcessorCount, 8);
    
    await Parallel.ForEachAsync(
        textList.Select((text, index) => (text, index)),
        new ParallelOptions { MaxDegreeOfParallelism = maxParallel },
        async (item, token) =>
        {
            results[item.index] = await EmbedSingleAsync(item.text, token);
        });
    
    return results;
}

निष्पादन टिपकॉन्फ़िगर करें SessionOptions सत्र बनाने के दौरान

var sessionOptions = new SessionOptions
{
    IntraOpNumThreads = 4,  // Threads within a single operation
    InterOpNumThreads = 2   // Threads across operations
};
var session = new InferenceSession(modelPath, sessionOptions);

बड़े दस्तावेजों के लिए स्मृति प्रबंधन

बहुत बड़ी दस्तावेज़ (novels,legal documentsM SK2 special handling to avoid

// For documents > MaxSegmentsToEmbed, use hierarchical extraction
if (segments.Count > _config.MaxSegmentsToEmbed)
{
    // Process in batches, keeping only top-K per batch
    // Then re-rank globally
    return await ExtractHierarchicalAsync(segments);
}

निष्पादन विशेषताएँ

एक सामान्य डेवलपनर मशीन पर वास्तविक-विश्व निष्पादन

ऑपरेटिंग |-----------|-----------|-------| | सम्मिलित सेगमेंट्स | घनत्व प्राप्त करना सेगमेंटों पर कोसाइन समानता | BM25 स्कोरिंग मेमोरी उल्टा अनुक्रमणिका | आरआरएफ संलयन श्रेणीः| <2ms | जोड़ें | पृष्ठ पीडीएफ ठ्̧रें

परीक्षण परिवेश: रिजेन | 5600 | X |(6- | Core | 3 | GB RAM | 5 | no GPU | 6 | Embedding uses all | MiniLM | 8 | L | 9 | v | 10 | 11 | Quantized | 12 | 13 | token max | 14 | 15 | thread parallel batching | 16 | Retrieval corpus | 17 | 18 | segments | 19 | Your mileage will vary with different models | 20 | hardware | 21 | and document complexity | 22

मुख्य ड्राइवर स्प्रेडपुट सम्मिलित कर रहा है मॉडल चयन | + | टोकन लंबाई |+ | बैच आकार | МSK3 | पुनःप्राप्ति और संश्लेषण मूल रूप से निःशुल्क हैं | - | वे मिलीसेकेंड लेती है | lm5 | यह | एमएसके6 | LLM अंतिम | " | सिद्धांत को सुदृढ़ करता है & #44; एमएसक8 | सस्ती सीपीयू कार्य करते हैं & #39; एम्सके9 | एम्बेडिंग & #43; एम एसके10 | पुनर्प्राप्ति & #45; एमஎஸ்के11 | प्रथम | मएसके12 | महंगे LLM केवल फिल्टरित सामग्री पर काम करती है & gt; एमएसएसके13

मापन: पदानुक्रमिक उद्धरण, बैचों में प्रसंस्करण करके और केवल ऊपर रखने के द्वारा पृष्ठ दस्तावेजों को संभालता है।

सारांश

DocSummarizer दिखाता है कि जटिल एनएलपी क्षमताओं को embeddings के लिए ONNX Runtime और generation के लिए Ollama की आवश्यकता नहीं होती

  • पूरी तरह स्थानीय रूप से चलाता है
  • अनुसूचित सारांश उत्पन्न करता है
  • किसी भी आकार के दस्तावेजों को हाथ में लेता है
  • ऑफ़लाइन काम करता है

इस उपकरण के निर्माण से प्रमुख अंतर्दृष्टि

  1. एम्बेडिंग आधार है good retrieval depends on good embeddings
  2. हाइब्रिड खोज या तो अकेले बीट करता है - सुदृढ़ता के लिए अर्थात्मक और शब्दावली का संयोजन करें
  3. MMR पुनरावृत्ति को रोकता है - विविधता प्रासंगिकता के समान महत्वपूर्ण है
  4. संरचना संबंधी मामले - दस्तावेज संरचना को ध्यान में रखते हुए
  5. एलएलएम अंतिम होना चाहिए - सबसे पहले सस्ती सीपीयू काम करें

आगे पढ़ने

कागजात और तकनीकी संदर्भ

संबंधित गहरी खोज

श्रृंखला तैयार करना

यह DocSummarizer श्रृंखला को समाप्त करता है

भाग 1 स्पष्ट करता है क्यों पाइपलाइन दृष्टिकोण नाजुक LLM कॉलों को हराता है. यह वास्तुशिल्प पैटर्नों को कवर करता है।

भाग 2 आपका त्वरित है।

भाग 3 (this article) is the deep dive for people who want to understand कैसे यह वास्तव में काम करता है

यदि आप अपना पाइपलाइन बना रहे हैं, तो , सभी तीनों को पढ़ें.

संबद्ध

logo

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