Skip to content

Neo4j Knowledge Graph

Overview

The Neo4j Knowledge Graph is a shared graph database that stores domain-labeled nodes and relationships across all agents in the system. It serves as the central knowledge layer, enabling agents to query contextually relevant information based on their persona and role.

The schema is defined in the agents-shared package (src/agents_shared/schemas/) and is consumed by individual agent repositories (e.g., wisp, minh).

Architecture

Knowledge Domains

Nodes are organized into four knowledge domains (defined in agents_shared.schemas.domains.KnowledgeDomain):

Domain Neo4j Label Primary Owner(s) Purpose
Literacy :Literacy Wisp Vocabulary, learning events, personal repos
Team :Team Minh Projects, documents, team members, features
Game :Game Minh Lore, locations, quests
Pedagogy :Pedagogy All agents Teaching methods, learning styles, learning paths

Domain access is persona-context-aware: agents in a team context can access Team, Literacy, and Pedagogy domains; agents in a game context can access Game, Literacy, and Pedagogy domains.

Node Types

All domain-specific nodes inherit from DomainNodeBase (in agents_shared.schemas.nodes), which provides:

  • get_domain() — returns the node's KnowledgeDomain
  • get_node_type() — returns the primary Neo4j label (e.g., "Word")
  • to_neo4j_labels() — generates combined label string (e.g., :Literacy:Word)
  • to_cypher_properties() — converts the model to a Cypher-compatible dict

Character nodes (non-domain): AgentNode, PersonaNode, PlayerNode, FamiliarNode

Literacy domain: WordNode, PersonalRepoNode, LearningEventNode

Team domain: ProjectNode, DocumentNode, TeamMemberNode, FeatureNode

Game domain: LoreNode, LocationNode, QuestNode

Pedagogy domain: TeachingMethodNode, LearningStyleNode, LearningPathNode

Relationships

Relationships are defined as RelationshipType models in agents_shared.schemas.relationships. Key relationships include:

Relationship Source → Target Description
HAS_PERSONA AI → Persona Agent has a context-specific persona
BONDED_TO Familiar → Player 1:1 familiar-player bond
COLLABORATES_WITH AI → AI Inter-agent collaboration
KNOWS Player → Word Vocabulary mastery (with mastery_level, times_correct, times_incorrect)
OBSERVED Familiar → LearningEvent Wisp observed a learning event
EXPERIENCED_BY LearningEvent → Player Event experienced by a player
CURATES AI → Document/Lore Agent curates knowledge
DOCUMENTED_IN Feature → Document Feature-to-doc mapping (includes is_stale flag)
IMPLEMENTED_IN Feature → Repository Feature-to-code mapping

Agents in the Graph

The graph seeds the following agents (defined in SEED_AGENTS in agents_shared.schemas.cypher):

Agent Type Role Personas
Minh Coworker Knowledge Architect / Librarian team + game
Wisp Familiar Personal 1:1 tutor
Roland Coworker Steward of Collaboration team + game (Tavern Keeper)
Pearl Coworker Customer Lifecycle Architect team + game (Merchant)
Arturo Coworker Product Lead team
Taryn Coworker The Smith team
Chisel Coworker Guardian team

Usage

Schema Setup

The cypher.py module provides Cypher query strings for database initialization:

from agents_shared.schemas.cypher import (
    CREATE_CONSTRAINTS,    # Uniqueness constraints
    CREATE_INDEXES,        # Performance indexes
    CREATE_DOMAIN_INDEXES, # Domain-specific composite indexes
    SEED_AGENTS,           # Agent and persona seed data
)

Run these in order against a Neo4j instance to bootstrap the schema.

Creating Nodes

from agents_shared.schemas.nodes import WordNode

word = WordNode(word="ephemeral", definition="lasting a very short time")

# Get Neo4j labels for the CREATE/MERGE statement
labels = WordNode.to_neo4j_labels()  # ":Literacy:Word"

# Get properties dict for Cypher parameters
props = word.to_cypher_properties()

Domain Queries

from agents_shared.schemas.domains import (
    get_domains_for_persona_context,
    validate_node_type_for_domain,
    KnowledgeDomain,
)

# What domains can a "team" persona access?
domains = get_domains_for_persona_context("team")
# [KnowledgeDomain.TEAM, KnowledgeDomain.PEDAGOGY, KnowledgeDomain.LITERACY]

# Validate a node type belongs to a domain
validate_node_type_for_domain(KnowledgeDomain.LITERACY, "Word")  # True

Pre-built Query Templates

cypher.py exports parameterized Cypher query templates:

  • QUERY_PLAYER_INSIGHTS — get learning events and struggling words for a player
  • QUERY_AGENT_PERSONAS — get all personas for a named agent
  • RECORD_LEARNING_EVENT — write a new learning event from Wisp
  • QUERY_STRUGGLING_WORDS — find words a player has struggled with repeatedly
  • QUERY_LEARNING_SUMMARY — summarized learning stats over a time window
  • QUERY_FRUSTRATION_CHECK — quick frustration-level check (recent hours)
  • QUERY_NEAR_MASTERY_WORDS — words close to mastery threshold
  • QUERY_DOMAIN_STATS — node counts per domain

Configuration

Environment Variables

The Neo4jClient class (agents_shared.knowledge.client) reads connection settings from three environment variables:

Variable Default Description
NEO4J_URI bolt://localhost:7687 Bolt protocol URI for the Neo4j instance
NEO4J_USER neo4j Authentication username
NEO4J_PASSWORD (empty string) Authentication password

Note: The shared client defaults NEO4J_PASSWORD to an empty string for local development convenience. Minh's knowledge scripts (init_knowledge_graph.py, import_knowledge.py) enforce a stricter policy — they require NEO4J_PASSWORD to be set and raise a RuntimeError if it is absent. This enforcement lives in the Minh repository, not in the shared client.

Client Initialization

The agents-shared package provides three ways to obtain a Neo4j connection:

from agents_shared.knowledge.client import Neo4jClient, get_client, neo4j_session

# 1. Direct instantiation — reads env vars on construction
client = Neo4jClient()

# 2. Singleton accessor — returns or creates a shared instance
client = get_client()

# 3. Async context manager — yields a Neo4j session, handles cleanup
async with neo4j_session() as session:
    result = await session.run("MATCH (n) RETURN count(n)")

Once initialized, the client exposes two primary methods:

  • client.query(cypher, **params) — run a read query, return results
  • client.execute(cypher, **params) — run a write transaction

Prerequisites

  • A running Neo4j instance (version TBD)
  • The agents-shared package installed as a dependency

Privacy Model

Player nodes store only hashed identifiers — no personally identifiable information (PII) is persisted in the graph. The hashing scheme works as follows:

  • PlayerNode.player_hash — a SHA-256 hash of the player's identity. The raw identifier is never stored.
  • FamiliarNode.bonded_player_hash — references the same SHA-256 hash, linking a familiar to its player without exposing PII.
  • PersonalRepoNode.player_hash — associates personal literacy repositories with a player via the same hash.

All player-referencing fields use the player_hash convention. No reverse-lookup table exists in the graph; the hash is one-way by design.

Data Retention

No graph-level retention, purge, or TTL policy is currently implemented. Nodes and relationships persist indefinitely once written.

The only TTL mechanism in the knowledge layer is an in-memory query cache (agents_shared.knowledge.cache) with a 60-second expiry. This cache reduces repeated Neo4j round-trips for identical queries but does not affect the persisted graph data.

Repositories

Repo Path What lives there
agents-shared src/agents_shared/schemas/ Node models, relationship types, Cypher queries, domain definitions
agents-shared src/agents_shared/finance/ Shared finance models (LedgerTransaction, Invoice, TaxDeadline) and utilities for fleet bookkeeping (PostgreSQL-based; see Finance Substrate below)
wisp src/services/knowledge_service.py Wisp's integration layer (WispKnowledgeService — reads/writes learning events)
minh Minh's knowledge-management integrations

Finance Substrate

The agents-shared package also houses a PostgreSQL-based finance substrate (agents_shared.finance) alongside the Neo4j knowledge graph schemas. This is a separate data layer — it uses the coworkers_shared PostgreSQL database (migration 006_finance_ledger.sql), not Neo4j.

The finance substrate provides typed money models for fleet bookkeeping:

  • LedgerTransaction — a signed money movement (positive = inflow, negative = outflow). Only Chisel writes ledger rows (single-writer doctrine); feeder agents (Pearl, Roland, Minh, Spark) emit events into Chisel's ingest path.
  • Invoice — a Pearl-originated accounts-receivable invoice. Status flow: draftapprovedsentpaid / overdue / void. Invoices cannot advance past draft (except to void) without a human approved_by on record — enforced by both a database CHECK constraint and model-level validation.
  • TaxDeadline — a tax/compliance deadline occurrence for Chisel's reminder schedulers.

Key design rules: - Money is integer cents (BIGINT / int). Floats never touch an amount — use parse_amount_to_cents() for parsing and format_cents() for display. - Automated feeder sources must carry an external_ref (idempotency key). Re-emitting the same event is a no-op via UNIQUE (source, external_ref).

from agents_shared.finance import (
    LedgerTransaction,
    Invoice,
    EntrySource,
    InvoiceStatus,
    parse_amount_to_cents,
    format_cents,
)

# Parse a human-readable amount to integer cents
cents = parse_amount_to_cents("$1,234.56")  # 123456

# Format cents for display
display = format_cents(-4200)  # "-$42.00"

The schema is applied via agents_shared.coworkers.connection.apply_migrations() — each agent calls ensure_schema_current() on boot to auto-apply pending migrations.

Wisp — WispKnowledgeService

Wisp's knowledge graph integration lives in wisp/src/services/knowledge_service.py (the path wisp/src/integrations/knowledge_graph.py cited in earlier drafts does not exist — that directory contains only an empty __init__.py).

WispKnowledgeService is a thin wrapper over the agents-shared client and query templates. It provides Wisp-specific convenience methods for reading and writing learning events while degrading gracefully when Neo4j is unavailable — if the graph database is down, Wisp continues to function without knowledge graph features rather than crashing.

  • docs/ai-agents/neo4j-schema.md — Neo4j schema reference
  • agents_shared/coworkers/migrations/006_finance_ledger.sql — Finance substrate DDL (transaction ledger, invoices, AR aging view, tax deadlines)

Feature Repositories

  • agents-shared → project_id: the-smithy1/agents/agents-shared
  • minh → project_id: the-smithy1/agents/minh
  • wisp → project_id: the-smithy1/agents/wisp

Code Paths to Explore

  • src/integrations/knowledge_graph.py in wisp
  • src/agents_shared/schemas/*.py in agents-shared
  • src/agents_shared/finance/models.py in agents-shared

See also

The portable technique in this document has been extracted into client-free patterns. This page keeps the implementation detail; the patterns carry the part that transfers.