# Att hämta och analysera webb innehåll med LLM i C#

<!--category-- AI, LLM, Agents, C#, Systems Design -->
<datetime class="hidden">2025-12-19T10:00</datetime>

> **📌 Nota**: Den här artikeln lär ut grunderna för web-inhalts extraktion med LLM genom att använda den enklaste möjliga metoden **[DocSummarizer](/blog/building-a-document-summarizer-with-rag)** [![GitHub-utsläpp](https://img.shields.io/github/v/release/scottgal/mostlylucidweb?filter=docsummarizer*&label=docsummarizer)](https://github.com/scottgal/mostlylucidweb/releases?q=docsummarizer) - den implementerar den här uppvisad arkitekturen men produktionen - klassificering M SK2 BERT-inbäddar , hybrid sökningar MSC4 spelregler för SPAs Mska5 SSRF skydd Mske6 validerade citationer M Ska7 och korrekta omvakningsspårning

När du frågar ChatGPT om " att läsa den här artikeln och summera den,

**LLMs surfar inte på webben. De resonerar över fragment din kod väljer.**

Denna artikel visar er hur man bygger detta i C# med Ollama - inga ramverk , bara praktisk kod som du kan debugera

> **TL;DR**: Koden tar fram ♫ → ♫ städar ♫→ ♫ bitar ♫ → ♫ väljer ♫. ♫ LLM ser bara de fragment du injicerar ♫

[TOC]

## Det enda som gör det här bra

Här är insikten som förändrar hur man bygger dessa system.

**Utvalet är en produktlemande.**

LLM är i nedströmmen av din val.., den kan inte återhämta informationen du inte hade, ', kan inte visa den,. När en agent ", misslyckas med att hitta svaret, ,", är problemet nästan aldrig samma modell som -, det är ', att din vallogi valde felpartier. [varför jag undviker ramverk som LangChain](/blog/why-i-dont-use-langchain) - de abstraherar bort den urvallogiken du behöver avbugera

Detta är anledningen till att "agenten går igenom maskinen " misslyckas tyst

```mermaid
flowchart LR
    URL[Full Page] --> Select[Your Selection]
    Select --> LLM[LLM Sees This]
    LLM --> Answer[Answer]
    
    URL -.->|"50KB"| Select
    Select -.->|"2KB"| LLM
    
    Miss[Missed Content] -.->|"Never seen"| X[❌]
    
    style Select stroke:#e74c3c,stroke-width:3px
    style Miss stroke:#95a5a6,stroke-width:2px,stroke-dasharray: 5 5
```

Modellet kan bara resonera kring vad du gav det.

---


## Designmål före kod

Innan man skriver något, så är detta begränsningarna.

| Begränsning | Varför det spelar roll |
|------------|----------------|
| **Ingen JS- Rendering** | endast statisk HTML (Playwright för SPAs
| **Begränsade tokens** | Makt budget per begäran (2-4K tokens typiskaM SK2 |
| **källa-begränsade svar** | LLM måste inte hallucinera webbkunskaper
| **Deterministisk urval** | Samma inmatning ♫ → ♫ Samma bitar ♫
| **Observerbara** | Melda in vad du har valt , varför , och vad du kastat bort

Om du kan förklara varför en chunk valdes

---


## Pipelinet

```mermaid
flowchart TB
    URL[URL] --> Fetch[1. Fetch]
    Fetch --> Clean[2. Clean]
    Clean --> Chunk[3. Chunk]
    Chunk --> Select[4. Select]
    Select --> LLM[LLM]
    LLM --> Answer[Answer]
    
    Fetch -.->|"57KB HTML"| Clean
    Clean -.->|"6KB text"| Chunk
    Chunk -.->|"5 chunks"| Select
    Select -.->|"2 chunks"| LLM
    
    style Select stroke:#e74c3c,stroke-width:3px
    style Clean stroke:#f39c12,stroke-width:3px
```

Varje steg minskar datan. [att analysera stora CSV-filer](/blog/analysing-large-csv-files-with-local-llms) - LLM-förklaringar , koder och väljer din kod

---


## Ett fungerande exempel

Under hela artikeln, använder vi

```
https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-10/overview
```

Och tre frågor om ökande specifikitet

1. " Vad är det som är nytt med '?
2. " Vilka förbättringar är nämnt
3. " Finns det något med AOT?

Detta håller exemplet grundade och visar hur urvalet spelar större roll när frågor blir specifika.

---


## Konfiguration

```bash
# Install Ollama from https://ollama.ai
ollama pull llama3.2:3b

# NuGet packages
dotnet add package AngleSharp      # HTML parsing
dotnet add package OllamaSharp     # Ollama client (5.1.x)
```

> **Ollamascharp-noten**: I versionen `GenerateAsync` återkommer `IAsyncEnumerable<GenerateResponseStream?>` - den skickar ut token medan de strömmar. `await foreach`. Samplesprojektpinner  5.1.5.

---


## steg 1: Fetch

Standard HTTP, men med detaljer som spelar roll

```csharp
public class WebFetcher : IDisposable
{
    private readonly HttpClient _http;
    
    public WebFetcher()
    {
        var handler = new HttpClientHandler
        {
            AllowAutoRedirect = true,
            MaxAutomaticRedirections = 5,  // Cap redirects
            AutomaticDecompression = DecompressionMethods.All
        };
        
        _http = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(30) };
        _http.DefaultRequestHeaders.Add("User-Agent", 
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
    }
    
    public async Task<string> FetchAsync(string url)
    {
        var response = await _http.GetAsync(url);
        
        // Bail if not HTML
        var contentType = response.Content.Headers.ContentType?.MediaType ?? "";
        if (!contentType.Contains("html") && !contentType.Contains("text"))
            throw new InvalidOperationException($"Not HTML: {contentType}");
        
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsStringAsync();
    }
    
    public void Dispose() => _http.Dispose();
}
```

**Vad det här hanterar**: Redirectioner | ( | Cappade | Compression | Timeouts | User | Agent | Content | Type Validation | 8

**Vad det inte gör**: JavaScript rendering , autentisering, hastighetsbegränsningM SK3 robotarMSC4 txtMska5 För produktionenMske6 lägga till perMsek7 datorverzögerelser och respektera kretspolitiker.

---


## steg 2: Klara

Den råa HTML är mestadels oväsen. Sätt den eller slösa bort token på layouten.

En typisk sida:

```
57KB HTML → 6KB useful text (90% reduction)
```

### Strategin

```mermaid
flowchart LR
    HTML[Raw HTML] --> Remove[Remove Noise]
    Remove --> Find[Find Main Content]
    Find --> Extract[Extract Text + Headings]
    Extract --> Normalize[Normalize]
    
    Remove -.->|"script, style, nav, ads"| Find
    Find -.->|"main, article, .content"| Extract
    
    style Remove stroke:#e74c3c,stroke-width:3px
    style Find stroke:#f39c12,stroke-width:3px
```

### Att rena är slösaktigt - Handle med omsorg

Din renare kan helt och hållet radera svaret. Vanliga misstag:

- Aggressiva väljare tar bort äkta innehåll
- Det mesta innehållet har ovanliga klass namn (`.article-body`, `.post-text`)
- Svaret finns i en kantbalk eller ett budlåda

**Mäxningar**:

- Säg rubriker eksplicitt (`h1`-`h6`)
- Ta bort genom *roll* och *känt boilerplate*, inte breda mönster
- Log: utlägsna elementväg + textlängd
- Fallback: om huvud innehållet är misstänkt kort

```csharp
public class HtmlCleaner
{
    private readonly HtmlParser _parser = new();
    
    // Known noise - remove these entirely
    private static readonly string[] NoiseElements = 
        { "script", "style", "nav", "footer", "aside", "iframe", "noscript" };
    
    // Boilerplate patterns - remove by role, not aggressive wildcards
    private static readonly string[] NoiseSelectors = 
    {
        "[role='navigation']", "[role='banner']", "[role='complementary']",
        "[class*='cookie']", "[class*='newsletter']", "[aria-hidden='true']"
    };
    
    // Where to find content - order matters (most specific first)
    private static readonly string[] ContentSelectors = 
        { "main", "article", "[role='main']", ".content", ".post-content" };
    
    public CleanResult Clean(string html)
    {
        var doc = _parser.ParseDocument(html);
        
        // Remove noise
        foreach (var tag in NoiseElements)
            foreach (var el in doc.QuerySelectorAll(tag).ToList())
                el.Remove();
        
        foreach (var selector in NoiseSelectors)
            foreach (var el in doc.QuerySelectorAll(selector).ToList())
                el.Remove();
        
        // Find main content
        IElement? main = null;
        string? matchedSelector = null;
        
        foreach (var selector in ContentSelectors)
        {
            main = doc.QuerySelector(selector);
            if (main != null) { matchedSelector = selector; break; }
        }
        
        // Fallback to body if main content is suspiciously short
        var text = main?.TextContent ?? "";
        if (text.Length < 500 && doc.Body != null)
        {
            main = doc.Body;
            matchedSelector = "body (fallback)";
            text = main.TextContent;
        }
        
        return new CleanResult
        {
            Text = NormalizeWhitespace(text),
            MatchedSelector = matchedSelector ?? "none",
            OriginalLength = html.Length
        };
    }
    
    private string NormalizeWhitespace(string text)
    {
        text = Regex.Replace(text, @"[ \t]+", " ");
        text = Regex.Replace(text, @"\n\s*\n+", "\n\n");
        return text.Trim();
    }
}

public record CleanResult(string Text, string MatchedSelector, int OriginalLength);
```

> **Läsbarhetens extraktion** ( betygsättande paragrafer , textdensitet ) är ett helt rabbitshål M SK3 För tillfället, väljare Mska5 baserade extraktionsverk för dokumentation och bloggar Mske6 Samplesprojektet inkluderar en betygsättare extraktor om du behöver den

---


## steg 3: Chunk

Du har 6KB ren text

- **Nadel i haystack**: LLMs presterar sämre när relevant information är gömd i stor kontext
- **kostnaderna**: Fler tokens = mer pengar och latens
- **Fokus**: Väljade chunks ger bättre resonemang än allt på en gång

Strategin spelar mer roll än du tror. [Artikel om RAGs arkitektur](/blog/rag-architecture) - gäller samma principer oavsett om du

### Naiva mening

Detta är en startpunkt

```csharp
public List<string> ChunkBySentence(string text, int maxTokens = 2000)
{
    var chunks = new List<string>();
    
    // WARNING: This breaks on abbreviations, decimals, URLs, code samples
    var sentences = text.Split(new[] { ". ", ".\n", "! ", "? " }, 
        StringSplitOptions.RemoveEmptyEntries);
    
    var current = new StringBuilder();
    var tokens = 0;
    
    foreach (var sentence in sentences)
    {
        var sentenceTokens = EstimateTokens(sentence);
        
        if (tokens + sentenceTokens > maxTokens && current.Length > 0)
        {
            chunks.Add(current.ToString().Trim());
            current.Clear();
            tokens = 0;
        }
        
        current.Append(sentence).Append(". ");
        tokens += sentenceTokens;
    }
    
    if (current.Length > 0)
        chunks.Add(current.ToString().Trim());
    
    return chunks;
}

// Rough estimate - OK for demos, not for billing
private int EstimateTokens(string text) 
    => (int)(text.Split(' ').Length * 1.3);
```

**Varför är detta naivt?**:

- `". "` bråk på "Dr. SmithM SK2 "vMSC4 URLs
- Uppskattning av token efter ordtal är ±20% av
- Inget överlappande mellan chunks ( förlorar kontext vid gränser)

### Bättre: Rubriken -Baserad skrift

För dokumentationen

```csharp
public List<ContentChunk> ChunkByHeadings(string html)
{
    var doc = new HtmlParser().ParseDocument(html);
    var chunks = new List<ContentChunk>();
    
    var headings = doc.QuerySelectorAll("h1, h2, h3");
    
    foreach (var heading in headings)
    {
        var content = new StringBuilder();
        content.AppendLine(heading.TextContent);
        
        var sibling = heading.NextElementSibling;
        while (sibling != null && !sibling.TagName.StartsWith("H"))
        {
            content.AppendLine(sibling.TextContent);
            sibling = sibling.NextElementSibling;
        }
        
        chunks.Add(new ContentChunk
        {
            Heading = heading.TextContent.Trim(),
            Content = content.ToString().Trim(),
            HeadingLevel = int.Parse(heading.TagName[1..])
        });
    }
    
    return chunks;
}
```

Detta bevarar dokumentens struktur och gör valet mer meningsfullt.

---


## steg 4: välja

Det är här som de flesta misslyckanden sker.

Du har 5 chunks. Användaren frågade " Vilka förbättringar finns i .NET

```mermaid
flowchart TB
    Q["Question: What perf improvements?"] --> Score[Score Each Chunk]
    
    subgraph Chunks
        C1["Chunk 1: Overview..."] 
        C2["Chunk 2: Runtime perf..."]
        C3["Chunk 3: Libraries..."]
        C4["Chunk 4: SDK changes..."]
    end
    
    Score --> C1
    Score --> C2
    Score --> C3
    Score --> C4
    
    C2 -->|"score: 3"| Top[Selected]
    C3 -->|"score: 1"| Top
    
    style C2 stroke:#27ae60,stroke-width:3px
    style C1 stroke:#95a5a6,stroke-width:2px,stroke-dasharray: 5 5
    style C4 stroke:#95a5a6,stroke-width:2px,stroke-dasharray: 5 5
```

### Keyword matching (SvårtM SK1 Debuggbart)

```csharp
public record ScoredChunk(string Content, string? Heading, int Score, List<string> MatchedKeywords);

public List<ScoredChunk> SelectByKeywords(
    List<ContentChunk> chunks, 
    string question, 
    int topK = 3)
{
    // Normalize and filter stopwords
    var keywords = question.ToLower()
        .Split(' ', StringSplitOptions.RemoveEmptyEntries)
        .Where(w => w.Length > 3)
        .Where(w => !Stopwords.Contains(w))
        .Select(w => w.Trim(',', '.', '?', '!'))
        .Distinct()
        .ToList();
    
    var scored = chunks.Select(chunk =>
    {
        var text = (chunk.Heading + " " + chunk.Content).ToLower();
        var matched = keywords.Where(kw => text.Contains(kw)).ToList();
        
        // Boost if keyword appears in heading
        var headingBoost = chunk.Heading != null && 
            keywords.Any(kw => chunk.Heading.ToLower().Contains(kw)) ? 2 : 0;
        
        return new ScoredChunk(
            chunk.Content, 
            chunk.Heading, 
            matched.Count + headingBoost,
            matched
        );
    })
    .OrderByDescending(x => x.Score)
    .Take(topK)
    .ToList();
    
    // LOG THIS - it's your debugging lifeline
    foreach (var s in scored)
        Console.WriteLine($"  [{s.Score}] {s.Heading ?? "(no heading)"}: {string.Join(", ", s.MatchedKeywords)}");
    
    return scored;
}

private static readonly HashSet<string> Stopwords = new()
    { "what", "how", "does", "the", "are", "is", "in", "for", "of", "to", "and" };
```

**Nyckliga förbättringar över naivt räknande**:

- Stoppord: utlästing ( annars " vad?
- Upphöjning av titeln (strukturell signal
- **Att logga in matchade nyckelord** - det här är din lösningsmedel

### Embedding-Based (Semantic  )

Raktalbegreppet misslyckas på synonymer. "perfM SK2 vunnit

Embeddings hittar semantisk likhet. Om du vill gå djupare in på embeddings och vektorsökningar , Jag omger detta i [RAG- primer-serien](/blog/rag-primer) och [vetenskapliga sökningar med ONNX](/blog/semantic-search-with-onnx-and-qdrant).

```csharp
public async Task<List<ScoredChunk>> SelectByEmbedding(
    List<ContentChunk> chunks, 
    string question, 
    int topK = 3)
{
    var questionEmbed = await EmbedAsync(question);
    
    // Cache these per URL in production
    var scored = new List<(ContentChunk Chunk, double Score)>();
    
    foreach (var chunk in chunks)
    {
        var chunkEmbed = await EmbedAsync(chunk.Content);
        var similarity = CosineSimilarity(questionEmbed, chunkEmbed);
        scored.Add((chunk, similarity));
    }
    
    return scored
        .OrderByDescending(x => x.Score)
        .Take(topK)
        .Select(x => new ScoredChunk(x.Chunk.Content, x.Chunk.Heading, (int)(x.Score * 100), new()))
        .ToList();
}

private async Task<double[]> EmbedAsync(string text)
{
    var request = new EmbedRequest { Model = "nomic-embed-text", Input = [text] };
    var response = await _ollama.EmbedAsync(request);
    return response.Embeddings.First().ToArray();
}
```

**Världshandel**:

- Keywords : Fast, lösbart, bokstavlig matchning
- Embeddings: Semantik, men långsammare och kräver kakning

För produktionen av cache-inbäddar per (URL, chunk hash ) i SQLite eller en vektordatabas som [Qdrant](/blog/building-a-lawyer-gpt-for-your-blog-part4).

---


## Att skicka till LLM

Strukturera prompten för att tvinga källan

```csharp
public string BuildPrompt(string url, List<ScoredChunk> chunks, string question)
{
    var sb = new StringBuilder();
    
    sb.AppendLine("You are answering a question using ONLY the content below.");
    sb.AppendLine("Rules:");
    sb.AppendLine("- Answer ONLY from the provided sources");
    sb.AppendLine("- Cite which SOURCE number supports each claim");
    sb.AppendLine("- Include 1-2 brief quotes as evidence");
    sb.AppendLine("- If the answer isn't in the sources, say 'Not enough information'");
    sb.AppendLine("- End with Confidence: High/Medium/Low");
    sb.AppendLine();
    
    for (int i = 0; i < chunks.Count; i++)
    {
        sb.AppendLine($"=== SOURCE {i + 1} ===");
        if (chunks[i].Heading != null)
            sb.AppendLine($"Section: {chunks[i].Heading}");
        sb.AppendLine($"From: {url}");
        sb.AppendLine(chunks[i].Content);
        sb.AppendLine();
    }
    
    sb.AppendLine($"Question: {question}");
    sb.AppendLine();
    sb.AppendLine("Answer (with citations and confidence):");
    
    return sb.ToString();
}
```

Detta rör sig från "chatty summary" till "analys med provenance

### På tal om Ollama

```csharp
public async Task<string> AskAsync(string prompt)
{
    var request = new GenerateRequest { Model = "llama3.2:3b", Prompt = prompt };
    
    var response = new StringBuilder();
    await foreach (var chunk in _ollama.GenerateAsync(request))
    {
        if (chunk?.Response != null)
            response.Append(chunk.Response);
    }
    
    return response.ToString().Trim();
}
```

---


## Det här är verktygsanvändningen

Vad vi - ' - har byggt är ett agentpattern utan ramverket. [varför jag föredrar den här metoden än LangChain](/blog/why-i-dont-use-langchain) - explicit orkestering slår mot magiska abstraktioner när det handlar om att riva saker

```mermaid
flowchart LR
    subgraph Tools["Tools (Deterministic)"]
        T1[fetch_url]
        T2[clean_html]
        T3[chunk_text]
        T4[select_relevant]
    end
    
    subgraph LLM["LLM (Reasoning)"]
        R[Interpret + Answer]
    end
    
    T1 --> T2 --> T3 --> T4 --> R
    
    R -->|"Low confidence"| Retry[Retry with different selection]
    Retry --> T4
    
    style T4 stroke:#e74c3c,stroke-width:3px
    style R stroke:#3498db,stroke-width:3px
```

Loopet: om självförtroendet är lågt , pröva igen med fler chunks eller olika nyckelord

```csharp
var answer = await AskAsync(prompt);

if (answer.Contains("Not enough information") || answer.Contains("Confidence: Low"))
{
    // Retry with more chunks
    var moreChunks = SelectByKeywords(allChunks, question, topK: 5);
    answer = await AskAsync(BuildPrompt(url, moreChunks, question));
}
```

---


## Vad går fel

```mermaid
flowchart TB
    subgraph Failures["Failure Modes"]
        F1[Cleaner removes content]
        F2[Chunking breaks mid-thought]
        F3[Selection picks wrong chunks]
        F4[LLM hallucinates connections]
    end
    
    F1 --> R1["'Not enough info' - answer existed"]
    F2 --> R2["Partial answer - context lost"]
    F3 --> R3["Wrong answer - right content skipped"]
    F4 --> R4["Confident but wrong"]
    
    style F3 stroke:#e74c3c,stroke-width:3px
```

**Debugeringsregeln**:

> Om svaret är fel, så är det nästan alltid eftersom **urvalet var fel**, inte för att modellen misslyckades

### Debugging-kontrollen

1. **Protokollera ausgewählta chunk IDs och poäng**
2. **Wys vilka nyckelord som paskade** ( eller inbäddade avståndarM SK1
3. **Spegla den exakta prompten igen** som skickats
4. **jämföra**: Var den rätta innehållet i den landa HTML: n

Misslyckanden är vanligtvis "'", inte LLM .

---


## Att sätta ihop det

```csharp
public class WebAnalyzer : IDisposable
{
    private readonly WebFetcher _fetcher = new();
    private readonly HtmlCleaner _cleaner = new();
    private readonly OllamaApiClient _ollama = new(new Uri("http://localhost:11434"));
    
    public async Task<AnalysisResult> AnalyzeAsync(string url, string question)
    {
        // 1. Fetch
        var html = await _fetcher.FetchAsync(url);
        
        // 2. Clean (with observability)
        var cleaned = _cleaner.Clean(html);
        Console.WriteLine($"Cleaned: {cleaned.OriginalLength} → {cleaned.Text.Length} bytes ({cleaned.MatchedSelector})");
        
        // 3. Chunk
        var chunks = ChunkByHeadings(html);
        Console.WriteLine($"Chunks: {chunks.Count}");
        
        // 4. Select (with logging)
        Console.WriteLine("Selection scores:");
        var selected = SelectByKeywords(chunks, question, topK: 3);
        
        // 5. Prompt + LLM
        var prompt = BuildPrompt(url, selected, question);
        var answer = await AskAsync(prompt);
        
        return new AnalysisResult
        {
            Answer = answer,
            ChunksUsed = selected.Count,
            SelectionScores = selected.Select(s => s.Score).ToList()
        };
    }
    
    public void Dispose() => _fetcher.Dispose();
}
```

Användning med vårt körande exempel:

```csharp
using var analyzer = new WebAnalyzer();

var result = await analyzer.AnalyzeAsync(
    "https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-10/overview",
    "What performance improvements are in .NET 10?"
);

Console.WriteLine(result.Answer);
```

Utgången består av citationer och tillit

```
Based on SOURCE 1 and SOURCE 2:

.NET 10 includes several performance improvements:
- JIT improvements including better inlining and method devirtualization (SOURCE 1)
- "Enhanced loop inversion for better optimization" (SOURCE 1)
- NativeAOT enhancements for improved code generation (SOURCE 2)

Confidence: High
```

---


## När detta fungerar ( och gör det inte

| Fungerar bra | Funger inte' Fungeras inte |
|------------|--------------|
| Dokumentation
| bloggar
| Teknikreferenser | | | Multi | - | Page research ||
| Statiskt HTML | Authentifierad innehåll ≥|

För JS-heavy sites, behöver du [Dramatiker för .NET](https://playwright.dev/dotnet/).

---


## Nyckelprincipen

```mermaid
flowchart LR
    subgraph Your["Your Code's Job"]
        direction TB
        F[Fetch reliably]
        C[Clean carefully]
        S[Select correctly]
        O[Observe everything]
    end
    
    subgraph LLM["LLM's Job"]
        direction TB
        R[Reason over what you gave it]
        A[Admit when it doesn't know]
    end
    
    Your --> LLM
    
    style S stroke:#e74c3c,stroke-width:3px
    style R stroke:#3498db,stroke-width:3px
```

LLM kan bara resonera kring vad du gav det.

Nej, fråga inte LLM att surfa.

---


## Det här är ett helt projekt

Den kompletta fungerande implementeringen: [Mostlylucid.LlmWebFetcher](https://github.com/scottgal/mostlylucidweb/tree/main/Mostlylucid.LlmWebFetcher)

Inklusive:

- `WebFetcher` - HTTP med rätt behandling
- `HtmlCleaner` - Noisereduktion + Fallback-strategier
- `ContentChunker` - Sanning och mening
- `WebContentAnalyzer` - Hela pipelinen med spårning
- `OllamaExtensions` - Helper för strömningar

```bash
cd Mostlylucid.LlmWebFetcher
dotnet run
```

---


## resurser

**Bibliotek**

- [Kampsform](https://anglesharp.github.io/) - HTML-analys
- [Ollamascharp](https://github.com/awaescher/OllamaSharp) - Ollama klient

**LLMs**

- [Ollama](https://ollama.ai/) - Lokal LLM körtid
- [llama3.2:3b](https://ollama.ai/library/llama3.2) - Schnella , kapabel modell
- [nominering-belagd-text](https://ollama.ai/library/nomic-embed-text) - Embeddings ~275MBM SK2

**Related Articles**

- **[DocSummarizer-serien](/blog/building-a-document-summarizer-with-rag)** - ** FAR BETTER  -metoden** för web-inhaltsbeteckningar. Den här artikeln visar det enklaste sättet att få fram och analysera; DocSummarizer visar produktionen - klassificerad sätt med BERT extraktion M SK3 hybrid sökningar , validerade citationer
- [Att analysera stora CSV-Dater med lokala LLM](/blog/analysing-large-csv-files-with-local-llms) - Samma mönster
- [Varför använder jag inte LangChain](/blog/why-i-dont-use-langchain) - ramverket - design utan agent
- [RAG Primer](/blog/rag-primer) - Understanding retrieval-augmented generation
- [Semantiska sökningar med ONNX och Qdrant](/blog/semantic-search-with-onnx-and-qdrant) - Lokala semantiska sökningar
- [Att bygga en advokat GPT](/blog/building-a-lawyer-gpt-for-your-blog-part1) - Hela RAG implementeringsserien

**Microsoft AI-skivan**

- [Semantisk kärna](https://github.com/microsoft/semantic-kernel) - Om du vill ha ett ramverk
- [Microsoft.Extensioner.AI](https://devblogs.microsoft.com/dotnet/introducing-microsoft-extensions-ai-preview/) - Abstraktioner