GraphRAG Teil 2:-Mindestlebend GraphRAG
Im TeilWir haben untersucht, warum GraphRAG wichtig ist. Mindesthaltbarkeit von GraphRAG mit drei Extraktionsmodi
| -Modus ruft LLM am besten für | -Modus an | ||
|---|---|---|---|
| Heuristisch (-Default)- | -M0- pro Stück Schnellindexierung strukturierter Markdown | - | - |
| Hybrid Ausgewogenheit von Geschwindigkeit und Qualität | |||
| LLM Höchste Entitätsqualität |
Alle Betriebsarten verwenden
- EnteDB für einen einheitlichen Speicher in einer einzigen Datei
- BM-25-Hybridsuche über RRF-Fusion
- Ollama für Synthese und optionale Batch-Klassifikation
Serie Navigation .:
- Teil - Grundlagen der GraphRAG
- Teil: Mindestlebensfähigkeit von GraphRAG Dieser Artikel
Code-Code Meistlylucid-.-GraphRag auf GitHub
Architektur im Überblick
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
Warum DuckDB.??
Microsoft®' s GraphRAG verwendet separaten Speicher für Vektoren -( , LanceDB , ), , Entities , ( , Parquet ,), , und Beziehungen .( , mehr Parkett , ). , DuckDB vereinfacht dies
- Einzeln
.duckdbDatei für alles - Native HNSW Vektorsuche über VSS-Erweiterung
- SQL für Vektorsuche und Graphentransversal
- Komplexität der Bereitstellung von Null
DuckDB ist nicht eine Graphen-Datenbank, und das ist der Punkt. Dies ist ,' , Neo , 4 , . , Traversen sind flach , , , SQL , und bewusst ,. , dass die Beschränkung hält das System debuggable und billig .
Speicherschema
Das Schema verwendet Beistelltische für Provenienz Wir können Fragen stellen, welche Brocken Entity X.?" direkt erwähnen.
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
}
Schlüsselentscheidung für das Design Nein VARCHAR[] für ProvenienzBeifügen von Tabellenentity_mentions, relationship_mentionsErmöglichen Sie effiziente Abfragen wie z.B. alle Brocken, die Docker nennen
Vektorensuche - Das HNSW Gotcha
Der HNSW Index von DuckDB-' löst nur mit 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
Verwendung array_cosine_similarity wird den Index nicht verwenden Es hat den HNSW Index ausgelöst. Auf nicht--'trivial corpora,' wird dadurch eine indizierte Abfrage in einen vollständigen Tabellenscan umgewandelt.
Entitätenextraktion
Dies ist, wo wir von Microsoft diverge nieren. LLM---per---Cunk-Extraktion Pässe verwendet in Microsoft-Referenz GraphRAG Pipeline, verwenden wir IDF---basierte statistische ExtraktionDas Ziel ist es, perfekte Entitäten zu erreichen. stabile corpus-Signale Das erfordert ein LLM, um zu produzieren . Dieses tradet einige Rückrufe für Determinismus., und berechenbare Kosten eine bewusste Wahl für technische Corpora:
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
Warum IDF-,-nicht festkodierte Listen
Der naive Ansatz ist ein hartkodierter HashSet<string> KnownTech = { "Docker", "Kubernetes", ... }Diese Pausen für ,MSK'
- Neue Technologien müssen die Liste aktualisieren
- Domänenspezifische Begriffe unterschiedliche Korpusarten unterschiedliche Einheiten
- Schreibfehler und Abweichungen
IDF-Frequenz des inversen Dokuments Löset dieses statistisch ,.' Ein Begriff ,''s IDF ist ,mesK2'
$$-Texte, {-IDF-}(-t)-=--Log-MSC6-frac-{-N.}{-df-MS K9-t-MSSK10
Worum es geht
- Gesamte Stückzahl
- Dokumente, die den Begriff $ enthalten
Hohe IDF-Werte Seltener Begriff Wahrscheinlich hat eine Entität, die sich in 5 von 100 befindet, eine höhere IDF als ", die bei 100 aus 100. erscheint.
Für mehr auf TF.-.IDF und BM.25,. Siehe meinen Beitrag auf hybride Suche mit BM-25.
Strukturelle Signale
Markdown-Struktur sagt uns, was - ' - wichtig ist
- Bezeichnung (
## Docker SetupEntität - Inlinecode (
`docker-compose`Entität - Verknüpfungen (
[Docker](https://docker.com)Verhältnis zwischen der Einheit und dem Unternehmen
// 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);
}
}
Verbindung Gewinnung von Verbindungen mit hohem Gehalt an Qualitäts-Beziehungen
Markdown-Links bieten ausdrücklich Beziehungen, die nicht erfordern LLM-Inferenz
// 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);
}
Deduplikation über BERT-Embeddings
Wir verwenden Entitätsnamen wie "-Docker-Komposition",-Docker-Komposite--kompose",- und "-Dacker-Compose"-komposite BERT-Einbettungen zum Nachweis semantischer Ähnlichkeiten
// 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);
}
}
}
Dieser Schritt ist in einem gebundenen Kandidaten-Set , aber die Anzahl der Kandidaten wird durch IDF Filterung und strukturelle Signale begrenzt , - , nicht Korpusgröße , . , Für Details auf BERT Einbettungen ,, , siehe semantische Suche mit ONNX und BERT.
Extraktionsmodi
Das CLI unterstützt drei Extraktionsmodi über --extraction-mode:
Heuristischer Modus
dotnet run --project Mostlylucid.GraphRag -- index ./Markdown --extraction-mode heuristic
Verwendet IDF Struktursignale zur Entitätserkennung mit optionaler LLM Batch-Klassifikation Null Anrufe per ,-'cunk LLM Nur Anrufe pro Einheiten der Typenklassifizierung (MSK)
Hybrid-Modus empfohlen
dotnet run --project Mostlylucid.GraphRag -- index ./Markdown --extraction-mode hybrid
Das Beste aus beiden Welten
- Heuristische ErkennungIDF - Struktursignale finden Entitätskandidaten
- LLM-VerbesserungEin Aufruf pro Dokument validiert Entitäten und extrahiert semantische Beziehungen
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
Für Dokumente mit einem Hybrid-Modus macht LLM-Aufrufe Für volle LLM-Modus, erhalten Sie :
- Deterministische Entity-Berichterstattung aus der Heuristik
- LLM---Qualität Beziehungsextraktion (-Semantik ,-nicht nur co---Ereignis
- Beschreibungen und validierte Typen
LLM-Modus (-Microsoft-Style-)-Style
dotnet run --project Mostlylucid.GraphRag -- index ./Markdown --extraction-mode llm
Vollständiger Ansatz von Microsoft GraphRAG LLM-Aufrufe pro Stück Die meisten teuren ,, aber höchste Qualität für unstrukturierten Text
Wann jedes Mal verwendet werden soll
| -Modus ruft LLM am besten für | -Modus an |
|---|---|
| Heuristisch , | , , ~1 , pro Einheiten , 50 , schnell indizierend , , , gut ,- , strukturierter Markdown |
| Hybrid Ausgewogenheit der Abdeckung und Qualität | |
| LLM Nicht strukturierte Prosa, maximale Qualität |
Für die technische Dokumentation - Start mit Hybrid Es gibt Ihnen semantische Beziehungen ohne die Per--... heuristisch bei reiner Geschwindigkeit: llm für narrative Texte
Hybride Suche - :-BM-25-BERT
Hybride Suche kombiniert zwei komplementäre Ansätze
Dichte (-BERT-): Versteht die Bedeutung von .-Dockercontainern, die mit " zusammenhängt Sparse (-BM-25):- Passt genau zu den Begriffen ., ", HNSW", nur zu den Bedingungen ", PSW".,
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
Was ist BM-25??
BM.25 (Best MatchErgebnisse von Dokumenten basierend auf Query Term Frequency).Die Formel...
]$$-Text ♪{-Scorer}(-Streamer,-Recorder )-Tracker_]{ ® T=1}^ TM\ TMD( TMX_ TMSSK7 TM \ TMCT { T( hm, TMS) TMKSK15 ktM( MX_1 TMSCDt + T1)}{ ( TMCSK21 TMK22 TMSB) TM-+ kt_1 nm\ TMcdt M(1 TMSV- .
Wichtige Intuitionen
- IDF-BezeichnungSeltene Wörter spielen eine Rolle mehr
- TF-SättigungDas Wort, das erscheint, ist mehr relevant als 1x.
- LängennormalisierungLange Dokumente erhalten keinen unfairen Vorteil
Für eine vollständige BM-Implementierung von , siehe hybride Suche und Indexierung.
Reziproker Rang Fusion
RRF verschmilzt Rankings aus unterschiedlichen Retrieval-Systemen... jede Rangposition bekommt eine Punktzahl.
,$$, Text , {, RRF, }(, D), MSC4, Summe_{-R , -R,}-R und {1}{-R
Wo verhindert, dass das oberste Ergebnis übergewichtig ist, erscheinen die Dokumente in beides Rankings werden aufgewertet
// 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;
}
Beispiel: Ein Dokument, das in dichter und in spärlicher Höhe auf Platz 1 der #1 rangiert
- Dichte: :, Stärke: =, Dicke:
- Sparse , : , \(1/(60+3) ,= , 0.0159\) ,
- Kombination aus MSK und 0.0323 Höher als entweder allein
Abfrage-Modi
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
Abfrageklassifikation
// 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;
}
Dieser Klassifikator ist bewusst einfach und leicht durch ein kleines Intent-Modell später zu ersetzen. Wenn keine Entities zusammenpassen, degradiert das System sauber zum reinen Hybrid Retrieval.
Verwendung von CLI
Indexierung
# 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 │
└───────────────┴───────┘
Abfragen
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)
Statistik
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
Kostenvergleich
Für Blog-Posts von (~500, Brocken, , und anderen Dokumenten von MKK4
Einsatz von |, MSFT GraphRAG, Heuristisches und Hybrides |, LLM, |, |-----------|---------------|-----------|--------|-----| ,| , Entitätsextraktion , | , , 1,000 , ruft , | , -0 ,-| , –0 , | ,„1,000" -Aufrufe ,| Weitere Informationen finden Sie unter: http:www.cordis.lu/ |-Klassifikation Einschließlich |-Bremsen (MSK) und |-Brnen (MSK) Zusammenfassungen aus der Gemeinschaft ,| , ~20 , | , 20 , | , -20 ,-| , ~20 ,"| | LLM-Aufrufe insgesamt | ~1,020 | ~24 | ~120 | ~1,024 | | Beziehungsqualität Semantische Semantik-Semantik - Semantisch Semanisch Semantisches Semanisches Semantischer Semantic Semanische semantische | | Kosten für -4o-omini-)o | ~$5-10 | ~$0.15 | ~$0.75 | ~$5-10 | | Kosten Weitere Informationen: http://europa.eu.int/en/comm/index_de.html
Hybrid-Modus ist der süße Punkt für den meisten technischen Inhalt erhalten Sie semantische Beziehungen . . ( . nicht nur co .- .occurrence . ) . bei ~10% . von MSFT .
Rough order---of---MagnitudenschätzungDie genauen Kosten hängen von der Größe des Stücks und der prompten Form ab.
Handelshemmnisse
Weitere Informationen finden Sie unter: http:www.cordis.lu/ |--------|-----------|--------|-----|---------------| Struktur der IDF-Struktur der |, IDF Struktur der +, IDP-Struktur des +, LLM pro Stück ,| , Beziehungen , | , Co , - , LLM , - , | , co ,- , lLM . - , bezeichnet | LLM-Aufrufe für (100, ), MSC4, MCSK5, MSR6, |, MSSK8, MSV9 und ~1,020 Beziehungsqualität niedrig hoch hoch hoch Funktioniert offline |. Ja |. Ja (.Ollama.).|.Ja (.OLLAMA.). API erforderlich |. Die beste Lösung für die Geschwindigkeit von |, -, kritisch Empfohlen Nicht strukturierter Text
Konzeptionell ist dies die gleiche Pipeline wie DocSummarizerBauen Sie zuerst die Struktur, dann lassen Sie ein LLM es erzählen.
Wo dies zusammenbricht Fiction oder erzählerischen Text ohne strukturelle Markup-Beschriftung.-Implizite Beziehungen ohne lexikalisches Signal.-Hoch mehrdeutige Entity-Namen, die Weltwissen erfordern, um zu disambiguieren.-Für diese Fälle verwenden Sie LLM-Modus oder Microsoft-'s volle Annäherung
ANHANG
Die Implementation ist minimal, die Linien über diese Dateien hinweg.
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
Quelle: Eurostat. Mostlylucid.GraphRag/
Verwandte Beiträge
- Teil - Grundlagen der GraphRAG Warum Wissensgraphen die RAG verbessern
- Hybride Suche mit BM-25 Scoring und Indexierung
- Semantische Suche mit ONNX und BERT BERT-Einbettungen in -.-NET
- DocSummarizer Werkzeug Mein Dokument Zusammenfassung Werkzeug, das Code mit diesem teilt
Externe Mittel
- DuckDB VSS Erweiterung HNSW-Vektorsuche
- Leiden Algorithmus Papier Gemeinschaftsdetektion
- RRF Papier Zusammenwachsender Rang Fusion