Skip to content

Fast GraphRAG

Fast GraphRAG uses Personalized PageRank to expand context from seed entities, without the overhead of community detection.

Overview

Fast GraphRAG (from circlemind-ai/fast-graphrag) is designed for:

  • Fast queries with minimal LLM calls
  • Incremental updates without full reprocessing
  • Cost efficiency by avoiding community detection
  • Large-scale graphs with millions of entities

Key idea: Use Personalized PageRank seeded from query entities to naturally surface relevant connected context.

How It Works

1. Insert Pipeline

Documents

Chunking

Entity & Relation Extraction (LLM + gleaning)

Graph Construction with Deduplication
    ├─→ Summarize duplicate descriptions
    └─→ Merge similar edges

HNSW Embedding of Entities

Storage

Key features:

  • LLM-based deduplication: Automatically summarizes duplicate entity descriptions
  • Edge merging: Combines similar relationships above threshold
  • Domain-aware extraction: Uses domain + example queries for better entity detection

2. Query Pipeline

Query

Extract Query Entities (LLM)

Vector Similarity Search (HNSW)

Seed Personalized PageRank

Rank Nodes (damping=0.85)

Token Budget Truncation

LLM Answer Generation

Key features:

  • No community detection: Faster indexing and updates
  • Personalized PageRank: Surfaces relevant connected nodes
  • Token budgets: Separate budgets for entities, relations, and chunks
  • Automatic pruning: Only includes nodes above score threshold

Installation

bash
pnpm add @graphrag-js/fast

Basic Usage

typescript
import { createGraph } from "@graphrag-js/core";
import { fastGraph } from "@graphrag-js/fast";
import { memoryStorage } from "@graphrag-js/memory";
import { openai } from "@ai-sdk/openai";

const graph = createGraph({
  model: openai("gpt-4o-mini"),
  embedding: openai.embedding("text-embedding-3-small"),
  provider: fastGraph({
    entityTypes: ["PERSON", "ORGANIZATION", "LOCATION", "CONCEPT"],
    domain: "Scientific research papers on climate change",
    exampleQueries: [
      "What organizations lead climate research?",
      "How does deforestation affect emissions?",
    ],
  }),
  storage: memoryStorage(),
});

await graph.insert(documents);

// PageRank retrieval (default)
const { text } = await graph.query("What are the main findings?");

// With references
const { text, context } = await graph.query("Key conclusions?", {
  withReferences: true,
});

Configuration

typescript
interface FastGraphConfig {
  entityTypes?: string[];
  domain?: string;
  exampleQueries?: string[];
  maxGleanings?: number;
  concurrency?: number;
  pagerank?: PageRankConfig;
  tokenBudgets?: TokenBudgets;
  mergePolicy?: MergePolicy;
}

Entity Types

Explicitly specify entity types to extract:

typescript
fastGraph({
  entityTypes: ["PERSON", "COMPANY", "PRODUCT", "TECHNOLOGY"],
})

Default: Auto-detect based on domain and example queries

Tips:

  • Use UPPERCASE for entity types (follows fast_graphrag convention)
  • Keep to 4-6 types for best results
  • Domain-specific types work better than generic types

Domain

Natural language description of your data domain:

typescript
fastGraph({
  domain: "Technical documentation for React.js library",
})

When to use:

  • Specialized or technical domains
  • Better entity extraction
  • Domain-specific relationships

Example Queries

Provide example questions users might ask:

typescript
fastGraph({
  exampleQueries: [
    "How do I use hooks in React?",
    "What's the difference between useState and useReducer?",
    "How does React rendering work?",
  ],
})

Benefits:

  • Guides entity extraction toward relevant concepts
  • Improves retrieval relevance
  • Optimizes graph structure for your use cases

PageRank Configuration

typescript
interface PageRankConfig {
  damping?: number;           // default: 0.85
  maxIterations?: number;     // default: 100
  tolerance?: number;         // default: 1e-6
  maxEntities?: number;       // default: 128
  scoreThreshold?: number;    // default: 0.05
}

Example:

typescript
fastGraph({
  pagerank: {
    damping: 0.85,        // Standard PageRank damping factor
    maxIterations: 100,   // Convergence iterations
    tolerance: 1e-6,      // Convergence tolerance
    maxEntities: 128,     // Maximum entities to include
    scoreThreshold: 0.05, // Minimum score to include
  },
})

Tuning tips:

  • Higher damping (0.9): More graph influence, less direct similarity
  • Lower damping (0.7): More direct similarity, less graph influence
  • Higher maxEntities: Better recall, higher cost
  • Higher scoreThreshold: Better precision, faster queries

Token Budgets

Control context size with separate budgets:

typescript
interface TokenBudgets {
  entities?: number;     // default: 4000
  relations?: number;    // default: 3000
  chunks?: number;       // default: 9000
}

Example:

typescript
fastGraph({
  tokenBudgets: {
    entities: 3000,    // Entity descriptions
    relations: 2000,   // Relationship descriptions
    chunks: 8000,      // Source chunks
  },
})

Total context budget: entities + relations + chunks (default: 16000 tokens)

Tuning tips:

  • Increase entity budget for entity-centric queries
  • Increase relation budget for relationship-centric queries
  • Increase chunk budget when you need more source text

Merge Policy

Configure deduplication and merging:

typescript
interface MergePolicy {
  maxNodeDescriptionSize?: number;  // default: 512
  edgeMergeThreshold?: number;      // default: 5
}

Example:

typescript
fastGraph({
  mergePolicy: {
    maxNodeDescriptionSize: 512,  // Trigger LLM summarization above this
    edgeMergeThreshold: 5,        // Merge similar edges above this count
  },
})

How it works:

  • When entity descriptions exceed maxNodeDescriptionSize, LLM summarizes them
  • When similar edges exceed edgeMergeThreshold, they're merged into one

Query Modes

PageRank Mode (Default)

Uses Personalized PageRank for context expansion:

typescript
const { text } = await graph.query("What are the key findings?");

// Explicit mode
const { text } = await graph.query("Question", { mode: "pagerank" });

How it works:

  1. Extract entities from query using LLM
  2. Vector search to find seed entities
  3. Run Personalized PageRank from seeds
  4. Rank and filter nodes by score
  5. Truncate to token budgets
  6. Generate answer with LLM

Best for:

  • Questions with clear entity mentions
  • Connected information retrieval
  • Multi-hop queries

Naive Mode

Falls back to pure vector search:

typescript
const { text } = await graph.query("Question", { mode: "naive" });

How it works:

  1. Vector search on chunks
  2. Return top-K chunks
  3. Generate answer with LLM

Best for:

  • Broad questions without clear entities
  • Baseline comparison
  • When PageRank doesn't find relevant entities

Query Options

typescript
interface FastQueryOptions {
  mode?: "pagerank" | "naive";
  withReferences?: boolean;
  stream?: boolean;
  contextOnly?: boolean;
}

Examples:

typescript
// With source references
const { text, context } = await graph.query("Question", {
  withReferences: true,
});

// Streaming response
const { textStream } = await graph.query("Question", {
  stream: true,
});

for await (const chunk of textStream) {
  process.stdout.write(chunk);
}

// Context only (no LLM generation)
const { context } = await graph.query("Question", {
  contextOnly: true,
});

Personalized PageRank Explained

PageRank measures node importance based on incoming links. Personalized PageRank biases the random walk toward specific seed nodes (query entities).

Algorithm:

1. Start with seed nodes (from query)
2. Initialize: 100% probability on seeds
3. For each iteration:
   - Distribute probability to neighbors
   - Apply damping factor (85% walk, 15% teleport back to seeds)
4. Converge when probabilities stabilize
5. Rank nodes by final probability

Result: Nodes strongly connected to query entities get higher scores.

Advantages

1. Fast and Cheap

  • No community detection: Saves time during indexing
  • Minimal LLM calls: Only for extraction and answer generation
  • Fast queries: PageRank is efficient on large graphs

2. Incremental Updates

  • Add new documents without rebuilding communities
  • Deduplication merges with existing entities
  • HNSW index supports incremental inserts

3. Scalable

  • Handles millions of entities
  • HNSW provides fast approximate search
  • Token budgets prevent context overflow

4. Flexible

  • No predefined community structure
  • Works with any graph topology
  • Adapts to query-specific contexts

When to Use Fast GraphRAG

Use Fast GraphRAG when:

  • ✅ You need fast query responses
  • ✅ Your graph is large (>100k entities)
  • ✅ You want incremental updates
  • ✅ Cost is a major concern
  • ✅ Queries have clear entity mentions

Don't use Fast GraphRAG when:

  • ❌ You need global thematic summaries (use Microsoft GraphRAG)
  • ❌ Your queries are very broad (use LightRAG hybrid mode)
  • ❌ Community structure is important for your use case

Complete Example

typescript
import { createGraph } from "@graphrag-js/core";
import { fastGraph } from "@graphrag-js/fast";
import { neo4jGraph } from "@graphrag-js/neo4j";
import { qdrantVector } from "@graphrag-js/qdrant";
import { redisKV } from "@graphrag-js/redis";
import { openai } from "@ai-sdk/openai";

const graph = createGraph({
  model: openai("gpt-4o-mini"),
  embedding: openai.embedding("text-embedding-3-small"),

  provider: fastGraph({
    entityTypes: ["PERSON", "ORGANIZATION", "LOCATION", "PRODUCT", "TECHNOLOGY"],
    domain: "Software engineering and open-source projects",
    exampleQueries: [
      "Which organizations contribute to React?",
      "What technologies does Netflix use?",
      "How are microservices related to Kubernetes?",
    ],
    pagerank: {
      damping: 0.85,
      maxEntities: 128,
      scoreThreshold: 0.05,
    },
    tokenBudgets: {
      entities: 4000,
      relations: 3000,
      chunks: 8000,
    },
    mergePolicy: {
      maxNodeDescriptionSize: 512,
      edgeMergeThreshold: 5,
    },
    maxGleanings: 1,
    concurrency: 8,
  }),

  storage: {
    graph: neo4jGraph({ url: process.env.NEO4J_URL, ... }),
    vector: qdrantVector({ url: process.env.QDRANT_URL, ... }),
    kv: redisKV({ host: process.env.REDIS_HOST, ... }),
  },
});

// Insert documents
await graph.insert(documents);

// Query with PageRank
const { text } = await graph.query(
  "What are the major open-source contributions by Google?"
);
console.log(text);

Comparison with Other Algorithms

FeatureFastLightRAGMicrosoftSimilarity
Entity extraction
Community detection
PageRank
Dual vectors
Indexing speedFastMediumSlowFastest
Query speedFastMediumMediumFast
CostLowMediumHighLow
Best forLarge graphsGeneral useDeep analysisPrototyping

Tips and Best Practices

1. Provide Domain Context

typescript
fastGraph({
  domain: "Technical API documentation for cloud services",
  exampleQueries: [
    "How do I configure AWS S3?",
    "What are the rate limits for the API?",
  ],
})

2. Tune PageRank Parameters

Start with defaults, then adjust based on results:

  • Too many irrelevant entities? Increase scoreThreshold
  • Missing relevant entities? Increase maxEntities
  • Need more graph influence? Increase damping

3. Monitor Token Budgets

Check if context is being truncated:

typescript
const { text, metadata } = await graph.query("Question");
console.log(metadata.tokensUsed, metadata.budgetExceeded);

4. Use Streaming for Long Answers

typescript
const { textStream } = await graph.query("Complex question", {
  stream: true,
});

for await (const chunk of textStream) {
  process.stdout.write(chunk);
}

5. Incremental Updates

Fast GraphRAG is optimized for incremental updates:

typescript
// Initial insert
await graph.insert(initialDocuments);

// Later: add more documents
await graph.insert(newDocuments);  // Efficiently merged

See Also

Released under the Elastic License 2.0.