GraphRAG Implementation (Technical)

Complete technical reference for the GraphRAG system

For conceptual overview and philosophy, see GraphRAG: The Living Brain


Implementation Overview

GraphRAG was implemented in five phases, each building on the previous:

Phase Name Focus
Phase 1 Foundation Schema, NetworkX, ego graphs
Phase 2 Semantic Search Embeddings, similarity
Phase 3 Narrative Extraction Story-physics edges, LLM extraction
Phase 4 Tiered Verification Fast/Medium/Slow checks
Phase 5 Analysis & Enhancement Community detection, tension

Phase 1: Foundation

Schema Extension

File: backend/graph/schema.py

Added embedding storage columns to the Node model:

# Phase 2: Embedding storage for semantic search
embedding = Column(JSON, nullable=True)
embedding_model = Column(String, nullable=True)
embedding_updated_at = Column(DateTime, nullable=True)

Core Graph Methods

File: backend/graph/graph_service.py

def to_networkx(self) -> nx.DiGraph:
    """Convert SQLite graph to NetworkX for algorithms."""

def ego_graph(self, entity_name: str, radius: int = 2) -> dict:
    """Get k-hop neighborhood around an entity."""

def get_nodes_by_type(self, node_type: str) -> List[Node]:
    """Filter nodes by type (CHARACTER, LOCATION, etc.)."""

def get_edges_by_type(self, relation_type: str) -> List[Edge]:
    """Filter edges by relation type (MOTIVATES, HINDERS, etc.)."""

def get_all_nodes(self) -> List[Node]:
    """Return all nodes in the graph."""

def get_all_edges(self) -> List[Edge]:
    """Return all edges in the graph."""

Embedding Service

File: backend/services/embedding_service.py

Multi-provider embedding with automatic model detection:

class EmbeddingProvider(ABC):
    """Base class for embedding providers."""

    @abstractmethod
    async def embed(self, text: str) -> List[float]: ...

    @abstractmethod
    async def embed_batch(self, texts: List[str]) -> List[List[float]]: ...


class OllamaEmbedding(EmbeddingProvider):
    """Ollama-based embeddings with automatic model detection."""

    EMBEDDING_MODELS = [
        "nomic-embed-text",
        "mxbai-embed-large",
        "all-minilm",
    ]

    FALLBACK_MODELS = [
        "llama3.2:3b",
        "mistral:7b",
    ]


class OpenAIEmbedding(EmbeddingProvider):
    """OpenAI text-embedding-3-small/large."""


class CohereEmbedding(EmbeddingProvider):
    """Cohere embed-english-v3.0."""

Usage:

from backend.services.embedding_service import get_embedding_service

service = get_embedding_service(provider="ollama")
embedding = await service.embed("Character description text")
similarity = service.cosine_similarity(embedding_a, embedding_b)

Embedding Index Service

File: backend/services/embedding_index_service.py

Manages embedding lifecycle:

class EmbeddingIndexService:
    async def index_node(self, node: Node) -> bool:
        """Generate and store embedding for a node."""

    async def reindex_all(self) -> Dict[str, int]:
        """Rebuild all embeddings."""

    async def get_stale_nodes(self) -> List[Node]:
        """Find nodes needing reindexing."""

Knowledge Router

File: backend/services/knowledge_router.py

Query classification and context assembly:

class KnowledgeRouter:
    async def route_query(self, query: str) -> RoutedContext:
        """
        Classify query → Retrieve from graph → Assemble context.

        Returns token-counted context block with priorities.
        """

Phase 3: Narrative Extraction

Narrative Ontology

File: backend/graph/narrative_ontology.py

17 edge types for story physics:

class NarrativeEdgeType(Enum):
    # Goal-Obstacle-Conflict
    MOTIVATES = "MOTIVATES"
    HINDERS = "HINDERS"
    CAUSES = "CAUSES"

    # Character Dynamics
    CHALLENGES = "CHALLENGES"
    KNOWS = "KNOWS"
    CONTRADICTS = "CONTRADICTS"

    # Narrative Threading
    FORESHADOWS = "FORESHADOWS"
    CALLBACKS = "CALLBACKS"

    # Basic Relationships
    LOCATED_IN = "LOCATED_IN"
    OWNS = "OWNS"
    PART_OF = "PART_OF"
    HAS_TRAIT = "HAS_TRAIT"
    LOVES = "LOVES"
    HATES = "HATES"
    ALLIES_WITH = "ALLIES_WITH"
    CONFLICTS_WITH = "CONFLICTS_WITH"
    REVEALS = "REVEALS"
    CUSTOM = "CUSTOM"

Narrative Extractor

File: backend/graph/narrative_extractor.py

LLM-based extraction with context awareness:

class NarrativeExtractor:
    def __init__(
        self,
        graph_service: KnowledgeGraphService,
        ollama_url: str = "http://localhost:11434/api/chat",
        model: str = "llama3.2:3b"
    ): ...

    async def extract_from_scene(
        self,
        scene_content: str,
        scene_id: str,
        current_beat: str = "Unknown"
    ) -> Dict[str, Any]:
        """
        Extract entities and relationships from scene text.

        Returns:
        - entities: New characters, locations, objects, events
        - relationships: Narrative edges between entities
        - flaw_challenge: Whether protagonist's flaw was tested
        - beat_alignment: Whether scene matches expected beat
        """

    async def merge_to_graph(self, extraction: Dict) -> Dict[str, Any]:
        """Merge extraction results into the knowledge graph."""

Extraction Prompt:

Extract from this scene:
- What MOTIVATES characters?
- What HINDERS their goals?
- Is the protagonist's fatal flaw being CHALLENGED?
- What does this FORESHADOW?
- What earlier events does it CALLBACK to?

Phase 4: Tiered Verification

Verification Service

File: backend/services/verification_service.py

Three-tier consistency checking:

class VerificationTier(Enum):
    FAST = "fast"      # <500ms - inline
    MEDIUM = "medium"  # 2-5s - background
    SLOW = "slow"      # 5-30s - on-demand


class VerificationService:
    async def run_fast_checks(
        self, content: str, context: Dict
    ) -> VerificationResult:
        """
        FAST tier checks:
        - Character alive/dead status
        - Known fact contradictions
        - Required callbacks present
        """

    async def run_medium_checks(
        self, content: str, context: Dict
    ) -> VerificationResult:
        """
        MEDIUM tier checks:
        - Flaw challenge gap detection
        - Beat alignment
        - Timeline consistency
        """

    async def run_slow_checks(
        self, content: str, context: Dict
    ) -> VerificationResult:
        """
        SLOW tier checks:
        - Full LLM semantic analysis
        - Delegates to GraphHealthService if available
        """

Scene Writer Integration

File: backend/services/scene_writer_service.py

Verification integrated into generation:

class SceneWriterService:
    async def verify_scene(
        self,
        content: str,
        scene_context: Optional[Dict] = None,
        run_background: bool = True
    ) -> Dict[str, Any]:
        """Run verification on generated content."""

    async def generate_and_verify(
        self,
        scene_id: str,
        scaffold: Scaffold,
        ...
    ) -> SceneGenerationResult:
        """Generate scene with automatic verification."""

Frontend Notification

File: frontend/src/lib/components/VerificationNotification.svelte

Toast notifications for verification issues:

  • Severity-based styling (critical/warning/info)
  • Dismiss individual or all
  • Actionable suggestions
  • Slide-in animation

File: frontend/src/lib/stores.js

Verification state management:

export const verificationNotifications = writable([]);
export const verificationSettings = persistentWritable('wf_verification_settings', {
  enabled: true,
  autoVerifyOnGenerate: true,
  showNotifications: true,
  minSeverity: 'warning'
});

export function addVerificationNotification(notification) { ... }
export function dismissVerificationNotification(id) { ... }
export function processVerificationResult(result, sceneId) { ... }

Phase 5: Analysis & Enhancement

Graph Analyzer

File: backend/graph/graph_analysis.py

Advanced narrative analysis:

class GraphAnalyzer:
    def detect_communities(self) -> Dict[str, List[str]]:
        """
        Louvain community detection on character nodes.
        Returns: {"primary_cast": [...], "secondary_cast": [...], ...}
        """

    def find_bridge_characters(self, top_k: int = 5) -> List[Dict]:
        """
        Betweenness centrality to find connecting characters.
        Returns role inference: protagonist/major/supporting/minor
        """

    def calculate_tension(self) -> Dict[str, Any]:
        """
        Tension = weighted sum of:
        - HINDERS edges (weight 2.0)
        - FORESHADOWS edges (weight 1.5)
        - CONTRADICTS edges (weight 3.0)
        - CHALLENGES edges (weight 2.5)

        Returns: score (0-1), level, indicators, recommendation
        """

    def analyze_pacing(self) -> Dict[str, Any]:
        """
        Categorize edges as action/setup/resolution.
        Returns: pacing type, ratios, recommendation
        """

    def get_narrative_summary(self) -> Dict[str, Any]:
        """
        Comprehensive analysis combining all methods.
        """

Settings Integration

File: backend/services/settings_service.py

Graph configuration schema:

"graph": {
    "edge_types": {
        "MOTIVATES": True,
        "HINDERS": True,
        "CHALLENGES": True,
        "FORESHADOWS": True,
        "CALLBACKS": True,
        "CONTRADICTS": False,  # Experimental
    },
    "extraction_triggers": {
        "on_manuscript_promote": True,
        "before_foreman_chat": True,
        "periodic_minutes": 0,
    },
    "verification_level": "standard",  # minimal | standard | thorough
    "embedding_provider": "ollama",    # ollama | openai | cohere | none
}

Validation rules added:

"graph.verification_level": {"type": str, "choices": ["minimal", "standard", "thorough"]},
"graph.embedding_provider": {"type": str, "choices": ["ollama", "openai", "cohere", "none"]},
"graph.extraction_triggers.periodic_minutes": {"type": int, "min": 0, "max": 60},

API Reference

Graph Analysis Endpoints

Endpoint Method Description
/graph/analysis/communities GET Detect character communities
/graph/analysis/bridges?top_k=5 GET Find bridge characters
/graph/analysis/tension GET Calculate narrative tension
/graph/analysis/pacing GET Analyze pacing
/graph/analysis/summary GET Comprehensive analysis

Semantic Search Endpoints

Endpoint Method Description
/graph/semantic-search POST Search by semantic similarity
/graph/ego-network/{entity} GET Get k-hop neighborhood
/graph/reindex-embeddings POST Rebuild embedding index
/graph/embedding-status GET Check index health
/graph/knowledge-query POST Full GraphRAG pipeline

Narrative Extraction Endpoints

Endpoint Method Description
/graph/extract-narrative POST Extract from text
/graph/extract-from-file POST Extract from file path
/graph/edge-types GET List available edge types

Verification Endpoints

Endpoint Method Description
/verification/run POST Run checks by tier
/verification/run-all POST Run all tiers
/verification/notifications GET Get pending notifications

Settings Endpoints

Endpoint Method Description
/settings/graph GET Get all graph settings
/settings/graph PUT Update graph settings

File Structure

backend/
├── graph/
│   ├── schema.py              # SQLAlchemy models (Node, Edge)
│   ├── graph_service.py       # Core graph operations
│   ├── graph_analysis.py      # Community detection, tension, pacing
│   ├── narrative_ontology.py  # Edge type definitions
│   └── narrative_extractor.py # LLM-based extraction
├── services/
│   ├── embedding_service.py        # Multi-provider embeddings
│   ├── embedding_index_service.py  # Embedding lifecycle
│   ├── knowledge_router.py         # Query classification & routing
│   ├── verification_service.py     # Tiered consistency checks
│   ├── scene_writer_service.py     # Generation with verification
│   └── settings_service.py         # Graph settings schema
└── api.py                          # All endpoints

frontend/
└── src/lib/
    ├── components/
    │   └── VerificationNotification.svelte
    └── stores.js  # Verification store section

Testing

All Python files pass syntax verification:

python3 -m py_compile backend/graph/schema.py
python3 -m py_compile backend/graph/graph_service.py
python3 -m py_compile backend/graph/graph_analysis.py
python3 -m py_compile backend/graph/narrative_ontology.py
python3 -m py_compile backend/graph/narrative_extractor.py
python3 -m py_compile backend/services/embedding_service.py
python3 -m py_compile backend/services/embedding_index_service.py
python3 -m py_compile backend/services/knowledge_router.py
python3 -m py_compile backend/services/verification_service.py
python3 -m py_compile backend/api.py

Manual Testing

Community detection:

curl http://localhost:8000/graph/analysis/communities

Tension calculation:

curl http://localhost:8000/graph/analysis/tension

Full verification:

curl -X POST http://localhost:8000/verification/run-all \
  -H "Content-Type: application/json" \
  -d '{"content": "Scene text here...", "tier": "fast"}'

Dependencies

Python (requirements.txt):

  • numpy>=1.24.0 - Cosine similarity calculations
  • networkx - Graph algorithms (already in project)
  • httpx - Async HTTP for Ollama/OpenAI/Cohere
  • aiohttp - Async HTTP for LLM extraction

Environment Variables:

  • OPENAI_API_KEY - For OpenAI embeddings (optional)
  • COHERE_API_KEY - For Cohere embeddings (optional)

Future Work

Not Yet Implemented

  • Frontend tension indicator component
  • Settings UI for graph configuration (SettingsGraph.svelte)
  • WebSocket for real-time tension updates
  • Embedding dimension normalization for provider switching

Deferred

  • frontend/src/routes/+page.svelte - VerificationNotification integration
  • backend/ingestor.py - Narrative prompt updates

Implementation: December 2025