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-sdk/fast

Basic Usage ​

typescript
import { createGraph } from "graphrag-sdk";
import { fastGraph } from "@graphrag-sdk/fast";
import { memoryStorage } from "@graphrag-sdk/in-memory-storage";
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-sdk";
import { fastGraph } from "@graphrag-sdk/fast";
import { neo4jGraph } from "@graphrag-sdk/neo4j";
import { qdrantVector } from "@graphrag-sdk/qdrant";
import { redisKV } from "@graphrag-sdk/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. Made with ❤️ by Narek.