Back to "Web-sisällön hakeminen ja analysointi LLM:ien kanssa C#"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

Agents AI C# LLM Systems Design

Web-sisällön hakeminen ja analysointi LLM:ien kanssa C#

Friday, 19 December 2025

Huomautukset: Tässä artikkelissa opetetaan verkkosisältöjen poiminnan perusteet LLM:ien avulla yksinkertaisin mahdollisin menetelmin. Tuotantokäyttötapauksissa (web-yhteenvedot, asiakirja-analyysit, agenttityökalut), ks. DocSummarizer GitHub-julkaisu - se toteuttaa tässä esiteltyä arkkitehtuuria, mutta tuotantoluokkaa: BERT-liitteet, hybridihaku, Playwright for SPAs, SSRF-suojaus, validoidut lainaukset ja asianmukainen kattavuusseuranta.

Kun pyydät ChatGPT:tä "lukemaan tämän artikkelin ja tiivistämään sen", mitä oikeasti tapahtuu? Jos kuvittelet tekoälyn avaavan selaimen ja lukevan niin kuin tekisit - niin se ei toimi.

LLM:t eivät selaile verkkoa, vaan pohtivat koodin valitsemia sirpaleita.

Tässä artikkelissa näytetään, kuinka voit rakentaa tämän C#-muodossa Ollamalla - ei kehyksiä, vain käytännön koodin, jonka voit debugata.

TL;DR: Koodisi noutaa → puhdistaa → pilkkuja → valitsee. LLM näkee vain pistämäsi sirpaleet. Valinta on suunnittelultaan häviöllinen - se on sekä rajoite että arkkitehtuuri.

Ainoa asia, joka saa tämän toimimaan

Tässä oivallus, joka muuttaa tapaa, jolla näitä järjestelmiä rakennetaan:

Valinta on tuotepäätös, josta myös suurin osa epäonnistumisista on peräisin.

LLM on alajuoksulla valinnassasi. Se ei saa takaisin tietoja, joita et näyttänyt. Kun agentti "ei löydä vastausta", ongelma ei ole juuri koskaan malli - valintalogiikkasi valitsi väärät osat. (Tämä on sama periaate taustalla.) miksi välttelen LangChainin kaltaisia kehyksiä - ne abstraktoivat pois valintalogiikan, jota sinun täytyy debugata.)

Siksi "agenttien selaaminen" epäonnistuu äänettömästi. Agentti vastaa itsevarmasti näkemänsä perusteella. Oikeaa sisältöä ei koskaan huomaa.

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

Malli voi vain päätellä, mitä annoit sille. Rakentakaa sen mukaan.


Suunnittelutavoitteet ennen koodia

Ennen kuin kirjoitat mitään, nämä ovat rajoitteet:

"Mitä väliä sillä on?" |------------|----------------| | Ei JS-renderöintiä Vain Static HTML (SPA:n pelaaja) | Kytketyt kuponkimerkit Kova budjetti per pyyntö (tyypillisiä 2-4K tokeneja) | Lähteen sitomat vastaukset LLM ei saa hallusinoida verkkotuntemusta | Määrittelevä valinta Sama syöttö → samat kappaleet (kiellettävissä) | Havaittavissa Kirjaa, mitä valitsit, miksi ja mitä hylkäsit

Jos et pysty selittämään, miksi kappale valittiin, et voi vianetsintää.


Putkijohto

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

Jokainen askel vähentää dataa. Kun LLM näkee sen, olet siirtynyt HTML:n 57 KB:stä ehkä 2 KB:hen. Jokainen vähennys on tappiollinen. Jokainen vähennys voi hylätä vastauksen. Tämä on sama kaava, jota käytän suurten CSV-tiedostojen analysointi - LLM-syyt, koodisi laskee ja valitsee.


Käytössä oleva esimerkki

Koko artikkelin ajan käytämme yhtä URL-osoitetta:

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

Ja kolme kysymystä, jotka koskevat lisääntyvää erityisyyttä:

  1. "Mitä uutta NET 10 -verkossa?"
  2. "Mitä suoritusparannuksia mainitaan?"
  3. "Onko AOT:sta mitään?"

Tämä pitää esimerkit pohjana ja osoittaa, miten valinnalla on enemmän merkitystä, kun kysymykset tarkentuvat.


Asetukset

# 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)

OllamaSharp-huomautus: versiossa 5.x GenerateAsync palautukset IAsyncEnumerable<GenerateResponseStream?> Se virtaa kuponkeja, kun niitä syntyy. await foreach. Näyteprojektin nastat 5.1.5.


Vaihe 1: Hae

Standardi HTTP, mutta yksityiskohdista, jotka ovat tärkeitä:

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

Se, mitä tämä käsittelee: Uudelleenohjaukset (katkaistu), pakkaus, aikalisät, Käyttäjä-agentti, sisällön tyyppivalidointi.

Mitä se ei tee: Javascript renderointi, aitous, nopeusrajoitus, robotit.txt. Tuotantoa varten lisää per-host viivästyksiä ja kunnioita craw-käytäntöjä.


Vaihe 2: Puhdas

Raw HTML on enimmäkseen melua. Puhdista se tai tuhlaat rahakkeita ulkoasuun.

Tyypillinen sivu:

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

Strategia

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

Siivous on varovaista

Siivoojasi voi ehdottomasti poistaa vastauksen.

  • Aggressiiviset valitsimet poistavat laillista sisältöä
  • Pääsisällöllä on epätavallisia luokkanimiä (.article-body, .post-text)
  • Vastaus on sivupalkissa tai soittolaatikossa

Vähennykset:

  • Pidä otsikot selkeästi (h1-h6)
  • Poista rooli sekä tunnettu kattilalevy, ei leveitä kuvioita
  • Loki: uutettu alkuaineen polku + tekstin pituus
  • Varautus: jos pääsisältö on epäilyttävän lyhyt, kokeile kehoa
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);

Readability nouto (kappaleiden korjuu, tekstin tiheys) on kokonainen kaninreikä. Tällä hetkellä valikointipohjaiset uuttotyöt dokumentointia ja blogeja varten. Näyteprojektiin kuuluu tarvittaessa pisteytysuutto.


Vaihe 3: Chunk

Sinulla on 6KB puhdasta tekstiä, mikset lähettäisi kaikkea?

  • Neula heinäsuovassa: LLM:t suoriutuvat huonommin, kun relevantit tiedot haudataan suuriin kontekstiin
  • Kustannukset: Lisää kuponkeja = lisää rahaa ja latenssia
  • Keskittyminen: Valitut kappaleet tuottavat parempaa ajattelua kuin kaikki kerralla

"Strategiikalla on enemmän merkitystä kuin arvaatkaan. RAG-arkkitehtuuria koskeva artikkeli - samat periaatteet pätevät riippumatta siitä, pilkotko nettisivuja tai asiakirjoja.

Naiivi tuomio nuuskimassa

Tämä on lähtökohta, ei tuotantokoodi:

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

Miksi tämä on naiivia:

  • ". " taukoja "Dr. Smith", "v1.0", URL-osoitteet
  • Token-arvio sanamäärällä on ±20 % miinuksella
  • Ei päällekkäisyyksiä kappaleiden välillä (hävittää kontekstin rajoilla)

Parempi: Otsikkopohjainen nuuskiminen

Dokumentaatioita varten kappaleittain:

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

Näin säilytetään asiakirjarakenne ja tehdään valinnasta merkityksellisempää.


Vaihe 4: Valitse

Suurin osa epäonnistumisista tapahtuu täällä ja vianetsintä aloitetaan täällä.

Sinulla on viisi kappaletta. Käyttäjä kysyi "Mitä suorituskyvyn parannuksia on .NET 10 -verkossa?" Vain 1-2 kappaletta mainitsee suorituskyvyn. Lähetä ne.

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

Avainsanat täsmäävät (yksinkertaiset, tunnistettavat)

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

Keskeisiä parannuksia naiiviin laskemiseen:

  • Lausekkeen poistaminen (muuten "mikä" ja "mikä" täsmää aina)
  • Suunnan nosto (rakennesignaali)
  • Kirjaus täsmäsi avainsanoihin Tämä on vianetsintäsi pelastusköysi.

Upotettava (semanttinen)

Asiasanat epäonnistuvat synonyymeissä. Perf ei vastaa suoritusparannuksia.

Jos haluat syventää upotuksia ja vektorihakua, käsittelen tätä laajasti RAG-perussarja sekä semanttinen etsintä ONNX:llä.

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

Kaupankäynti:

  • Asiasanat: Nopea, kiistanalainen, kirjaimellinen täsmäytys
  • Upotukset: Semanttinen, mutta hitaampi ja väliseinää kaipaava

Tuotantoa varten välimuisti upotetaan SQLite-tietokantaan (URL, bulk hash) tai vektoritietokantaan, kuten Qdrant.


Lähetetään LLM:ään

Rakenna ripeys pakottaa lähteeseen sidottuja vastauksia viittaamalla:

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

"Tyylikkäästä tiivistelmästä" siirryttiin "analyysiin alkuperän kanssa".

Ollaman kutsuminen

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

Tämä on työkalun käyttö

Olemme rakentaneet agenttikuvion ilman kehystä. Miksi pidän enemmän tästä lähestymistavasta kuin LangChainista? - selkeä orkestrointi voittaa taikaobstraktiot, kun on kyse vioista.

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

Looppi: Jos itseluottamus on heikkoa, kokeile lisää kappaleita tai erilaisia hakusanoja.

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

Mikä menee pieleen

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

Vianetsintäsääntö:

Jos vastaus on väärä, se johtuu lähes aina siitä, että valinta oli väärä, ei siksi, että malli olisi epäonnistunut.

Vianetsintätarkistuslista

  1. Lokilla valitut lohkon tunnisteet ja pisteet
  2. Näytä, mitkä avainsanat täsmäävät (tai upotusetäisyydet)
  3. Toista tarkka pikavippi joka lähetettiin
  4. VertaaOliko oikea sisältö haetussa HTML:ssä? Puhdistettua tekstiä?

Yleensä vika ei ole LLM, vaan yläjuoksulla.


Sen kokoaminen yhteen

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

Käytä juoksuesimerkkiämme:

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

Tuotteeseen kuuluvat lainaukset ja luottamus:

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

Kun tämä toimii (ja ei toimi)

Toimii hyvin, ei toimi |------------|--------------| Dokumentaatio JavaScript SPAs Blogit, artikkelit Dynaaminen/vuorovaikutteinen sisältö Tekniset viitetiedot Monisivuinen tutkimus Staattinen HTML Virallinen sisältö

JS-koville sivustoille tarvitset Pelaaja .NETille.


Avainperiaate

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 voi vain päätellä, mitä annoit sille. Valinta on sinun vastuullasi.

Älä pyydä LLM:ää selaamaan vaan järkipuhetta.


Täyden näytteen projekti

Työskentelyn täydellinen toteutus: Enimmäkseen lucid.LlmWebFetcher

Sisältää:

  • WebFetcher - HTTP kunnollisella käsittelyllä
  • HtmlCleaner - Melun poisto + varastrategiat
  • ContentChunker - Tuomittu ja orientoitu poraus
  • WebContentAnalyzer - Putket täynnä puunkorjuuta
  • OllamaExtensions - Auttaja vastausten suoratoistoon
cd Mostlylucid.LlmWebFetcher
dotnet run

Resurssit

Kirjastot

LLM-yhdisteet

Aiheeseen liittyvät artikkelit

Microsoft AI Stack

logo

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