Graph Memory¶
Most agentic systems split state across multiple stores: a vector database for retrieval, a document store for raw content, a relational table for task status, and a cache for intermediate results. This fragmentation creates synchronization problems and limits the kinds of queries the system can answer about its own state.
Lethe stores all session state in a single Neo4j graph database. Tasks, evidence, and their relationships coexist in one topology. This design enables Personalized PageRank to traverse both structural relationships (task decomposition, dependency ordering) and semantic similarity links in a single computation, and it eliminates the consistency overhead of maintaining separate stores.
Schema¶
The graph contains two node types and five edge types.
Node Types¶
classDiagram
class TaskNode {
+NodeId id
+ShortId short_id
+str description
+str embed_text
+TaskStatus status
+str result_summary
+str prepared_prompt
+str antifocus
+ndarray embedding
+ndarray result_embedding
+float created_at
+serialize_context()
}
class EvidenceNode {
+NodeId id
+ShortId short_id
+str content
+str embed_text
+float strength
+str source_description
+ndarray embedding
+bool superseded
+serialize_context()
}
TaskNode --> TaskNode : SUBTASK_OF
TaskNode --> TaskNode : DEPENDS_ON
TaskNode --> EvidenceNode : PRODUCED
EvidenceNode --> EvidenceNode : SUPERSEDES
TaskNode --> TaskNode : SIMILAR_TO
EvidenceNode --> EvidenceNode : SIMILAR_TO
TaskNode represents a unit of work in the agent's DAG. Tasks have a lifecycle (PENDING → ACTIVE → DONE or FAILED) and carry both a content embedding and a result embedding. Task creation and mutation happen exclusively through DIN operations.
The embed_text field contains an embedding-optimized text projection --- not the raw description, but a version rewritten by the LLM to embed well with the Qwen3-Embedding model. This dual representation (natural-language description for the LLM, concise 8--12 word embed_text for the vector space) addresses a fundamental mismatch: LLM-generated prose suffers from length collapse and anisotropy when embedded directly, producing vectors that cluster regardless of semantic content. The projection layer produces one sentence stating the single core fact, preserving technical terms and proper nouns.
EvidenceNode represents a finding with a strength score in \([0, 1]\), full content (zero truncation), and a source description for provenance tracking. Evidence nodes are immutable once created; the superseded flag soft-deletes evidence that has been replaced by a synthesis. Strength is derived from goal alignment via linear mapping.
Edge Types¶
| Edge | Direction | Semantics |
|---|---|---|
SUBTASK_OF |
child → parent | Task decomposition hierarchy |
DEPENDS_ON |
dependent → dependency | Execution ordering constraint |
PRODUCED |
task → evidence | Provenance: which task discovered this evidence |
SUPERSEDES |
new → old | Evidence replacement chain |
SIMILAR_TO |
bidirectional | Cosine similarity above threshold |
Strong ID Types¶
Nodes use two identifier systems:
- NodeId --- UUID primary key, used internally for all graph operations
- ShortId --- hex-truncated identifier like git short hashes (
t-a3f1,e-7c0b), used in the DIN protocol for compact LLM communication
The separation prevents the LLM from needing to work with full UUIDs while maintaining referential integrity.
Memory Types¶
The schema above defines the structural vocabulary. These node and edge types implement three distinct memory roles, each serving a different function in the agent's reasoning process:
Working Memory¶
Active tasks represent the agent's current plan. The task DAG encodes decomposition (SUBTASK_OF), ordering (DEPENDS_ON), and execution state (status lifecycle). The ready_set query identifies tasks that are PENDING and have all dependencies satisfied (see ready set):
Episodic Memory¶
Evidence nodes store discovered findings --- web page content, research results, synthesized conclusions. Each evidence node carries:
- Content --- full text, never truncated
- Strength --- quality score derived from goal-alignment (GA), refined by NLI corroboration from independent sources
- Embedding --- 2560-dimensional vector from
embed_textprojection - Source --- description of where the evidence came from
Semantic Memory¶
SIMILAR_TO edges create a sparse semantic graph connecting nodes whose embeddings exceed a cosine similarity threshold (default 0.55, calibrated for Qwen3-Embedding asymmetric prefixes). These edges are synchronized each iteration via a four-phase pipeline: compute candidate pairs, classify via bidirectional DeBERTa NLI (support, contradiction, confidence, independence, equivalence), commit edges, and refine evidence strengths from NLI corroboration.
This semantic graph serves the same role as a nearest-neighbor index in a vector database, but within the graph topology. Personalized PageRank traverses SIMILAR_TO edges alongside structural edges, discovering semantically related content that is not structurally connected. This is a key design decision: rather than maintaining a separate vector store (as in typical RAG architectures), the semantic index lives in the same graph as the structural relationships, enabling a single random walk to traverse both.
Evidence Lifecycle¶
Evidence enters the graph through a multi-stage ingestion pipeline. When the research engine discovers web pages, the following process transforms raw HTML/PDF/video content into embedded, scored, and deduplicated graph nodes:
flowchart TB
TOOL["web_search / fetch_url tool call"]
RESEARCH["ResearchEngine / BoundedCrawlProvider"]
PAGES["Discovered pages"]
CHUNK["chunk.py<br/>paragraph-group chunking"]
EMBED["embed_doc()"]
GA["Goal alignment scoring<br/>soft_gate(GA, ga_floor)"]
DEDUP["Three-layer dedup<br/>URL + hash + cos ≥ 0.95"]
STRENGTH["batch_strengths()<br/>linear GA → strength"]
PERSIST["store.create_node()"]
LINK["PRODUCED edge<br/>task → evidence"]
SYNTH["SYNTHESIZE op<br/>agent-created summary"]
SUPER["SUPERSEDES edge<br/>summary → chunks"]
TOOL --> RESEARCH --> PAGES --> CHUNK
CHUNK --> EMBED --> GA
GA --> DEDUP
DEDUP -->|novel| STRENGTH --> PERSIST --> LINK
DEDUP -->|duplicate| SKIP["skipped"]
LINK -.->|later| SYNTH --> SUPER
Ingestion¶
When the research engine returns discovered pages, the evidence ingestion pipeline processes each page:
-
Chunking --- pages are split into paragraph groups by
chunk_page(). The algorithm splits on markdown headers and double-newlines, then merges consecutive sections that fall below 100 characters. Oversized sections are split at sentence boundaries, with a hard cap as fallback. Target range is 100--500 characters per chunk. Each chunk becomes a separateEvidenceNodelinked to its producing task via aPRODUCEDedge. Pages shorter than 400 characters are stored as a single node. -
Embedding --- each chunk's
embed_textis embedded via the embedding model. The page-level embedding is a position-weighted mean of chunk embeddings (\(w_i = 1/\sqrt{i+1}\), L2-normalized), giving earlier sections higher influence. This page embedding is used for GA scoring and deduplication. -
Goal alignment scoring --- each chunk's goal alignment is computed and mapped to a strength value via linear interpolation. Evidence with low GA receives near-floor strength rather than being dropped --- the
soft_gatepreserves all evidence at reduced influence. -
Deduplication --- three layers prevent redundant evidence: URL fingerprinting and content hashing (checked in
apply_graph_ops/execute_calls_and_ingestbefore ingest), plus semantic similarity (cosine > 0.95 against existing evidence, checked during ingest viaEvidenceIndex). All layers must pass before a new node is created. The three layers address distinct failure modes: URL dedup catches re-fetched pages, hash dedup catches identical content from different URLs, and semantic dedup catches paraphrased versions of the same finding. A single semantic pass would miss exact duplicates (cheaper to detect) and would require embedding every candidate before checking. -
Strength assignment --- linear map:
strength = floor + clamp01(GA) * (ceil - floor), wherefloor=0.30,ceil=0.95(see EmbedConfig). The GA value is already clamped to \([0,1]\) and multiplied bysoft_gate(GA, ga_floor)during the scoring pass, so the strength mapping itself is a simple affine transform.
Synthesis¶
The LLM can issue a SYNTHESIZE operation that creates a new summary evidence node superseding multiple chunk-level nodes. The summary receives a SUPERSEDES edge to each source node, and the source nodes are marked superseded=True. Superseded nodes are excluded from context composition but remain in the graph for provenance.
SIMILAR_TO Synchronization¶
The sync_similar_edges function maintains the semantic memory layer with a
critical design constraint: edges are created only between nodes with
different parents (via PRODUCED or SUBTASK_OF). Evidence chunks from
the same research share topic trivially; SIMILAR_TO provides inter-topic
PPR topology exclusively. This prevents the semantic graph from degenerating
into dense intra-cluster cliques that would overwhelm PageRank diffusion.
The synchronization process:
- Group nodes by parent task
- Sample representative embeddings per group; compute inter-group centroid distances as a pre-filter
- For qualifying cross-parent pairs above threshold (default 0.55), create or update
SIMILAR_TOedges with cosine similarity and 9 NLI-derived scores as properties - Prune edges that have fallen below threshold
This runs at two points: mid-iteration (after task selection, before context expansion) and at session finalization. The threshold (0.55) is calibrated from live Qwen3-Embedding data — cross-group max similarities cluster at 0.46–0.61 with asymmetric doc prefixes.
Node Immutability¶
Terminal nodes (status DONE or FAILED) are immutable. Once a task reaches a terminal state, its content, embeddings, and relationships cannot be modified. This invariant (principle P5) ensures that:
- Completed work cannot be corrupted by later operations
- Failed approaches are preserved as anti-patterns for the LLM
- The task DAG is a reliable audit trail of the agent's reasoning
Replacement of failed work is done by creating new subtasks, not by modifying existing ones.
Graph Store¶
The GraphStore class is the sole write path to Neo4j (principle P6). All mutations go through store.create_node(), store.update_node(), or store.create_edge(), which enforce:
- ACID transactions --- every mutation is wrapped in a Neo4j transaction
- Immutability checks --- attempts to modify terminal nodes raise
ImmutableNodeError - Type validation --- patches are typed (
TaskPatch,EvidencePatch), not arbitrary dicts - Cycle detection --- after operations are applied within
iterate(),detect_cycleschecks the edge set for DAG violations and rolls back the write transaction if found
No other module in the system writes to Neo4j directly. Tools, the research engine, and the LLM produce data structures that the orchestrator applies through the store. Service connection details are in Neo4jConfig.
Further Reading¶
- Lyon, W. "Building Context Graphs for AI Agents." Neo4j Developer Blog, 2025 --- Three-tier graph memory (short-term, long-term, reasoning) with provenance edges. Lethe uses a similar unified-graph approach but substitutes the POLE+O entity ontology with task/evidence domain types and adds embedding algebra over node properties.
- Anokhin, A. et al. "AriGraph: Learning Knowledge Graph World Models with Episodic Memory." 2024 --- Knowledge graphs outperform vector-only memory in complex environments; validates the design choice of graph over flat vector store.
- Gutiérrez, B.J. et al. "HippoRAG: Neurobiologically Inspired Long-Term Memory for LLMs." 2024 --- PPR over knowledge graphs for multi-hop retrieval; Lethe extends this with submodular packing under a token budget.
- Paul, S.K. et al. "GAAMA: Graph Augmented Associative Memory for Agents." 2026 --- Hybrid retrieval combining cosine similarity with edge-type-aware PPR on concept-mediated graphs. Closest recent peer to Lethe's PPR + semantic
SIMILAR_TOdesign, applied to multi-session conversational memory rather than task/evidence DAGs. - Rasmussen, J. et al. "Graphiti: Building Real-Time Knowledge Graphs for Agentic Applications." 2025 --- Neo4j-backed temporal knowledge graphs with hybrid retrieval (BM25 + vectors + graph traversal). Strongest production reference for unified graph memory with provenance; Lethe is session-scoped and task-centric rather than multi-session conversational.