# 图图RAG 部分 *2: 最低可视图形

<datetime class="hidden">2025-12-27T14:00</datetime>

<!-- category -- ASP.NET, GraphRAG, DuckDB, Vector Search, Machine Learning, Knowledge Graphs -->
内 [部分#1](/blog/graphrag-knowledge-graphs-for-rag)“,”我们探究了为什么“Gagrag 很重要”,“.”现在让我们来“'”构建一个 **最低可行最低限度** 具有三种提取模式@: @%

| 模式 @ MS K1 @ LLM 呼叫@|# 最佳为 @|}
|------|-----------|----------|
| **华量主义** (}默认=)}|}0}每片块 |}快速索引 *,}结构化标记点
| **混合混合物** @| @ @ 1}每份文件 @ I| @ 平衡速度和质量 @ MPK3#
| **法学硕士** 最大实体质量 * @|*

所有模式都使用 @ : @%

- **鸭子数据库** 在单个文件中统一存储 {(\ +} 图
- **BM=25 +BERT混合搜索** 通过RRF聚集
- **奥拉马** 合成和可选分批分类: (-零API成本=)

**系列导航:**

- [1: 图表RAG基本原理](/blog/graphrag-knowledge-graphs-for-rag)
- **“2: 最低可视图形”部分** @(_这篇文章#)}

> **代码=:** [多数液化.+GraphRag](https://github.com/scottgal/mostlylucidweb/tree/main/Mostlylucid.GraphRag) 吉特胡卜山

[TOC]

## 建筑结构概览

```mermaid
flowchart LR
    subgraph Indexing
        MD[Markdown Files] --> CH[Chunker]
        CH --> EMB[BERT Embeddings]
        CH --> EXT[Entity Extractor]
        EXT --> |heuristics + links| ENT[Entities]
        ENT --> REL[Relationships]
        REL --> COM[Communities]
    end
    
    subgraph Storage
        EMB --> DB[(DuckDB)]
        ENT --> DB
        REL --> DB
        COM --> DB
    end
    
    subgraph Query
        Q[Query] --> CLASS{Classify}
        CLASS --> |local| HS[Hybrid Search]
        CLASS --> |global| CS[Community Search]
        CLASS --> |drift| BOTH[Both + Synthesis]
        HS --> LLM[Ollama]
        CS --> LLM
        BOTH --> LLM
    end
    
    DB --> HS
    DB --> CS
    
    style MD stroke:#22c55e,stroke-width:2px
    style DB stroke:#3b82f6,stroke-width:2px
    style LLM stroke:#a855f7,stroke-width:2px
```

## 为什么鸭子 DuckDB?

微软's GrapRAG 使用单独的矢量存储@(LanceDB),}实体 @(ParquetMS K4和关系 *(more Parquett}). DuckDB 简化此\:{

- 单單 `.duckdb` 用于所有文件的文件
- 通过 VSS 扩展的本地 HNSW 矢量搜索
- SQL 用于矢量搜索和图形交叉
- 零部署复杂性

**DuckDB 不是一个图形数据库@ - @ 和\ MS K1}%s the point=.** 这是't Neo 4j. Traversals 是浅浅的{,SQL-}基于{MS K5和深思熟虑的\.}这个限制使得系统可以调试且廉价}.}

## 存储方案

Schema 用途 **出处的合并表格** @-}我们可查询"}其中部分提及实体 X#?"}直接查询□:}

```mermaid
erDiagram
    documents ||--o{ chunks : contains
    chunks ||--o{ entity_mentions : has
    chunks ||--o{ relationship_mentions : has
    entities ||--o{ entity_mentions : mentioned_in
    entities ||--o{ relationships : source
    entities ||--o{ relationships : target
    relationships ||--o{ relationship_mentions : mentioned_in
    communities ||--o{ community_members : contains
    entities ||--o{ community_members : belongs_to
    
    chunks {
        varchar id PK
        varchar document_id FK
        text text
        float[] embedding
    }
    
    entities {
        varchar id PK
        varchar name
        varchar type
        int mention_count
    }
    
    entity_mentions {
        varchar entity_id FK
        varchar chunk_id FK
    }
```

关键设计决定 **无 无 `VARCHAR[]` 出处**“.”合并表格“(”`entity_mentions`, `relationship_mentions`@ )_ 启用有效的查询, 如@ MS K1 get all produces 提及 Docker@ MPK2}

### 矢量搜索@: @HNSW

DuckDB 's HNSW 指数仅触发 `array_cosine_distance` + `ORDER BY` + `LIMIT`:

```csharp
// GraphRagDb.cs - SearchChunksAsync
cmd.CommandText = $"""
    SELECT id, document_id, text, chunk_index, 
           array_cosine_distance(embedding, $1::FLOAT[{_dim}]) as distance
    FROM chunks 
    WHERE embedding IS NOT NULL
    ORDER BY distance
    LIMIT $2
    """;
// Convert distance to similarity: 1.0f - distance
```

**使用 `array_cosine_similarity` 不使用索引** -它贏了@’t 啟動 HNSW index @.在非 MS K3trivial corpora,}這會轉換一個「~5ms 索引化的查詢成完整的表格 scan_.#

## 开采实体

这是我们与微软的 MSMK0Qs 方法相左的地方 @.\% **LLM-PER-Chunk 抽取通行证** 用于 微软'}参考图示RAG proper *,}我们使用的 **以统计提取为基础**目标为#'}完美实体: @- 200)}It_'*s *稳定的,production-相对信号* 需要使用LLM来制作.,这样可以让某些人回忆起确定性-, -可审计性-I, -和可预见的成本--

```mermaid
flowchart TB
    subgraph "Phase 1: Signal Collection"
        TEXT[All Chunks] --> IDF[Compute IDF Scores]
        TEXT --> STRUCT[Structural Signals]
        STRUCT --> HEAD[Headings]
        STRUCT --> CODE[Inline Code]
        STRUCT --> LINKS[Links]
        IDF --> RARE[High-IDF = Rare Terms]
        RARE --> CAND[Candidates]
        HEAD --> CAND
        CODE --> CAND
        LINKS --> |explicit rels| LINKREL[Link Relationships]
    end
    
    subgraph "Phase 2: Dedup"
        CAND --> EMBED[BERT Embeddings]
        EMBED --> SIM[Similarity > 0.85]
        SIM --> MERGE[Merge Duplicates]
    end
    
    subgraph "Phase 3: Classify"
        MERGE --> LLM{LLM Available?}
        LLM --> |yes| BATCH[Single Batch Call]
        LLM --> |no| HEUR[Heuristic Types]
    end
    
    style IDF stroke:#f59e0b,stroke-width:2px
    style BATCH stroke:#a855f7,stroke-width:2px
```

### 为何没有硬码名单?

天真的方法是硬码的 `HashSet<string> KnownTech = { "Docker", "Kubernetes", ... }`.此间歇为 @:@

- 需要更新列表“)”
- 不同的实体 @)
- 拼错和变异

**以色列国防军 @(#Inverse Document 频率@)** 在统计上解决此问题@ . @ a term='_%s 以国防军是:}#

$$\Text{ID}(tMPK3=MS K5WG}\Frac}MS K7N}{dfMSKO9T{)}$$

在哪里:

- @$N$} @ =}总块数
- $_t$}包含此术语的文档

高以色列国防军 @= **极值** “=”可能是一个实体“.”、“"”、“Docker @" ”出现在“MS K4 ”中,

更多有关TF @-#IDF和BM_25,>的更多信息,请见我的文章 [使用 BM25 进行混合搜索](/blog/rag-hybrid-search-and-indexing).

### 结构信号

Markdown结构告诉我们什么\"'"重要"\"I:\"

- **标题标题标题** (`## Docker Setup`* →实体*
- **内嵌代码** (`` `docker-compose` ``* →实体*
- **链接链接链接** (`[Docker](https://docker.com)`@) @ @ →_ 实体 @ I+} 关系

```csharp
// EntityExtractor.cs - structural signal extraction
private void ExtractStructuralEntities(string chunk, string chunkId)
{
    // Headings: ## Docker Compose Setup → "Docker Compose Setup"
    foreach (Match m in Regex.Matches(chunk, @"^#{1,3}\s+(.+)$", RegexOptions.Multiline))
    {
        var heading = m.Groups[1].Value.Trim();
        AddCandidate(heading, chunkId, weight: 2.0); // Higher weight
    }
    
    // Inline code: `docker-compose` → "docker-compose"
    foreach (Match m in Regex.Matches(chunk, @"`([^`]+)`"))
    {
        AddCandidate(m.Groups[1].Value, chunkId, weight: 1.5);
    }
}
```

### Link Explication on @(_High#-}品质关系=)

提供标记下链接 **明确** 需要LLM 推断@: @%

```csharp
// EntityExtractor.cs - ExtractLinks  
foreach (Match m in Regex.Matches(chunk, @"\[([^\]]+)\]\((/blog/[^)]+)\)"))
{
    var linkText = m.Groups[1].Value;  // "semantic search"
    var slug = m.Groups[2].Value;       // "/blog/semantic-search-with-qdrant"
    yield return new Relationship(linkText, $"blog:{slug}", "references", chunkId);
}
```

### 通过 BERT 嵌入嵌入的演化

实体名称, 如 @ " @ dockercompropose @ MPK1 @ I"docker@ MS K3compose+",} 和 @ mskK5DockerCompose@"应该合并@ OMK7我们使用 **BERT 嵌入** 用于检测语义相似性@: @%

```csharp
// EntityExtractor.cs - DeduplicateAsync
var embeddings = await _embedder.EmbedBatchAsync(candidates.Select(c => c.Name), ct);

for (int i = 0; i < candidates.Count; i++)
{
    for (int j = i + 1; j < candidates.Count; j++)
    {
        var similarity = CosineSimilarity(embeddings[i], embeddings[j]);
        if (similarity > 0.85)
        {
            // Merge into canonical entity (keep higher mention count)
            canonical.MentionCount += duplicate.MentionCount;
            canonical.ChunkIds.UnionWith(duplicate.ChunkIds);
        }
    }
}
```

这一步是O(n ²)在捆绑的候选人框内,但候选人数受以色列国防军过滤和结构信号的束缚。 <{-不是人身大小@.关于BERT嵌入的详情*,}请参见 [用 ONNX 和 BERT 进行语义搜索](/blog/semantic-search-with-onnx-and-qdrant).

## 采掘模式

CLI 支持三种采掘方式 `--extraction-mode`:

### 超常模式 @ ( @ default@ MS K1 @

```bash
dotnet run --project Mostlylucid.GraphRag -- index ./Markdown --extraction-mode heuristic
```

使用以国防军的“+”结构信号进行实体检测; “,”采用可选 LLM 批量分类;“MS K2” **每千分之零 -chunkLLM电话** @- Τ~1筿杠 50( 砰) 闽 .

### 混合模式 @(}推荐@)

```bash
dotnet run --project Mostlylucid.GraphRag -- index ./Markdown --extraction-mode hybrid
```

两个世界最优秀的

1. **体温检测**机构信号寻找实体候选人
2. **提高LLLM**: / 一次呼叫 *文档文档文档文件* 验证实体并提取语义关系

```mermaid
flowchart LR
    subgraph "Per Document"
        CHUNKS[Document Chunks] --> HEUR[Heuristic Extraction]
        HEUR --> CAND[30 Candidates]
        CAND --> LLM[Single LLM Call]
        LLM --> ENT[Validated Entities]
        LLM --> REL[Semantic Relationships]
    end
    
    style HEUR stroke:#22c55e,stroke-width:2px
    style LLM stroke:#a855f7,stroke-width:2px
```

用于 & M5}文档的“ 62” 块@ MS K2 ” 混合模式 ***5LLM电话*** -=YTET -伊甸园字幕组=- 翻译:

- 具有确定性的实体的超经济主义覆盖范围
- LLM- 质量关系提取\ (\ semantic,\ 不只是CONSK3\ occurence})
- 说明和审定类型

### LLM 模式 @( @ 微软@ -_ style})

```bash
dotnet run --project Mostlylucid.GraphRag -- index ./Markdown --extraction-mode llm
```

完整的微软 GraphRAG 接近@: **2+LLM每块呼叫** (实体提取@+关系提取 @).最昂贵}但无结构文本质量最高

### 何时使用

| 模式 @ MS K1 @ LLM 呼叫@|# 最佳为 @|}
|------|-----------|----------|
| **华量主义** “| ~1 每个“50实体” @|快速索引@,好#-有条理的标记 {|
| **混合混合物** 每份文件的| 平衡覆盖面和质量 *|*
| **法学硕士** |{2/ 每块 @|无结构的插曲}, 最高品质@|

对于技术文档来说, “,” 开始于 **混合混合** 模式@. 它给予您语义关系, 而没有 per-chunk cost *. **超经济** 用于纯速@,}或 **千米** 用于描述文本@. @%

## 混合搜索@: @ BM25} @ +_BERT

混合搜索结合了两种互补的方法 @:

**{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}高音** 理解意指@. @ @ @ "# Docker 容器 @ MPK2_ 匹配 @ I" @ containerization@ MS K4}
**粗略的“(”BM25):** 符合精确条件@. @ @"#HNSW}"只匹配\"HNSZ".

```mermaid
flowchart LR
    Q[Query] --> BERT[BERT Embedding]
    Q --> BM25[BM25 Tokenize]
    
    BERT --> DENSE[Dense Search<br/>HNSW Index]
    BM25 --> SPARSE[Sparse Search<br/>TF-IDF Scoring]
    
    DENSE --> RRF[RRF Fusion]
    SPARSE --> RRF
    
    RRF --> TOP[Top K Results]
    TOP --> ENR[Enrich with<br/>Entities + Rels]
    
    style RRF stroke:#f59e0b,stroke-width:2px
```

### 什么是BM25?

BM25 (Best 匹配\ 25)}基于答题频率的分数文档_.}公式{:}

$$\Text{Score}(D□,MS K4=MSKO6Sump_{ MS K20QQKK21i MS22D) +KZKKK25 MS26KK26C多T(1K27 MS28KKBK29BMK29b\cdot

关键直觉

- **工龄**@: @rare words matter more #("#HNSW#"} @MS K3#"#")#
- **TF 饱和度**“:”字表示“10”字是‘'’字;“10”字比 1x更相关。
- **长度正常化**@:# 长文件@ '} 不会得到不公平的优势

完整 BM25 执行@,见 [混合查找和索引](/blog/rag-hybrid-search-and-indexing).

### 相互融合 (RRF)

RRF将不同检索系统的排名合并 . 每个级别职位都得分*:

$$\Text{RRF}}(d) MS K4 NSK5Sump_{r @\inR}#\sfrac}{1}{k@+{(d□)}$$#

在“$”和“(”中, “MSK2 ”通常在“MSC3”中防止顶部结果超量加权@.”文件中出现的文档 **两者兼** 排名获得提升@: @%

```csharp
// SearchService.cs - RRF fusion
const int k = 60;

foreach (var (chunk, rank) in denseResults.Select((c, i) => (c, i)))
    scores[chunk.Id] = 1.0 / (k + rank + 1);

foreach (var (chunk, rank) in sparseResults.Select((c, i) => (c, i)))
{
    var rrfScore = 1.0 / (k + rank + 1);
    if (scores.TryGetValue(chunk.Id, out var existing))
        scores[chunk.Id] = existing + rrfScore;  // Boost for appearing in both!
    else
        scores[chunk.Id] = rrfScore;
}
```

缩略语@: 文档在密密度中位居 @#1, 在稀树枝中位列{#3

- {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}
- {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2款
- **合并: 0.0323** @(#higher 都比独自一人高 #)#

## 查询模式

```mermaid
flowchart TB
    Q[Query] --> CLASS[Classify Query]
    
    CLASS --> |"How do I use X?"| LOCAL[Local Search]
    CLASS --> |"What are the themes?"| GLOBAL[Global Search]  
    CLASS --> |"How does X relate to Y?"| DRIFT[DRIFT Search]
    
    LOCAL --> HS[Hybrid Search] --> CTX1[Chunk + Entity Context]
    GLOBAL --> CS[Community Summaries] --> MAP[Map-Reduce]
    DRIFT --> BOTH[Local + Communities] --> SYN[Synthesize]
    
    CTX1 --> LLM[LLM Answer]
    MAP --> LLM
    SYN --> LLM
    
    style LOCAL stroke:#22c55e,stroke-width:2px
    style GLOBAL stroke:#3b82f6,stroke-width:2px
    style DRIFT stroke:#a855f7,stroke-width:2px
```

### 查询分类

```csharp
// QueryEngine.cs
private static QueryMode ClassifyQuery(string query)
{
    var q = query.ToLowerInvariant();
    if (q.Contains("main theme") || q.Contains("summarize") || q.Contains("overview"))
        return QueryMode.Global;
    if (q.Contains("relate") || q.Contains("connect") || q.Contains("compare"))
        return QueryMode.Drift;
    return QueryMode.Local;
}
```

此分类器刻意简单 {-} 并且很容易用一个小意图模型替换。 如果没有任何实体匹配\,}, 这个系统会干净地降解为纯混合检索@.}

## CLI 使用率

### 索引编制

```bash
# Heuristic mode (default) - fast, no per-chunk LLM
dotnet run --project Mostlylucid.GraphRag -- index ./test-markdown

# LLM mode - Microsoft-style classification
dotnet run --project Mostlylucid.GraphRag -- index ./test-markdown --extraction-mode llm
```

```text
GraphRAG Indexer
  Source: test-markdown
  Database: graphrag.duckdb
  Model: llama3.2:3b
  Extraction: Heuristic (IDF + signals)

Initializing...
Indexing docker-development-deep-dive.md: 0%
Indexing docker-swarm-cluster-guide.md: 40%
Indexing dockercomposedevdeps.md: 80%
Indexing complete: 100%
Classifying entities...: 0%
Extracted 168 entities, 315 rels (4 LLM calls): 100%
Found 10 communities: 100%
Summarizing c_0_2 (12 entities): 20%
Summarizing c_0_8 (4 entities): 80%

────────────────── Indexing Complete ───────────────────
┌───────────────┬───────┐
│ Metric        │ Count │
├───────────────┼───────┤
│ Documents     │ 5     │
│ Chunks        │ 62    │
│ Entities      │ 168   │
│ Relationships │ 312   │
│ Communities   │ 10    │
└───────────────┴───────┘
```

### 询问

```bash
dotnet run --project Mostlylucid.GraphRag -- query "How do I use Docker Compose?"
```

```text
──────────────────── Local Search ────────────────────

Query: How do I use Docker Compose?

╭─Answer────────────────────────────────────────────────╮
│ To run the services defined in the                    │
│ devdeps-docker-compose.yml file, you need to run the  │
│ following command in the same directory as the file:  │
│                                                       │
│ docker compose -f .\devdeps-docker-compose.yml up -d  │
│                                                       │
│ This command will start the containers in detached    │
│ mode.                                                 │
╰───────────────────────────────────────────────────────╯

Related Entities: Docker, container, services, image

Sources: 5 chunks (top score: 0.016)
```

### 状态

```bash
dotnet run --project Mostlylucid.GraphRag -- stats
```

```text
─────────────── GraphRAG Database Stats ────────────────
┌───────────────┬───────┐
│ Metric        │ Count │
├───────────────┼───────┤
│ Documents     │     5 │
│ Chunks        │    62 │
│ Entities      │   168 │
│ Relationships │   312 │
│ Communities   │    10 │
└───────────────┴───────┘

Database size: 7.76 MB
```

## 费用比较

@100Q 博客文章@(~500Q块},Q}{~100}文档_):

|操作|MSFT TreamraGMS K2Heuristic □|混合 *|LM{|
|-----------|---------------|-----------|--------|-----|
@|实体提取@| 1,000呼叫#|}0 MS K5 NSK6 *| I1,000 呼{|
|+文件强化@|+ @-}#|+{- _|>#MS K6*呼叫<|+-+|}
|~|#~4}|
社群摘要 @|_~20} @MS K3} @ ~20} @ | @ @ NSK6 @ *|# @
| **LLM电话总数** | ~1,020 | ~24 | ~120 | ~1,024 |
| **关系质量** -=YTET -伊甸园字幕组=- 翻译:
| **成本: (gpt-4O-mini})** | ~$5-10 | ~$0.15 | ~$0.75 | ~$5-10 |
| **成本: ( Ollama+)** |+N/+A|+$0+|#$0}|+MS K7_|

**混合模式是甜点** 在MSFT='*s 成本为 @.}的“~10%”中,

> 精确成本取决于块大小和快速形状@.}

## 权衡取舍

-=YTET -伊甸园字幕组=- 翻译:
|--------|-----------|--------|-----|---------------|
“|”实体检测单位“|” 以色列国防军“MS K2”结构“|” 国防军“MSC4” 结构“|” 以国防军“NSK6” 建筑“MSKO6” 每个块的|” LLM
-=YTET -伊甸园字幕组=- 翻译:
@| @LLM 呼叫@(100 @ docs @MS K2} @ ~24#| #~120#| @~24#
-=YTET -伊甸园字幕组=- 翻译:
@|_在网外工作#|}是| 是( Ollama) | 是的(Ollama□) NSK8API需要{|
超速- 关键键 *|* **建议建议** | 遗产对称 @| 无条理的文本@|

这条管道与 [DocSummamer 缩写器](/blog/docsummarizer-tool)“: ”建构结构,先是“, ”,然后让一个LLM来讲解它。”

> **断裂的地方:** 没有结构标记的字形或叙述文字 .\"隐含关系,没有词汇信号.... "高度模糊的实体名称 需要世界知识来掩盖\." 对于这些案例来说...,使用LLM模式或者微软=' 's full apply

## 守则法典

执行中最小值为 @ - @ MPK1 横跨这些文档的线条@ MS K2

```
Mostlylucid.GraphRag/
├── Storage/GraphRagDb.cs              # DuckDB with HNSW + provenance
├── Services/EmbeddingService.cs       # ONNX BERT wrapper
├── Services/OllamaClient.cs           # LLM client
├── Extraction/
│   ├── IEntityExtractor.cs            # Extractor interface
│   ├── EntityExtractor.cs             # Heuristic mode
│   ├── HybridEntityExtractor.cs       # Hybrid mode (recommended)
│   └── LlmEntityExtractor.cs          # Full LLM mode
├── Search/SearchService.cs            # BM25 + BERT hybrid
├── Graph/CommunityDetector.cs         # Leiden + summarization
├── Query/QueryEngine.cs               # Local/Global/DRIFT
├── Indexing/MarkdownIndexer.cs        # Chunking
├── GraphRagPipeline.cs                # Orchestration
├── Models.cs                          # Shared types + ExtractionMode enum
└── Program.cs                         # CLI
```

**资料来源::** [`Mostlylucid.GraphRag/`](https://github.com/scottgal/mostlylucidweb/tree/main/Mostlylucid.GraphRag)

## 相关员额

- [1: 图表RAG基本原理](/blog/graphrag-knowledge-graphs-for-rag) “-”为什么知识图能改善RAG?
- [使用 BM25+%](/blog/rag-hybrid-search-and-indexing) 深潜到BM=25}评分和索引
- [用 ONNX 和 BERT 搜索语义](/blog/semantic-search-with-onnx-and-qdrant) @-_BERT 嵌入@.NET
- [DocSummarizer 工具](/blog/docsummarizer-tool) - 我的文档总结工具 与此共享代码

## 外部资源

- [Duck DB VSS 扩展](https://duckdb.org/docs/extensions/vss.html) hNSW 矢量搜索
- [Leiden Algorithm 纸张](https://arxiv.org/pdf/1810.08473.pdf) 社区检测
- [RRF文件](https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf) -对等合唱团