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
图图RAG 部分 *2: 最低可视图形
Saturday, 27 December 2025
内 部分#1“,”我们探究了为什么“Gagrag 很重要”,“.”现在让我们来“'”构建一个 最低可行最低限度 具有三种提取模式@: @%
| 模式 @ MS K1 @ LLM 呼叫@ | # 最佳为 @ | } |
|---|---|---|
| 华量主义 (}默认=)} | }0}每片块 | }快速索引 *,}结构化标记点 |
| 混合混合物 @ | @ @ 1}每份文件 @ I | @ 平衡速度和质量 @ MPK3# |
| 法学硕士 最大实体质量 * @ | * |
所有模式都使用 @ : @%
- 鸭子数据库 在单个文件中统一存储 {(\ +} 图
- BM=25 +BERT混合搜索 通过RRF聚集
- 奥拉马 合成和可选分批分类: (-零API成本=)
系列导航:
- 1: 图表RAG基本原理
- “2: 最低可视图形”部分 @(_这篇文章#)}
代码=: 多数液化.+GraphRag 吉特胡卜山
建筑结构概览
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#?"}直接查询□:}
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:
// 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, -和可预见的成本--
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 进行混合搜索.
结构信号
Markdown结构告诉我们什么"'"重要""I:"
- 标题标题标题 (
## Docker Setup* →实体* - 内嵌代码 (
`docker-compose`* →实体* - 链接链接链接 (
[Docker](https://docker.com)@) @ @ →_ 实体 @ I+} 关系
// 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 推断@: @%
// 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 嵌入 用于检测语义相似性@: @%
// 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 进行语义搜索.
采掘模式
CLI 支持三种采掘方式 --extraction-mode:
超常模式 @ ( @ default@ MS K1 @
dotnet run --project Mostlylucid.GraphRag -- index ./Markdown --extraction-mode heuristic
使用以国防军的“+”结构信号进行实体检测; “,”采用可选 LLM 批量分类;“MS K2” 每千分之零 -chunkLLM电话 @- Τ~1筿杠 50( 砰) 闽 .
混合模式 @(}推荐@)
dotnet run --project Mostlylucid.GraphRag -- index ./Markdown --extraction-mode hybrid
两个世界最优秀的
- 体温检测机构信号寻找实体候选人
- 提高LLLM: / 一次呼叫 文档文档文档文件 验证实体并提取语义关系
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})
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".
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(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 执行@,见 混合查找和索引.
相互融合 (RRF)
RRF将不同检索系统的排名合并 . 每个级别职位都得分*:
\(\Text{RRF}}(d) MS K4 NSK5Sump_{r @\inR}#\sfrac}{1}{k@+{(d□)}\)#
在“$”和“(”中, “MSK2 ”通常在“MSC3”中防止顶部结果超量加权@.”文件中出现的文档 两者兼 排名获得提升@: @%
// 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 都比独自一人高 #)#
查询模式
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
查询分类
// 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 使用率
索引编制
# 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
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 │
└───────────────┴───────┘
询问
dotnet run --project Mostlylucid.GraphRag -- query "How do I use Docker Compose?"
──────────────────── 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)
状态
dotnet run --project Mostlylucid.GraphRag -- stats
─────────────── 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 缩写器“: ”建构结构,先是“, ”,然后让一个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/
相关员额
- 1: 图表RAG基本原理 “-”为什么知识图能改善RAG?
- 使用 BM25+% 深潜到BM=25}评分和索引
- 用 ONNX 和 BERT 搜索语义 @-_BERT 嵌入@.NET
- DocSummarizer 工具 - 我的文档总结工具 与此共享代码
外部资源
- Duck DB VSS 扩展 hNSW 矢量搜索
- Leiden Algorithm 纸张 社区检测
- RRF文件 -对等合唱团