# DocSummarizer Μέρος 3 ΜSK1 Προηγμένες έννοιες ΜSK2 Το "Έχω πάει πολύ μακριά 🤦" Deep Dive

<!--category-- AI, LLM, RAG, BERT, ONNX, C#, .NET, Embeddings -->
<datetime class="hidden">2025-12-21T12:00</datetime>

## Εισαγωγή

Αυτό είναι **ΕΠΙΧΕΙΡΗΣΗ 3** από την σειρά DocSummarizer:

1. **[Μέρος 1: επιχειρήσεις Χτίζοντας ένα σύνολο εγγράφων με RAG](/blog/building-a-document-summarizer-with-rag)** - Η αρχιτεκτονική και γιατί η προσέγγιση του σωλήνα χτυπά άγρια κλήσεις LLM
2. **[Μέρος 2: Χρησιμοποιώντας το εργαλείο](/blog/docsummarizer-tool)** - QuickΜSK1start οδηγός για το CLI
3. **Μέρος 3: Προηγμένες έννοιες** (Αυτό το άρθροΜSK1 ΜSK2 Το "Έχω πάει πολύ μακριά ΜСК4 τεχνική βαθιά πτώση
4. **[Μέρος 4: Κατασκευή αγωγών RAG](/blog/docsummarizer-rag-pipeline)** - Χρησιμοποιήστε τη βιβλιοθήκη NuGet για να δημιουργήσετε τις δικές σας εφαρμογές RAG

---


Αυτό είναι μέρος της "Time-Boxed ToolsM SK2 προσέγγισηΜSK3 μου δίνει ένα σταθερό παράθυρο για να χτίσω κάτι λειτουργικόMSKA4 Ενισχύει τις αποφάσεις και παράγει κώδικα εργασίας αντί θεωρητικά σχέδιαMKA5

Το DocSummarizer ξεκίνησε ως μια επίδειξη του πώς *Θα πρέπει* Δημιουργήστε σύνολα εγγράφων με LLMs - αναφέρθηκε η προσέγγιση του σωλήνα που περιγράψα στο Μέρος ΜSK1 αναφέρθηκαν Τα περισσότερα tutorials σας δείχνουν πώς να σπάσει το κείμενο σε ένα LLM και την ελπίδα για το καλύτερο. αναφέρθηκα θέλω να δείξω τη σωστή αρχιτεκτονικήM SK3 αναφέρθήκαμε αναφέρθέντα, αναφέρουσα ενσωμάτωσηςMK5 αναφέρουμε ανακάλυψη, αναφέρων επικύρωση αναφοράς

Αλλά όπως πάντα κάνω, Έχω ενδιαφέρον για το χώρο προβλήματοςΜSK1 Τέσσερις ημέρες αργότερα,Έχω ενδιαφέρον'Διακόπτει στην έρευναM SK4Πώς η παραγωγήMK5Συστήματα αναβάθμισης πραγματικά χειρίζονται τη συνοψίαMSKA6Τι κάνει την ανάκτηση εργασία καλάMKA7Γιατί κάνουν μερικές ενσωμάτωση υπερβαίνει άλλουςMCA8

Εφαρμόστηκα εκδόσεις αυτών των προσεγγίσεων. Τι ξεκίνησε με "ΜSK2 το σωστό πρότυποM SK3 έγινε ONNX ενσωμάτωσης που τρέχει τοπικάMK4 υβριδική αναζήτηση συνδυάζοντας BM25 με πυκνό ανάκτημα, Μέγιστη περιθωριακή σχετικότητα για την ποικιλομορφίαMST7 και Reciprocal Rank Fusion για τη σύνδεση σήματοςMSC8

**Δίκαιη προειδοποίηση**: Αυτό είναι το "Έχω πάει πολύ μακριάΜSK2 βαθιά βυθίζοντας ΜSK3 Εάν θέλετε απλά να χρησιμοποιήσετε το εργαλείο ΜСК4 διαβάστε Μέρος ΜΣΚ5 Αν θέλετε να καταλάβετε *Γιατί* λειτουργεί και *Πώς* Τα κομμάτια ταιριάζουν μαζί, Συνεχίστε την ανάγνωση.

Αυτό το άρθρο καλύπτει:

- **Εισαγωγή της ποινής**: Πώς να μετατρέψετε τα μοντέλα ΜSK1MiniLM/BGEM SK3GTEMK4 Μετατρέψτε το κείμενο σε vectors
- **Δραστηριότητες ONX Runtime**: Λειτουργούν μοντέλα ML τοπικά χωρίς Python ή cloud APIs
- **RAG (Retrieval-Αυξημένη γενιάM SK2**: εγγυάται το Grounding LLM παραγωγή σε πηγή υλικού
- **Η υβριδική αναζήτηση**: Συνδυάζοντας σημαντική και λεξική ανάκτηση με RRF

[TOC]

## Η αρχιτεκτονική στο φως

Πριν βυθίσουμε σε συγκεκριμένα, εδώΜSK1 πώς τα κομμάτια ταιριάζουν μαζί:

```mermaid
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-ΜSK1 θα πρέπει να βρείτε σχετικές ενότητες. Παραδοσιακή αναζήτηση λέξεων-κλειδιών αποτυγχάνει

- Ο χρήστης "Πώς μπορώ να επαναφέρω τη συσκευή?"
- Το εγχειρίδιο λέει "για την αποκατάσταση των ρυθμίσεων του εργοστασίουΜSK1
- Αναζήτηση λέξεων-κλειδιών λείπει (no κοινές λέξειςΜSK1

χρειάζεσαι **Σημαντική αναζήτηση** - ταιριάζει με την έννοιαΜSK1 όχι μόνο λέξεις.

### Τι είναι οι ενσωματωμένες?

Οι ενσωμάτωσεις επιλύουν αυτό μετατρέποντας το κείμενο σε πυκνούς vectors ( αναφορές αριθμών ΜSK1 αναφέρουν ότι η σύλληψη σημαίνει σημαντική ΜSK2 αναφέρει Παρόμοιες έννοιες = αναφέρει παρόμοιους vectors Μ SK4 αναφέρθηκε ανεξάρτητα από την ακριβή γραφή

Εδώ ' ικανοποιεί τη διαίσθηση ΜSK1 ικανό να φανταστεί ένα ΜSK2 ικανή διάσταση χώρο όπου κάθε κομμάτι κειμένου έχει μια θέση . ικανότητα κειμένων με παρόμοιες έννοιες συγκέντρωση μαζί

```mermaid
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
```

### Γιατί η Κρίση Μετασχηματιστές (Not Raw BERT)?

**Πρόβλημα**: Χρειαζόμουν ενσωμάτωση που λειτουργεί για την σημαντική ομοιότηταΜSK1 Το Raw BERT έχει σχεδιαστεί για τα καθήκοντα ταξινόμησηςΗ , δεν αναζητά ομοιότητεςΗ ΜSK3

**Λύση**: Χρήση **Γνώμη μετασχηματιστών** - αναφέρει μοντέλα ειδικά εκπαιδευμένα σε καθήκοντα ομοιότητας χρησιμοποιώντας αντίθετη μάθηση ΜSK1 αναφέρει ότι το ' αναφέρει με βάση την [Αρχιτεκτονική BERT](https://arxiv.org/abs/1810.04805) Αξίζει να σημειωθεί ότι το .

Μοντέλα όπως `all-MiniLM-L6-v2` και `bge-small-en-v1.5` Εκπαιδεύτηκαν σε δισεκατομμύρια ζευγάρια κειμένου όπως:

- "Πώς να επαναφέρετε τη συσκευή ΜSK1 ΜSK2 " Αποκαταστήστε τις ρυθμίσεις του εργοστασίου
- "Πώς να επαναφέρετε τη συσκευή ΜSK1 ΜSK2 " Προδιαγραφές του προϊόντος "

Η κατάρτιση τους διδάσκει: παρόμοιες έννοιες ΜSK1 στενούς vectors Η ΜSK2 συνειδητή ομοιότηταΗ ).

> **Σχετικά**: Εάν θέλετε να καταλάβετε πώς λειτουργούν τα μοντέλα μετασχηματιστών σε βαθύτερο επίπεδο ΜSK1 συμπεριλαμβανομένων των μηχανισμών προσοχής ΜSK2 κωδικοποίηση - αρχιτεκτονική αποκρυπτογράφησης ΜСК4 και γιατί λειτουργούν οι ενσωμάτωσες ΜΚ5 δείτε το άρθρο μου στο [Πώς λειτουργεί η Neural Machine Translation](/blog/how-neural-machine-translation-works). καλύπτει τις ίδιες έννοιες μετασχηματιστών από μια προοπτική μετάφρασηςΜSK1

**εφαρμογή**: Λαμβάνουμε το μοντέλοΗ ' αναφέρει την επιφάνεια εξόδου και εφαρμόζει **Μιλάμε για τη συγχώνευση** - μέση ενσωμάτωση όλων των tokens για να πάρει ένα μόνο vector για ολόκληρο το κείμενοΜSK1

```mermaid
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
```

## ONNX: Running ML μοντέλα τοπικά

### Το πρόβλημα: Python Dependency Hell

Ήθελα ενσωμάτωση σε " προσαρμογή εργασίας ΜSK1 όταν κάποιος τρέχει το εργαλείο ΜSK2 Η τυπική προσέγγιση :

1. Εγκατάσταση Python + PyTorch ΜSK1 μετασχηματιστές
2. Κατεβάστε μοντέλα με μη αυτόματο τρόπο
3. Ελπίδα έκδοση συγκρούσεις Don' αναμένεται να σπάσει όλα

Αυτό συμβαίνει. Οι χρήστες θέλουν `docsummarizer -f doc.pdf`, όχι ΜSK1οδηγός εγκατάστασης.

### Γιατί το ONNX?

ONNX (Open Neural Network ExchangeΗ ) είναι μια ανοικτή μορφή για τα μοντέλα MLΤο χαρακτηριστικό δολοφονίαςη : **Δραστηριότητες χωρίς Python**.

**Τι μπορώ να κάνω με το ONNX Runtime:**

1. **0 Εξωτερικές εξαρτήσεις** - Όχι PythonΜSK1 PyTorch, Δοκιμαστές CUDA
2. **Auto- μεταφορτώνονται μοντέλα** - Πρώτη εκκίνηση λήψεις από HuggingFace ΜSK1MB, τότε cached
3. **Καθαρό .NET** - Δουλεύει οπουδήποτε .NET τρέχει (Windows,LinuxMSC4 macOSMSc5 ARM64)
4. **Συμπεράσματα CPU** - Δεν απαιτείται GPUΜSK1 τρέχει σε φθηνό υλικό

Εμπόριο-ΜSK1 Λίγο πιο αργά από GPU PyTorch, αλλά πολύ γρηγορότερα από το να ζητάτε από τους χρήστες να εγκαταστήσουν PythonM SK3

### Το μητρώο μοντέλων

Το DocSummarizer περιλαμβάνει διάφορα ενσωματωμένα μοντέλα, ο καθένας με διαφορετικά εμπορικάΜSK1:

| Μοντέλο ΜSK1 Μετρήματα ΜSK2 Μαξ Τόκενς | Μέγεθος (Quantized) MK6 Χρησιμοποιήστε την περίπτωση MC7 Απαιτείτε οδηγίες SMK8
|-------|-----------|------------|------------------|----------|---------------------|
| `AllMiniLmL6V2` ΜΣΚ0 ΜΣΚ1 MΣΚ2 MSSK3 | MSM5MB| Προεπιλεγμένη MSM7 γρήγορη , καλή ποιότητα | Όχι
| `BgeSmallEnV15` ΜΣΚ0 ΜΣΚ1 MΣΚ2 ΜΑΣΚ3 ΜΕΣΚ4 ΜΙΣΚ5MB | Καλύτερα για τεχνικές δοκίμια | Ναι
| `GteSmall` ΜΣΚ0 ΜΣΚ1 MΣΚ2 ΜΑΣΚ3 ΜΕΣΚ4 ΜΙΣΚ5MB | Γενικός σκοπός | Όχι
| `MultiQaMiniLm` ΜΣΚ0 ΜΣΚ1 MΣΚ2 ΜΑΣΚ3 ΜΑΣΚ4 ΜΗΣΚ5ΜΒ | Βελτιστοποιημένο για QΜσΚ7Α Γ.

**Σημείωση**: Όλες οι εισαγωγές καταχώρισης υποδεικνύουν το WordPieceΜSK1συμβατικές εξαγωγές ONNX Η χρήση του ( `vocab.txt`). BPE / Τα μοντέλα του Unigram δεν υποστηρίζονται ακόμη ΜSK2

**BGE μορφή διδασκαλίας**: Μερικά μοντέλα Η ( όπως η BGE και η ) απαιτούν προσαρμογές για βέλτιστες επιδόσεις Η . Η ακριβής μορφή εξαρτάται από το μοντέλοΗ

```csharp
// 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` πεδία. Πάντα αναφορά ποιότητας ανάκτησης όταν εργάζονται με την εκπαίδευσηΜSK1μεταφερόμενα μοντέλα.

Εδώ' αναφέρει πώς λειτουργεί το μητρώο μοντέλων:

```csharp
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

Διαφορετικά μοντέλα χρησιμοποιούν διαφορετικούς tokenizers. `all-MiniLM-L6-v2` Το μοντέλο χρησιμοποιεί το tokenization του WordPiece ( σε σχέση με BERT ΜSK1 που διαιρεί άγνωστες λέξεις σε τακούνια υπογραμμάτων ΜSK2 Άλλα μοντέλα μπορούν να χρησιμοποιήσουν BPE (Byte

**Σημαντικό**: Οι tokenizers μοντέλου πρέπει να ταιριάζουν με τον εκπαιδευτικό tokenizer για το . Τα αποτυπώματα του μητρώου μας τα οποία απαιτούνται από κάθε μοντέλο για το tokeniser για το MK2 **Προς το παρόν εφαρμόζεται: WordPiece** ΚΕΦΑΛΑΙΟ 0 `vocab.txt`). BPE /Unigram υποστήριξη μέσω `tokenizer.json` είναι προγραμματισμένο αλλά δεν έχει ακόμη εφαρμοστεί - ενδεχομένως να παραμείνει στα μοντέλα του WordPiece στο μητρώο για τώραΜSK1 ενδέχεται

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

Ετικέτες :

| Εισαγωγή ΜSK1 Τοξίνες ΜSK2
|-------|--------|
| `"embedding"` | `["em", "##bed", "##ding"]` |
| `"DocSummarizer"` | `["doc", "##su", "##mm", "##ari", "##zer"]` |
| `"the quick brown"` | `["the", "quick", "brown"]` |

### Πλύσιμο με μάσκα προσοχής

Μετά το BERT επεξεργάζεται τα tokens, λαμβάνουμε μια κρυμμένη κατάσταση για κάθε token. Μέση συγκέντρωση μέσους όρους αυτάΜSK2 αλλά μόνο πάνω από πραγματικά tokens M SK3 δεν paddingMK4

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

### Το πλήρες ενσωματωμένο σωλήνα

Εδώ' διατηρεί την πλήρη ροή από το κείμενο στην ενσωμάτωση:

```csharp
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:Retrieval-Αυξημένη γενιά

### Το πρόβλημα: LLMs Μπορεί να επιτύχει't Διαβάστε 500-PageDocuments

**Η αφελής προσέγγιση αποτύχει:**

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

Ακόμη και με τα 128K πλαίσια παράθυραΜSK1 μπορείτε να κάνετε το ' απλά σπάσει τεράστια έγγραφα σε:

- **ΤΡΟΥΚΑΣΙΑ**: Μόνο οι πρώτες ΜSK1 σελίδες ταιριάζουν, ξεκούρασε αγνοείται
- **ΧΑΛΟΥΣΙΝΑΣΙΑ**: LLM εφευρέσεις ΜSK1πρεπε" να συμπληρώσει τα κενά
- **κόστος**: Επεξεργασία ΜSK1MB του κόστους κειμένου ΜSK2 ανά ερώτηση
- **Ποιότητα**: LLMs μπερδεύονται με ένα τεράστιο πλαίσιο ΜSK1lost στο μέσο ΜSK2 problem )

### Η λύση: RAG (RetrievalM SK2Αυξημένη γενιάMK3

Αντί να στείλετε όλα τα, επιχειρήσεις στέλνετε μόνο αυτό πουΜSK1 αναφέρει σχετικό: επιχειρήσεις

1. **Chunking**: Διαχωρίστε το έγγραφο σε τμήματα
2. **Ενσωμάτωση**: Μετατρέψτε τα τμήματα σε vectors ΜSK1εξουσιαστική εκπροσώπηση)
3. **Επαναφορά**: Βρείτε το ΜSK1 τα πιο σχετικά τμήματα για την ερώτηση
4. **Σύνθεση**: LLM συνοψίζει μόνο αυτά τα τμήματα

**Γιατί αυτό λειτουργεί:** Το LLM βλέπει 10KB του πολύ σχετικού περιεχομένου αντί για ΜSK1MB του συνήθως irrelevant κειμένου.

```mermaid
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 υποστηρίζει πολλαπλές στρατηγικές που βασίζονται στη δομή εγγράφων:

```csharp
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 Retrieval

Για μεγαλύτερα έγγραφα , DocSummarizer εξάγει μεμονωμένα τμήματα ( αναφορές ΜSK2 αναφορές στοιχεία λίστας ΜSK3 αναφορές μπλοκ κώδικας

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

### Το πρόβλημα: Semantic Search Returns Διπλασιάζεται

**Χωρίς MMR**, ανάκληση για ΜSK1Πώς λειτουργεί το cachingΗ ?" επιστρέφειΗ ΜSK3

1. "Ανακαλύψιμη ανασκόπηση ΜSK1 ΜSK2 ομοιότητες )
2. "Εισαγωγή στην κάθαρσηΜSK1 Η ΜSK2 ομοιότηταΗ )
3. "Τι είναι το caching ΜSK1 ΜSK2 ομοιότητες )
4. "Σχετικά με τις λεπτομέρειες εφαρμογής του Cache ΜSK1 ΜSK2 ομοιότητες )

Το κορυφαίο 3 αναφέρει τα αποτελέσματα όλα λένε το ίδιο πράγμα ΜSK1 αναφέρω ' αναμένεται να σπαταλήσει το παράθυρο πλαίσιο για την επανάληψη

### Η λύση:Μεγάλη περιθωριακή σχετικότητα M SK1MMR)

Εξισορρόπηση MMR **Σημαντικότητα** (ομοιότητες για να ζητήσετεΜSK1 με **ποικιλομορφία** (διαφορετικότητα σε ήδη ληφθεί-Επιλεγμένα αντικείμεναΜSK2

**Φόρμουλα:**
ΜΣΚ0MMR = ΜΣΚ2lambda \cdot \text ΜSK5simΜSK6 ΜСК7 ερώτηση ΜΚ8 Μσκ9 Μск10 Μスク11 Μsk12lombda Μ스크13 Μسک14cdo_{σεΜSK1σε \σε επιλεγμένο}σε ΜSK4textM SK5simMK6σε

**ΤΙ ΕΙΝΑΙ ΠΡΟΣΩΠΙΚΟ ΣΧΕΔΙ** Ποινικοποιεί τους υποψηφίους παρόμοιους με εκείνους που έχουν ήδη ανακοινωθεί- μη επιλεγμένα τμήματα. Αυτό εμποδίζει το σύνολο να είναι ΜSK2 ανακοινώθηκε εκδόσεις της ίδιας παραγράφου.

```mermaid
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 ΜSK1 ΜSK2lambda \cdot sim(M SK5 centroidMK6_{σεΜSK1σε \σε Επιλεγμένο}σε SIMσε(σε ΜSK5σε

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

## Η υβριδική αναζήτηση με RRF

### Το πρόβλημα: Semantic Search χάνει ακριβείς αγώνες

Πήγα σε αυτό όταν δοκιμάζω:

**Ζητήστε**: ΜSK1Τι γνωρίζει το ' για το τελικό σημείο API για την εξακρίβωση?"

**Σεμάντικη αναζήτηση επιστρέφει:**

1. "Περίληψη ροής συνδεσιμότητας χρήστηΜSK1 Η ΜSK2 αυξάνει την ομοιότηταΗ )
2. "Ασφάλεια βέλτιστες πρακτικές ΜSK1 Η ΜSK2 επιτυγχάνει ομοιότητες Η ΜSK3
3. "Διαχείριση συνεδριάσεωνΜSK1 Η ΜSK2 επιτυγχάνει ομοιότητεςΗ )

**Τι λείπει**: Το πραγματικό τελικό σημείο API κρυμμένο σε παραδείγματα κωδικούΜSK1 `POST /api/v1/auth/login`

**Γιατί**: Τα ενσωματωμένα μοντέλα εκπαιδεύονται σε φυσική γλώσσαM SK1 δεν κωδικοποιεί/URLsMK3 ακριβείς όρουςMST4 Το τελικό σημείο `POST /api/v1/auth/login` ' " Αυτοποίηση τελικού σημείου ΜSK2 ΜSK3 ' Έχει γραπτή τεχνική αναφορά

### Η Λύση:Hybrid Search M SK1Semantic ΜSK2Lexical)

Συνδυάστε δύο μεθόδους ανάκτησης με συμπληρωματικές δυνάμεις:

| Τύπος αναζήτησης ΜSK1 Δυνάμεις ΜSK2 αδυναμίες |
|-------------|-----------|------------|
| **ΕΡΓΑΣΙΑ ΜΑΘΗΜΑ 1 ΕΠΙΤΡΟΦΟ** | Σεμάντικη κατανόησηΜSK1 συνώνυμες ΜSK2 Μπορεί να χάσει τα ακριβή παιχνίδια ΜСК3 σπάνιες όροι ΜΚ4
| **Εξοικονόμηση (BMΜSK1** | Ακριβής αντιστοιχία λέξεων-κλειδιώνΜSK1 σπάνιοι όροι ΜΣΚ2 Χωρίς σεμνητική κατανόηση ΜΣΚ3

Η υβριδική αναζήτηση συνδυάζει και τα δύο με τη χρήση της Reciprocal Rank Fusion (RRF):

```mermaid
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
```

**Σημείωση**: Τα αποτελέσματα RRF που εμφανίζονται είναι εικονογραφητικάΜSK1 Η σταθερή k=60 είναι τυποποιημένηΗ πραγματική κατάταξη του ; εξαρτάται από το πλήρες υποψήφιο σύνολο

### Εφαρμογή RRF

```csharp
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 ( Καλύτερη Συμφωνία ΜSK2 είναι ο κλασικός αλγόριθμος ανακάλυψης πληροφοριών ΜΚ3 Συνδυάζει συχνότητα όρου ΜΣΚ4 αντιστροφική συχνότητα εγγράφου ΜСК5 και τυποποίηση μήκους έγγραφα ΜΚΑ6

```csharp
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 για την κεντρικότητα του περιεχομένου

### Το πρόβλημα: Πώς διακρίνεις το κεντρικό περιεχόμενο από την Trivia?

Συνοψίζοντας ένα μυθιστόρημα, έλαβα αποτελέσματα όπως:

> "Ο πρωταγωνιστής φορούσε ένα μπλε καπέλοΜSK1 Ο Watson σημείωσε ότι ο καιρός ήταν ελαφρύ ΜSK2 Η μελέτη είχε έπιπλα

Αυτά είναι ακριβή εκχυλίσματα ειδικά, επιχειρήσεις, αλλά ειδικότερα' αναφέρει **Χρώμα** ( αναθεώρηση ΜSK1 αναθεωρήσεις λεπτομέρειες ΜSK2 δεν **Κύρια σημεία κλάσης**.

**Η πρόκληση**: Πώς να πείτε τη διαφορά μεταξύΜSK1

- **Κύρια περιεχόμενο**:Εμφανίζονται σε όλο το έγγραφο ΜSK1Οι ονομασίες χαρακτήρων,Τα κύρια θέματαΗ ΜSK3Κλειδιά γεγονόταΗ )
- **Υποστήριξη λεπτομέρειες**: Εμφανίζεται σε ορισμένες ενότητες ΜSK1subplots, εξηγήσεις
- **Χρώμα**: Σπάνια ΜSK1 ειδικές λεπτομέρειες ΜSK2ποιος φορούσε , περιγραφές έπιπλα

### Η λύση:TFΜSK1IDF για την ταξινόμηση κεντρικότητας

TF-IDF (Τερματική Συχνότητα ΜSK2 Αντίστροφη Συχνότητα ΕγγράφουΣυγκριτικές εκτιμήσεις **Πόσο κεντρικός είναι ο όρος στο έγγραφο**, σίγουρα δεν έχει την αληθινή του αξία.

**ΛΟΓΙΚΕΣ ΕΠΙΧΕΙΡΕΥΣΕΙΣ**

- **Υψηλή DF (>50% αναφορές σκουπιδιών ΜSK1 αναφορές**ΜΣΚ0 "Sherlock", □"WatsonMK4ΜSK5κοντάρισμαΜСК6Μσκ7 Κορυφαίο περιεχόμενο
- **Μεσαίο DF (20-50%)**ΜΣΚ0 "Baker Street", "ερευνήσειςM SK4 □→Διαβάστε περισσότερα
- **Λιγός DF (<20%)**: ΜSK1μαύρο κάλυμμα",ΕπιπλασιασμόςM SK3επιπλάνησηMK4Η ΜSK5Φυσικό χρώμα

Αυτό δεν είναι αλήθεια (a επαναλαμβανόμενες αξιώσεις μπορεί να είναι ψευδείςΜSK1 ένα σπάνιο γεγονός μπορεί να ήταν αληθινό).ΑυτόM SK3 *Κεντρικότητα του εγγράφου*.

```mermaid
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
```

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

## Το πλήρες σιδηροδρόμιο BertRag

DocSummarizer' παραγωγικό σωλήνα ΜSK1`BertRagSummarizer`) συνδυάζει όλες αυτές τις έννοιεςΜSK1

```csharp
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,I'έτυχε αυτά τα προβλήματα ΜSK2και θα σας αρέσει επίσης):

1. **Τόκενιζερ λανθασμένα → ανοησίες ενσωμάτωσης**: Κατεβάζοντας ένα λεξιλόγιο WordPiece για ένα BPEΜSK1 διατηρημένο μοντέλο παράγει έγκυρο- προβλέψιμο, αλλά σεμάντικα άχρηστο vectorsM SK3 Πάντα επαληθεύστε το tokenizer ταιριάζει με το μοντέλαMSKA4 αναγνωρίζει εκπαιδευτικό καθεστώςMKA5

2. **Η κυριαρχία των -τοπικών bias σε ένα-κεντροειδείς βαθμολογίες**: Χρησιμοποιώντας ένα έγγραφο κεντροειδές συστηματικά κατεβάσει- αναγνωρίζει θέματα μειοψηφίας ΜSK2 αναγνωρίζοντας διαφορές ΜSK3 αναγνωρίστε εξαιρέσεις , αναγνωρίζεται περιπτώσεις άκρων Μsk5 αναγνωρισμένο Multi - αναγνώριση ανάκτησης διορθώνει αυτό, αλλά προσθέτει πολυπλοκότητα

3. **BM25 χτυπά πυκνή αναζήτηση σε σπάνιους όρους**: Εάν το ερώτημά σας περιέχει τεχνικό φράγμα ή σωστές αναφορές δεν είναι καλά γνωρίζετε-προσωπεύεται στο ενσωματωμένο μοντέλοΜSK2 δεδομένα κατάρτισης, λεξική αντιστοιχίαM SK4BMMK5 θα υπερεκτερεύσει σεμάντικη αναζήτηση ΜSK6 Αυτός είναι ο λόγος για τον οποίο η υβριδική αναζήτηση έχει σημασία .

4. **OCR σκουπίδια σε σαρωμένα PDFs**: Το Docling είναι καλόΤο , αλλά τα σφάλματα του OCR συνδέονται με το . Αν δείτε ανοησία στα σύνολαΤο , ελέγξτε την παραγωγή από το Docling πρώτα Το - ο συνοψιστής μπορεί να επιτύχει το ' η εισαγωγή απορριμμάτων

5. **Οι συνοπτικές εκτιμήσεις χαμηλού επιπέδου - πρέπει να καλύπτουν τη γλώσσα**: Εάν ενδιαφέρεστε το ' αναφέρει μόνο βλέποντας ΜSK2 αναφέρουν από ένα έγγραφο ΜSK3 αναφέρουν φράσεις όπως " αναφέρθηκε αμέσως στο " αναφέρει ή Το " διατηρεί συμπεράσματα Η " αναφέρει ότι είναι άδικο η . αναφέρει πως το σύστημα πρέπει να πει Η " διατηρήσει δείκτες σε μετρητά της " αναφέρει και αποφεύγει οριστικούς τερματισμούς

6. **Ετικέτες hallucination**: Μικρά LLMs ΜSK1B-3B παραμύθιαM SK3 μερικές φορές εφευρέσει αξιόπιστηMK4αξιολογημένες αναφορές.Εμείς επικυρώνουμε παρτίνιζοντας την παραγωγή για `[chunk-N]`, ειδήσεις επαλήθευσης N υπάρχει στα κομμάτια πηγής ειδήσεις, και σηματοδότηση ή επισκευή αξιώσεις που αναφέρουν λείπουν κομμάτι ειδήσεις . ειδήσεις Αν δείτε `[chunk-999]` για ένα 10-chunk έγγραφο, το LLM σας αγωνίζεται με την εργασίαM SK2

Αυτά είναι'τ bugs ΜSK1τ 'τ κληρονομικές εντάσεις στο χώρο σχεδιασμού.Τα καλά συστήματα παραγωγής αναγνωρίζουν και τα ελαχιστοποιούν

## Πρακτικές παρατηρήσεις

### Αποθήκευση και δείγμα ειλικρίνειας

Κατά την επεξεργασία πολύ μεγάλων εγγράφων,Το DocSummarizer δεν ενδιαφέρει το ' προσπαθεί να ενσωματώσει τα πάντα Το - χρησιμοποιεί σεμάντικη προΜSK3φιλτράρισμα για να επιλέξει αντιπροσωπευτικά τμήματαΗ . **Αυτό σημαίνει ότι η περίληψη βασίζεται σε δείγμα, όχι το πλήρες έγγραφο.**

Το σύστημα διαχειρίζεται αυτό με διαφάνεια:

```csharp
// 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}";
```

**Σημαντικό**: Αυτό είναι ένα *Σύνοψη των αποδεκτών στοιχείων*, δεν εγγυάται την πλήρη κάλυψη του εγγράφου - ΜSK2 Όταν λέμε ΜSK3 Σύνδεσμοι 3%", ότι Η ' αναφέρει ακριβώς τι συνέβη η - το σύστημα είδε Η 3% από το έγγραφο και συνοψίστηκε ότι ο .

**Η δειγματοληψία δεν έγινε τυχαία** - ανέφερε ότι ανέφερε' ανέφερε *Σεμάντικα*. Χρησιμοποιούμε πολλαπλές κλάσεις - για να διασφαλίσουμε ότι τα θέματα μειοψηφίας είναιΜSK2 εξαιρούνται ΜSK3 Ένας τυχαίος Μσκ4 θα μπορούσε να χάσει όλα τα περιοριστικά και περιθώρια περιστατικών ΜСК5 Μια σημαντική Μσκ6 προσπαθεί να συλλάβει ένα αντιπροσωπευτικό τμήμα από κάθε κύριο θέμα ΜΣΚ7 Σχεδόν ΜSK8 εξακολουθεί να έχει μερική κάλυψη ΜΚ9 αλλά αυτό σημαίνει ΜSK10 εν προκειμένω διαφοροποιημένη μερικής κάλυψης

**Προσαρμοστικό δείγμα με πολλαπλές ερεθίσματα θεμάτων**: Το προειδοποιητικό φίλτρο - χρησιμοποιεί πολλαπλές ερεθίσματα ΜSK2μεταξύ-μετάξύ-στυλ κλουστοποίηση ενός στρωμένου δείγματος) για να εξασφαλιστεί ότι τα θέματα μειοψηφίας είναιM SK6 συστηματικά αποκλείεταιMK7Αυτό εμποδίζει το ΜSK8κυνητικές θεματικές παραμέτρους" όπου ένα ενιαίο κεντρικό κάτωM Sk10μετοξύ σημαντική

Από `SegmentExtractor.cs`:

```csharp
// 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));
```

Αυτή είναι η έρευνα-informed ΜSK1eviding single-query αναμνηστική κατάρρευσηM SK3 αλλά πρακτικό MSKA4 τρέχει σε δευτερόλεπτα στο CPUMKA5

**Γιατί όχι μόνο ενσωματώνουν τα πάντα?**

Για ένα 500- αναλυτικό έγγραφο ΜSK1 αναλυτικά τμήματα Η ), ενσωματώνει τα πάντα θα λειτουργούσαν, αλλά δεν είναι βέβαιο ότι η ' εξασφαλίζει το καλύτερο

- **κόστος**: O(Από το ) οι ενσωμάτωσεις κυριαρχούν στο χρόνο εκκίνησης ΜSK3 Σε ΜSK4 segments /secM SK6 ότι Το ' Το 13+ δευτερόλεπτα μόνο για ενσωματώματα πριν ακόμη ξεκινήσετε την ανάκτηση
- **Ποιότητα**: Ενσωματώνοντας τα πάντα αυξάνει τον θόρυβο στην πισίνα ανάκτησης. Πάντα χρειάζεστε κατάταξηΜSK2, οπότε γιατί ενσωματωμένα τμήματα που δεν θα κατατάξουν ποτέ υψηλάM SK3
- **Πρακτικότητα**: Μνήμη και περιορισμοί ακτινοβολίας ουσίαΜSK1 Διατήρηση ΜSK2 384-μετροί μνήμης και υπολογιστών Μ SK4 ομοιότητες κοσίνης ανά ερώτηση είναι σπατάλη όταν χρειάζεστε μόνο την κορυφή

Η πολλαπλή δειγματοληψία - σας παρέχει το καλύτερο από τις δύο ειδήσεις: ειδικά ευρεία κάλυψη θέματος με ιχνηλαστή υπολογιστή ειδικά. ειδικά

### Περιεχόμενο Κλείνοντας:Γιατί Retrieval IsnΜSK1t Just Search

Αυτό που μου άρεσε've περιγράφεται παραπάνω δεν είναι't μόνο M SK2retrieval" ΜSK4 ότιMK5t ένα συγκεκριμένο πρότυπο καλούμε **Εξέγερση Fuzzy Context Dragging** ([CFCD](/blog/constrained-fuzzy-context-dragging)). Η εικόναΜSK1

> Οι περισσότεροι συνοψιστές συνεχίζουν να προσθέτουν το πλαίσιο. DocSummarizer *Πήγαινε μπροστά* Μόνο αυτό που επιβιώνει καθοριστική επιλογή, στη συνέχεια αφήνει το μοντέλο να γράψει ομαλά μέσα σε εκείνα τα όριαΜSK1

Εδώ' αναφέρει πώς οι χάρτες του σωλήνα DocSummarizer σε CFCD:

| CFCD Σχεδιασμός | DocSummarizer Implementation ΜSK2
|--------------|------------------------------|
| **Αποκάλυψη Salience** (fuzzy) ΜSK2 Ενσωμάτωσης ΜSK3 κεντροειδής ομοιότητας , TF Μ SK5IDF κεντρικότητα ΜΚ6
| **Αποτελεσματική προώθηση** | MMR , BM ΜSK2 RRF συγχώνευση , κορυφαία-K επιλογή ΜSK5
| **Άντορ Λέγκερ** | Το ανακτημένο τμήμα που περιλαμβάνει τα αναγνωριστικά αναφοράς ΜSK1
| **Η περιορισμένη γενιά** | Σύνθεση σύντομα συνδέεται με αποδεκτά στοιχεία ΜSK1

**Γιατί αυτό έχει σημασία**: Το μοντέλο δεν μετράει' αποφασίζει τι μετράται' σχετικό ΜSK3 ο σωλήνας ανάκτησης ευνοεί. Το πρότυπο παράγει μόνο ρευστά εντός των συνόρων που αγαπάμε' εγκαθιδρύθηκε. Αυτός είναι ο λόγος για τον οποίο μικρά τοπικά μοντέλα λειτουργούν ΜSK7 οι ανδρείες κάνουν τη βαριά αναρρίχηση .

Το "anchor ledgerM SK1 στην πράξη μοιάζει με αυτό ΜSK2conceptually):

```json
{
  "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"
  }
}
```

Στη συνέχεια, στη σύνθεση έλαβε το :

- "Κάθε αίτηση πρέπει να αναφέρεται σε ένα ενσωματωμένο ID chunkΜSK1
- "Αν η κάλυψη ΜSK1 ΜSK2 γλώσσα αποθήκευσης ΜСК3
- "Χρησιμοποιήστε αυτούς τους όρους συνεκτικάΜSK1

Αυτός είναι ο λόγος για τον οποίο η :

- **Τα παρακάτω παράθυρα είναι κόκκινα** - ίσως το πρόβλημα δεν είναιΗ ' μπορεί να ταιριάζει περισσότερο κείμενοΗ ΜSK2 μπορεί να αποφασίσει τι αξίζει να επιβιώσει
- **Αποτυγχάνει η αναδρομική συνοπτική** - αναφέρει ότι αντιμετωπίζει την ενδιάμεση παραγωγή ως κείμενο ΜSK1 αναφέρει πως δεν έχει περιορισμένη δομή
- **Το LLM μπορεί να επιτύχει' επιτυγχάνεται " επιτύχθηκε " επιπλέον προωθούνται περιορισμοί** -ΜSK1 διατηρεί δομήΗ ΜSK2 δεν προειδοποιεί ότι μπορεί να μετακινηθεί από

Το CFCD είναι η ίδια φιλοσοφική διαίρεση με [Περιορισμένη ψευδαίσθηση](/blog/constrained-fuzziness-pattern), [Ετικέτες Fuzzy MoM](/blog/constrained-mom-mixture-of-models), επιχειρήσεις και [Φωτογραφία Summarizer](/blog/constrained-fuzzy-image-intelligence) - πιθανότητα προτείνειΜSK1 καθορισμός εξακολουθεί Η - εφαρμόστηκε κατά τη διάρκεια του χρόνουΗ ΜSK3 αναμνηστικό άξοναη ΜSK4

### ποσοτικοποίηση

Τα μοντέλα ONNX μπορούν να ποσοτικοποιηθούν ( με μειωμένη ακρίβειαΜSK1 για μικρότερο μέγεθος και γρηγορότερη παραπομπή ΜSK2 Το εμπόριο -off είναι ελάχιστη απώλεια ποιότητας

| μοντέλο ΜSK1 Πλήρης ακρίβεια ΜSK2 Κβαντισμένο | Διαφορά ποιότητας
|-------|---------------|-----------|-------------------|
| όλοιΜSK1MiniLM-Μ SK3MK4 ΜSK5 ΜΚ6MB ΜΣΚ7 23MB
| bge -small-M SK3vMK4 ΜSK5 □133MB ΜSK7 ─34MB

### Batch επεξεργασία και συναλλαγές

Για τα μεγάλα έγγραφα, ειδικά το , είναι κρίσιμο για τις επιδόσεις του . ειδικά ο ONNX Runtime ειδικά η ' αναφέρει `InferenceSession` Μπορεί γενικά να μοιράζεται σε διάφορες γραμμές με ασφάλεια για τις επιχειρήσεις , αλλά η απόδοση εξαρτάται από τη ρύθμιση της συνεδρίας για τις εταιρίες :

```csharp
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` Όταν δημιουργείται η συνεδρίαση:

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

### Διαχείριση μνήμης για μεγάλα έγγραφα

Πολύ μεγάλα έγγραφα (novelsΜSK1 νόμιμα έγγραφοΗ ) απαιτεί ειδική μεταχείριση για να αποφευχθεί η OOMη :

```csharp
// 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);
}
```

## Χαρακτηριστικά επιδόσεων

Real-world επιδόσεις σε μια τυπική μηχανή προγραμματιστή ΜSK1Ryzen M SK2X, ΜSK4GB RAMMSKA5 δεν υπάρχει GPUMKA6

| Δραστηριότητες ΜSK1 Διαδίκτυο ΜSK2 Σημειώσεις |
|-----------|-----------|-------|
| **Ενσωμάτωση** | ΜSK1 segments/sec ΜSK3 μεγέθους μπάτσου 64, όλαMK5MiniLMM SK6Μσκ7Mσκ8 ποσοτικοποιημένο
| **Δύσκολη απόσυρση** | ΜSK1ms ΜSK2 Συνομοσύνη Cosine πάνω 500 segments
| **Η ΜΜΣΑΔΕ ΑΥΤΟΚΙΝΗΤΑΙ** | ΜSK1μς ΜSK2Σε - μνήμη αναστρέψιμος δείκτης
| **RRF συγχώνευση** | ΜSK1ms ΜSK2 Combine 3 βαθμολογίες |
| **Τελικά-toΜSK1end M SK2page PDF)** | Η ΜΣΚ1Επιφέρει ΜΣΚ2Περιλαμβάνει κουνάρισμαΗ MΣΚ3ενσωμάτωσηΗ ,ανακάλυψηΗ Σύνθεση LLMη ΜΣΚΑ6

**Περιβάλλον δοκιμών**: Ryzen 5600X ΜSK2core), ΜSK4GB RAMMK5 καμία GPU. Embedding χρησιμοποιεί όλαM SK7MiniLMM Sk8LMesk9Mosk10(μετρημένα,SSK13Token maxSMK14SS8-Tread παράλληλη συσκευασίαSmK16Retrieval corpusS:SSC18 segmentsMSC19Το δευτερόλεπτο σας θα ποικίλει με διαφορετικά μοντέλαMS,hardwareS, και πολυπλοκότητα εγγράφου

**Ο κύριος οδηγός είναι ενσωματωμένος διαδρομή** ( επιχειρηματικό μοντέλο επιλογή ΜSK1 μήκος token ΜSK2 μεγέθους μπάτσου ). Retrieval και συγχώνευση είναι βασικά δωρεάν - παίρνουν εκατομμύρια δευτερόλεπτα MK5 Αυτό ενισχύει το MC6LLM τελευταία UC7 αρχή EC8 κάνει φθηνή εργασία CPU IC9 εκμετάλλευσης NC10 ανακάλυψης CS11 πρώτος CC12 δαπανηρή LLM εργασία μόνο σε φίλτρα περιεχόμενο UC13

**Σκάλινγκ**: Η ιεραρχική εξόρυξη χειρίζεται ΜSK1 σελίδες έγγραφα ΜSK2novels , εγχειρίδια ΜΚ4 με επεξεργασία σε συσκευές και διατηρώντας μόνο κορυφαία ΜΣΚ5K ανά συσκευή στη μνήμη ΜСК6

## Συνοπτικά

Το DocSummarizer αποδεικνύει ότι οι εξελιγμένες ικανότητες NLP δεν επιτυγχάνουν το ' απαιτούν cloud APIs ή Python εξαρτήσεις. Με ONNX Runtime για ενσωμάτωση και Ollama για γενιά, μπορείτε να χτίσετε ένα ολοκληρωμένο σωλήνα RAG που επιτύχει:

- Λειτουργεί πλήρως τοπικά
- Προϊόντα που μπορούν να παρακολουθούνται, αναφέρθηκαν συνοπτικά
- Διαχειρίζεται έγγραφα οποιουδήποτε μεγέθους
- Δραστηριότητες offline

Τα βασικά στοιχεία από την κατασκευή αυτού του εργαλείου:

1. **Οι εγκαταστάσεις είναι το θεμέλιο** - Η καλή ανάκτηση εξαρτάται από τις καλές ενσωμάτωσεις
2. **Η υβριδική αναζήτηση χτυπά και μόνη της** - Συνδυάζει σημαντικό και λεξικό για σταθερότητα
3. **Το MMR αποτρέπει την επανάληψη** Η ποικιλομορφία είναι τόσο σημαντική όσο και η σημασία της
4. **Σχετικά με τη δομή** - Σεβασμός της δομής του εγγράφου ΜSK1κατευθυντήριες γραμμές ΜSK2 τμήματα  βελτιώνει τα αποτελέσματα
5. **Το LLM πρέπει να έρθει τελευταία** - Κάντε φθηνή εργασία CPU πρώταΜSK1 δαπανηρή LLM εργάζεται μόνο σε φίλτρα περιεχομένου

## Περισσότερα Διαβάστε

### Έγγραφα και τεχνικές αναφορές

- [Χαρτί Μπέρτ](https://arxiv.org/abs/1810.04805) - Ο αρχικός κωδικοποιητής μετασχηματιστή
- [ΠΡΟΣΟΧΗ ΜΣΚ0 ΜΕΣΒΕΡΤ](https://arxiv.org/abs/1908.10084) - BERT για ενσωμάτωση ποινής
- [Η ΜΣΑΔΕ ΕΞΕΡΓΑΣΕΙ](https://www.elastic.co/blog/practical-bm25-part-2-the-bm25-algorithm-and-its-variables) - Το κλασικό αλγόριθμο ανάκτησης
- [Χαρτί RRF](https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf) - επιτυγχάνει αμοιβαία κατάταξη συγχώνευση
- [Δραστηριότητες ONX Runtime](https://onnxruntime.ai/) - CrossΜSK1platform ML παραπομπή

### Σχετικά με Deep Dives

- [Πώς λειτουργεί η Neural Machine Translation](/blog/how-neural-machine-translation-works) - καλύπτει την αρχιτεκτονική μετασχηματιστώνΜSK1 μηχανισμούς προσοχής, και ενσωμάτωσης από μια προοπτική μετάφρασης Η πολλές από τις ίδιες έννοιες εφαρμόζονται στην κατανόηση εγγράφωνΗ ΜSK4

## Επεξεργασία της σειράς

Αυτό καταλήγει στο συμπέρασμα της σειράς DocSummarizer. Εδώ' αναφέρει πώς τα τρία μέρη ταιριάζουν μαζίM SK2

**[ΕΠΙΧΕΙΡΗΣΗ 1](/blog/building-a-document-summarizer-with-rag)** εξηγήσεις *Γιατί* Η προσέγγιση του σωλήνα χτυπά άγρια κλήσεις LLM.Αυτή καλύπτει τα αρχιτεκτονικά πρότυπα (chunkingM SK2 ιεραρχική μείωσηMK3 επικύρωση αναφοράςΜSK4 που κάνει οποιοδήποτε σύνολο εγγράφου λειτουργεί καλάMST5Το εργαλείο εξελίχθηκε από τότε το μέρος ΜσΚ6 γράφτηκεΜΣΚ7 αλλά οι αρχές παραμένουν έγκυρεςMSC8

**[ΕΠΙΧΕΙΡΗΣΗ 2](/blog/docsummarizer-tool)** Είναι ο γρήγορος οδηγός εκκίνησης -.ΕγκατάστασηΜSK2Modes, πρότυπαM SK4 κοινές περιπτώσεις χρήσηςMSKA5Αν θέλετε απλά να χρησιμοποιήσετε το εργαλείοMKA6 ότιMK7 όλα όσα χρειάζεστε

**ΕΠΙΧΕΙΡΗΣΗ 3** (Αυτό το άρθροΜSK1 είναι η βαθιά κλίση για τους ανθρώπους που θέλουν να κατανοήσουν *Πώς* πράγματι λειτουργεί: BERT vs φράση μετασχηματιστές , γιατί το ONNX έχει σημασία, tokenization gotchasM SK3 υβριδική εμπορία αναζήτησης ΜSK4ΜSK5 και τι διακοπές στην παραγωγή

Εάν σας αρέσει' αναφέρει την κατασκευή του δικού σας σωλήνα, αναφέρεται να διαβάζετε όλα τα τρίαΜSK2 αναφέρθηκε Αν σας αρεθεί' αναφέρει απλά χρησιμοποιώντας το εργαλείο, αναφέρει ανάγνωση Μέρος M SK5 αναφέρει και ίσως σχήμα Μέρος

## Σχετικά

- [Ανάλυση CSV με τοπικά LLMs](/blog/analysing-large-csv-files-with-local-llms)
- [Συλλογή και ανάλυση περιεχομένου Web με LLMs](/blog/fetching-and-analysing-web-content-with-llms)