# DocSummarizer Teil 3- > Fortgeschrittene Konzepte :- > Die "- > Ich war zu weit entfernt von Deep Dive

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

## Einleitung

Das ist **Teil** von der DocSummarizer-Serie

1. **[Teil .1: . Aufbau eines Dokument-Zusammenfassung mit RAG](/blog/building-a-document-summarizer-with-rag)** Die Architektur und warum der Pipeline-Ansatz naive LLM-Aufrufe schlägt
2. **[Teil mit dem Werkzeug](/blog/docsummarizer-tool)** Kurzanleitung für den CLI
3. **Teil - Fortgeschrittene Konzepte** Dieser Artikel ist zu weit gegangen, der technische Tieftauchgang
4. **[Teil Gebäude RAG Pipelines](/blog/docsummarizer-rag-pipeline)** Verwenden Sie die NuGet-Bibliothek, um Ihre eigenen RAG-Apps zu erstellen

---


Dies ist ein Teil meiner Zeit-- ,Boxed Tools ," , approach .: , geben mir ein festes Fenster zu bauen etwas funktionale , . , Es zwingt Entscheidungen und produziert Arbeitscode statt theoretische Designs .

DocSummarizer begann als Demonstration, wie Sie *sollte* Build Dokument-Zusammenfassungen mit LLMs Die meisten Tutorials zeigen Ihnen, wie man Text in eine LLM schiebt und Hoffnung auf die beste . Ich wollte die richtige Architektur zeigen

Aber wie ich immer tue, habe ich mich für den Problemraum interessiert.. Vier Tage später...,... IM']d gegraben in die Forschung.MSC4. Wie funktioniert die Produktion?-?grade-Systeme tatsächlich mit Zusammenfassungen umgehen?MSC6. Was Retrieval gut macht??. Warum machen einige Einbettungen andere besser als ??

Ich implementierte Versionen dieser Ansätze.. , Was begann als " , hier , ' , das richtige Muster wurde ONNX Embeddings laufen lokal , , , Hybrid-Suche kombiniert BM...25 , mit dichtem Retrieval ,, , Maximal Marginal Relevanz für Diversity , und Reziprokaler Rang Fusion für die Kombination von Signalen , . ,

**Faire Warnung**Dies ist die -", die ich zu weit ging - ", Tieftauchgang -., Wenn Sie nur das Tool verwenden möchten - ,, lesen Sie Teil -MCK5, wenn Sie verstehen wollen *warum* es funktioniert und *Wie* die Stücke passen zusammen ,, , halten lesen , . ,

Dieser Artikel umfasst:

- **Satzeinbettungen**Wie Transformator-Modelle ( – MiniLM2 – / – GTE-) – Text in Vektoren verwandeln
- **ONNX Laufzeit**Ausführen von ML-Modellen lokal ohne Python oder Cloud-APIs
- **RAG-Erzeugung )**Erdung LLM-Ausgang im Ausgangsmaterial
- **Hybride Suche**Kombination semantischer und lexikalischer Retrieval mit RRF

[TOC]

## Die Architektur auf einen Blick

Vor dem Eintauchen in Spezifika,, hier', wie die Stücke zusammenpassen

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

## Einbettungen verstehen

### Das Problem: Wie finden Sie Relevante Inhalte ohne Schlüsselwörter?

Beim Zusammenfassen eines Handbuchs der 500--Seite müssen Sie relevante Abschnitte finden.

- Benutzer fragt, wie ich das Gerät zurücksetze
- Manual sagt, dass die Werkseinstellungen wiederhergestellt werden sollen.
- Stichwortsuche verfehlt es (-Keine geteilten Wörter)-

Du brauchst **semantische Suche** Nicht nur Wörter, sondern auch Wörter, die mit der Bedeutung , übereinstimmen

### Was sind Einbettungen?

Embeddings lösen dies, indem sie Text in dichte Vektoren verwandeln.(-Arrays von Zahlen.MSC1., die semantische Bedeutung erfassen. .. Ähnliche Bedeutungen.

Hier stellt sich die Intuition – : – einen Raum vor, in dem jedes Stück Text eine Position hat.

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

### Warum Sentence Transformer Nicht Raw BERTR.)?

**Problemstellung**Ich brauchte Einbettungen, die für semantische Ähnlichkeiten arbeiten.......... und Raw BERT wurde für Klassifizierungsaufgaben entwickelt.

**Lösung**Verwendung **Satztransformatoren** Modelle, die speziell auf Ähnlichkeitsaufgaben mit kontrastiven Lernprozessen trainiert wurden, auf der Grundlage des [BERT-Architektur](https://arxiv.org/abs/1810.04805) aber fein, fein, anders abgestimmt

Modelle wie `all-MiniLM-L6-v2` und `bge-small-en-v1.5` wurden auf Milliarden von Textpaaren wie trainiert

- Wie das Gerät zurückgesetzt wird" Wie das System zurückgesetzt werden kann↔ Wie die Werkseinstellungen wiederhergestellt werden können" Wie die ) ähnlich
- Wie das Gerät zurückgesetzt werden kann" Wie das System zurückgesetzt wird↔ Wie das Produkt Spezifikationen

Das Training lehrt sie, ähnliche Bedeutungen, nahe Vektoren, hohe Kosinus-Ähnlichkeit

> **Verbunden**Wenn Sie verstehen wollen, wie Transformatorenmodelle auf einer tieferen Ebene funktionieren, und warum Embeddings arbeiten, sehen Sie meinen Artikel über [Wie neurale maschinelle Übersetzung funktioniert](/blog/how-neural-machine-translation-works)Es deckt die gleichen Trafokonzepte aus einer Übersetzungsperspektive ab.

**Durchführung**Wir nehmen die Ausgabeschicht des Modells, und verwenden **durchschnittliche Bündelung** Die Einbettungen aller Token durchschnittlich, um einen einzelnen Vektor für den gesamten Text zu erhalten.

```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-:-Laufen von ML Modellen vor Ort

### Das Problem der Python-Abhängigkeit Hölle

Ich wollte Einbettungen, um einfach zu arbeiten, wenn jemand das Tool ausführt.

1. Installieren Sie Python-Transformatoren
2. Modelle manuell herunterladen
3. Hope Versionskonflikte brechen nicht alles

Das saugt die Benutzer wollen `docsummarizer -f doc.pdf`Nicht eine Anleitung für die Einstellung des Schrittes

### Warum ONNX-??

ONNX - Open Neuronal Network Exchange - Open Neural Network Exchange – ist ein offenes Format für ML-Modelle. **Laufzeitableitung ohne Python**.

**Was ich mit ONNX Runtime erhalte**

1. **Null externe Abhängigkeiten** Keine Python- oder PyTorch-Treiber
2. **Auto-Modelle herunterladen** Erste Downloads von HuggingFace ausführen
3. **Reines .-NET** Funktioniert überall, wo . NET läuft.( Windows, , Linux, MacOS,, ARM-64)
4. **CPU-Schlussfolgerung** Keine GPU erforderlich , läuft auf billige Hardware

Handelt leicht langsamer als GPU PyTorch, aber viel schneller, als Benutzer zu bitten, Python, . zu installieren

### Das Musterregister

DocSummarizer enthält mehrere Einbettungsmodelle, jeweils mit unterschiedlichem Trade

|-Modell Abmessungen |-Max-Token Größe (-Quantisiert )-|-Anwendungsfall |-Benötigt Anleitung |-
|-------|-----------|------------|------------------|----------|---------------------|
| `AllMiniLmL6V2` |-M384-|-MNSK3-MUSK4-MKSK5-MB-MWSK6-Standard-Schnell-,-Gute Qualität
| `BgeSmallEnV15` Besser für technische docs
| `GteSmall` |-M384-|-MNSK3-MUSK4-MKSK5-MB-MWSK6-Allgemeines Verwendungszweck
| `MultiQaMiniLm` |-M384-|-MNSK3-MUSK4-MKSK5-MB-MWSK6-Optimiert für Q-&-A-MASSK8-Nr.

**Anmerkung**Alle Registrierungseinträge zeigen auf WordPiece.-.compatible ONNX-Exporte. `vocab.txt`BPE-/-Unigrammmodelle werden noch nicht unterstützt

**BGE-Anleitungsformat**Einige Modelle, wie z.B. BGE, erfordern Präfixe für eine optimale Performance. Das genaue Format hängt vom Modell ab.

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

Die Registry-Tracks, die Modelle benötigen Anweisungen über `RequiresInstruction` und `QueryInstruction` fields-. , immer Benchmark Retrieval Qualität bei der Arbeit mit instruction-.- , basierenden Modellen ,. ,

Hier ist die Art und Weise, wie das Modell-Register arbeitet.

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

### Tokenization-:- WordPiece vs BPE

Verschiedene Modelle verwenden verschiedene Tokenizers `all-MiniLM-L6-v2` Modell verwendet WordPiece-Tokenisierung, die unbekannte Wörter in Subwort Tokens aufteilt Andere Modelle können BPE verwenden ( Byte .- .Pair Encoding, ) oder Unigram tokenizers .

**Wichtig**Unsere Registrierungsspuren, die Tokenizer jedes Modell erfordert **Derzeit implementierte WordPiece** ("via `vocab.txt`Unterstützung des BPE-/-Unigramms über `tokenizer.json` ist geplant, aber noch nicht implementiert.Verhalten Sie sich an die WordPiece-Modelle in der Registry für jetzt.

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

Beispiel für Tokenisierung

Eingabe-Marken
|-------|--------|
| `"embedding"` | `["em", "##bed", "##ding"]` |
| `"DocSummarizer"` | `["doc", "##su", "##mm", "##ari", "##zer"]` |
| `"the quick brown"` | `["the", "quick", "brown"]` |

### Mean Pooling mit Aufmerksamkeit Maske

Nachdem BERT die Tokens verarbeitet, erhalten wir einen versteckten Zustand für jeden Token, ., Mean Pooling Durchschnitte diese, MSC2 aber nur über echte Token.

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

### Die komplette Einbettungspipeline

Hier ist der volle Fluss vom Text zum Einbetten.

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

## Erhöhte Erzeugung

### Das Problem: Die LLMs können Dokumente lesen

**Der naive Ansatz fehlschlägt.**

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

Selbst mit Kontextfenstern von 128-K können Sie riesige Dokumente in :-Cocktails deponieren

- **Spannung**Nur die ersten Seiten passen zu ,e Rest ignoriert
- **Halluzination**LLM erfindet Facts, um Lücken zu füllen
- **Kosten**Verarbeitung von Textkosten 2MB pro Abfrage
- **Qualität**LLMs werden mit massivem Kontext verwechselt.

### Die Lösung: :-RAG Retrieval---Augmentierte Generation: )-

Anstatt alles zu senden, schicken Sie nur das, was relevant ist.

1. **Zucken**Unterteilen Sie das Dokument in Segmente
2. **Einbetten**Segmente nach Vektoren umrechnen
3. **Wiedereinsetzung**Suchen Sie die relevantesten Segmente für die Abfrage
4. **Synthese**LLM fasst nur jene Segmente zusammen

**Warum das funktioniert** Die LLM sieht -10-KB von hoch relevanten Inhalten anstelle von -2-MB von meist irrelevantem Text.

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

### Dokument Chunking-Strategien

DocSummarizer unterstützt mehrere Chunking-Strategien basierend auf der Dokumentenstruktur.

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

### Segmentextraktion für Feinst---Grained Retrieval

Für längere Dokumente: DocSummarizer extrahiert einzelne Segmente: (.Sentences:,.List items: ,.Codeblöcke: ).

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

### Das Problem: Semantische Suche gibt doppelte Rückgaben zurück

**Ohne MMR**Wie funktioniert das Caching?

1. Übersicht über das Einstecken von Daten"Ausblenden von Daten
2. Einführung in das Caching", Ähnlichkeit mit (0.94,
3. Was ist Caching??"?
4. Details zur Implementierung des Caches". Ähnlichkeit von (0.85.

Die oberen Ergebnisse sagen alle dasselbe:

### Die Lösung: Maximale Marginal-Relevanz:

MMR-Salden **Relevanz** Ähnlichkeit zu Abfragen mit **Vielfalt** Unähnlichkeit zu bereits ausgewählten Gegenständen

**Formel ,:-**
$$-MMR-=-\-lambda-\-cdot S\-Text-MSC5-sim}(-Simm,-Abfrage)-MSSK9-MS K10-M--\-Lambda-)-MSD14-Cdot-MSZ15-max_Ausgewählte }Ausgewählte\Ausgewählter {Ausgewählter }(Ausgekürzter S,Ausgelesener SMS-Text

**Was es tut** Straft Kandidaten ähnlich wie bereits gewählte Segmente.. Dies verhindert, dass die Zusammenfassung der Version des gleichen Absatzes ..

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

Die Formel „:"

]$$-MMR ®=-\-lambda ® \-cdot sim T(-s,-centroid ® )--MSC7-(1------\-lambenda ?)-MSSK12-cdot Â\-max_Ausgewählte }Ausgewählte(Ausgewählter ,Ausgewählter ')$$

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

## Hybride Suche mit RRF

### Das Problem: Semantische Suche vermisst exakte Übereinstimmungen

Ich rannte in diese beim Testen

**Abfrage**Der API-Endpunkt für die Authentifizierung von ?"

**Semantische Suche zurückgegeben**

1. Benutzer-Login-Flussübersicht" hohe Ähnlichkeit)
2. Sicherheits-bewährte Verfahren
3. "-Sessionsmanagement"-Segment mit hoher Ähnlichkeit)-Signität

**Was es verpasst hat**Der tatsächliche API-Endpunkt, der in Codebeispielen vergraben ist `POST /api/v1/auth/login`

**Warum?**Embedding-Modelle werden auf natürliche Sprache trainiert......... nicht code.. ../ .URLs... `POST /api/v1/auth/login` doesns.' match semantisch mit dem Endpunkt der Aushöhlung von " ," , - , es ist eine buchstäbliche technische Referenz

### Die Lösung: Hybride Suche: Semantisch Lexisch )

Kombinieren Sie zwei Retrieval-Methoden mit komplementären Stärken

Suchtyp Stärke Stärke Schwäche
|-------------|-----------|------------|
| **Dichte (-Einbettung )-** Memanisches Verständnis Memantische Synonyme Memanisches Verstehen Memanischer Verstand Kann exakte Übereinstimmungen verfehlen
| **Sparse (-BM-25)-** Genaues Schlüsselwort passend zu den seltenen Begriffen von ,. Kein semantisches Verständnis

Hybride Suche kombiniert beides mit reziproker Rank Fusion

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

**Anmerkung**Die angezeigten RRF-Scores sind illustrativ......... die Konstante k.=60... ist standard.... die aktuelle Rangfolge hängt vom vollständigen Kandidatensatz ab.

### RRF-Umsetzung

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

### Das Sparse Retrieval Workhorse

BM-25 M(Best MatchingTM25)- ist der klassische Informationswiederholalgorithmus, der den Begriff Frequenz kombiniert., inverse Dokumentfrequenz und Dokumentenlängennormalisierung.

```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 für die inhaltliche Zentralität

### Das Problem: Wie unterscheiden Sie Kerninhalte von Trivia?

Als ich einen Roman zusammenfasste, bekam ich Ergebnisse wie:

> Der Protagonist trug einen blauen Mantel... Watson bemerkte, dass das Wetter mild war.

Dies sind genaue Extraktionen, aber sie **Farbe** Details zum Einstellen von Informationen nicht **Kernpunkte der Handlung**.

**Herausforderung**Wie erklären Sie den Unterschied zwischen

- **Kerninhalt**Im gesamten Dokument erscheinen die Namen der Zeichen, Hauptthemen und Schlüsselereignisse.
- **Unterstützende Details**In einigen Abschnitten erscheinen Erläuterungen zu )
- **Farbe**Details zu den Möbeln, die jemand trug ,

### Die Lösung für die Klassifizierung der Zentralität

TF.-.IDF.Term Frequency - Inverse Dokumenthäufigkeit - Schätzungen **wie zentral ein Begriff für das Dokument ist**Nicht sein Wahrheitswert.

**Logik ,: ,**

- **Hohes DF (MSK) von Stücken**:-Sherlock",-SharlockM"-Watson",-Surking"-Serlock→-Kerngehalt
- **Mittlerer DF-Wert**Weitere Informationen sind abrufbar unter: http:europa.eu.int
- **Niedriger DF-Wert**"Blauer Mantel ",Black Möbel "Black

Dies ist nicht über Wahrheit, die wiederholte Behauptung kann falsch sein, ,. Eine seltene Tatsache kann wahr sein,).. *Zentralität des Dokuments*.

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

## Die komplette BertRag Pipeline

DocSummarizer, Produktionspipeline von '`BertRagSummarizer`Alle diese Konzepte werden zusammengeführt.

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

## Häufige Störungsmodi

Beim Bauen und Verwenden von DocSummarizer-,-I'-ve traf diese Probleme (- und Sie werden auch

1. **Tokenizer-Mismatch Unsinn Einbettungen**Das Laden eines WordPiece Vokabelns für ein BPE-Modell erzeugt gültige, aber semantisch bedeutungslose Vektoren.Verifizieren Sie immer den Tokenizer, der dem Trainingsregime des Modells entspricht.

2. **Dominant---topische Voreingenommenheit bei der Einzelbetrachtung von --Centroiden**Die Verwendung eines Dokuments zentroid systematisch down--rankt Minderheitenthemen (...Beschränkungen,.Ausnahmen,. Randfälle).. Multi--.Anchor Retrieval behebt dies, fügt aber Komplexität hinzu.

3. **BM.25. Beats dichte Suche auf seltenen Begriffen**Wenn Ihre Abfrage technisches Jargon oder richtige Substantive enthält, die nicht gut sind, wird das Embedding-Modell die semantische Suche übertreffen.

4. **OCR-Müll in gescannten PDFs**Docling ist gut , aber OCR-Fehler Verbindung , wenn Sie Unfug in Zusammenfassungen sehen , überprüfen Sie die Markdown-Ausgabe von Docling erste , - , der Resumpler can , ' , fixieren Müll Eingang ,. .

5. **Niedrige --Erfassungszusammenfassungen müssen die Sprache absichern**Wenn du nur 3% siehst, sind die Phrasen eines Dokuments ,e wie "eultimately"e oder "in schlussfolgern"e unehrlich.e Das System muss sagen, in den abgetasteten Abschnitten "e und vermeiden definitive Endungen .e

6. **Zitierhalluzination**Wir validieren, indem wir die Ausgabe für `[chunk-N]`Das Verifizieren von N existiert in den Quellstücken., und die Kennzeichnung oder Reparatur von Ansprüchen, die fehlende Stücke zitieren. `[chunk-999]` für ein Dokument mit dem Befehl 10--Cunk,-D Ihr LLM kämpft mit der Aufgabe.

Dies sind die inhärenten Spannungen im Designraum von -. Gute Produktionssysteme erkennen und mildern sie.

## Praktische Überlegungen

### Abdeckung und Probenahme Ehrlichkeit

Bei der Verarbeitung von sehr großen Dokumenten versucht DocSummarizer nicht, alles einzubetten. Es verwendet semantisches Pre-Molekül zur Auswahl repräsentativer Segmente. **Dies bedeutet, dass die Zusammenfassung auf einer Probe basiert, nicht auf dem vollständigen Dokument.**

Das System handhabt diese transparent

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

**Wichtig**Dies ist ein *Zusammenfassung der abgerufenen Beweise*Nicht eine Garantie von der vollen Abdeckung von -, wenn wir sagen, dass "ampled 3%",, das genau, was passiert ist -, das System sah 3%, von dem Dokument und fasste, dass -.,

**Die Probenahme ist zufällig** ,-, es , ', es *semantisch*Wir verwenden Multi--Anchor-Clustering, um sicherzustellen, dass Minderheitenthemen ausgeschlossen sind'T .Ein zufälliges 3%I könnte alle Einschränkungen und Randfälle verpassen ..A semantic .3%C versucht, ein repräsentatives Segment aus jedem Hauptthema zu erfassen ,.Es ist immer noch partielle Abdeckung,Aber es ist bewusst verschiedene Teilabdeckung.

**Adaptive Probenahme mit mehreren Themenankern**- Filter verwendet mehrere Anker - Mittel - Stil-Clustering eines geschichteten Samples , um sicherzustellen, dass Minderheitenthemen systematisch ausgeschlossen werden . Dies verhindert die Dominante Thema Bias", wo ein einzelnes Zentrioid nach unten - ist wichtig - aber nicht - Seltene Inhalte wie Einschränkungen , Ausnahmen , oder Schlussfolgerungen .

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

Dies ist die Forschung.-.informiert.(.vermeiden einzelne...-.query Rückruf Kollaps, aber praktisch. -. es läuft in Sekunden auf CPU.

**Warum nicht einfach alles einbetten?**

Für ein Dokument auf der Seite von (2,000+, Segmenten, das alles einbettet, aber optimal ist :,

- **Kosten**Die Embeddings dominieren die Laufzeit.
- **Qualität**Alles einbetten erhöht das Rauschen im Retrieval-Pool.Sie brauchen noch Ranking, also warum Embed Segmente, die nie hoch rangieren.
- **Zweckmäßigkeit**Speicher- und Latenzzwänge für die Verarbeitung von Daten über Speicher und Datenverarbeitung Die Kosinusgleichheiten pro Abfrage sind verschwenderisch, wenn Sie nur das Top benötigen.

Das Multi---Anchor-Sampling gibt Ihnen das Beste aus beiden, die breite Themenabdeckung mit traktierbaren Computing-.

### Kontext Dragging - Warum Retrieval ist gerade Suche

Was oben beschriebene I''ve ist nur ein spezielles Muster, das ich nenne. **Beschränkte Fuzzy Kontext Drackging** ([CFCD](/blog/constrained-fuzzy-context-dragging)Die Einsicht

> Die meisten Resumblierer halten Kontext hinzufügen *zieht nach vorn* nur das, was überlebt deterministische Auswahl... dann lässt das Modell fließend in jene Grenzen schreiben...

Hier geht es darum, wie die DocSummarizer-Pipeline zu CFCD.:

Anwendung von DocSummarizer
|--------------|------------------------------|
| **Salience-Aufdeckung** (-Fuzzy-)-Embeddings-,-Centrioid-Ähnlichkeit - Zentralität von TF, --IDF
| **Deterministische Förderung** MMR,BM25,C RRF Fusion M,C nach oben MMR-MMR--K Auswahl M|C
| **Ankerbuch** Das abgerufene Segment, das mit Zitier-IDs gesetzt wird
| **Beschränkte Erzeugung** Synthesis-Prompt durch gefundene Beweise begrenzt

**Warum das so wichtig ist**Das Modell entscheidet nicht, was die Retrieval-Pipeline betrifft.Das Modell erzeugt nur fließend innerhalb der Grenzen wir.'.ve set... Aus diesem Grund arbeiten kleine lokale Modelle.Die Anker machen das schwere Heben.

In der Praxis sieht das Anchor-Zeichnungsbuch von " aus wie dieses Konzept von MKK3.

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

Dann in der Synthese-Prompt

- Jeder Anspruch muss einen enthaltenen Klumpen zitieren.
- ," , wenn Abdeckung , < , , 5%, , Hedge-Sprache , " ,
- Verwenden Sie diese Begriffe konsequent.

Dies ist der Grund, warum

- **Längere Kontextfenster sind ein roter Hering** Das Problem ist, dass mehr Text passt.
- **Rekursive Zusammenfassung scheitert** Es behandelt Zwischenausgabe als Text, nicht eingeschränkte Struktur
- **Die LLM kann ,' , , " , forget , " , geförderte Einschränkungen** Nicht Prosa kann es driften aus

CFCD ist die gleiche philosophische Spaltung wie [Beschränkte Fuzziness](/blog/constrained-fuzziness-pattern), [Beschränkte Fuzzy MoM](/blog/constrained-mom-mixture-of-models)L 347 vom 20.12.2013, S. 671. [Bildzusammenfassung](/blog/constrained-fuzzy-image-intelligence) Wahrscheinlichkeit schlägt vor, dass der Determinismus andauert und entlang der Zeit angewendet wird

### Quantifizierung

ONNX-Modelle können quantifiziert werden Präzise reduzierte ) für kleinere Größe und schnellere Rückschlüsse Der Handel ist minimaler Qualitätsverlust

Modell | Quantisierter Qualitätsunterschied | Quantifizierter Qualitätsunterschiede
|-------|---------------|-----------|-------------------|
-, MiniLM, -, MKS3, MSC4, |, M90MB, MSSK7, MCSK8, MC9, MSV10, MSR11, MWSK11
|-bge---small---en---v1.5-|-m133-mB-|-mb-|-|

### Stapelverarbeitung und -konkurrenz

Für große Dokumente ist das Batch-Embedding von entscheidender Bedeutung für die Performance. `InferenceSession` kann in der Regel über Threads sicher geteilt werden, aber die Leistung hängt von der Sitzungskonfiguration ab.

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

**Leistungstipp**Konfigurieren `SessionOptions` bei der Erstellung der Sitzung

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

### Speicherverwaltung für große Dokumente

Sehr große Dokumente (-Neulinge ,-Rechtsdokumente )-Bestimmungen erfordern eine besondere Handhabung, um zu vermeiden, OO:-Betrachtung

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

## Leistungsmerkmale

Reale Leistung auf einer typischen Entwickler-Maschine

Anmerkung zu diesem Abschnitt:
|-----------|-----------|-------|
| **Einbetten** ~150, Segmente /, sek. |, Batchgröße 64,, alle -, MiniLM, -, L, 6-, quantisiert
| **Dichte Retrieval** Cosine-Ähnlichkeit gegenüber Segmenten von 500
| **Bewertung des BM-25** ,| , , <5 , | , In ,-, memory inverted Index , | ,
| **RRF Fusion** ,| , , <2 , | , kombinieren , 3 , Rankings , -|
| **Ende der MSK-Seite** |---~15-20--|-- Einschließlich Chunking-,-Embedding-MSC4-Retrieval-,--LLM-Synthese

**Prüfumgebung**: Ryzen 5600X (6-Core-),-M32-GB RAM-MSC5-kein GPU-.-Embedding verwendet alle --MiniLM-MCSK8-MKSK9-v(-quantisierte ,-MSC13-Token max.),-8--thread paralleles Batching.-Retrieval-Corpus:-MSSK18-Segmente. Ihre Kilometerzahl variiert mit verschiedenen Modellen , ,-Hardware , ,- und Dokumentenkomplexität ,.-

**Haupttreiber ist die Einbettung des Durchsatzes** ( model choice + marked length MS K2 charge size ). wiederherstellen und Fusion sind im Grunde frei .- , sie nehmen Millisekunden .. , Dies verstärkt das . " , LLM last ?" , Prinzip : tun billige CPU-Arbeiten . , , Retrieval MSC11 , zuerst .SK12 , teure LLM Arbeit nur auf gefilterten Inhalt .

**Skalierung**Die hierarchischen Extraktionsgriffe der Dokumente der Seite 500+.(.novels,.). durch Verarbeitung in Chargen und nur top--.K pro Charge im Speicher..

## Zusammenfassung

DocSummarizer zeigt, dass ausgeklügelte NLP-Fähigkeiten don-'-t Cloud-APIs oder Python-Abhängigkeiten erfordern..-t Mit ONNX Runtime for embedddings und Ollama for generation?,-t können Sie eine komplette RAG-Pipeline bauen, die -: -

- Läuft ganz lokal
- Produziert rückverfolgbare Zusammenfassungen
- Handhabt Dokumente jeder Größe
- Funktioniert offline

Die wichtigsten Erkenntnisse aus dem Aufbau dieses Tools

1. **Einbettungen sind die Grundlage** Guter Retrieval hängt von guten Einbettungen ab
2. **Hybride Suche schlägt entweder allein** Kombination semantisch und lexikalisch für Robustheit
3. **MMR verhindert Wiederholung** Diversität ist ebenso wichtig wie Relevanz
4. **Strukturfragen** Beachtet die Dokumentenstruktur, verbessert die Ergebnisse
5. **LLMs sollten zuletzt kommen** Kostengünstige CPU-Arbeit zuerst – teure LLM-Arbeit nur auf gefilterten Inhalt

## Literaturhinweise

### Papiere und technische Referenzen

- [BERT Papier](https://arxiv.org/abs/1810.04805) Der originale Transformator-Encoder
- [Satz: --BERT](https://arxiv.org/abs/1908.10084) BERT für Satzeinbettungen
- [Erklärt](https://www.elastic.co/blog/practical-bm25-part-2-the-bm25-algorithm-and-its-variables) Der klassische Retrieval-Algorithmus
- [RRF Papier](https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf) Zusammenwachsender Rang Fusion
- [ONNX Laufzeit](https://onnxruntime.ai/) --Kreuzung--Plattform-ML-Inferenz

### Verwandte tiefe Tauchgänge

- [Wie neurale maschinelle Übersetzung funktioniert](/blog/how-neural-machine-translation-works) Überdeckt die Transformatorenarchitektur,. Aufmerksamkeitsmechanismen,. und Einbettungen aus einer Übersetzungsperspektive.Viele der gleichen Konzepte gelten für Dokumentenverständnis.).

## Verpacken der Serie

Damit endet die DocSummarizer-Serie... Hier ist es so, wie die drei Teile zusammenpassen.

**[Teil](/blog/building-a-document-summarizer-with-rag)** erklärt *warum* Der Pipeline-Ansatz schlägt naive LLM-Aufrufe...Es deckt die Architekturmuster ab. (.chunking.MS K2. Hierarchische Reduktion.,. citation validation.MSC4., die jeden Dokumentzusammenfassung arbeiten gut machen. .. Das Werkzeug, das seit Teil 1. entwickelt wurde, wurde geschrieben, aber die Prinzipien bleiben gültig.

**[Teil](/blog/docsummarizer-tool)** ist dein schnelles --Starthandbuch ,. , Installation , , , Modi , , , Vorlagen ,, , häufige Anwendungsfälle ,. , Wenn du nur das Tool verwenden möchtest , dass , ' , alles was du brauchst .. ,

**Teil** Dieser Artikel ist der tiefe Tauchgang für Leute, die verstehen wollen *Wie* Es funktioniert tatsächlich ,: , BERT vs Satz Transformatoren , , , warum ONNX zählt , , , Tokenization gotchas , , , Hybrid-Suchhandel , und was bricht in der Produktion ,. ,

Wenn Sie Ihre eigene Pipeline bauen , lesen Sie alle drei , . , wenn Sie gerade mit dem Tool ,, , Lesen Sie Teil , 2 , und vielleicht skim Teil .3' Fehler Modi Abschnitt , . ,

## Verbunden

- [Teil 5------DoomSummarizer--:-Tiefforschung](/blog/doomsummarizer-deep-research) Diese Konzepte werden weitergeführt::, Signal-Adaptive RRF-,, Entitätsprofilvektoren mit HNSW Entdeckung,, TextRank Komprimierung, und eingeschränkte parallele lange Generation-
- [CSV-Analyse mit lokalen LLMs](/blog/analysing-large-csv-files-with-local-llms)
- [Abrufen und Analysieren von Webinhalten mit LLMs](/blog/fetching-and-analysing-web-content-with-llms)