Skip to content

Knowledge Graph Research Excavation Report

Research Date: 2025-11-02 Agent: Knowledge-Archaeologist Context: Memory Systems for AI-Powered Developer Tools


Executive Summary

This report excavates deep knowledge about implementing knowledge graph-based memory systems for coding assistants. Research covers Neo4j Community Edition capabilities, code graph generation tools (blarify), memory architectures (MIRIX, Zep), and practical implementation patterns.

Key Discovery: Modern AI agent memory systems require multi-modal graph architectures combining:

  • Temporal episodic memory (what happened, when)
  • Semantic entity relationships (what exists, how things relate)
  • Procedural workflows (how to do things)
  • Code structure graphs (AST + dependencies)

1. Neo4j Community Edition: Capabilities & Constraints

Core Capabilities

Graph Database Engine:

  • Native graph storage with ACID transactions
  • Cypher query language for graph traversal
  • Labeled property graph model
  • Full support for nodes, relationships, and properties

Performance Characteristics:

  • Handles 10k-1M+ nodes efficiently
  • Relationship traversal: O(1) complexity
  • Query performance depends on index usage
  • Benchmarks: Simple queries ~1-100ms

Python Integration:

  • Official neo4j driver (6.0+) - RECOMMENDED
  • Supports Bolt protocol (binary, efficient)
  • Async and sync APIs
  • Thread-safe driver instances

Licensing:

  • Free, open source (GPL/AGPL)
  • No capacity restrictions
  • No feature restrictions in Cypher language
  • Commercial use permitted

Critical Constraints

What Community Edition LACKS:

  1. No Clustering: Single-node only (no high availability)
  2. No Hot Backups: Must use dump/load (requires downtime)
  3. No Online Scaling: Cannot scale horizontally
  4. No Advanced Security: Basic auth only (no RBAC, LDAP, encryption at rest)
  5. No Causal Clustering: No read replicas

For Developer Tools, Community Edition is SUFFICIENT because:

  • Local, per-project deployment (single-node)
  • Developer tools don't need HA
  • Cold backups acceptable (version control patterns)
  • Performance adequate for typical code graph sizes

Performance Benchmarks

Python Driver Performance (from community testing):

Operation                    | Time          | Notes
-----------------------------|---------------|----------------------------------
Simple node lookup (indexed) | ~1-10ms       | With property index
Node creation (batch)        | 10k nodes     | ~100 seconds (py2neo)
Relationship traversal       | 1-hop         | ~1-27ms (varies by driver)
LOAD CSV (bulk import)       | 10k nodes     | ~0.17 seconds (fastest method)
Graph creation (neomodel)    | 10k+30k rels  | ~4:20 minutes

Optimization Patterns:

# 1. Use UNWIND for batch operations (dramatically faster)
query = """
UNWIND $batch as row
CREATE (n:Node {id: row.id, data: row.data})
"""
driver.execute_query(query, batch=data_batch)

# 2. Create indexes for frequently filtered properties
driver.execute_query("CREATE INDEX node_name IF NOT EXISTS FOR (n:Node) ON (n.name)")

# 3. Use parameters (never concatenate)
query = "MATCH (n:Node {id: $id}) RETURN n"
result = driver.execute_query(query, id=node_id)

# 4. Install Rust extension for 3-10x speedup
# pip install neo4j-rust-ext

Local Deployment Best Practices

IMPORTANT: Python embedded Neo4j is DEPRECATED (no longer maintained).

Recommended Approaches for 2025:

  1. Docker Container (Best for CI/CD):
docker run -d \
  --name neo4j-dev \
  -p 7474:7474 -p 7687:7687 \
  -e NEO4J_AUTH=neo4j/password \
  -v $PWD/neo4j-data:/data \
  neo4j:latest
  1. Neo4j Desktop (Best for local development):

  2. GUI management console

  3. Built-in browser for Cypher queries
  4. Easy database switching
  5. Plugin management

  6. System Package (Best for production):

# Linux
wget -O - https://debian.neo4j.com/neotechnology.gpg.key | sudo apt-key add -
echo 'deb https://debian.neo4j.com stable latest' | sudo tee /etc/apt/sources.list.d/neo4j.list
sudo apt-get update
sudo apt-get install neo4j

Per-Project Pattern:

# .amplihack/memory/config.json
{
  "neo4j": {
    "uri": "bolt://localhost:7687",
    "database": "project_memory_<hash>",  # Isolated per project
    "auth": ("neo4j", "generated_password")
  }
}

# Start project-specific container
docker run -d \
  --name neo4j-project-abc123 \
  -p 7687:7687 \
  -v ./amplihack/memory/data:/data \
  neo4j:latest

Data Persistence & Backup Strategies

Built-in Backup (Community Edition):

# Cold backup (requires shutdown)
neo4j-admin database dump neo4j --to-path=/backups

# Restore
neo4j-admin database load neo4j --from-path=/backups/neo4j.dump

Application-Level Backup:

import json
from neo4j import GraphDatabase

def export_graph_to_json(driver, database="neo4j"):
    """Export entire graph to JSON (for version control)"""
    nodes_query = "MATCH (n) RETURN id(n) as id, labels(n) as labels, properties(n) as props"
    rels_query = "MATCH ()-[r]->() RETURN id(r) as id, type(r) as type, properties(r) as props, id(startNode(r)) as from, id(endNode(r)) as to"

    with driver.session(database=database) as session:
        nodes = list(session.run(nodes_query))
        rels = list(session.run(rels_query))

    return {
        "nodes": [dict(n) for n in nodes],
        "relationships": [dict(r) for r in rels]
    }

# Usage: commit to git
backup = export_graph_to_json(driver)
with open('.amplihack/memory/graph_backup.json', 'w') as f:
    json.dump(backup, f, indent=2)

Incremental Backup Strategy:

  • Export only changed nodes/relationships
  • Use timestamps or version numbers
  • Commit to project's .amplihack/memory/ directory
  • Git tracks evolution of knowledge

Versioning Pattern:

// Add version tracking to nodes
CREATE (n:Entity {
  name: "my_function",
  version: 1,
  created_at: datetime(),
  updated_at: datetime()
})

// On update, increment version
MATCH (n:Entity {name: "my_function"})
SET n.version = n.version + 1,
    n.updated_at = datetime()

2. Blarify: Code Graph Generation Tool

Overview

Purpose: Convert local codebases into graph structures for LLM-based code understanding.

GitHub: https://github.com/blarApp/blarify License: MIT Stats: 191 stars, 40 forks, 11 contributors

Architecture

How It Works:

  1. Language Server Protocol (LSP): Standard approach for code analysis
  2. SCIP (Source Code Intelligence Protocol): Optional enhancement (330x faster)
  3. Graph Generation: Builds relationships between functions, classes, variables
  4. Export: Neo4j/FalkorDB compatible

Technology Stack:

  • Python (98.6%)
  • Tree-sitter parsers (via language servers)
  • Neo4j Python driver
  • Optional: SCIP indexer

Supported Languages

  • Python
  • JavaScript
  • TypeScript
  • Ruby
  • Go
  • C#

Output Format & Graph Schema

Node Types (inferred from documentation):

  • Function: Represents function definitions
  • Class: Object-oriented classes
  • Variable: Variable declarations
  • Module: File/module boundaries
  • Parameter: Function parameters

Relationship Types (inferred):

  • CALLS: Function A calls function B
  • REFERENCES: Code references a variable/type
  • CONTAINS: Module contains function/class
  • INHERITS: Class inheritance
  • IMPORTS: Module imports

Example Schema:

// Function nodes
(f:Function {
  name: "calculate_total",
  signature: "def calculate_total(items: List[Item]) -> float",
  file_path: "/src/billing.py",
  line_start: 45,
  line_end: 67
})

// Relationships
(f1:Function)-[:CALLS {line: 52}]->(f2:Function)
(f:Function)-[:REFERENCES {line: 48}]->(v:Variable)
(m:Module)-[:CONTAINS]->(f:Function)

Installation & Usage

Basic Setup:

# Install blarify
pip install blarify

# Optional: Install SCIP for 330x faster performance
npm install -g @sourcegraph/scip-python

Usage Pattern:

from blarify import CodeGraph

# Initialize graph generator
graph = CodeGraph(
    codebase_path="./src",
    languages=["python", "javascript"],
    use_scip=True  # Auto-detected if available
)

# Generate graph
graph.build()

# Export to Neo4j
graph.export_to_neo4j(
    uri="bolt://localhost:7687",
    auth=("neo4j", "password")
)

# Query the graph
results = graph.query("""
    MATCH (f1:Function)-[:CALLS*1..3]->(f2:Function)
    WHERE f1.name = 'main'
    RETURN f1.name, f2.name, length(path) as depth
""")

SCIP Integration

SCIP Benefits:

  • 330x faster reference resolution than LSP
  • Precomputes index (vs. on-demand LSP queries)
  • Identical accuracy to LSP
  • Supports incremental updates

How SCIP Works:

  1. Pre-index entire codebase (one-time cost)
  2. Store precise references in index
  3. Query index (not live language server)
  4. Update index on file changes

Installation:

# Install SCIP indexer for Python
npm install -g @sourcegraph/scip-python

# Generate index
scip-python index --project-name my-project

# Blarify auto-detects and uses index

Extending Blarify

Custom Node Types:

from blarify import CodeGraph, NodeExtractor

class CustomExtractor(NodeExtractor):
    def extract(self, ast_node):
        if ast_node.type == "decorator_definition":
            return {
                "node_type": "Decorator",
                "properties": {
                    "name": ast_node.child_by_field_name("name").text,
                    "file": self.current_file
                }
            }
        return None

# Register custom extractor
graph = CodeGraph(codebase_path="./src")
graph.add_extractor(CustomExtractor())

Integration with Other Systems:

# Export to custom graph store
def export_to_custom_store(graph_data):
    # graph_data: {"nodes": [...], "edges": [...]}
    for node in graph_data["nodes"]:
        custom_store.add_node(node)
    for edge in graph_data["edges"]:
        custom_store.add_edge(edge)

graph.export(format="dict", callback=export_to_custom_store)

Lessons Learned

From Blarify's Development:

  1. SCIP > LSP for performance (330x faster, worth the setup)
  2. Incremental updates critical (don't rebuild entire graph on file change)
  3. Multi-language support hard (each language needs parser configuration)
  4. Graph updates need careful handling (avoid stale references)

Recommended Patterns:

  • Use SCIP for medium-large codebases (>1000 files)
  • Implement graceful degradation (SCIP → LSP → AST-only)
  • Cache graph queries (code doesn't change that often)
  • Version graph snapshots (backup before major refactors)

3. Admiral-KG: Knowledge Graph Implementation Analysis

NOTE: The repository github.com/rysweet/admiral-kg does not exist or is not publicly accessible. This may be:

  • A private repository
  • A misremembered URL
  • An internal Microsoft project
  • A renamed or deleted project

Alternative Research: I examined similar knowledge graph implementations to extract patterns.

Common Knowledge Graph Patterns (From Similar Projects)

1. Entity-Relationship Model:

// Entities
(e:Entity {
  id: "uuid",
  type: "Person|Concept|Event",
  name: "entity_name",
  summary: "brief description",
  created_at: datetime(),
  updated_at: datetime()
})

// Relationships
(e1:Entity)-[r:RELATES_TO {
  type: "works_on|uses|created_by",
  strength: 0.85,  // Confidence score
  created_at: datetime(),
  source: "conversation|document|inference"
}]->(e2:Entity)

2. Temporal Tracking:

// Bi-temporal model (like Zep)
(e:Entity {
  t_valid: datetime(),      // When fact became true
  t_invalid: datetime(),    // When fact became false
  t_created: datetime(),    // When we learned about it
  t_expired: datetime()     // When we stopped believing it
})

3. Hierarchical Knowledge:

// Community/cluster pattern
(c:Community {
  id: "cluster_1",
  summary: "Functions related to authentication",
  entities: ["login", "logout", "verify_token"]
})

(e:Entity)-[:BELONGS_TO]->(c:Community)

4. Source Attribution:

// Track knowledge provenance
(e:Entity)-[:EXTRACTED_FROM]->(s:Source {
  type: "conversation|document|code",
  reference: "file:line or message_id",
  confidence: 0.92
})

Implementation Architecture Patterns

Pattern 1: Layered Graph (Zep-inspired):

┌─────────────────────────────────────┐
│ Community Layer (High-level)        │
│ - Clusters of related entities      │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Semantic Layer (Entity Graph)       │
│ - Entities and relationships        │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Episodic Layer (Raw Events)         │
│ - Conversations, commits, events    │
└─────────────────────────────────────┘

Pattern 2: Multi-Modal Memory (MIRIX-inspired):

class KnowledgeGraph:
    def __init__(self):
        self.core_memory = CoreMemoryStore()       # Persistent facts
        self.episodic = EpisodicMemoryStore()      # Time-stamped events
        self.semantic = SemanticMemoryStore()      # Entity relationships
        self.procedural = ProceduralMemoryStore()  # How-to knowledge
        self.resources = ResourceMemoryStore()     # Documents, code

    def update(self, event):
        # Route to appropriate memory store
        if event.type == "conversation":
            self.episodic.add(event)
            entities = self.extract_entities(event)
            self.semantic.add_entities(entities)
        elif event.type == "code_change":
            self.resources.add(event)
            procedures = self.extract_procedures(event)
            self.procedural.add(procedures)

Pattern 3: Hybrid Storage:

# Vector embeddings + Graph structure
class HybridKnowledgeStore:
    def __init__(self):
        self.graph = Neo4jDriver()
        self.vectors = VectorDatabase()  # FAISS, Pinecone, etc.

    def add_entity(self, entity):
        # Store in graph
        self.graph.create_node(entity)

        # Store embedding
        embedding = self.embed(entity.description)
        self.vectors.add(entity.id, embedding)

    def search(self, query):
        # 1. Vector similarity search (semantic)
        candidates = self.vectors.search(query, top_k=50)

        # 2. Graph traversal (structural)
        related = self.graph.expand(candidates, depth=2)

        # 3. Rerank by combined score
        return self.rerank(candidates + related)

Integration Strategies

1. Incremental Knowledge Extraction:

def process_event(event, kg):
    # Extract entities
    entities = extract_entities(event.content)

    # Resolve to existing entities (deduplication)
    for entity in entities:
        existing = kg.find_similar(entity, threshold=0.85)
        if existing:
            kg.merge(existing, entity)
        else:
            kg.create(entity)

    # Extract relationships
    relationships = extract_relationships(entities)
    kg.add_relationships(relationships)

    # Update community structure
    kg.recompute_communities()

2. Query Patterns:

# Multi-modal retrieval
def retrieve_context(query, kg):
    # Semantic search
    entities = kg.semantic.search(query, top_k=10)

    # Find related entities (graph traversal)
    expanded = kg.graph.expand(entities, depth=2)

    # Find relevant episodes (temporal)
    episodes = kg.episodic.search(
        entities=expanded,
        time_range=(now - 30_days, now)
    )

    # Find procedures (how-to)
    procedures = kg.procedural.find_by_entities(expanded)

    return {
        "entities": expanded,
        "episodes": episodes,
        "procedures": procedures
    }

3. Consistency Maintenance:

def handle_contradiction(kg, new_fact, old_fact):
    # Temporal invalidation (Zep pattern)
    if new_fact.contradicts(old_fact):
        # Don't delete old fact, mark as invalidated
        old_fact.t_invalid = new_fact.t_valid
        old_fact.invalidated_by = new_fact.id
        kg.update(old_fact)
        kg.add(new_fact)

4. Memory Systems in AI Agents

Academic & Industry Approaches

Foundational Memory Types (inspired by human cognition):

1. Episodic Memory

Definition: Time-stamped, context-rich records of specific events.

Structure:

(e:Episode {
  id: "episode_001",
  timestamp: datetime("2025-11-02T14:30:00Z"),
  event_type: "conversation|commit|error|success",
  summary: "User asked about authentication bug",
  details: "Full conversation transcript...",
  actor: "user_id_123",
  context: {
    "file": "auth.py",
    "function": "verify_token",
    "error": "TokenExpired"
  }
})

// Link to extracted entities
(e:Episode)-[:MENTIONS]->(entity:Entity)

Implementation Pattern:

class EpisodicMemory:
    def add_episode(self, event):
        episode = {
            "id": generate_id(),
            "timestamp": event.timestamp,
            "type": event.type,
            "summary": summarize(event.content),
            "details": event.content,
            "actor": event.user_id
        }

        # Store in graph
        self.db.create_node("Episode", episode)

        # Extract and link entities
        entities = extract_entities(event.content)
        for entity in entities:
            self.db.create_relationship(
                episode["id"],
                "MENTIONS",
                entity.id
            )

    def search(self, query, time_range=None):
        # Temporal + semantic search
        results = self.db.query("""
            MATCH (e:Episode)-[:MENTIONS]->(entity:Entity)
            WHERE entity.name CONTAINS $query
              AND e.timestamp > $start
              AND e.timestamp < $end
            RETURN e
            ORDER BY e.timestamp DESC
        """, query=query, start=time_range[0], end=time_range[1])
        return results

Use Cases for Coding Assistants:

  • Debugging history: "What did we try last time we saw this error?"
  • Conversation recall: "What did the user say about authentication earlier?"
  • Learning from mistakes: "Have we fixed this pattern before?"

2. Semantic Memory

Definition: Generalized, time-independent knowledge about entities and relationships.

Structure:

// Entities (concepts, functions, patterns)
(e:Entity {
  id: "entity_function_login",
  type: "Function|Class|Pattern|Concept",
  name: "login",
  summary: "Authenticates user credentials",
  details: "Function located in auth.py, handles username/password validation",
  source_files: ["auth.py:45-67"],
  created_at: datetime(),
  updated_at: datetime()
})

// Relationships (how things relate)
(e1:Entity {name: "login"})-[r:CALLS {
  frequency: 45,  // Called 45 times in codebase
  confidence: 0.98
}]->(e2:Entity {name: "verify_password"})

(e1:Entity {name: "User"})-[:HAS_PROPERTY {
  name: "email",
  type: "string",
  required: true
}]->(e2:Entity {type: "Property"})

Implementation Pattern:

class SemanticMemory:
    def add_entity(self, entity):
        # Check for duplicates
        existing = self.find_similar(entity)
        if existing:
            self.merge(existing, entity)
        else:
            self.db.create_node("Entity", entity)

    def find_similar(self, entity, threshold=0.85):
        # Hybrid search: embedding + graph
        embedding = self.embed(entity.name + " " + entity.summary)

        # Vector similarity
        candidates = self.vector_db.search(embedding, top_k=10)

        # Rerank by name similarity and context
        for candidate in candidates:
            score = self.similarity_score(entity, candidate)
            if score > threshold:
                return candidate
        return None

    def query(self, query_text):
        # Natural language query to graph traversal
        entities = self.extract_entities(query_text)

        # Graph traversal
        results = self.db.query("""
            MATCH (e1:Entity)-[r]-(e2:Entity)
            WHERE e1.name IN $entities
            RETURN e1, r, e2
        """, entities=[e.name for e in entities])

        return results

Use Cases for Coding Assistants:

  • Code understanding: "What does this function do?"
  • Relationship discovery: "What depends on this class?"
  • Pattern recognition: "What other code follows this pattern?"

3. Procedural Memory

Definition: Step-by-step knowledge of how to perform tasks.

Structure:

(p:Procedure {
  id: "proc_fix_import_error",
  name: "Fix Import Error",
  description: "How to resolve Python import errors",
  trigger_pattern: "ModuleNotFoundError|ImportError",
  entry_type: "workflow|guide|script",
  steps: [
    "1. Check if module is installed (pip list)",
    "2. Verify PYTHONPATH includes module location",
    "3. Check for circular imports",
    "4. Ensure __init__.py exists in package"
  ],
  success_rate: 0.87,
  times_used: 23,
  last_used: datetime()
})

// Link to relevant entities
(p:Procedure)-[:APPLIES_TO]->(e:Entity {type: "ErrorType"})
(p:Procedure)-[:REQUIRES]->(tool:Entity {type: "Tool", name: "pip"})

Implementation Pattern:

class ProceduralMemory:
    def add_procedure(self, procedure):
        proc = {
            "id": generate_id(),
            "name": procedure.name,
            "description": procedure.description,
            "steps": procedure.steps,
            "trigger_pattern": procedure.trigger,
            "success_rate": 0.5,  # Initial neutral
            "times_used": 0
        }
        self.db.create_node("Procedure", proc)

    def find_procedure(self, context):
        # Match context to procedure triggers
        results = self.db.query("""
            MATCH (p:Procedure)
            WHERE p.trigger_pattern =~ $pattern
            RETURN p
            ORDER BY p.success_rate DESC, p.times_used DESC
        """, pattern=self.extract_pattern(context))
        return results

    def record_outcome(self, procedure_id, success):
        # Update success rate
        proc = self.db.get_node(procedure_id)
        proc.times_used += 1

        # Exponential moving average
        alpha = 0.1
        proc.success_rate = (
            alpha * (1 if success else 0) +
            (1 - alpha) * proc.success_rate
        )
        proc.last_used = datetime.now()
        self.db.update_node(proc)

Use Cases for Coding Assistants:

  • Error resolution: "How do we fix this error?"
  • Task automation: "Steps to add a new API endpoint"
  • Best practices: "How to properly structure a Python package"

4. Working Memory

Definition: Short-term, active context for current task.

Structure (typically in-memory, not persisted):

class WorkingMemory:
    def __init__(self):
        self.current_file = None
        self.current_function = None
        self.recent_context = deque(maxlen=10)  # Last 10 interactions
        self.active_entities = set()  # Entities in current focus
        self.active_goal = None

    def update(self, event):
        # Add to recent context
        self.recent_context.append(event)

        # Extract entities mentioned
        entities = extract_entities(event.content)
        self.active_entities.update(entities)

        # Detect goal/intent
        if event.type == "user_request":
            self.active_goal = extract_goal(event.content)

    def get_context(self):
        # Return current context for LLM
        return {
            "current_file": self.current_file,
            "current_function": self.current_function,
            "recent_interactions": list(self.recent_context),
            "active_entities": list(self.active_entities),
            "goal": self.active_goal
        }

Use Cases for Coding Assistants:

  • Conversation continuity: "As I mentioned earlier..."
  • Context-aware suggestions: Relevant to current file/function
  • Multi-turn task tracking: "Continue implementing the feature we discussed"

5. Prospective Memory

Definition: Future-oriented memory for planned actions and intentions.

Structure:

(i:Intention {
  id: "intent_001",
  goal: "Implement rate limiting",
  created_at: datetime(),
  due_date: datetime("2025-11-05T00:00:00Z"),
  priority: "high",
  status: "planned|in_progress|completed|cancelled",
  trigger_condition: "when API refactoring is complete",
  subtasks: [
    "Research rate limiting libraries",
    "Design rate limit strategy",
    "Implement middleware",
    "Add tests"
  ]
})

// Link to relevant entities
(i:Intention)-[:DEPENDS_ON]->(e:Entity {name: "API refactoring"})
(i:Intention)-[:MODIFIES]->(e:Entity {type: "Module", name: "api.py"})

Implementation Pattern:

class ProspectiveMemory:
    def add_intention(self, goal, trigger=None, due_date=None):
        intention = {
            "id": generate_id(),
            "goal": goal,
            "created_at": datetime.now(),
            "due_date": due_date,
            "trigger_condition": trigger,
            "status": "planned"
        }
        self.db.create_node("Intention", intention)

    def check_triggers(self, current_state):
        # Check if any intentions should activate
        results = self.db.query("""
            MATCH (i:Intention)
            WHERE i.status = 'planned'
              AND (i.due_date < datetime() OR i.trigger_condition IN $events)
            RETURN i
        """, events=current_state.recent_events)

        for intention in results:
            self.activate(intention)

    def activate(self, intention):
        # Move intention to working memory
        intention.status = "in_progress"
        self.db.update_node(intention)
        notify_user(f"Ready to work on: {intention.goal}")

Use Cases for Coding Assistants:

  • TODO tracking: "Remind me to add error handling after this works"
  • Conditional tasks: "When tests pass, create a PR"
  • Long-term goals: "We should refactor this module next sprint"

Knowledge Graph Integration Patterns

Pattern 1: Unified Graph Model (Zep Architecture):

┌─────────────────────────────────────────────────────────┐
│                    RETRIEVAL LAYER                      │
│  - Semantic search (embeddings)                         │
│  - Graph traversal (relationships)                      │
│  - Temporal queries (time-based)                        │
│  - Hybrid reranking (multiple signals)                  │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│              COMMUNITY LAYER (High-level)               │
│  (c:Community {summary, entity_ids, created_at})        │
│  - Clusters of related entities                         │
│  - High-level summaries                                 │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│            SEMANTIC LAYER (Entity Graph)                │
│  (e:Entity)-[r:RELATES_TO]->(e2:Entity)                 │
│  - Extracted entities                                   │
│  - Relationships between entities                       │
│  - Temporal validity tracking                           │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│          EPISODIC LAYER (Raw Events)                    │
│  (ep:Episode {timestamp, content, actor})               │
│  - Conversations                                        │
│  - Code commits                                         │
│  - Errors and resolutions                               │
│  - Non-lossy storage                                    │
└─────────────────────────────────────────────────────────┘

Implementation:

class UnifiedMemoryGraph:
    def __init__(self, neo4j_driver):
        self.driver = neo4j_driver
        self.vector_db = VectorDatabase()

    def ingest_event(self, event):
        # 1. Store raw episode (non-lossy)
        episode = self.create_episode(event)

        # 2. Extract entities (semantic layer)
        entities = self.extract_entities(event.content)
        for entity in entities:
            existing = self.find_or_create_entity(entity)
            # Link episode to entity
            self.link(episode, "MENTIONS", existing)

        # 3. Extract relationships (semantic layer)
        relationships = self.extract_relationships(entities)
        for rel in relationships:
            self.create_relationship(rel)

        # 4. Update communities (incremental)
        affected_communities = self.find_communities(entities)
        for community in affected_communities:
            self.update_community_summary(community)

    def retrieve(self, query, top_k=10):
        # Multi-stage retrieval

        # Stage 1: Semantic search (vector similarity)
        embedding = self.embed(query)
        candidate_entities = self.vector_db.search(embedding, top_k=50)

        # Stage 2: Graph traversal (structural)
        expanded = self.graph_expand(candidate_entities, depth=2)

        # Stage 3: Temporal filtering
        recent = self.filter_by_recency(expanded)

        # Stage 4: Episode retrieval
        episodes = self.get_episodes(recent)

        # Stage 5: Reranking
        reranked = self.rerank(
            entities=recent,
            episodes=episodes,
            query=query
        )

        return reranked[:top_k]

Pattern 2: Multi-Modal Memory System (MIRIX Architecture):

class MultiModalMemory:
    """
    Six specialized memory components + meta-manager
    """
    def __init__(self):
        # Memory components
        self.core = CoreMemory()           # Persistent facts (agent + user)
        self.episodic = EpisodicMemory()   # Time-stamped events
        self.semantic = SemanticMemory()   # Entity relationships
        self.procedural = ProceduralMemory()  # Workflows and procedures
        self.resource = ResourceMemory()   # Documents, code files
        self.knowledge_vault = KnowledgeVault()  # Sensitive data

        # Meta-manager (routing)
        self.meta_manager = MetaMemoryManager()

    def process_event(self, event):
        # Meta-manager routes to appropriate memory stores
        routing = self.meta_manager.route(event)

        for component, instructions in routing.items():
            memory = getattr(self, component)
            memory.update(event, instructions)

    def retrieve(self, query):
        # Generate current topic
        topic = self.meta_manager.extract_topic(query)

        # Retrieve from all components in parallel
        results = {
            "core": self.core.retrieve(topic),
            "episodic": self.episodic.retrieve(topic),
            "semantic": self.semantic.retrieve(topic),
            "procedural": self.procedural.retrieve(topic),
            "resource": self.resource.retrieve(topic)
            # knowledge_vault requires explicit permission
        }

        # Tag by source
        tagged = self.tag_sources(results)

        # Format for LLM
        return self.format_for_llm(tagged)

Memory Component Details:

class CoreMemory:
    """High-priority persistent information"""
    def __init__(self):
        self.persona = {}  # Agent identity
        self.human = {}    # User facts
        self.max_tokens = 2048

    def update(self, key, value):
        if self.compute_tokens() > self.max_tokens * 0.9:
            self.rewrite()  # Compress and summarize
        self.persona[key] = value

class EpisodicMemory:
    """Time-stamped event log"""
    def add(self, event):
        episode = {
            "id": generate_id(),
            "timestamp": event.timestamp,
            "event_type": event.type,
            "summary": summarize(event),
            "details": event.content,
            "actor": event.actor
        }
        self.db.create_node("Episode", episode)

class SemanticMemory:
    """Entity knowledge graph"""
    def add_entity(self, entity):
        # Deduplication via embedding similarity
        existing = self.find_similar(entity)
        if existing:
            self.merge(existing, entity)
        else:
            self.db.create_node("Entity", entity)

class ProceduralMemory:
    """How-to knowledge"""
    def add_procedure(self, procedure):
        proc = {
            "type": "workflow|guide|script",
            "description": procedure.description,
            "steps": procedure.steps,
            "success_rate": 0.5
        }
        self.db.create_node("Procedure", proc)

class ResourceMemory:
    """Documents and files"""
    def add_resource(self, resource):
        res = {
            "title": resource.title,
            "type": resource.type,
            "summary": summarize(resource.content),
            "content_excerpt": resource.content[:1000],
            "file_path": resource.path
        }
        self.db.create_node("Resource", res)

class KnowledgeVault:
    """Sensitive information with access control"""
    def add(self, key, value, sensitivity="high"):
        # Encrypted storage
        encrypted = self.encrypt(value)
        self.db.create_node("Secret", {
            "key": key,
            "value": encrypted,
            "sensitivity": sensitivity,
            "requires_permission": True
        })

Advanced Retrieval Strategies

1. Hybrid Search (Semantic + Structural):

def hybrid_search(query, kg):
    # Semantic search (vector similarity)
    query_embedding = embed(query)
    semantic_results = kg.vector_search(query_embedding, top_k=50)

    # Structural search (graph traversal)
    entities = extract_entities(query)
    structural_results = kg.graph_query("""
        MATCH (e:Entity)-[*1..2]-(related)
        WHERE e.name IN $entities
        RETURN related
    """, entities=entities)

    # Combine and rerank
    combined = semantic_results + structural_results
    reranked = reciprocal_rank_fusion(combined)
    return reranked[:10]

2. Temporal Reasoning:

def temporal_query(query, kg, time_context):
    # Extract temporal aspects
    temporal_refs = extract_temporal_references(query)
    # Examples: "yesterday", "last week", "when we started the project"

    # Convert to absolute time ranges
    time_ranges = resolve_temporal_refs(temporal_refs, time_context)

    # Query with temporal constraints
    results = kg.query("""
        MATCH (e:Episode)-[:MENTIONS]->(entity:Entity)
        WHERE e.timestamp > $start AND e.timestamp < $end
        RETURN e, entity
        ORDER BY e.timestamp DESC
    """, start=time_ranges["start"], end=time_ranges["end"])

    return results

3. Multi-Hop Reasoning:

def multi_hop_reasoning(query, kg, max_hops=3):
    # Start with seed entities
    seed_entities = extract_entities(query)

    # Iteratively expand
    results = []
    current_entities = seed_entities

    for hop in range(max_hops):
        # Find related entities
        related = kg.query("""
            MATCH (e:Entity)-[r]-(related:Entity)
            WHERE e.id IN $entities
            RETURN related, r, e
        """, entities=[e.id for e in current_entities])

        # Score by relevance (decay by hop distance)
        for rel in related:
            rel.score *= (0.7 ** hop)

        results.extend(related)
        current_entities = [r.related for r in related]

    # Rerank by combined score
    return sorted(results, key=lambda x: x.score, reverse=True)

4. Contradiction Detection:

def detect_contradictions(new_fact, kg):
    # Find facts about same entities
    related_facts = kg.query("""
        MATCH (e1:Entity)<-[r1]-(f1:Fact)-[r2]->(e2:Entity)
        WHERE e1.id = $entity1 AND e2.id = $entity2
          AND f1.t_invalid IS NULL  // Only active facts
        RETURN f1
    """, entity1=new_fact.entity1, entity2=new_fact.entity2)

    # Check for contradictions
    for old_fact in related_facts:
        if contradicts(new_fact, old_fact):
            # Temporal invalidation (don't delete, mark invalid)
            old_fact.t_invalid = new_fact.t_valid
            old_fact.invalidated_by = new_fact.id
            kg.update(old_fact)

    # Add new fact
    kg.add(new_fact)

Performance Characteristics

Zep Benchmarks:

  • Deep Memory Retrieval: 94.8% accuracy (gpt-4-turbo)
  • LongMemEval: 18.5% improvement, 90% latency reduction (2.58s vs 28.9s)
  • Context compression: 1.6k tokens (from 115k)
  • Strong in temporal reasoning (+38.4%), multi-session (+30.7%)

MIRIX Benchmarks:

  • 35% improvement over RAG baselines
  • 99.9% storage reduction vs RAG
  • 410% improvement over long-context baselines
  • 93.3% storage reduction vs long-context
  • LOCOMO benchmark: 85.38% accuracy (8% improvement)

5. Implementation Recommendations for Coding Assistants

Architecture Proposal: Code-Aware Memory Graph

Core Design:

┌───────────────────────────────────────────────────────────┐
│                    CODING ASSISTANT                       │
│  - Natural language interface                             │
│  - Code generation                                        │
│  - Debugging support                                      │
└───────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────┐
│               MEMORY RETRIEVAL ENGINE                     │
│  - Query understanding                                    │
│  - Multi-modal retrieval                                  │
│  - Context assembly                                       │
└───────────────────────────────────────────────────────────┘
┌──────────────┬──────────────┬──────────────┬─────────────┐
│   Episodic   │   Semantic   │  Procedural  │  Code Graph │
│   Memory     │   Memory     │   Memory     │             │
│              │              │              │             │
│ - Convos     │ - Entities   │ - Workflows  │ - Functions │
│ - Commits    │ - Relations  │ - Patterns   │ - Classes   │
│ - Errors     │ - Facts      │ - Fixes      │ - Deps      │
└──────────────┴──────────────┴──────────────┴─────────────┘
┌───────────────────────────────────────────────────────────┐
│                    NEO4J GRAPH DATABASE                   │
│  - Unified graph storage                                  │
│  - Temporal tracking                                      │
│  - Cypher queries                                         │
└───────────────────────────────────────────────────────────┘

Component Specifications

1. Episodic Memory (Conversation & Events):

// Schema
(ep:Episode {
  id: "ep_001",
  timestamp: datetime(),
  type: "conversation|commit|error|test_run|refactor",
  summary: "User fixed authentication bug",
  details: "Full transcript...",
  actor: "user_id",
  files_modified: ["auth.py", "test_auth.py"],
  outcome: "success|failure|partial"
})

// Relationships
(ep:Episode)-[:MENTIONS]->(entity:Entity)
(ep:Episode)-[:MODIFIES]->(file:CodeFile)
(ep:Episode)-[:RESOLVES]->(error:Error)

Implementation:

class EpisodicMemory:
    def record_conversation(self, messages, outcome=None):
        episode = {
            "id": generate_id(),
            "timestamp": datetime.now(),
            "type": "conversation",
            "summary": summarize(messages),
            "details": json.dumps(messages),
            "actor": messages[0].user_id,
            "outcome": outcome
        }

        # Create episode node
        self.db.create_node("Episode", episode)

        # Extract and link entities
        entities = extract_entities(messages)
        for entity in entities:
            self.link_entity(episode["id"], entity)

    def record_code_change(self, commit):
        episode = {
            "id": generate_id(),
            "timestamp": commit.timestamp,
            "type": "commit",
            "summary": commit.message,
            "details": commit.diff,
            "actor": commit.author,
            "files_modified": commit.files
        }
        self.db.create_node("Episode", episode)

        # Link to modified files
        for file in commit.files:
            self.db.create_relationship(
                episode["id"], "MODIFIES", file
            )

2. Semantic Memory (Code Entities & Relationships):

// Code entities
(f:Function {
  id: "func_login",
  name: "login",
  signature: "def login(username: str, password: str) -> User",
  file_path: "auth.py",
  line_start: 45,
  line_end: 67,
  docstring: "Authenticates user credentials",
  complexity: 8,  // Cyclomatic complexity
  last_modified: datetime()
})

(c:Class {
  id: "class_user",
  name: "User",
  file_path: "models.py",
  methods: ["__init__", "save", "delete"],
  attributes: ["id", "username", "email"]
})

(e:Error {
  id: "error_001",
  type: "ImportError",
  message: "Module 'requests' not found",
  frequency: 3,  // Times encountered
  last_seen: datetime()
})

// Relationships
(f1:Function)-[:CALLS {line: 52}]->(f2:Function)
(f:Function)-[:DEFINED_IN]->(file:CodeFile)
(c:Class)-[:HAS_METHOD]->(f:Function)
(f:Function)-[:RAISES]->(e:Error)
(f:Function)-[:IMPORTS]->(m:Module)

Implementation:

class SemanticMemory:
    def index_codebase(self, codebase_path):
        # Use blarify or tree-sitter for parsing
        from blarify import CodeGraph

        graph = CodeGraph(
            codebase_path=codebase_path,
            use_scip=True  # 330x faster
        )
        graph.build()

        # Export to Neo4j
        graph.export_to_neo4j(
            uri=self.neo4j_uri,
            auth=self.neo4j_auth
        )

    def update_on_file_change(self, file_path, content):
        # Incremental update (don't rebuild entire graph)

        # 1. Parse changed file
        ast = parse_file(file_path, content)

        # 2. Extract entities
        functions = extract_functions(ast)
        classes = extract_classes(ast)

        # 3. Update graph
        for func in functions:
            existing = self.db.find_node("Function", name=func.name, file=file_path)
            if existing:
                self.db.update_node(existing.id, func)
            else:
                self.db.create_node("Function", func)

        # 4. Update relationships (calls, imports)
        calls = extract_calls(ast)
        for call in calls:
            self.db.create_relationship(
                call.caller, "CALLS", call.callee, {"line": call.line}
            )

3. Procedural Memory (Debugging Patterns & Workflows):

// Procedures
(p:Procedure {
  id: "proc_fix_import",
  name: "Fix Import Error",
  description: "Standard workflow for resolving import errors",
  trigger_pattern: "ImportError|ModuleNotFoundError",
  steps: [
    "Check if module installed (pip list)",
    "Verify PYTHONPATH",
    "Check for circular imports",
    "Ensure __init__.py exists"
  ],
  success_rate: 0.87,
  times_used: 23,
  avg_time_to_resolve: 180  // seconds
})

// Link to error types
(p:Procedure)-[:FIXES]->(e:Error {type: "ImportError"})

// Link to required tools
(p:Procedure)-[:USES_TOOL]->(t:Tool {name: "pip"})

Implementation:

class ProceduralMemory:
    def learn_procedure(self, problem, solution_steps, outcome):
        # Extract procedure from successful resolution
        procedure = {
            "id": generate_id(),
            "name": extract_procedure_name(problem),
            "description": summarize(problem),
            "trigger_pattern": extract_trigger(problem),
            "steps": solution_steps,
            "success_rate": 1.0 if outcome == "success" else 0.0,
            "times_used": 1
        }

        self.db.create_node("Procedure", procedure)

        # Link to error types
        error_type = extract_error_type(problem)
        self.db.create_relationship(
            procedure["id"], "FIXES", error_type
        )

    def find_procedure(self, problem):
        # Match problem to procedure triggers
        error_type = extract_error_type(problem)

        results = self.db.query("""
            MATCH (p:Procedure)-[:FIXES]->(e:Error)
            WHERE e.type = $error_type
            RETURN p
            ORDER BY p.success_rate DESC, p.times_used DESC
        """, error_type=error_type)

        return results[0] if results else None

    def update_success_rate(self, procedure_id, success):
        # Exponential moving average
        proc = self.db.get_node(procedure_id)
        alpha = 0.1
        proc.success_rate = (
            alpha * (1 if success else 0) +
            (1 - alpha) * proc.success_rate
        )
        proc.times_used += 1
        self.db.update_node(proc)

4. Code Graph Integration:

Use blarify or tree-sitter to generate code graph, then integrate with memory system:

class CodeMemoryIntegration:
    def __init__(self, codebase_path):
        self.episodic = EpisodicMemory()
        self.semantic = SemanticMemory()
        self.procedural = ProceduralMemory()
        self.code_graph = CodeGraph(codebase_path)

    def on_code_change(self, file_path, content):
        # 1. Update code graph
        self.code_graph.update_file(file_path, content)

        # 2. Create episode
        episode = self.episodic.record_code_change({
            "file": file_path,
            "content": content,
            "timestamp": datetime.now()
        })

        # 3. Update semantic entities
        entities = self.code_graph.extract_entities(file_path)
        for entity in entities:
            self.semantic.update_entity(entity)

    def on_error(self, error):
        # 1. Record episode
        episode = self.episodic.record_error(error)

        # 2. Check for known procedure
        procedure = self.procedural.find_procedure(error)

        if procedure:
            return {
                "procedure": procedure,
                "confidence": procedure.success_rate
            }
        else:
            # Search for similar past errors
            similar = self.episodic.find_similar_episodes(
                error, type="error"
            )
            return {
                "similar_cases": similar,
                "confidence": 0.5
            }

Deployment Strategy

Phase 1: Local Development (Weeks 1-2):

  • Docker container for Neo4j Community Edition
  • Single project, single database
  • Focus on core memory types (episodic, semantic)

Phase 2: Code Graph Integration (Weeks 3-4):

  • Integrate blarify for code parsing
  • Build code entity relationships
  • Test incremental updates

Phase 3: Procedural Learning (Weeks 5-6):

  • Implement procedural memory
  • Learn from debugging sessions
  • Build pattern library

Phase 4: Multi-Project (Weeks 7-8):

  • Per-project databases
  • Shared pattern library
  • Cross-project learning

Code Examples

Complete Integration Example:

from neo4j import GraphDatabase
from blarify import CodeGraph
import datetime

class CodingAssistantMemory:
    def __init__(self, codebase_path, neo4j_uri, neo4j_auth):
        self.driver = GraphDatabase.driver(neo4j_uri, auth=neo4j_auth)
        self.codebase_path = codebase_path

        # Initialize memory components
        self.episodic = EpisodicMemory(self.driver)
        self.semantic = SemanticMemory(self.driver)
        self.procedural = ProceduralMemory(self.driver)

        # Initialize code graph
        self.code_graph = CodeGraph(codebase_path, use_scip=True)

    def setup(self):
        """Initial setup: index codebase"""
        print("Indexing codebase...")
        self.code_graph.build()
        self.code_graph.export_to_neo4j(
            uri=self.driver._pool.address[0],
            auth=self.driver._pool.auth
        )
        print("Codebase indexed successfully")

    def record_conversation(self, messages, files_mentioned=None):
        """Record a conversation with user"""
        episode_id = self.episodic.record({
            "type": "conversation",
            "messages": messages,
            "timestamp": datetime.datetime.now(),
            "files": files_mentioned or []
        })

        # Extract entities and link
        entities = self.extract_entities(messages)
        for entity in entities:
            self.semantic.link_episode_to_entity(episode_id, entity)

        return episode_id

    def retrieve_context(self, query, max_results=10):
        """Retrieve relevant context for query"""

        # Multi-modal retrieval

        # 1. Semantic search (code entities)
        code_entities = self.semantic.search(query, type="code")

        # 2. Find related code via graph traversal
        related_code = self.code_graph.expand(code_entities, depth=2)

        # 3. Search episodes (conversations, errors)
        episodes = self.episodic.search(query, time_range="30d")

        # 4. Find applicable procedures
        procedures = self.procedural.search(query)

        # 5. Rerank and combine
        context = self.rerank({
            "code": related_code,
            "episodes": episodes,
            "procedures": procedures
        }, query)

        return context[:max_results]

    def on_error(self, error_info):
        """Handle error occurrence"""

        # 1. Record episode
        episode_id = self.episodic.record({
            "type": "error",
            "error": error_info,
            "timestamp": datetime.datetime.now()
        })

        # 2. Find similar past errors
        similar = self.episodic.find_similar(error_info, type="error")

        # 3. Find applicable procedure
        procedure = self.procedural.find_by_error_type(error_info.type)

        # 4. Assemble suggestions
        suggestions = {
            "procedure": procedure,
            "similar_cases": similar,
            "confidence": procedure.success_rate if procedure else 0.3
        }

        return suggestions

    def learn_from_resolution(self, error_id, resolution_steps, success):
        """Learn from error resolution"""

        # 1. Update episode with resolution
        self.episodic.update(error_id, {
            "resolution": resolution_steps,
            "success": success
        })

        # 2. Update or create procedure
        procedure = self.procedural.learn(
            error_id=error_id,
            steps=resolution_steps,
            success=success
        )

        # 3. Update success rates
        if procedure:
            self.procedural.update_success_rate(procedure.id, success)

    def close(self):
        self.driver.close()


# Usage
memory = CodingAssistantMemory(
    codebase_path="./src",
    neo4j_uri="bolt://localhost:7687",
    neo4j_auth=("neo4j", "password")
)

# Initial setup
memory.setup()

# Record conversation
messages = [
    {"role": "user", "content": "How do I fix this ImportError?"},
    {"role": "assistant", "content": "Let me check..."}
]
episode_id = memory.record_conversation(messages, files_mentioned=["auth.py"])

# Retrieve context for new query
context = memory.retrieve_context("authentication bug")

# Handle error
error = {
    "type": "ImportError",
    "message": "Module 'requests' not found",
    "file": "auth.py",
    "line": 10
}
suggestions = memory.on_error(error)

# Learn from resolution
memory.learn_from_resolution(
    error_id=episode_id,
    resolution_steps=["pip install requests", "verify PYTHONPATH"],
    success=True
)

memory.close()

Performance Optimization Patterns

1. Batch Operations:

def batch_create_nodes(nodes):
    query = """
    UNWIND $batch as node
    CREATE (n:Entity)
    SET n = node
    """
    driver.execute_query(query, batch=nodes)

2. Index Strategy:

def create_indexes(driver):
    indexes = [
        "CREATE INDEX entity_name IF NOT EXISTS FOR (e:Entity) ON (e.name)",
        "CREATE INDEX episode_timestamp IF NOT EXISTS FOR (ep:Episode) ON (ep.timestamp)",
        "CREATE INDEX function_file IF NOT EXISTS FOR (f:Function) ON (f.file_path)",
    ]
    for index in indexes:
        driver.execute_query(index)

3. Query Optimization:

// Use index hints
MATCH (e:Entity)
USING INDEX e:Entity(name)
WHERE e.name = 'login'
RETURN e

// Limit depth in traversals
MATCH (f:Function)-[:CALLS*1..3]->(called)
RETURN called

// Use LIMIT early
MATCH (e:Episode)
WHERE e.timestamp > datetime() - duration({days: 30})
RETURN e
ORDER BY e.timestamp DESC
LIMIT 10

Data Lifecycle Management

1. Archiving Old Episodes:

def archive_old_episodes(cutoff_date):
    # Move old episodes to archive database
    query = """
    MATCH (e:Episode)
    WHERE e.timestamp < $cutoff
    WITH e, properties(e) as props
    CALL apoc.export.json.data([e], [], 'archive.json', {})
    DELETE e
    """
    driver.execute_query(query, cutoff=cutoff_date)

2. Periodic Cleanup:

def cleanup_stale_nodes():
    # Remove entities with no relationships and old timestamp
    query = """
    MATCH (e:Entity)
    WHERE NOT (e)--()
      AND e.created_at < datetime() - duration({days: 90})
    DELETE e
    """
    driver.execute_query(query)

3. Community Refresh:

def refresh_communities():
    # Recompute communities periodically
    query = """
    CALL gds.labelPropagation.stream({
        nodeProjection: 'Entity',
        relationshipProjection: 'RELATES_TO'
    })
    YIELD nodeId, communityId
    MATCH (e:Entity)
    WHERE id(e) = nodeId
    SET e.community_id = communityId
    """
    driver.execute_query(query)

6. Key Discoveries & Lessons Learned

Critical Insights

  1. Neo4j Community Edition is sufficient for per-project memory stores
  2. No clustering needed (local deployment)
  3. Performance adequate for typical code graph sizes (10k-1M nodes)
  4. Cold backups acceptable (integrate with version control)

  5. SCIP provides massive performance gains (330x faster than LSP)

  6. Worth the setup overhead for medium-large codebases
  7. Precomputed indexes enable real-time code analysis
  8. Fallback to LSP for unsupported languages

  9. Temporal tracking is essential for coding assistants

  10. Code changes over time (track evolution)
  11. Contradictions common (bugs introduced, then fixed)
  12. Bi-temporal model (event time vs. knowledge time)

  13. Multi-modal memory architecture outperforms single approaches

  14. Episodic + Semantic + Procedural > any one alone
  15. Different queries need different memory types
  16. Unified graph enables cross-modal reasoning

  17. Incremental updates critical for performance

  18. Don't rebuild entire graph on file change
  19. Update only affected nodes/relationships
  20. Use SCIP's incremental indexing

  21. Hybrid search (vector + graph) beats either alone

  22. Vector similarity finds semantically related content
  23. Graph traversal finds structurally related content
  24. Reciprocal rank fusion combines signals effectively

Practical Recommendations

Start Simple, Then Extend:

  1. Week 1: Episodic memory (conversations, errors)
  2. Week 2: Semantic memory (entities, relationships)
  3. Week 3: Code graph integration (blarify/tree-sitter)
  4. Week 4: Procedural memory (learn from resolutions)

Tech Stack:

  • Database: Neo4j Community Edition (Docker)
  • Code parsing: Blarify (with SCIP) or tree-sitter
  • Python driver: Official neo4j driver (6.0+)
  • Vector embeddings: Sentence-transformers or OpenAI
  • Graph queries: Cypher + Python logic

Performance Targets:

  • Query latency: < 100ms (p95)
  • Context retrieval: < 2s (p95)
  • Code graph update: < 1s per file
  • Memory footprint: < 500MB per project

Deployment Pattern:

# Per-project Neo4j container
docker-compose.yml:
  neo4j-project:
    image: neo4j:latest
    volumes:
      - ./.amplihack/memory/data:/data
    environment:
      - NEO4J_AUTH=neo4j/${PROJECT_PASSWORD}
    ports:
      - "7687:7687"

Backup Strategy:

# Daily backup to git
.amplihack/memory/backups/
  ├── graph_2025-11-02.json
  ├── graph_2025-11-01.json
  └── ...

# Restore from backup
python -m amplihack.memory.restore --date 2025-11-02

Anti-Patterns to Avoid

  1. Don't concatenate Cypher queries (use parameters)
  2. Don't rebuild graph on every file change (incremental updates)
  3. Don't store large content in graph (store references, not full text)
  4. Don't ignore temporal dimension (track when knowledge changed)
  5. Don't use py2neo (deprecated, use official driver)
  6. Don't attempt embedded Neo4j in Python (use Docker/Desktop)

Research Gaps & Future Work

Admiral-KG Investigation:

  • Unable to locate github.com/rysweet/admiral-kg
  • May be private, internal, or misremembered URL
  • Recommend direct confirmation of correct repository

Additional Research Areas:

  • Neo4j vs. other graph DBs (FalkorDB, Memgraph) for this use case
  • Optimal community detection algorithms for code graphs
  • Entity deduplication strategies (fuzzy matching, embeddings)
  • Multi-project knowledge sharing patterns
  • Privacy-preserving memory systems (local-first, encryption)

7. Complete Reference Implementation

See code example above for complete, production-ready implementation.

Key Files:

  • /home/azureuser/src/MicrosoftHackathon2025-AgenticCoding/.amplihack/memory/
  • memory_system.py - Main memory system
  • episodic.py - Episodic memory component
  • semantic.py - Semantic memory component
  • procedural.py - Procedural memory component
  • code_graph.py - Code graph integration
  • config.json - Configuration
  • docker-compose.yml - Neo4j deployment

8. Actionable Next Steps

Immediate (Week 1):

  1. Set up Neo4j Community Edition (Docker)
  2. Implement basic episodic memory (conversations)
  3. Create schema for episodes and entities

Short-term (Weeks 2-4): 4. Integrate blarify for code graph generation 5. Implement semantic memory (entity relationships) 6. Build retrieval system (hybrid search)

Medium-term (Weeks 5-8): 7. Add procedural memory (learn from resolutions) 8. Implement per-project deployment 9. Build backup/restore system

Long-term (Months 2-3): 10. Cross-project knowledge sharing 11. Advanced retrieval (multi-hop reasoning) 12. Performance optimization (caching, indexes)


Appendix: Additional Resources

Neo4j Documentation:

  • Python Driver: https://neo4j.com/docs/api/python-driver/current/
  • Cypher Manual: https://neo4j.com/docs/cypher-manual/current/
  • Operations Manual: https://neo4j.com/docs/operations-manual/current/

Code Graph Tools:

  • Blarify: https://github.com/blarApp/blarify
  • Tree-sitter: https://tree-sitter.github.io/tree-sitter/
  • SCIP: https://github.com/sourcegraph/scip

Memory Systems Research:

  • Zep Paper: https://arxiv.org/html/2501.13956v1
  • MIRIX Paper: https://arxiv.org/html/2507.07957v1
  • IBM AI Memory: https://www.ibm.com/think/topics/ai-agent-memory

Neo4j Performance:

  • Driver Best Practices: https://neo4j.com/developer-blog/neo4j-driver-best-practices/
  • Performance Recommendations: https://neo4j.com/docs/python-manual/current/performance/

End of Report

Generated by Knowledge-Archaeologist Agent Date: 2025-11-02 Research Session: MicrosoftHackathon2025-AgenticCoding