# DocSummarizer Part 3 - Kehitykselliset käsitteetM SK2 \"Jäysin liian pitkälle |🤦" syvänpyynti

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

## Esitys

Tämä on **Osa 3** DocSummarizer -sarjasta:

1. **[Osa 1: Laatiminen asiakirjan yhteenvetovälineeksi RAG:in avulla](/blog/building-a-document-summarizer-with-rag)** - Architektura ja se, miksi putkilähestymistapa voittaa naiivia LLM-puheluja
2. **[Osa 2: Ohjelmien käyttö](/blog/docsummarizer-tool)** -QuickM SK1CLI:n aloitusohje
3. **Osa 3: Kehityskäsitykset** (artikkeliM SK1 - Tekninen syvänpyynti
4. **[Osa 4: Rakenne RAG-putket](/blog/docsummarizer-rag-pipeline)** - NuGet-kirjaston avulla rakennetaan omia RAG- sovelluksia

---


Tämä on osa omaa lähestymistapaani.

DocSummarizer aloitti osoituksena siitä, miten *olisi* Build document summarizers with LLMs - osaan esittämäni pipeline-lähestymistapa | Most tutorials show you how to shove text into an LLM and hope for the best | . | Halusin osoittaa asianmukaista rakennetta |

Mutta kuten aina, olen kiinnostunut ongelmatilanteesta. Neljä päivää myöhemmin olen tutkinut tutkimusta. Miten tuotanto ja luokitusjärjestelmät todella käsittelevät yhteenvetoa? ? What makes retrieval work well?

Panin täytäntöön näihin lähestymistapaan liittyviä versioita. Se, mikä alkoi "joka on ' oikea malliM SK3, muuttui paikallisesti toimiviksi ONNX-embeddingiksiMSC4 hybridinen haku, joka yhdistää BMMST5 tiheään hakuunMSL6 monimuotoisuuden enimmäismarginaalien merkityksenMSV7 ja signaalien yhdistämiseen sovellettavan vastavuoroisen rangaistusfuusionMSP8

**Oikeusvaroitus**: Tämä on " menin liian pitkälle" syvänpyyntiM SK3 Jos haluatte vain käyttää välinettäMSC4 lukea osan 2. jos haluatte ymmärtää *miksi* se toimii ja *miten* osat sopivat yhteen, lukee edelleenM SK1

Tämä artikla kattaa:

- **Sentenssien sisällyttäminen**: Miten transformatorimallit (MiniLM/BGEM SK3GTEMSC4 muuttavat tekstin vektoriksi
- **ONNX:n käyttöaika**: ML-mallien käyttäminen paikallisesti ilman Pythonia tai pilvipalveluja
- **RAG (Retrieval-Augmented GenerationM SK2**: Grounding LLM-tulos lähteaineessa
- **Hybridinen haku**: Semanttinen ja lexikalinen haku yhteensovittaminen RRF:n kanssa

[TOC]

## Architecture at a Glance -katsaus

Ennen yksityiskohtien tarkastelua, täällä 'kuten osat sopivat yhteenM SK2

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

## Ymmärrys sisällytyksistä

### Ongelma: Miten löydetään asiaankuuluva sisältö ilman avainsanoja?

500-sivukirjan yhteenvedon tekemisessä, on löydettävä asiaankuuluvat osatM SK2 Perinteinen avainsanojen haku epäonnistuuMSC3

- käyttäjä kysyy: " Miten voin palauttaa laitteet?
- Käsikirjassa sanotaan "Toistaa tehtaiden järjestelytM SK1
- Tärkeimmät sanat jäävät huomaamatta (ei yhteisiä sanojaM SK1

Tarvitaan **semanttinen haku** - merkityksen mukaan vastaaminen

### Mitä liitännäisaineet ovat?

Siirrot ratkaisevat tämän muuttamalla tekstin tiheään vektoriin. (lukujen järjestelmät ), jotka sisältävät semanttiset merkityksetM SK2 Samankaltaisia merkityksiä.

Tässä on ”'” -intuitio.“:” kuvitelkaa 384-” -ulottuvuusaluetta, jossa jokaisella tekstillä on paikkansa.

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

### Miks Sentence Transformers (Not Raw BERTM SK1

**Ongelma**: Tarvitsinkin semanttiseen samankaltaisuuteen soveltuvia sisällytyksiä . Raaka BERT on suunniteltu luokittelutehtäviin

**Ratkaisu**: käyttö **sentence transformerit** ”-” -mallit on koulutettu erityisesti vastaavuustehtäviin, joissa käytetään ristiriitaista oppimista. [BERT-arkkitehtuuri](https://arxiv.org/abs/1810.04805) mutta hienoa-muunneltu eri tavallaM SK1

Muodot kuten `all-MiniLM-L6-v2` ja `bge-small-en-v1.5` oli koulutettu miljooniin tekstipareihin, kuten ”:”

- "Laitteen uudelleensijoittaminenM SK1 ↔ | "Tehtaiden ohjelmien palauttaminen" \(samaa tapaaMSC6
- "Laitteen uudelleensijoittaminenM SK1 ↔ \"Product specifications" |(dissimilar | )

Koulutus opettaa heille: samankaltaisia merkityksiä M SK1 läheiset vektorit (yleinen kosineeritys

> **Yhtenevä**: Jos haluatte ymmärtää, miten transformaattoreiden mallit toimivat syvällisemmällä tasolla |- mukaan lukien huomiomekanismit | , | Enkoder | | 3 | Decoder-arkkitehtuuri | 4 | ja miksi sisällytykset toimivat | [Miten neurokoneen käännöstoiminta toimii](/blog/how-neural-machine-translation-works). Se kattaa samat transformaattoreiden käsitteet käännösnäkymissä.

**täytäntöönpano**: Otamme mallin ' tuotantolaitteen ja soveltamme sitä **keskimääräinen poolointi** - keskitetään kaikkien tokenien sisällytykset saadakseen yhden vektorin koko tekstille

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

### Ongelma: Pythonin riippuvuus helvetti

Halusin embeddings to "just work", kun joku käyttää välinettä

1. asentaa Pythonin + PyTorchin + transformaatiot
2. Laaditaan mallit käsin
3. Toivon, että versioon liittyvät ristiriidat eivät häiritse kaikkea

Tämä on naurettavaa. käyttäjät haluavat `docsummarizer -f doc.pdf`, ei ole 30-vaiheen käyttöönottoohje

### Miksi ONNX?

ONNX (Open Neural Network Exchange **runtime-johdanto ilman Pythonia**.

**Mitä saan ONNX Runtimesta:**

1. **Nolla ulkoiset riippuvuussuhteet** - Ei Pythonia
2. **Auto-download models** - Ensin HuggingFace-ohjelmasta peräisin olevia laadintoja ~23-34MBM SK2 ja sen jälkeen tallennetut
3. **Puhdas .NET** - Soveltuu kaikkialla .NET toimii Windows, LinuxM SK4 macOSMST5 ARMMSC6
4. **CPU:n johtopäätös** - Ei tarvita GPU:tä

Kauppa-off: Lyhyesti hitaampaa kuin GPU PyTorch, mutta paljon nopeammin kuin pyydetään käyttäjiä ottamaan käyttöön Python

### mallirekisteri

DocSummarizer sisältää useita sisällyttämistä koskevia malleja, kukin eri kaupan kanssaM SK1offs:

| malli | | | Mitot || | Max-tokenit || | Suuruus |  |Quantized | miSK5 | MiSK6 | käyttötarvikkeita | MISK7 | vaaditaan ohjeita |
|-------|-----------|------------|------------------|----------|---------------------|
| `AllMiniLmL6V2` |  384
| `BgeSmallEnV15` |  384
| `GteSmall` |  384
| `MultiQaMiniLm` |  384

**Huomio**: Kaikki rekisteritulokset viittaavat WordPiece-järjestelmään -onniskelpoisiin ONNXin vienteihin `vocab.txt`). BPE

**BGE-ohjeet**: Joissakin muodoissa, kuten BGE:ssä, on edellytettävä optimaalista suorituskykyä.

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

Rekisteri seuraa, mitkä mallit tarvitsevat ohjeita `RequiresInstruction` ja `QueryInstruction` kentät. Aina vertailukelpoinen saatavuuden laatu, kun työskennellään ohjeiden kanssa

Tässä on mallirekisterin käyttötapa'

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

### Tokenointi: WordPiece vs BPE

Erilaiset mallit käyttävät erilaisia tokenizereja. `all-MiniLM-L6-v2` mallissa käytetään WordPiece-tokenointia (kuten BERT),, joka jakaa tuntemattomat sanat alasanatokeneihinM SK2 Muissa muissa malleissa voidaan käyttää BPE-tokenseja (ByteMSC4Pair EncodingMST5 tai Unigram tokenizersMSV6

**Merkittävä**: Mallin tokenisaattorit on sovitettava koulutustokenizeriinM SK1 Rekisterimme seuraa, minkälainen tokenizer kutakin mallia varten tarvitaan **Tällä hetkellä toteutettu: WordPiece** (via `vocab.txt`). BPE `tokenizer.json` on suunniteltu, mutta ei vielä pantu täytäntöön - pysy WordPiece-mallien kanssa rekisterissä toistaiseksi

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

Esimerkki tokenointi:

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

### Keskimääräinen keskittäminen huomio Maskin avulla

Sen jälkeen, kun BERT käsittelee tokkeita, saamme kullekin tokenille peitetyn tilanteen . Keskimääräinen poolointi keskittää nämäM SK2 mutta vain reaalitokkiin (ei paddingMSC4

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

### Täysin sisällytetty putki

Tässä' on täydellinen virta tekstistä sisällytykseen :

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

### Ongelma: LLM:t eivät voi't lukea M SK2Page-asiakirjoja

**Naiivi lähestymistapa epäonnistuu:**

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

Jopa 128K konteksti-ohjelmien kanssa, ei voida vain siirtää valtavat asiakirjat \:\

- **Poikkeus**: Ainoastaan ensimmäiset 100 sivut sopivat
- **Hallucinaatio**: LLM keksii "todisteetM SK2 puutteiden täyttämiseksi
- **Kustannus**: Tietojenkäsittely 2MB tekstikustannuksista $$$ kysymyksestä kohti
- **Laatu**: LLM:t sekoittuvat massiiviseen kontekstiin.

### Ratkaisu:RAG M SK1Retrieval-Augmented GenerationMSC3

Sen sijaan, että lähettäisimme kaiken, lähettäisimme vain sen, mikä on asianmukaista.

1. **Englanninkielinen kieli**: Siirretään asiakirja osiksi
2. **Sisällyttäminen**: Siirtää segmentit vektoreihin (semanttinen esittely
3. **Takaaminen**: Ottakaa kysymykseen tarkoituksenmukaisimmat segmentit
4. **Synteesi**: LLM tiivistää vain ne segmentit

**Miksi tämä toimii:** LLM katsoo, että 10KB on erittäin merkityksellinen sisältö sen sijaan, että

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

### Dokumenttistrategiat

DocSummarizer tukee useita chunkkistrategioita, jotka perustuvat asiakirjarakenteeseen:

```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 -jäljittämiseen tarkoitetun osa-alueen ekstrakointi

pitempien asiakirjojen osalta

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

### Ongelma: Semanttinen haku palauttaa päällekkäisyyksiä

**Ilman MMR:tä**, hakeminen "Kaikkien tallennusten käsittelyä koskevat kysymykset

1. "Katsauksen yleiskatsausM SK1 (0.95 vastaavuus)
2. "Introduction Caching-järjestelmään
3. "Mitkä ovat tallennetukset?
4. "Cache-järjestelmän täytäntöönpanon yksityiskohdat

Ylimmäistulokset 3 sanovat kaikki samaa:

### Ratkaisu: Maksimaali rajallinen merkitys M SK1MMR)

MMR:n tasapaino **asianmukaisuus** (kysymyksen vastaavuus **monimuotoisuus** (ylivoimaisuus jo olemassa oleviin toimenpiteisiin

**Formula:**
$$MMR = SSK2lambda S\cdot \textM SK5sim}(sMSC7kysymys_{sM SK1 \valitussa} ,\teksti{sim}(s\, s

**Mitä se tekee:** Rangaistetaan ehdokkaita, jotka ovat samankaltaisia jo olemassa oleville segmenteille.

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

Lauseke:

$$MMR = \lambda \cdot sim(s, centroid)-(1-\Lambda)\cdot_{s

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

## Hybridinen haku RRF:n avulla

### Ongelma: Semanttinen haku ei vastaa täsmällisesti toisiaan

törmäsin tähän testattaessani:

**Kysymys**: "', mitkä ovat API:n loppupisteet todentamiseksi

**Semanttinen haku palasi:**

1. "User login flow overview
2. " Turvallisuutta koskevat parhaat käytännöt
3. "

**Se, mitä se jäi jäljelle**: Todelliset API-yhteyksien loppupisteet kirjatut koodiesimerkeissä: `POST /api/v1/auth/login`

**Miksi?**: Embedding-mallit on koulutettu luonnollisella kielellä `POST /api/v1/auth/login` ' ei vastaa semanttisesti "todistuksen loppukohtaaM SK2 - se' on kirjallinen tekninen viite

### Ratkaisu:Hybridinen haku M SK1Semantiikka + Lexikaali

Yhdistetään kaksi jäljittämismenetelmää täydentäviin vahvuuksiin:

| hakutyyppi | vahvuudet SSK2 heikkoudet
|-------------|-----------|------------|
| **Tiheys (EmbeddingM SK1** | Semanttinen ymmärrysM SK1 Synonymit | Voi olla virheenmukaisia täsmällisiä vastauksia
| **Säästö (BMM SK1** | Täsmällinen avainsanojen yhteensopivuusM SK1 harvinaisia termejä | Semanttista ymmärrystä ei ole

Hybridinen haku yhdistää molemmat vastavuoroisen luokan fuusiota käyttäen (RRFM SK1

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

**Huomio**: RRF-tulokset ovat ilmentyviä . pysyvä kM SK2 on standardi; todellinen luokitus riippuu täydestä ehdokaskokonaisuudesta

### RRF:n täytäntöönpano

```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: Puolustustyövoima

BM25 M SK1Best Matching 25) on klassinen tietopyynnön algorytmi.

```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 sisällön keskittämiseksi

### Ongelma: Miten erotetaan perussisältöä Triviasta?

Kun esitän yhteenvedon romaanista, sain sellaisia tuloksia kuin:

> Watson totesi, että sää oli maltillinen.

Nämä ovat täsmällisiä ekstrahteja, mutta ne **väri** ( **keskeiset plot-kohdat**.

**Haasteena**: Miten sanotte eroa

- **Perussisältö**: Ilmenee koko asiakirjan läpi (nimitykset, , pääaiheet,, keskeiset tapahtumat
- **Tukevat yksityiskohdat**: Vaikuttaa eräissä kohdissa (subplotsM SK2selvitykset
- **Värvi**: HarvinainenM SK1 yksityiskohtaiset tiedot (kuka kenenkään käyttämä tekstiili oli

### Keskeisyyden luokitteluun sovellettava ratkaisu

TF-IDF **miten keskeinen käsite asiakirjassa on**, ei sen todellista arvoa

**Logic:**

- **Korkeat DF:t (>50% chunkistaM SK1**Ydinsisältö
- **Keskikokoinen DF (20-50%)**:
- **Alhainen DF (<20%)**:

Tässä ei ole kyse todellisuudesta. Toistuvat väitteet voivat olla virheellisiä, harvinaiset tosiasiat voivat olla totta *asiakirjan keskittyminen*.

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

DocSummarizerin tuotantoputki '`BertRagSummarizer`) yhdistää kaikki nämä käsitteet

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

## Yleiset epäonnistumismenettelyt

Kun rakennatte ja hyödynnätte DocSummarizeriä, IM SK1on törmännyt näihin ongelmiin (ja te olette törmänneet niihin myös):

1. **Tokenizerin epäjohdonmukaisuus → järjettömiä sisällytyksiä**: Laaditaan WordPiece vocab BPE:n mallille-koulutettu malli tuottaa päteviä -näkökkäitä mutta semantisesti merkityksettömiä vektoreitaM SK3 Tarkistakaa aina, että tokenizer vastaa mallia

2. **Dominantinen-topicinen ennakkoluulot yksittäisessäM SK1centroidien tulosten osalta**: Yhden asiakirjan keskittäminen järjestelmällisesti alaspäin, - vähemmistöä koskevien aiheiden tärkeysasteen laskeminen,

3. **BM25 voittaa tiheän etsinnän harvinaisissa termeissä**: Jos kysymyksessänne on puutteellisia teknisiä käsitteitä tai asianmukaisia nimikkeitä, embedding-mallissa esitetty - ' koulutustiedot, , lexical matching (BM

4. **OCR-jätte skannattujen PDF:ien muodossa**: tiivistelmä on hyvä , mutta OCR-virheet moninkertaistuvatM SK2 Jos näette tiivistetyissä sanoissa hölynpölyä, tarkistakaa ensin tiivistelemästä peräisin olevan merkitsemistuloksen.

5. **Alhainen -coverage-summary on suojattava kielellä**: Jos olette nähneet vain 3% asiakirjasta, , lauseet kuten ", viime kädessä " tai " päätelmissä " ovat epärehellisiä . Järjestelmän on sanottava " näytteillä otetuissa kohdissa " ja vältettävä lopullisia päätteitä

6. **Viittaushallucinaatio**: Pienet LLM:t (1.5B-3B-paramitM SK3 joskus keksitään todennäköisiä `[chunk-N]`, varmistetaan, että N on olemassa alkuperäisissä chunkeissa, ja ilmoitetaan tai korjataan väitteitä, jotka viittaavat puuttuneisiin chunkkeihin `[chunk-999]` 10-chunk-asiakirjasta, LLM:ssänne on vaikeuksia tehtävän kanssa

Nämä eivät ole ”'” -virheitä -“ ne” –'“ ovat luonnollisia jännitteitä suunnittelun alueella”–.” Hyvät tuotantojärjestelmät tunnustavat ja lieventävät niitä“.”

## Käytännön näkökohdat

### Puite- ja näytteenotto rehellisyyttä

Kun hallinnoidaan hyvin suuria asiakirjoja, DocSummarizer ei yritä sisällyttää kaikkea. **Tämä tarkoittaa, että yhteenveto perustuu esikuvaan, eikä koko asiakirjaan .**

Järjestelmä käsittelee tätä avoimesti:

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

**Merkittävä**: Tämä on *kerättyjen todisteiden yhteenveto*, ei taata, että asiakirja on kattava kokonaisuudessaan -. Kun sanomme, että \"\ampled \ 3%",\, että '\ on juuri se, mitä tapahtui \(-\), järjestelmä näki \[3%\] asiakirjan ja tiivisti sen

**Sampling ei ole random** - it *semantiikka*. Me käytämme monimuotoistaM SK1kanker klusterointia varmistaaksemme, että vähemmistökysymykset eivät jää ulkopuolelle 't excluded. Lukuisat ‐3% saattavat jättää huomiotta kaikki rajoitukset ja rajalliset tapauksetMSC5 Semanttinen ‐ 3% yrittää ottaa yhden edustavan osan kunkin tärkeän aiheen sisällöstä.

**Adaptiivinen näytteenotto useilla aihekohtaisilla ankareilla**: Ennen -filteriä käytetään monenlaisia ankaria |( |k | - | tarkoittaa - ‐tyyppistä ryhmittymää stratifioidussa näytteessä |) | varmistamaan, että vähemmistön aiheet eivät ole järjestelmällisesti suljettu pois ‐' | Tämä estää ‐ " ‐dominanteen aiheellisen ennakkoluulonnon ‐

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

Tämä on tutkimusta-tiedonmukaista (avoittavat yhden ainoan-kysymyksen palauttamisen romahtamistaM SK3 mutta käytännönläheistä MSC4 se toimii sekunnissa CPU:ssäMST5

**Miksi ei vain sisällytä kaikkea?**

500-sivuasiakirjan (2,000+segmenttien), sisällyttäminen kaikkiin toimisi, mutta ei oleM SK3 optimaalistaMSC4

- **Kustannus**:, O, (, N, M SK2, sisällytykset hallitsevat runtime-aikaa
- **Laatu**: Kaikkien sisällyttäminen lisää kuormia hakukeskuksessaM SK1 Te tarvitsette edelleen luokitusta, joten miksi sisällytetään segmentit, jotka eivät koskaan ole korkeita?
- **Käytännönmukaisuus**: Muistia ja latentiarajoituksia koskevat rajoitukset ovat tärkeitäM SK1 \2,000 \ 384- \dim-vektoreiden säilyttäminen muistissa ja laskemisessa on turhaa.

Monitasoinen-johtomuunnos antaa parhaan mahdollisen laajan aihekokonaisuuden molemmista näkökohdista ja helppotajuisen tietojenkäsittelyn avulla.

### Kontextin vetäminen: Miks Retrieval Isn't Just Search

Kuten edellä kuvasin, tämä ei ole vain "jäljitettävyys, vaan myös ", -, se on erityinen malli, jota kutsun **Rajoitettu hämärän taustan vetäminen** ([CFCD](/blog/constrained-fuzzy-context-dragging)). Näkemys

> Useimmat yhteenvetoasiat lisäävät jatkuvasti asiasisältöä. DocSummarizer *etenee* vain se, mikä selviytyy deterministisesta valiotuksesta,, sallii sen, että malli kirjoittaa sujuvasti niiden rajojen sisälläM SK1

Tässä on esimerkki siitä, miten DocSummarizerin putkilinja kartoittaa CFCD:tä.

| CFCD-käsite | DocSummarizerin täytäntöönpano
|--------------|------------------------------|
| **Salienssin havaitseminen** (
| **Deterministinen edistäminen** | MMR
| **Johtokirja** | Saavutettu segmentti, joka on sidottu lainausnimityksiin.
| **Rajoitettu sukupolvi** | Päätöslauselmaesitys rajoittuu kerättyihin todisteisiin

**Miksi tämä on tärkeää?**Tämä on syy siihen, että pienet paikalliset mallit toimivat.

Käytännössä "anchor ledger" näyttää tällaiselta.

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

Sen jälkeen synteesissä prompt:

- "Kaikissa vaatimuksissa on mainittava sisällytetty chunk ID
- "Jos kattavuus on
- " Käyttäkää näitä termejä johdonmukaisesti

Siksi:

- **Pitkämmät konteksti ikkunat ovat punainen hering** Ongelma ei ole se, etteikö teksti olisi sopivampi, vaan se, mikä ansaitsisi selviytyä
- **Rekursiivinen yhteenveto on epäonnistunut** - se kohtelee välitulosta tekstinä
- **LLM voi't "forgetM SK2 edistää rajoituksia** ”-, ne, ', rakenteelliset rakenteet ja , eivät ole prose, sillä ne voivat vetäytyä

CFCD on sama filosofinen jako kuin [Rajoitettu epämääräisyys](/blog/constrained-fuzziness-pattern), [Rajoitettu hämärä MoM](/blog/constrained-mom-mixture-of-models), ja [Kuvan yhteenveto](/blog/constrained-fuzzy-image-intelligence) - todennäköisyys ehdottaa

### Kvantointi

ONNX-mallit voidaan mitata (vähennetty täsmällisyys ) pienemmät mittasuhteet ja nopeampi johtopäätös

| malli | täydellinen täsmällisyys | määritelty S| laatuerot M|
|-------|---------------|-----------|-------------------|
|, kaikki, -,MiniLM,-,L,6-,v,M SK4, |, \90,MB, ||, | 23, MB, ¬| ja |
| bge -small-enM SK3vMSC4 |

### Määrärahat ja kilpailu

Suurten asiakirjojen osalta `InferenceSession` voidaan jakaa yleisesti threadeissä turvallisesti, mutta suorituskyky riippuu istunnon järjestelystä

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

**suorituskykyhuippu**: Ohjelmointi `SessionOptions` kokouksen luomisen yhteydessä:

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

### Suurten asiakirjojen muistin hallinta

Erittäin suuret asiakirjat (novels, oikeudelliset asiakirjanmuodotM SK2 edellyttävät erityistä käsittelyä, jotta vältettäisiin

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

## suorituskyvyn erityispiirteet

Todellinen-maailman suorituskyky tyypillisellä kehittäjäkoneella M SK1Ryzen 5600X, 32GB RAMMSC5 GPU:ta ei ole

| Operaatio | | | Lähtöputki
|-----------|-----------|-------|
| **Sisällyttäminen** |
| **Tiukka haku** |
| **BM25 -tulokset** |
| **RRF:n fuusio** |
| **Päätös Page PDF** |

**Testiympäristö**: Ryzen 5600X SSK2core), 32GB RAMM SK5 ei GPUMSC6 Integroinnissa käytetään kaikkiaMSSK7MiniLMMST8LMTSK9vMS2 MTSK11quantisoituMSR12 \MSR13\tokenin enimmäismahdollisuus\MST14\ \SST15\tahden rinnakkaista batsointia\.\ jäljittämiskokonaisuus\MSSK17\ |MST18\ segmentit\M SSK19\ Mileage vaihtelee eri malleihin nähden\,\ Hardware\MTSKS21\ ja asiakirjan monimutkaisuus\ MSSK22\

**Pääjohtaja sisällyttää läpikulun** (mallin valinta +tokenen pituus | +kuormituksen suuruus \ ). Poistaminen ja fuusio ovat pohjimmiltaan vapaita \- ne vievät miljoonia sekuntia \

**Laajennus**: Hierarkkisen ekstraktion avulla käsitellään SSK1 sivuasiakirjoja (kertomuksia, ,käsitteitä, SSK4 jaloittain ja ainoastaan yläpuolella.

## Lyhyesti sanottuna

DocSummarizer osoittaa, että monimutkaiset NLP-valmiudet eivät tarvitse pilvi API:ita tai Pythonin riippuvuuksia. OnNXin runtimen avulla integroinnissa ja generationissa Ollamassa, voidaan rakentaa täydellinen RAG-putken, joka

- Käytetään täysin paikallisesti
- Tuotetaan jäljitettävissä olevat, mainitut yhteenvedot
- Käsittää kaikenlaisista asiakirjoista
- Toimia offline-tilassa

Keskeiset näkemykset tämän välineen rakentamisesta:

1. **Sisällytykset ovat perusta** - Hyvä haku riippuu hyvistä sisällytyksistä
2. **Hybridinen haku voittaa joko yksin** - Semanttinen ja lexiikka yhdistetään luovuuteen
3. **MMR estää toistumisen** - Monimuotoisuus on yhtä tärkeää kuin merkitys
4. **Rakennekysymykset** - Asiakirjojen rakenteen noudattaminen
5. **LLM:n pitäisi tulla viimeiseksi** - Tehkää ensin halpa CPU-työ, kallis LLM-työ vain filtroituun sisältöön

## Lisä lukeminen

### Asiakirjat ja tekniset viittaukset

- [BERT-paperi](https://arxiv.org/abs/1810.04805) - Alkuperäinen transformatorien enkoder
- [Sentenssi-BERT](https://arxiv.org/abs/1908.10084) - BERT lauseen sisällyttämistä varten
- [BM25 Selitetään](https://www.elastic.co/blog/practical-bm25-part-2-the-bm25-algorithm-and-its-variables) - Klassinen hakualgoritmi
- [RRF-paperi](https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf) - Vastavuoroinen luokitus fuusio
- [ONNX:n käyttöaika](https://onnxruntime.ai/) - Yhdyskuntalaajuinen ML-päätelmä

### Vastaavia syvänmeren uima-alueita

- [Miten neurokoneen käännöstoiminta toimii](/blog/how-neural-machine-translation-works) - kattaa muuntajaarkkitehtuurin , huomiomekanismit M SK2 ja sisällytykset käännösnäkymien näkökulmasta |  monet samat käsitteet pätevät asiakirjan ymmärtämiseen

## Serien laatiminen

Tämä päättää DocSummarizer-sarjasta.

**[Osa 1](/blog/building-a-document-summarizer-with-rag)** selittää *miksi* pipeline-lähestymistapa voittaa naiivia LLM-puheluja. Se kattaa rakenteelliset mallit (chunkingM SK2 hierarkiallinen vähentäminen , viittausvaliottaminen ), jotka tekevät jokaisesta asiakirjan yhteenvetoasiakkaasta toimivan hyvin

**[Osa 2](/blog/docsummarizer-tool)** on nopeaa-käynnistysohjetta. asentaminen , muodotMST3 mallitMTSK4 yleiset käyttökäytännöt MTSK5 Jos haluatte vain käyttää välinettä, seMSSK7 on kaikki, mitä tarvitsette

**Osa 3** (this articleM SK1 on syvänmeren uima, jota ihmiset haluavat ymmärtää *miten* se todellakin toimii: BERT vs sentence transformersM SK1 miksi ONNX on tärkeä, tokenointi gotchasMSC3 hybridi hakukauppaMST4offsMSV5 ja mikä murtaa tuotantoaMSM6

Jos olette rakentamassa omaa putkea, lukekaa kaikki kolme osaa.

## Yhtenevä

- [CSV-analyysi paikallisten LLM:ien kanssa](/blog/analysing-large-csv-files-with-local-llms)
- [Fetching and Analysing Web Content with LLMs](/blog/fetching-and-analysing-web-content-with-llms)