GraphRAG: Γιατί η αναζήτηση διάνυσμα σπάει στο επίπεδο Corpus (ελληνικά (Greek))

GraphRAG: Γιατί η αναζήτηση διάνυσμα σπάει στο επίπεδο Corpus

Friday, 26 December 2025

//

21 minute read

Το σύστημά σας RAG είναι καλό στις ερωτήσεις "needle": να ανακτήσει μερικά σχετικά κομμάτια και να συνθέσει μια απάντηση. Αγωνίζεται με δύο κοινούς τύπους ερωτημάτων:

  • Αίσθηση: "Ποια είναι τα κύρια θέματα σε αυτό το σώμα;"
  • Συνδετικό: "Πώς σχετίζεται το Χ με το Υ σε διάφορα έγγραφα;"

Αυτά δεν απαντήθηκαν από κανένα κομμάτι. κάλυψη + συσπείρωση + σύνδεση.

Γιατί η αναζήτηση φορέα αποτυγχάνει εδώ:

  • Κατατάσσει κομμάτια. ανεξάρτητα από ομοιότητα με το ερώτημα
  • Η ομοιότητα βελτιστοποιεί τη σημασία, όχι παγκόσμια κάλυψη
  • Οι προσθήκες αιχμαλωτίζουν το "τι ακούγεται παρόμοιο," όχι το "τι συνδέεται με τι"

Μπορείς να το αναγκάσεις με υποκίνηση και μετα-επεξεργασία, αλλά καταλήγεις να ξαναχτίσεις μια λύση σε σχήμα γραφήματος.

Η βασική διορατικότητα: GraphRAG αλλάζει τη μονάδα ανάκτησης. Για ερωτήσεις σώματος δεν θέλετε "top-K παρόμοια κομμάτια," θέλετε συνδεδεμένες κοινότητες έννοιας (και τις περιλήψεις τους), έτσι ώστε το μοντέλο βλέπει δομή, όχι θραύσματα.

GraphRAG προέρχεται από τη Microsoft Research's χαρτί και είναι διαθέσιμη ως Εφαρμογή ανοικτού κώδικα. Διατηρεί διανυσματική αναζήτηση για συγκεκριμένες ερωτήσεις, αλλά προσθέτει ένα γράφημα γνώσης και περιλήψεις της κοινότητας για τη λογική σε επίπεδο σώματος.

Όταν ΔΕΝ χρησιμοποιήσετε GraphRAG

Πριν βουτήξουμε μέσα, ας είμαστε ξεκάθαροι για το πότε αυτό είναι υπερβολή:

  • Μικρά σύνολα εγγράφων (κάτω από ~50 έγγραφα): απλά χρησιμοποιήστε διανυσματική αναζήτηση
  • Μόνο "πώς μπορώ" ερωτήσεις: GraphRAG willn't help
  • Ενιαίο περιεχόμενο (χωρίς ποικιλία οντότητας): δεν υπάρχει δομή γραφήματος προς εκμετάλλευση
  • Cost-consisted: η ευρετηρίαση απαιτεί πολλές κλήσεις LLM

Εάν οι χρήστες σας κάνουν μόνο συγκεκριμένες ερωτήσεις, μείνετε με σημασιολογική αναζήτησηGraphRAG λάμπει όταν οι χρήστες χρειάζονται το μεγάλη εικόνα, και αυτό είναι ένα μικρότερο ακροατήριο από ό, τι προτείνουν οι πωλητές.

Εισαγωγή

Πλοήγηση σειράς: Αυτό είναι το μέρος 6 της σειράς RAG:

Σε όλη αυτή τη σειρά, έχουμε κατασκευάσει όλο και πιο εξελιγμένα συστήματα RAG. Ξεκινήσαμε με βασική διανυσματική αναζήτηση, πρόσθεσε υβριδική λέξη-κλειδί+σεμαντική ανάκτηση, και ολοκληρωμένη αυτόματη ευρετηρίαση. Αλλά όλες αυτές οι προσεγγίσεις μοιράζονται έναν θεμελιώδη περιορισμό: βρίσκουν παρόμοια κομμάτια, όχι Συνδεδεμένες έννοιες- Για τι πράγμα μιλάς; επίπεδο σώματος ερωτήσεις (θέματα που καλύπτουν πολλά έγγραφα) που χρειάζεστε δομή.

Η συνιστώμενη διαδρομή: Αν έχετε ήδη Qdrant-based τοπική αναζήτηση εργασίας (όπως κάνουμε), πρωτότυπο με το Python sidecar για να επικυρώσει την αξία, να κρατήσει διανυσματικά φορείς για την τοπική αναζήτηση, και να προσθέσετε ένα ελαφρύ γράφημα για παγκόσμια / DRIFT ερωτήματα. Πηγαίνετε "πλήρη GraphRAG" μόλις έχετε αποδείξει τους χρήστες κάνουν αυτές τις ερωτήσεις.

Το Πρόβλημα με το Καθαρό Διανυσματικό RAG

Επιτρέψτε μου να σας δείξω τι εννοώ με ένα συγκεκριμένο παράδειγμα.

Τι ΠΡΑΓΜΑΤΙΚΑ ΠΡΑΓΜΑΤΙΚΑ ΠΡΑΓΜΑΤΙΚΑ

Ερώτηση: "Πώς μπορώ να χρησιμοποιήσω HTMX με Alpine.js;"

Διαδικασία Vector RAG:

  1. Ενσωματώστε την ερώτηση: [0.234, -0.891, 0.567, ...]
  2. Βρείτε παρόμοια κομμάτια στο Qdrant
  3. Επιστροφή top-K ταιριάζει με HTMX και Alpine.js
  4. LLM συνθέτει απάντηση από αυτά τα κομμάτια

Αυτό λειτουργεί επειδή η ερώτηση και το σχετικό περιεχόμενο είναι Σημασιολογικά παρόμοιαΟι προσθήκες αιχμαλωτίζουν αυτή την ομοιότητα.

// This is what our current SemanticSearchService does
var embedding = await _embeddingService.GetEmbeddingAsync(query);
var results = await _qdrantService.SearchAsync(
    collectionName: "blog_posts",
    queryVector: embedding,
    limit: 10
);
// Returns chunks about HTMX, Alpine.js, frontend patterns

Όπου ο Βέκτορ ΡΙΖ Αγωνίζεται

Ερώτηση: "Ποιες είναι οι βασικές τεχνολογίες για τις οποίες γράφω και πώς σχετίζονται μεταξύ τους;"

Ποιος φορέας RAG επιστρέφει:

Result 1: "HTMX makes it easy to add AJAX to your pages..."
Result 2: "Docker Compose orchestrates multiple containers..."
Result 3: "PostgreSQL's full-text search is surprisingly capable..."
Result 4: "Alpine.js provides reactive state management..."

Αναφέρει Docker, PostgreSQL, HTMX, ONNX... αλλά δεν τα ομαδοποιεί ή δεν εξηγεί πώς συνδέονται.

Το πρόβλημα: Αυτό το ερώτημα απαιτεί συγκέντρωση και κατανόηση των σχέσεων σε όλο το σώμα, πρέπει να:

  • Προσδιορισμός όλων των αναφερομένων τεχνολογιών
  • Κατανοητό που χρησιμοποιείται από κοινού
  • Συγκεντρώστε τα σε συνεκτικά θέματα

Αν προσπαθήσεις να το επιδιορθώσεις με υποκίνηση, καταλήγεις να ξαναβρείς ένα γράφημα.

Εισάγετε GraphRAG

GraphRAG είναι η λύση της Microsoft Research σε αυτό το πρόβλημα. Χαρτί GraphRAG Εντοπίστηκαν οι δύο τύποι ερωτημάτων που χειρίζονται ανεπαρκώς οι ΚΓΠΕ κατά την έναρξη της διαδικασίας (αισθητική και Συνδετικό) και κατασκεύασε ένα σύστημα ειδικά για την αντιμετώπισή τους.

Αντί απλά να ενσαρκώνει κομμάτια, GraphRAG δημιουργεί ένα γράφημα γνώσεων που συλλαμβάνει οντότητες και τις σχέσεις τους, στη συνέχεια τις συγκεντρώνει σε κοινότητες με περιλήψεις.

Πώς λειτουργεί το GraphRAG

Αγωγός με μια ματιά:

  • Ευρεσιτεχνία: Chunks → οντότητες/σχέσεις → γράφημα → κοινότητες → περιλήψεις
  • Ερώτηση: Τοπικά = κομμάτια + γράφημα γειτονιά

Η GraphRAG προσθέτει πολλά εξαρτήματα στον αγωγό ΚΓΠΕ, ομαδοποιημένα σε τρεις κατηγορίες:

  1. Εξαγωγή (μονάδες + σχέσεις)
  2. Κατασκευή γραφήματος (Αποθήκευση γραφήματος γνώσης)
  3. Ανακεφαλαίωση (κοινοτική ανίχνευση + ιεραρχία)
flowchart TB
    subgraph "Traditional RAG (What We Have)"
        A[Documents] --> B[Chunks]
        B --> C[Embeddings]
        C --> D[Vector Store]
    end

    subgraph "GraphRAG Additions"
        B --> E[Entity Extraction]
        E --> F[Relationship Extraction]
        F --> G[Knowledge Graph]
        G --> H[Community Detection]
        H --> I[Community Summaries]
    end

    subgraph "Query Time"
        J[User Query] --> K{Query Type?}
        K -->|Specific| L[Local Search]
        K -->|Global| M[Global Search]
        K -->|Hybrid| N[DRIFT Search]

        D --> L
        G --> L
        I --> M
        G --> N
        I --> N
    end

    style E stroke:#f9f,stroke-width:2px
    style H stroke:#bbf,stroke-width:2px
    style I stroke:#9f9,stroke-width:2px

Βήμα 1: Εξαγωγή οντοτήτων

Ένα LLM διαβάζει κάθε κομμάτι και εκχυλίσματα οντότητες (τα πράγματα που συζητούνται):

Chunk: "Docker Compose makes it easy to define multi-container applications.
        I use it with PostgreSQL for my blog's database layer."

Extracted Entities:
- Docker Compose (technology)
- PostgreSQL (database)
- blog (project)
- database layer (concept)

Βήμα 2: Εξαγωγή σχέσεων

Η ίδια LLM προσδιορίζει τον τρόπο με τον οποίο οι οντότητες σχετίζονται μεταξύ τους:

Relationships:
- Docker Compose --[used_with]--> PostgreSQL
- blog --[has_component]--> database layer
- PostgreSQL --[implements]--> database layer

Βήμα 3: Κατασκευή γραφημάτων γνώσης

Όλες οι οντότητες και οι σχέσεις σχηματίζουν ένα γράφημα:

graph LR
    subgraph "Frontend Cluster"
        HTMX[HTMX]
        Alpine[Alpine.js]
        Tailwind[Tailwind CSS]
    end

    subgraph "Infrastructure Cluster"
        Docker[Docker]
        Compose[Docker Compose]
        Postgres[PostgreSQL]
        Qdrant[Qdrant]
    end

    subgraph "AI/ML Cluster"
        ONNX[ONNX Runtime]
        Embeddings[Embeddings]
        RAG[RAG]
    end

    HTMX -->|used_with| Alpine
    HTMX -->|styled_by| Tailwind
    Alpine -->|styled_by| Tailwind

    Docker -->|orchestrated_by| Compose
    Compose -->|runs| Postgres
    Compose -->|runs| Qdrant

    ONNX -->|generates| Embeddings
    Embeddings -->|stored_in| Qdrant
    RAG -->|uses| Embeddings
    RAG -->|uses| Qdrant

    style HTMX stroke:#f9f
    style Docker stroke:#bbf
    style RAG stroke:#9f9

Βήμα 4: Κοινοτική ανίχνευση (Leiden Algorithm)

Η Αλγόριθμος Leiden συστάδες πυκνά συνδεδεμένοι κόμβοι σε κοινότητες. Αυτό έχει σημασία επειδή σας δίνει σταθερή συστάδες για να συνοψίσει και να ανακτήσει; οι κοινότητες γίνονται μονάδες ανάκτησης σας για παγκόσμια ερωτήματα.

  • Κοινότητα 1: "Frontend Stack" (HTMX, Alpine.js, Tailwind)
  • Κοινότητα 2: "Υποδομές Container" (Docker, Compose, PostgreSQL, Qdrant)
  • Κοινότητα 3: "RAG Pipeline" (ONNX, Embeddings, Qdrant, RAG)

Προσέξτε πώς εμφανίζεται το Qdrant σε δύο κοινότητες: γεφυρώνει υποδομές και AI/ML.

Βήμα 5: Κοινοτικές πληροφορίες

Ένα LLM δημιουργεί περιλήψεις για κάθε κοινότητα σε κάθε επίπεδο ιεραρχίας:

Community 1 Summary (Frontend Stack):
"The frontend approach combines HTMX for server-driven interactivity
with Alpine.js for client-side state management, styled using Tailwind CSS.
This stack prioritizes HTML-first development with minimal JavaScript,
focusing on progressive enhancement over SPA complexity."

Community 2 Summary (Container Infrastructure):
"The blog runs on Docker Compose, orchestrating PostgreSQL for persistent
storage, Qdrant for vector search, and the ASP.NET Core application.
This containerized architecture enables consistent local development
and production deployment."

Τρόποι Ερωτήματος

GraphRAG παρέχει τρεις τρόπους ερώτημα, κάθε βελτιστοποιημένο για διαφορετικούς τύπους ερωτήσεων:

Παγκόσμια Αναζήτηση

Καλύτερα για: "Ποια είναι τα κύρια θέματα;" "Περιγράψτε τα βασικά θέματα."

Χρησιμοποιεί περιλήψεις κοινότητας (όχι μεμονωμένα κομμάτια) για να απαντήσει σε ερωτήσεις λογοτεχνίας:

Query: "What technologies does this blog cover most?"

Process:
1. Retrieve all community summaries
2. Map: Ask LLM to extract technology themes from each summary
3. Reduce: Combine partial answers into final response

Response:
"The writing centres on three technology clusters:
1. **Frontend Development** - HTMX, Alpine.js, Tailwind CSS for minimal-JS web UIs
2. **AI/ML Infrastructure** - RAG pipelines, ONNX embeddings, vector search with Qdrant
3. **DevOps/Containerization** - Docker, PostgreSQL, ASP.NET Core deployment"

Τοπική αναζήτηση

Καλύτερα για: "Πώς μπορώ να ρυθμίσω το Χ;" "Τι είναι το Υ;"

Συνδυάζει οντότητα-εστιασμένο γράφημα τραβερσάλ με την παραδοσιακή διανυσματική αναζήτηση:

Query: "How do I use Qdrant with ONNX embeddings?"

Process:
1. Identify entities in query: Qdrant, ONNX, embeddings
2. Retrieve graph neighborhood around those entities
3. Also retrieve vector-similar chunks
4. Combine into rich context for LLM

Response includes:
- Direct relationships (ONNX generates embeddings stored in Qdrant)
- Related entities (all-MiniLM-L6-v2 model, cosine similarity)
- Specific code examples from vector-retrieved chunks

Αναζήτηση DRIFT

Καλύτερα για: "Πώς σχετίζεται το Χ με το Υ;" "Σύγκριση Α και Β."

Αναζήτηση DRIFT (Δυναμική λογική και συνέπεια με Ευέλικτη Traversal), όπως περιγράφεται στα έγγραφα GraphRAG, συνδυάζει την τοπική αναζήτηση με το πλαίσιο της κοινότητας. Χρησιμοποιεί ακόμα συλλογισμό LLM πάνω από ανακτημένο δομημένο πλαίσιο (όχι μαγικό συμπέρασμα γραφήματος), αλλά η δομή βοηθά το LLM να δει συνδέσεις που θα αστοχούσε με επίπεδα κομμάτια.

Query: "How do the frontend and backend technologies connect?"

Process:
1. Start with entities: HTMX, ASP.NET Core
2. Traverse graph to find connection paths
3. Include community summaries for context
4. Generate answer showing the full picture

Response:
"HTMX makes requests to ASP.NET Core endpoints, which query PostgreSQL
and Qdrant. The connection flows through the API layer, where endpoints
return HTML fragments that HTMX swaps into the DOM. Alpine.js handles
client-side state for interactive components like search typeahead."

Συγκρίνοντας GraphRAG με το τρέχον σύστημά μας

Ας χαρτογραφήσουμε τις έννοιες GraphRAG σε αυτό που έχουμε ήδη σε Mostlylucid.SemanticSearch:

Συστατικό < Τρέχων Σύστημα < > Ισοδύναμο GraphRAG |-----------|---------------|---------------------| | Ενσωμάτωση ~ ONNX (all-MiniLM-L6-v2) ~ Ίδια (ή OpenAI) ~ | Vector Store Qdrant Qdrant / LanceDB | Εξαγωγή οντότητας Καμιά εκχύλιση με ισχύ LLM | Γράφημα γνώσης Δεν υπάρχει βάση δεδομένων γραφήματος / in-memory | Ανίχνευση της Κοινότητας Κανένας αλγόριθμος Λέιντεν | Ερώτηση: Ειδικά | SemanticSearchService.SearchAsync() Τοπική Αναζήτηση | Ερώτηση: Global Δεν υποστηρίζεται ~ Παγκόσμια Αναζήτηση ~

Οι τρέχουσες λαβές εφαρμογής μας Τοπική αναζήτηση GraphRAG θα προσθέσει Παγκόσμια Αναζήτηση και Αναζήτηση DRIFT ικανότητες.

// What we have today (Local Search equivalent)
public async Task<List<SearchResult>> SearchAsync(string query, int limit = 10)
{
    var embedding = await _embeddingService.GetEmbeddingAsync(query);
    return await _qdrantService.SearchAsync("blog_posts", embedding, limit);
}

// What GraphRAG would add
public async Task<string> GlobalSearchAsync(string query)
{
    // 1. Retrieve community summaries (not chunks)
    var summaries = await _graphService.GetCommunitySummariesAsync();

    // 2. Map: Extract relevant themes from each summary
    var partialAnswers = await Task.WhenAll(
        summaries.Select(s => _llm.ExtractThemesAsync(query, s))
    );

    // 3. Reduce: Combine into final answer
    return await _llm.SynthesizeAsync(query, partialAnswers);
}

Προσεγγίσεις εφαρμογής

Υπάρχουν τρεις τρόποι για να προσθέσετε GraphRAG σε ένα υπάρχον σύστημα.

Επιλογή 1: Python Sidecar (Συνιστάται για Εξερεύνηση)

Εκτελέστε το GraphRAG της Microsoft ως ξεχωριστή υπηρεσία:

# docker-compose.graphrag.yml
services:
  graphrag:
    build:
      context: ./graphrag
    volumes:
      - ./data/input:/app/input
      - ./data/output:/app/output
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}

  graphrag-api:
    build:
      context: ./graphrag-api
    ports:
      - "8001:8000"
    depends_on:
      - graphrag
// GraphRagClient.cs - Call from ASP.NET Core
public class GraphRagClient
{
    private readonly HttpClient _http;

    public GraphRagClient(HttpClient http)
    {
        _http = http;
        _http.BaseAddress = new Uri("http://graphrag-api:8000");
    }

    public async Task<string> GlobalSearchAsync(string query)
    {
        var response = await _http.PostAsJsonAsync("/query/global", new { query });
        var result = await response.Content.ReadFromJsonAsync<GraphRagResponse>();
        return result.Answer;
    }

    public async Task<string> LocalSearchAsync(string query)
    {
        var response = await _http.PostAsJsonAsync("/query/local", new { query });
        var result = await response.Content.ReadFromJsonAsync<GraphRagResponse>();
        return result.Answer;
    }
}

Pros: Χρήση της εφαρμογής της Microsoft που έχει δοκιμαστεί στη μάχη, γρήγορη στο πρωτότυπο Κατά: Python εξάρτηση, LLM κόστος για ευρετηρίαση, cross-process επικοινωνία

Επιλογή 2: .NET Native (Δρόμος παραγωγής)

Κατασκευάστε τα βασικά συστατικά σε C#. Η BERT με βάση την εξαγωγή και Ollama μοτίβα από DocSummarizer Δούλεψε παρόμοια εδώ.

Εξαγωγή οντότητας

Ζητήστε από ένα LLM για να αναγνωρίσετε πράγματα (μονάδες) σε κάθε κομμάτι - δομημένες οντότητες αντί για θέματα ελεύθερης μορφής:

public async Task<List<Entity>> ExtractEntitiesAsync(string chunk)
{
    var prompt = $"""
        Extract entities from this text. Return JSON array.
        Types: technology, concept, project, person, organization
        Text: {chunk}
        Format: [{{"name": "Docker", "type": "technology"}}]
        """;

    var response = await _ollama.GenerateAsync(prompt);
    return JsonSerializer.Deserialize<List<Entity>>(response);
}

Απαιτήσεις παραγωγής: LLM JSON έξοδος Γουίλ Αυτό δεν είναι προαιρετική σκλήρυνση.

  • Συσταλμένη γενιά σχημάτων (Ollama's format: json, λειτουργία OpenAI κλήση)
  • Αναπροσπαθήστε τους βρόχους επισκευής (Ανιχνεύστε παραμορφωμένο JSON, ζητήστε LLM να το διορθώσετε)
  • Εκχύλιση υποβρυχίων (προτυπα regex για τους τύπους κοινών οντοτήτων)

Οι LLMs είναι πιθανοποιητικές, ο αγωγός εξαγωγής σας δεν πρέπει να είναι.

Εξαγωγή σχέσεων

Μόλις έχετε οντότητες, ρωτήστε το LLM πώς συνδέονται:

public async Task<List<Relationship>> ExtractRelationshipsAsync(
    string chunk, List<Entity> entities)
{
    var names = string.Join(", ", entities.Select(e => e.Name));
    var prompt = $"""
        Given entities: {names}
        Extract relationships. Return JSON array.
        Text: {chunk}
        Format: [{{"source": "Docker", "target": "PostgreSQL", "rel": "runs"}}]
        """;

    return JsonSerializer.Deserialize<List<Relationship>>(
        await _ollama.GenerateAsync(prompt));
}

Αποθήκευση γραφήματος με ομαλοποίηση οντότητας

Ο μεγαλύτερος πρακτικός πόνος είναι ψευδώνυμο οντότητας: "ASP.NET Core," "ASP.NET" και "aspnetcore" θα πρέπει να είναι ο ίδιος κόμβος.

public class KnowledgeGraph
{
    private readonly Dictionary<string, Entity> _entities = new();
    private readonly List<Relationship> _relationships = new();

    public void AddEntity(Entity entity)
    {
        var key = Normalise(entity.Name);  // "ASP.NET Core" → "aspnetcore"
        _entities[key] = entity;
    }

    private string Normalise(string name) =>
        name.ToLowerInvariant().Replace(".", "").Replace("-", "").Trim();
}

Για σοβαρή χρήση, σκεφτείτε την αποαντιγραφή της οντότητας με βάση την ενσωμάτωση: εάν δύο ονόματα οντοτήτων έχουν παρόμοια ενσωμάτωση, είναι πιθανώς το ίδιο πράγμα.

Σημεία πόνου στην παραγωγή (Η τιμή του GraphRAG εξαρτάται από την ποιότητα του γραφήματος):

  • Συνώνυμο/Επιτραπέζιοι πίνακες: διατήρηση Κανονικών ονομάτων και γνωστών ψευδώνυμων
  • Έλεγχος σχημάτων σχέσεων: Επιτρέπονται περιορισμοί για την πρόληψη παραισθησιακών τύπων σχέσεων
  • Εμπιστοσύνη βαθμολόγηση + κλάδεμα: δεν είναι εξίσου αξιόπιστες όλες οι σχέσεις που έχουν εξαχθεί
  • Εκτεταμένη επανα-δείκτης: όταν τα έγγραφα ενημερώνονται, θα πρέπει να επιδιορθώσετε το γράφημα, όχι να το ανοικοδομήσετε

Τραβερσάλ γραφήματοςName

Η εύρεση συνδεδεμένων οντοτήτων είναι μια αναζήτηση πλάτους-πρώτη:

public List<Entity> GetNeighbors(string entityName, int depth = 1)
{
    var result = new HashSet<Entity>();
    var queue = new Queue<(string Name, int Depth)>();
    queue.Enqueue((Normalise(entityName), 0));

    while (queue.Count > 0)
    {
        var (name, d) = queue.Dequeue();
        if (d >= depth) continue;

        // Find all entities connected to this one
        var neighbours = _relationships
            .Where(r => Normalise(r.Source) == name || Normalise(r.Target) == name)
            .SelectMany(r => new[] { r.Source, r.Target });

        foreach (var neighbour in neighbours)
            if (_entities.TryGetValue(Normalise(neighbour), out var entity))
                if (result.Add(entity))
                    queue.Enqueue((Normalise(neighbour), d + 1));
    }
    return result.ToList();
}

Ανίχνευση της Κοινότητας

Αυτή είναι μια γραμμή αναφοράς συνδεδεμένων συστατικών στοιχείων, Όχι, όχι. Πλήρης Leiden. Leiden βελτιστοποιεί για την αρθρωτότητα (πυκνές εσωτερικές συνδέσεις, αραιές εξωτερικές αυτές). Για μια σωστή εφαρμογή, χρησιμοποιήστε μια βιβλιοθήκη γραφήματος ή φέρτε τον αλγόριθμο.

public List<Community> DetectCommunities(KnowledgeGraph graph)
{
    // Connected components: group everything reachable together
    var visited = new HashSet<string>();
    var communities = new List<Community>();

    foreach (var entity in graph.GetAllEntities())
    {
        if (visited.Contains(entity.Name)) continue;
        
        // BFS to find all connected entities
        var community = new Community();
        var queue = new Queue<string>();
        queue.Enqueue(entity.Name);

        while (queue.Count > 0)
        {
            var name = queue.Dequeue();
            if (!visited.Add(name)) continue;
            community.Entities.Add(graph.GetEntity(name));
            foreach (var neighbor in graph.GetNeighbors(name, depth: 1))
                queue.Enqueue(neighbor.Name);
        }
        communities.Add(community);
    }
    return communities;
}

Ανακεφαλαίωση της Κοινότητας

Κάθε κοινότητα παίρνει μια περίληψη που περιγράφει το θέμα της.

public async Task<string> SummarizeCommunityAsync(Community community)
{
    var entities = string.Join("\n", 
        community.Entities.Select(e => $"- {e.Name}: {e.Description}"));
    
    var prompt = $"""
        Summarize what unites these concepts (2-3 sentences):
        {entities}
        """;

    return await _ollama.GenerateAsync(prompt);
}

Επιλογή 3: Υβριδικό (Pragmatic Middle Ground)

Κρατήστε το Qdrant για τοπική αναζήτηση, προσθέστε ένα ελαφρύ στρώμα γραφήματος για ερωτήσεις Global / DRIFT.

Ταξινόμηση ερωτημάτων

Πρώτον, ανιχνεύστε τι είδους ερώτηση είναι αυτή:

// WARNING: Toy heuristic for illustration only.
// In production, use a classifier prompt or few-shot rules and log misroutes.
private QueryMode ClassifyQuery(string query)
{
    var q = query.ToLowerInvariant();
    
    if (q.Contains("main theme") || q.Contains("summarize") || q.Contains("what topics"))
        return QueryMode.Global;
    
    if (q.Contains("relate") || q.Contains("connect") || q.Contains("compare"))
        return QueryMode.Drift;
    
    return QueryMode.Local;
}

Τοπική Αναζήτηση (Ενισχυμένη)

Χρησιμοποιήστε την υπάρχουσα διανυσματική αναζήτηση, εμπλουτισμένη προαιρετικά με πλαίσιο γραφήματος:

private async Task<string> LocalSearchAsync(string query)
{
    // Existing semantic search (what we have today)
    var chunks = await _semanticSearch.SearchAsync(query, limit: 10);

    // NEW: Enrich with related entities from graph
    var entities = await _graphService.ExtractEntitiesFromQueryAsync(query);
    var related = await _graphService.GetEntityContextAsync(entities);

    return await _llm.GenerateAsync(query, FormatContext(chunks, related));
}

Παγκόσμια Αναζήτηση (Νέα Δυνατότητα)

Χάρτης-μείωση σε περιλήψεις κοινότητας (δεν απαιτείται διανυσματική αναζήτηση):

private async Task<string> GlobalSearchAsync(string query)
{
    var summaries = await _graphService.GetAllCommunitySummariesAsync();

    // Map: Extract relevant info from each community
    var partials = await Task.WhenAll(
        summaries.Select(s => _llm.ExtractRelevantInfoAsync(query, s)));

    // Reduce: Combine into final answer
    return await _llm.SynthesizeAsync(query, partials.Where(p => !string.IsNullOrEmpty(p)));
}

Αναζήτηση DRIFT (Συναισθηματικές ερωτήσεις)

Συνδυάστε τα τοπικά αποτελέσματα με το κοινοτικό πλαίσιο για "πώς το Χ σχετίζεται με το Υ":

private async Task<string> DriftSearchAsync(string query)
{
    var localResults = await LocalSearchAsync(query);
    
    var entities = await _graphService.ExtractEntitiesFromQueryAsync(query);
    var communities = await _graphService.GetCommunitiesForEntitiesAsync(entities);
    var themes = string.Join("\n", communities.Select(c => c.Summary));

    return await _llm.GenerateAsync(
        $"Question: {query}\n\nDetails:\n{localResults}\n\nBroader themes:\n{themes}",
        systemPrompt: "Synthesize the details with the thematic context.");
}

Εκτιμήσεις κόστους και επιδόσεων

Η GraphRAG έχει σημαντικές συναλλαγές σε σύγκριση με τις καθαρές RAG διάνυσμα.

Κόστος ευρετηρίου

Το κόστος εξαγωγής οντότητας/σχέσης ποικίλλει σημαντικά ανά μοντέλο, άμεσο σχεδιασμό και μέγεθος κομματιού. μία ή δύο κλήσεις LLM ανά κομμάτι συν έναν μικρότερο αριθμό εκκλήσεων για περιλήψεις της κοινότητας.

□ Λειτουργία Διανυσματικών RAG □ GraphRAG □ |-----------|------------|----------| | Ενσωμάτωση 1 call/chunk 1 call/chunk το ίδιο | Εξαγωγή οντότητας/συναλλαγής Δεν υπάρχει 1-2 LLM calls/chunk | Ανακεφαλαίωση της Κοινότητας Δεν υπάρχει κανένας αριθμός κλήσης/κοινότητα LLM

Για ένα σώμα των 1.000 αναρτήσεων blog με 5 κομμάτια το καθένα (5.000 κομμάτια συνολικά), μόνο διανυσματική ευρετηρίαση είναι ουσιαστικά μόνο το κόστος προσθήκης. GraphRAG προσθέτει χιλιάδες LLM καλεί για εξαγωγή και summarisation. Το ακριβές κόστος εξαρτάται σε μεγάλο βαθμό από την επιλογή του μοντέλου σας και την άμεση απόδοση; χρησιμοποιώντας τοπικά μοντέλα (Ollama με llama3.2 ή παρόμοια) εξαλείφει εντελώς το κόστος API, η οποία είναι η συνιστώμενη προσέγγιση για πειραματισμό.

Κόστος ερώτησης

Τύπος ερώτησης - Διανυκτέρευση RAG - GraphRAG Τοπικά - GraphRAG Global - Global |------------|------------|----------------|-----------------| | Αναζήτηση διάνυσμα 1 call 1 call 1 call 0 calles | Τραβερσάλη γραφήματοςName ~ 0 ~ 1-2 ερωτήσεις ~ 0 ~ | Τηλεφωνήματα LLM - 1 - 2 - 1 - 2 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 2 - 1 - 1 - 1 - 2 - 1 - 1 - 1 - 1 - 1 - 1 - 2

Η Global Search είναι πιο ακριβή ανά ερώτηση, αλλά απαντά σε ερωτήσεις που η Τοπική Αναζήτηση απλά δεν μπορεί. Μπορείτε επίσης να κρύψετε τις παγκόσμιες απαντήσεις και να τις ανανεώσετε μόνο όταν αλλάζει το σώμα.

Λειτουργεί αποτυχία GraphRAG

Το GraphRAG δεν είναι μαγικό.

  • Σφάλματα εξαγωγής: LLMs miss οντότητες ή παραισθήσεις σχέσεις
  • Ψευδώνυμο οντότητας: "ASP.NET Core" vs "ASP.NET" vs "aspnetcore" γίνονται ξεχωριστοί κόμβοι
  • Περιστροφή γραφήματος: Όταν ενημερώσετε γιατροί, το γράφημα μπορεί να γίνει μπαγιάτικο
  • Κοινοτικές περιλήψεις μπαγιάτικες: Οι πληροφορίες δεν ενημερώνονται αυτόματα όταν οι οντότητες αλλάζουν

Η ομαλοποίηση της οντότητας είναι ο μεγαλύτερος πρακτικός πόνος.

  • Κανονικές ονομασίες + ψευδώνυμα
  • Ομαλοποίηση της τομής και της στίξης
  • Αποπαράδοση οντότητας με προαιρετική βάση την ενσωμάτωση

Ενσωμάτωση με την αναζήτηση blog μας

Εδώ είναι πώς GraphRAG θα μπορούσε να ενισχύσει την υπάρχουσα σημασιολογική αναζήτηση του blog:

Τρέχουσα ροή

User types in search → SemanticSearchService → Qdrant → Results

Ενισχυμένη Ροή

Ο ταξινομητής κάνει ερωτήσεις σε διαφορετικές στρατηγικές αναζήτησης. παγκόσμια ερώτηση ροή - σημειώστε ότι δεν αγγίζει ποτέ το διανυσματικό κατάστημα:

sequenceDiagram
    participant U as User
    participant API as Search API
    participant C as Query Classifier
    participant G as Global Search
    participant KG as Knowledge Graph

    U->>API: "What topics does this blog cover?"
    API->>C: Classify query
    C-->>API: QueryMode.Global

    API->>G: GlobalSearch(query)
    G->>KG: GetCommunitySummaries()
    KG-->>G: [Frontend, Infrastructure, AI/ML]
    G->>G: MapReduce over summaries
    G-->>API: Synthesized answer

    API-->>U: "The blog covers three main areas..."

Συγκρίνετε αυτό με ένα τοπική ερώτηση, η οποία συνδυάζει διανυσματική αναζήτηση με πλαίσιο γραφήματος για πλουσιότερες απαντήσεις:

sequenceDiagram
    participant U as User
    participant API as Search API
    participant C as Query Classifier
    participant L as Local Search
    participant Q as Qdrant
    participant KG as Knowledge Graph

    U->>API: "How do I use HTMX?"
    API->>C: Classify query
    C-->>API: QueryMode.Local

    API->>L: LocalSearch(query)
    L->>Q: Vector search
    Q-->>L: Relevant chunks
    L->>KG: GetEntityContext("HTMX")
    KG-->>L: Related: Alpine.js, Tailwind, ASP.NET
    L-->>API: Answer with rich context

    API-->>U: "HTMX is used with Alpine.js for..."

Η βασική διαφορά: παγκόσμιες ερωτήσεις συνολικά περιλήψεις της κοινότητας (θεματικά σε επίπεδο corpus), ενώ τα τοπικά ερωτήματα ανακτούν συγκεκριμένα κομμάτια εμπλουτισμένα με σχέσεις οντότητας.

Μια απλούστερη εναλλακτική: GraphRAG είναι ακόμα πάρα πολύ ένα ερευνητικό εργαλείο - εκχύλιση οντότητας, κατασκευή γραφημάτων, και η κοινοτική ανίχνευση προσθέτει σημαντική πολυπλοκότητα και το κόστος LLM. Για τις περισσότερες περιπτώσεις χρήσης, BERT ενσωμάτωση + BM25 ταίριασμα λέξης-κλειδί Δουλεύει καλύτερα. Ο Κόντι του Sourcegraph χρησιμοποιεί για τη νοημοσύνη κώδικα, και τι DocSummarizer χρήσεις για την ανασύνθεση εγγράφων. Το μοτίβο: υβριδική ανάκτηση χειρίζεται τη σημασία; οι λαβές LLM συναρμολόγηση, όχι λήψη αποφάσεωνΠαίρνεις το 80% του οφέλους με το 20% της πολυπλοκότητας.

Εφαρμογή Sketch

Το API είναι απλό: ταξινομήστε το ερώτημα, διαδρομή προς τον κατάλληλο χειριστή:

[HttpGet("api/search")]
public async Task<IActionResult> Search([FromQuery] string q, [FromQuery] string mode = "auto")
{
    if (mode == "auto")
        mode = ClassifyQuery(q);

    // global/local return synthesised answers; default returns raw search results
    return mode switch
    {
        "global" => Ok(await _graphRag.GlobalSearchAsync(q)),  // synthesised answer
        "local" => Ok(await SearchWithGraphContext(q)),        // answer with citations
        _ => Ok(await _semanticSearch.SearchAsync(q))          // raw ranked results
    };
}

Χρησιμοποιήστε διανυσματικά σημεία για "πώς μπορώ" ερωτήσεις, γραφήματα για "ποια είναι τα θέματα" ερωτήσεις.

Συμπέρασμα

GraphRAG επεκτείνει RAG από "βρείτε παρόμοια κομμάτια" να "κατανοήσει τη δομή της γνώσης." Δεν είναι μια αντικατάσταση για διανυσματική αναζήτηση? είναι μια ενίσχυση που επιτρέπει νέους τύπους ερωτημάτων.

Τι προσθέτει το GraphRAG:

  • Εξαγωγή οντότητας και σχέσεων
  • Κατασκευή γραφημάτων γνώσεων
  • Κοινοτική ανίχνευση και ιεραρχικές περιλήψεις
  • Global Search for sensemaking questions
  • Αναζήτηση συνδετικού συλλογισμού

Πότε να το χρησιμοποιήσετε:

  • Έχετε μια σημαντική συλλογή εγγράφων
  • Οι χρήστες ρωτούν "ποια είναι τα θέματα" ερωτήσεις τύπου
  • Το περιεχόμενό σας έχει σαφείς οντότητες και σχέσεις
  • Θέλετε να επιφάνει συνδέσεις αυτόματα

Διαδρομή εφαρμογής:

  1. Πρώτον, ρωτήστε: το χρειάζεστε πραγματικά αυτό; BERT + BM25 υβριδική ανάκτηση χειρίζεται τις περισσότερες περιπτώσεις χρήσης
  2. Εάν ναι, πρωτότυπο με Python sidecar για την επικύρωση της τιμής
  3. Κατασκευή .NET native εάν το κόστος/Latency ύλη
  4. Χρήση τοπικών LLMs (Ollama) για τον έλεγχο του κόστους ευρετηρίου

Πόροι

GraphRAG Official:

Σειρά RAG:

Απλή εναλλακτική λύση (BERT + BM25):

Finding related posts...
logo

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