GraphRAG-osuus 2: Vähimmäiskäytännöllinen GrafRAG (Suomi (Finnish))

GraphRAG-osuus 2: Vähimmäiskäytännöllinen GrafRAG

Saturday, 27 December 2025

//

12 minute read

Siihen Osa 1, tutkimme, miksi GraphRAG on tärkeä. vähimmäiskäytännöllinen GraphRAG kolmeen ekstraktiomuotoon:

Mode LLM-puhelut Parasta m
Heuristinen (defaultM SK1 \0 kutakin chunkia kohden SSK4 Nopeiden indeksointien järjestelmällinen merkitseminen
Hybridi SSK1 asiakirjaa kohti Nopeuden ja laadun tasapaino
LLM

Kaikki muodot käyttävät:

  • DuckDB-ohjelma yhtenäistettyyn varastointiin (vectorit
  • BM25 M SK1 BERT-hybridinen haku RRF:n fuusio
  • Ollama synteesiin ja valinnaiseen ryhmäluokitteluun MSK00 API-kustannuksiaM SK1

Serienavigointi:

Kode: Mostlylucid.GraphRag GitHubissa

Luettelo arkkitehtuurista

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

Miksi DuckDB?

Microsoft's GraphRAG käyttää erillistä varastointia vektoreille M SK1LanceDB), entiteetit (ParquetMSC4 ja suhteet |(more Parquet | ). | DuckDB yksinkertaistaa tätä

  • Yksittäinen .duckdb asiakirja kaikkiin
  • Native HNSW vektorin haku VSS-laajennuksella
  • SQL sekä vector-tutkimukseen että grafiikan läpikulkuun
  • Nollakäytön monimutkaisuus

DuckDB ei ole grafiikkatietokanta - ja ettäM SK1 on kohta Tämä ei ole 't NeoM SK1j . Traversaalit ovat alhaisia, SQLMSC4perustaisia, ja tarkoituksellisiaMST6 Tämä rajoittaminen pitää järjestelmän parannuskeinaltavissa ja edullisissa

varastointijärjestelmä

Järjestelmä käyttää liittää alkuperätaulukoita Voimme kysyä "mikä chunkit mainitsevat elimen 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
    }

Keskeinen suunnittelupäätös: ei VARCHAR[] alkuperä. Liittyvät taulukoihinentity_mentions, relationship_mentions) mahdollistaa tehokkaat kysymykset, kuten " saa kaikki Dockeria mainitsevat chunkit

Vektorin haku: HNSW Gotcha

DuckDB's HNSW-indeksi aktivoi vain 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

Käyttämällä array_cosine_similarity ei käytetä indeksiä - se voittaaM SK1 ei käynnistetä HNSW-indeksiä . muilla kuin triviallisilla yhtiöillä, tämä muuttaa ~5ms indeksoidun kysymyksen täydelliseksi taulukkoskanneriksiMSC6

Entiteettien ekstrahointi

Tässä yhteydessä eroamme Microsoftin '-lähestymistavasta LLM-perM SK1kuormitusyhteyksiä käytetään Microsoftin viiteohjelmassa GraphRAG pipeline IDF-perustainen tilastollinen extraktointiTavoite ei ole täydelliset yksiköt vaan ne vakaa,korpusM SK1suhteelliset signaalit , jotka eivät edellytä LLM:tä tuottamaan

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

Miksi IDF:ssä ei ole hardcoded-luetteloita?

Naiivi lähestymistapa on HashSet<string> KnownTech = { "Docker", "Kubernetes", ... }. Tämä katkeaa :

  • Uusia tekniikoita (you'd tarvitsee ajantasaistaa luettelon
  • Domeniini-erityiset termit M SK1ertyinen corpus =erilaiset yksiköt)
  • Misspellingit ja muutokset

IDF (Reverse Document FrequencyM SK1 ratkaistaan tämä tilastollisesti. Termi's IDF onM SK2

$$\tekstiM SK1IDF}(tMSC3 = \logMST6fracMSSK7N}{dfMSL9t\MST10

:

  • $NM SK1 = kokonaismäärä
  • \(df(t)\) = termiä sisältävät asiakirjat

Korkea IDF = harvinaisuus = on todennäköistä, että entisillä, jotka ovat chunkissa 5 ja 5, on suurempi IDF kuin " ja ", jotka on chunkkissa 100 ja 9

Lisätietoa TF-IDF ja BMM SK1:sta voi tutustua postissani hybridinen haku BM25.

Rakennesignaalit

Markdown-rakenne kertoo meille, mikä on tärkeä

  • Otsakkeet (## Docker Setup)
  • Sisäinen koodi (`docker-compose`)
  • Linkki ([Docker](https://docker.com))
// 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);
    }
}

Linkkilähetys (YleinenM SK1Tulosuhteet laatuun )

Markdown-linkit tarjoavat selväsanainen suhteet, joissa ei tarvita LLM:n johtopäätöstä:

// 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:n sisällyttämisen välityksellä tapahtuva vähentäminen

Entiteettien nimet, kuten "Docker Compose", "dockerM SK3composeMST4 ja SSK5DokerCompose" olisi yhdistettäväMSC7 Me käytämme BERT-säännökset semanttista samankaltaisuutta havaitsemiseksi:

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

Tämä askel on O(nM SK1 rajoitetussa ehdokaskokonaisuudessa, mutta ehdokasvaltioiden lukumäärät rajataan IDF-filtrointiin ja rakenteellisiin signaaleihin. semanttinen haku ONNXin ja BERT:n avulla.

Poistotoimet

CLI-järjestelmä tukee kolmea ekstraktiomuotoa --extraction-mode:

Heuristinen menettely (DefaultM SK1

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

Käytetään IDF + rakenteellisia signaaleja entiteetin havaitsemiseksiM SK1 valinnaisen LLM-heittoluokittelun kanssa Zero per-chunk LLM-puheluja - vain ~1 puhelua SSK2 yksiköistä tyyppien luokitteluun

Hibriittimodus ( SuositettuM SK1

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

Kummankin maailman paras:

  1. Heuristinen havaitseminen: IDF
  2. LLM:n parantaminen: Yhden puhelun kohden asiakirja validoi entiteetit ja ekstrahoi semanttisia suhteita
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

5-asiakirjoihin, joissa on 62 chunkkit, hybridimode tekee 5 LLM-puhelut (vs 124 täysimääräisen LLM-modeen osaltaM SK2 Saatte:

  • Heuristiikkaa koskeva deterministinen entiteetin kattavuus
  • LLM-laatusuhteiden ekstrakointi (semanttinenM SK2 ei vain yhteisvaikutus -todennäköisyysMSC4
  • Kuvaukset ja vahvistetut tyypit

LLM-modus (Microsoft-StyleM SK2

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

Full Microsoft GraphRAG approach: 2 LLM-puhelut kutakin chunkia kohti (yksiköiden ekstraktointi +suhteiden extraktointi

Milloin kutakin käytetään

Mode LLM-puhelut Parasta m
Heuristinen ~1 kutakin SSK2 osapuolta kohden
Hybridi SSK1 asiakirjaa kohti kattavuuden ja laadun tasapaino
LLM

Tekninen asiakirja, aloitetaan hybridi mode. Siihen sisältyy semanttisia suhteita ilman perinpohjaista kustannuksia. Heuristinen puhtaan nopeuden osalta, tai Llm narratiiviseen tekstiin.

Hybridinen haku: BMM SK1 + BERT

Hybridinen haku yhdistää kaksi täydentävää lähestymistapaa:

Tiheys (BERTM SK1 Ymmärtää merkityksen. M SK1Docker-säiliösäiliöt" vastaa "säiliön sisällyttäminenMSC4 Säästö (BMM SK1 Vastaa täsmällisille ehdoille. M SK1HNSW

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

Mikä on BM25?

BM25 M SK1Meiten vastaavuus 25)tulokset asiakirjoissa kysymyksen termien määrän perusteella

$$_ \tekstiMSC4IDFMST5qMSSK6i+) \cdot \ \fracMSL10fMLS11q\MSL12i=MLS13 DMSS14 ♪\cdotti (kMNS17 | | + , \ 1)}{f ,MNS20q , MNS21 , i+MNS22 , D\ MLS23 ,

Keskeiset intuiatiot:

  • IDF-nimitys: Harvoilla sanoilla on enemmän merkitystä.
  • TF-tyydytys: Sana, joka ilmenee 10x ei oleM SK2t 10x merkityksellisempi kuin
  • Pituuden normalisointi: Pitkät asiakirjat eivät saa epäoikeudenmukaista etua

Täydellinen täytäntöönpano BM25 ja , ks. yhdistetty haku ja indeksointi.

Vastavuoroinen luokitus fuusio (RRFM SK1

RRF yhdistää eri hakujärjestelmien luokitukset. Jokainen luokitteluasema saa tulostaM SK1

$$_{r \in RM SK2 \frac{1}{k M+ rMSC6dMST7

Jos $kM SK1 (tyypillisesti SSK3 estää ylipainon korkeimmassa tuloksessa molemmat luokitukset paranevat:

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

Esimerkki: Asiakirja, joka on luokiteltu M SK1 tiheään ja #3 harvaan

  • Tiheys
  • Tappiot: M SK1 | = | |
  • Yhdistetty: M SK1 (ylempi kuin yksittäinen

Kysymysmenettelyt

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

Kysymyksen luokittelu

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

Tämä luokittelujärjestelmä on tarkoituksellisesti yksinkertainen - ja helppo korvata pienillä tarkoitusmallilla myöhemminM SK1 Jos yksiköt eivät vastaa toisiaan, järjestelmä heikentyy puhtaasti puhtaalle hybridipäätökseen

CLI:n käyttö

Indexointi

# 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    │
└───────────────┴───────┘

Kysymys

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)

Tilastot

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

Kustannusten vertailu

For 100 blog posts

Operaatio MSFT GraphRAG
yksiköiden ekstrahointi 1,000 puhelut SSK3 \0 ♫0 ♫
Asiakirjojen parantaminen
luokittelu mukaan luettuna S~4 paketti M - ~4 paketti
Yhteisön yhteenvedot
LLM-puhelujen kokonaismäärä ~1,020 ~24 ~120 ~1,024
Suhteiden laatu Semanttinen SSK1 Yhteinen - toistuminen S Seманttinen Semantinen SSK5
Kustannus (gpt-4oM SK2mini ) ~$5-10 ~$0.15 ~$0.75 ~$5-10
Kustannus (OllamaM SK1 NM SK1A

Hibridinen järjestelmä on paras vaihtoehto useimman teknisen sisällön osalta saatte semanttisia suhteita.

Tiukka määrä--laajuuden arvioinnin määrittelyM SK2todelliset kustannukset riippuvat kokosta ja pikamuodosta

Ratkaisut

Aspekti SSK1 Heuristinen MSFT GraphRAG Hybridinen
Entiteettien havaitseminen IDF + rakenteellisuus МSK3 IIDF + rakenteellisyys LLM kutakin chunkia kohti
Suhteet Yhteinen-tapaus LLM-inferred Yhteinen- tapaus | LLM SK8inferired \
LLM-puhelut (100 asiakirjat) ~24 ~120
Suhteiden laatu Alhainen SSK2 Korkea Alhanteinen SSK4 Korkean S
Työskennellään offline-tilassa Kyllä Kyllä
Paras SSK1 NopeusM SK2 Kriittinen Suositus Perinteinen kompatti Teksti ilman rakennetta

Conceptually, tämä on sama putki kuin DocSummarizer: rakentamaan ensin rakennettaM SK1 sitten antamaan LLM:n kertoa siitä

Missä tämä murenee Fiction- tai narrative text without structural markup. Implicit relationships with no lexical signal

Luettelo

Toteutuminen on vähimmäis - ~2,000 rivit näissä asiakirjoissa

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

Lähde: Mostlylucid.GraphRag/

Ulkoiset resurssit

Finding related posts...
logo

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