# 使用 C# 中的 LLMM 获取和分析网络内容

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

> **注**:本条用最简单的方法讲授网络内容提取的基本内容,使用LLMs与LLMs使用最简单的方法。 **[DocSummamer 缩写器](/blog/building-a-document-summarizer-with-rag)** [![释放GitHub](https://img.shields.io/github/v/release/scottgal/mostlylucidweb?filter=docsummarizer*&label=docsummarizer)](https://github.com/scottgal/mostlylucidweb/releases?q=docsummarizer) - 它执行此处显示但生产级显示的结构:BERT嵌入、混合搜索、SPA的普layright、SSRF保护、经验证的引用和适当的覆盖跟踪。

当你问ChatgPT“阅读和总结这篇文章”时,到底发生了什么?如果你想象人工智能打开浏览器,像你那样阅读那不是它的工作方式。

**LLMs不会浏览网络,他们会为代码的碎片找理由**

这篇文章教您如何在 C # 与 Ollama 构建这个框架, 只是实用代码, 您可以调试 。

> **TL; DR TR; TL; TDR**: 您的代码获取 ‘ 清除 ’ 块 ’ 选择 。 LLM 只看到您输入的碎片 。 选择因设计而丢失 - 即限制 和 结构 。

[TOC]

## 使这项工作奏效的一件事情

以下是你如何建立这些系统的洞察力:

**选择是产品决定。它也是大多数失败的来源。**

LLM 是您选择的下游 。 它无法收回您没有显示的信息 。 当代理“ 无法找到答案” 时, 问题几乎从来不是模型  问题在于您的选择逻辑选择错误的块 。 (这是相同的原则 ) [为什么我避开兰钱这样的框架](/blog/why-i-dont-use-langchain) - 他们抽象地删除了选择逻辑 你需要调试。 )

这就是为什么“代理浏览”无声无息地失败。 代理根据它所看到的, 充满信心的回答。 你永远不知道它错过了正确的内容 。

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

模型只能说明你给了它什么。

---


## 《守则前设计目标》

在撰写任何文件之前,这些是制约因素:

限制 为何重要
|------------|----------------|
| **没有联署材料** 只有静态 HTML (SPA的玩家)
| **环形符号** 每个请求的硬预算(2至4K象征性的典型) 
| **源源解答** LLM不能让网络知识产生幻觉
| **确定性选择** 相同的输入  相同的块 (可调试)
| **可观察** 记录你选择什么,为什么, 和你抛弃什么

如果您无法解释为什么选中块块, 您无法调试失败 。

---


## 管道

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

每一步都会减少数据。 当LLM看到数据时, 您已经从 HTML 的 57 KB 增加到相关文本的大约 2KB 。 每一次减少都会丢失。 每一次减少都会丢弃答案 。 这与我所用的模式相同 。 [分析大型 CSV 文件](/blog/analysing-large-csv-files-with-local-llms) - LLM的原因,你的代码计算和选择。

---


## 运行中实例

在整个文章中,我们将使用一个 URL:

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

还有三个越来越具体的问题:

1. "什么是新的在. NET 10?"
2. "提到哪些业绩改进?"
3. "有关于奥特的事吗?"

以此来维持这些实例的基础,并显示随着问题变得具体,选择更为重要。

---


## 设置设置设置设置设置设置设置

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

> **OllamaSharp 注释**:5.x版本, `GenerateAsync` 返回返回返回 `IAsyncEnumerable<GenerateResponseStream?>` - 当它们产生时,它们会流传象征物。 `await foreach`抽样项目针5.1.5。

---


## 第1步:获取

标准HTTP,但细节很重要:

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

**这是什么手柄**: 重定向(上限)、压缩、超时、用户代理、内容类型验证。

**是什么,它没有**: JavaScript 映射、 认证、 限速、 机器人. txt。 对于生产, 增加每个主机的延迟, 并尊重爬行政策 。

---


## 第2步:清洁

Raw HTML 主要是噪音。 清理它或者在布局上浪费标牌 。

典型页面 :

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

### 战略 " 战略 " 战略 " 战略 " 战略 " 战略 " 战略 " 战略 " 战略 " 战略 " 战略 " 战略 " 战略 " 战略 " 战略 " 战略 " 战略 "  "  "

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

### 清洁失利 -- -- 谨慎处理

你的清洁工可以完全删除答案 常见错误:

- 侵略选择器删除合法内容
- 主要内容有不寻常的类名(`.article-body`, `.post-text`)
- 答案在侧边栏或图例框中

**缓解措施**:

- 明确保留标题(`h1`-`h6`)
- 删除时间 *角色角色角色角色角色角色角色角色角色角色角色* 和 *已知锅炉板*而非广义模式
- 日志: 提取元素路径+文字长度
- 后退: 如果主内容短得令人怀疑, 试体

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

> **可读性提取** (校验段落、文字密度) 是一个完整的兔子洞。 目前, 基于选择器的提取方法可以用于文档和博客。 样本项目包括一个评分提取器, 如果您需要的话 。

---


## 步骤3:整块

你有6KB的干净文本 为什么不全部寄出去?

- **干草堆中的针头**:当相关的信息被掩埋在大背景中时,LLM女士的表现更差
- **成本成本成本成本成本**: 更多的象征 = 更多的金钱和延缓
- **焦点**:选定的块块会比每件事情同时得到更好的推理

踢踏策略比你预期的要重要得多。 [RAG 建筑结构条款](/blog/rag-architecture) - 无论你把网页或文件打成块,同样的原则都适用。

### Naive 判决决赛

这是一个起点,不是生产代码:

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

**为何如此天真**:

- `". "` 在“史密斯医生”、“V1.0”、“URLs”上
- 按字数计算 Tok 的 Tok 估计值为 + 20% 关闭
- 块之间没有重叠( 关闭边框的上下文)

### 更好:以标题为主的决赛

对于文件,按节块块:

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

这保留了文件结构并使选择更具意义。

---


## 第4步:选择

这是大多数失败发生的地方。 并且调试应该从那里开始。

您有 5 块。 用户询问“ . NET 10 中的性能改进是什么? ” 只有 1 2 块提到性能。 发送这些 。

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

### 关键字匹配( 简单、 可调试)

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

**与天真计算相比的主要改进**:

- 删除末字( 否则“ 什么” 和“ 总是匹配)
- 助推(结构信号)
- **登录匹配的关键字** - 这是你的调试生命线

### 嵌入式( 语义)

关键词在同义词上失败。 “ perf” 与“ 性能改进” 不符 。

嵌入器发现语义相似性。 如果您想要更深入嵌入和矢量搜索, 我将在 [RAG 开源系列](/blog/rag-primer) 和 [使用 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();
}
```

**取舍**:

- 关键词:快速、可调试、字面匹配
- 嵌入:语义,但慢,需要缓缓

生产时, SQLite 或一个矢量数据库中每个(URL、块散列)的缓存嵌入(URL、块散列) [解冻](/blog/building-a-lawyer-gpt-for-your-blog-part4).

---


## 发送到LLM

以引证为根据的快速强制答复的结构:

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

这从“粗略摘要”到“从源头分析”的转变。

### 呼叫奥拉马

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

---


## 这是工具使用工具

我们所建立的是一个没有框架的代理模式。 [为什么我更喜欢这个方法 而不是兰钱](/blog/why-i-dont-use-langchain) - 明确的管弦在调试时比魔法抽象要强得多。

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

循环: 如果信任度低, 则使用更多块或不同关键字重试 。

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

---


## 哪里出错

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

**调试规则**:

> 如果答案是错的, 几乎总是因为 **选择错误**不是因为模型失败

### 调试检查列表

1. **日志选中的块 ID 和分数**
2. **显示符合哪些关键字** (或嵌入距离)
3. **重放精确提示** 所发送的
4. **比较比**: 获取的 HTML 中的正确内容是否正确? 清洁文本? 有块吗 ?

失败通常不是LLM 而是上游

---


## 将它放在一起

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

使用我们的运行示例 :

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

产出包括引文和信任:

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

---


## 当这工作(和不工作)

工作顺利 工作不工作
|------------|--------------|
* 文献 * JavaScript SPA * * 文档 * JavaScript SPA * * 文档 * JavaScript SPA * 文档 * JavaScript SPA * 文档 * JavaScript SPA * 文档 * JavaScript SPA * 文档 * JavaScript SPA *
部落格文章、文章、动态/互动内容
* 技术参考资料 * 多页研究 *


对于JS - 重的场地,你需要 [.NET 的播放器](https://playwright.dev/dotnet/).

---


## 关键原则

```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浏览 找理由

---


## 完整抽样项目

完成工作执行: [最湿润的 LlmWebFreacher](https://github.com/scottgal/mostlylucidweb/tree/main/Mostlylucid.LlmWebFetcher)

包括:

- `WebFetcher` - 妥善处理的HTTP
- `HtmlCleaner` - 消除噪音+后退战略
- `ContentChunker` - 判刑和基于标题的块块
- `WebContentAnalyzer` - 通通输油管道,并进行伐木
- `OllamaExtensions` - 流流响应帮助者

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

---


## 资源资源资源 资源资源资源 资源资源 资源资源

**图书馆图书馆**

- [闪闪角角](https://anglesharp.github.io/) - HTML 解析
- [奥利亚马沙尔普](https://github.com/awaescher/OllamaSharp) - 奥亚马客户

**LLM 立 体**

- [奥拉马](https://ollama.ai/) - 当地LLM运行时间
- [3.2.3:3b](https://ollama.ai/library/llama3.2) - 快速、能力强型号
- [混血文字](https://ollama.ai/library/nomic-embed-text) - 嵌入(~27500MB)

**相关条款**

- **[DocSummaizer 丛书](/blog/building-a-document-summarizer-with-rag)** - **方法的改进** 用于网络内容摘要。 这篇文章展示了最简单的获取和分析方式; DocSummarizer 展示了BERT提取、混合搜索、验证引用和适当覆盖跟踪的生产级别方式。
- [使用本地LLMS 分析大型 CSV 文件](/blog/analysing-large-csv-files-with-local-llms) - 同样模式:LLM原因,代码计算
- [为什么我不用兰钱](/blog/why-i-dont-use-langchain) - 无框架制剂设计
- [RAG 开源器](/blog/rag-primer) - 了解检索-增强一代人
- [使用 ONNX 和 Qdrant 搜索语义](/blog/semantic-search-with-onnx-and-qdrant) - 局部语义搜索
- [a 建造律师GPT](/blog/building-a-lawyer-gpt-for-your-blog-part1) - 全面RAG实施系列

**微软 AI 堆叠**

- [语义内核Name](https://github.com/microsoft/semantic-kernel) - 如果你想要一个框架
- [微软扩展。 AI](https://devblogs.microsoft.com/dotnet/introducing-microsoft-extensions-ai-preview/) - 抽象