Skip to content

Commit 1bf6528

Browse files
ArchieIndianclaude
andauthored
Add memory-graph-builder: structured knowledge graph from MEMORY.md (#28)
Parses flat MEMORY.md into nodes with categories, entities, and typed relationships. Detects duplicates (Jaccard >0.7), contradictions, and stale entries. Generates compressed memory digest saving 30-60% tokens. Inspired by OpenLobster's Neo4j graph memory. Cron: nightly 10pm. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 17e85a0 commit 1bf6528

4 files changed

Lines changed: 786 additions & 0 deletions

File tree

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
---
2+
name: memory-graph-builder
3+
version: "1.0"
4+
category: openclaw-native
5+
description: Parses OpenClaw's flat MEMORY.md into a structured knowledge graph — detects duplicates, contradictions, and stale entries, then builds a compressed memory digest optimized for system prompt injection.
6+
stateful: true
7+
cron: "0 22 * * *"
8+
---
9+
10+
# Memory Graph Builder
11+
12+
## What it does
13+
14+
OpenClaw stores agent memory in a flat `MEMORY.md` file — one line per fact, no structure, no relationships. This works until your agent has 200+ memories and half of them are duplicates, three contradict each other, and the whole file costs 4,000 tokens every session.
15+
16+
Memory Graph Builder treats MEMORY.md as a raw data source and builds a structured knowledge graph on top of it. Each memory becomes a node with typed relationships to other nodes. The graph enables:
17+
18+
- **Duplicate detection** — "User prefers dark mode" and "User likes dark theme" are the same fact
19+
- **Contradiction detection** — "User uses Python 3.8" vs "User uses Python 3.12"
20+
- **Staleness detection** — Facts older than a configurable threshold that haven't been referenced
21+
- **Memory digest** — A compressed, relationship-aware summary that replaces raw MEMORY.md in the system prompt, saving 30-60% tokens
22+
23+
Inspired by OpenLobster's Neo4j-backed graph memory system, adapted to work on top of OpenClaw's existing MEMORY.md without requiring a database.
24+
25+
## When to invoke
26+
27+
- Automatically, nightly at 10pm (cron)
28+
- After bulk memory additions (e.g., after project-onboarding)
29+
- When the agent's context initialisation feels slow (memory bloat)
30+
- Manually to audit memory quality
31+
32+
## Graph structure
33+
34+
Each memory line becomes a node:
35+
36+
```yaml
37+
nodes:
38+
- id: "mem_001"
39+
text: "User prefers Python for backend work"
40+
category: preference # preference | fact | project | person | tool | config
41+
entities: ["user", "python", "backend"]
42+
added_at: "2026-03-01"
43+
last_referenced: "2026-03-15"
44+
confidence: 0.9
45+
edges:
46+
- from: "mem_001"
47+
to: "mem_014"
48+
relation: related_to # related_to | contradicts | supersedes | depends_on
49+
```
50+
51+
## How to use
52+
53+
```bash
54+
python3 graph.py --build # Parse MEMORY.md, build graph
55+
python3 graph.py --duplicates # Show duplicate clusters
56+
python3 graph.py --contradictions # Show contradicting pairs
57+
python3 graph.py --stale --days 30 # Show memories not referenced in 30 days
58+
python3 graph.py --digest # Generate compressed memory digest
59+
python3 graph.py --digest --max-tokens 1500 # Digest with token budget
60+
python3 graph.py --prune --dry-run # Show what would be removed
61+
python3 graph.py --prune # Remove duplicates + stale entries
62+
python3 graph.py --stats # Graph statistics
63+
python3 graph.py --status # Last build summary
64+
python3 graph.py --format json
65+
```
66+
67+
## Cron wakeup behaviour
68+
69+
Nightly at 10pm:
70+
71+
1. Read MEMORY.md
72+
2. Rebuild graph (incremental — only re-processes new/changed lines)
73+
3. Detect duplicates and contradictions
74+
4. Flag stale entries (>30 days unreferenced by default)
75+
5. Generate fresh memory digest
76+
6. Write digest to `~/.openclaw/workspace/memory-digest.md`
77+
7. Log summary to state
78+
79+
## Memory digest
80+
81+
The digest is a compressed representation of the knowledge graph optimized for LLM consumption. Instead of dumping every raw line, it:
82+
83+
- Groups related memories by category
84+
- Merges duplicate facts into single entries
85+
- Marks contradictions with `[CONFLICT]` so the agent can resolve them
86+
- Omits stale entries below a confidence threshold
87+
- Respects a configurable max-token budget
88+
89+
Example digest output:
90+
91+
```markdown
92+
## Preferences
93+
- Prefers Python for backend, TypeScript for frontend
94+
- Dark mode everywhere; compact UI layouts
95+
- Commit messages: imperative mood, max 72 chars
96+
97+
## Active Projects
98+
- openclaw-superpowers: skill library, 40 skills, MIT license
99+
- personal-site: Next.js 14, deployed on Vercel
100+
101+
## People
102+
- Alice (teammate): works on auth, prefers Go
103+
104+
## Conflicts (needs resolution)
105+
- [CONFLICT] Python version: "3.8" vs "3.12" — ask user to clarify
106+
```
107+
108+
## Procedure
109+
110+
**Step 1 — Build the graph**
111+
112+
```bash
113+
python3 graph.py --build
114+
```
115+
116+
**Step 2 — Review duplicates and contradictions**
117+
118+
```bash
119+
python3 graph.py --duplicates
120+
python3 graph.py --contradictions
121+
```
122+
123+
Fix contradictions by editing MEMORY.md directly or asking the agent to clarify.
124+
125+
**Step 3 — Prune stale entries**
126+
127+
```bash
128+
python3 graph.py --prune --dry-run
129+
python3 graph.py --prune
130+
```
131+
132+
**Step 4 — Generate and use the digest**
133+
134+
```bash
135+
python3 graph.py --digest --max-tokens 1500
136+
```
137+
138+
Point OpenClaw's memory injection at `~/.openclaw/workspace/memory-digest.md` instead of raw MEMORY.md.
139+
140+
## State
141+
142+
Graph structure, digest cache, and audit history stored in `~/.openclaw/skill-state/memory-graph-builder/state.yaml`.
143+
144+
Fields: `last_build_at`, `node_count`, `edge_count`, `duplicate_count`, `contradiction_count`, `stale_count`, `digest_tokens`, `build_history`.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
version: "1.0"
2+
description: Knowledge graph built from MEMORY.md — nodes, edges, digest, and audit metrics.
3+
fields:
4+
last_build_at:
5+
type: datetime
6+
node_count:
7+
type: integer
8+
default: 0
9+
edge_count:
10+
type: integer
11+
default: 0
12+
duplicate_count:
13+
type: integer
14+
default: 0
15+
contradiction_count:
16+
type: integer
17+
default: 0
18+
stale_count:
19+
type: integer
20+
default: 0
21+
digest_tokens:
22+
type: integer
23+
default: 0
24+
nodes:
25+
type: list
26+
description: All memory nodes in the graph
27+
items:
28+
id: { type: string }
29+
text: { type: string }
30+
category: { type: enum, values: [preference, fact, project, person, tool, config, other] }
31+
entities: { type: list, items: { type: string } }
32+
added_at: { type: string }
33+
last_referenced: { type: string }
34+
confidence: { type: float }
35+
is_duplicate_of: { type: string }
36+
is_stale: { type: boolean }
37+
edges:
38+
type: list
39+
description: Relationships between memory nodes
40+
items:
41+
from: { type: string }
42+
to: { type: string }
43+
relation: { type: enum, values: [related_to, contradicts, supersedes, depends_on, duplicate_of] }
44+
weight: { type: float }
45+
build_history:
46+
type: list
47+
description: Rolling log of graph builds (last 20)
48+
items:
49+
built_at: { type: datetime }
50+
node_count: { type: integer }
51+
duplicates_found: { type: integer }
52+
contradictions_found: { type: integer }
53+
stale_found: { type: integer }
54+
digest_tokens: { type: integer }
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Example runtime state for memory-graph-builder
2+
last_build_at: "2026-03-15T22:00:12.000000"
3+
node_count: 48
4+
edge_count: 15
5+
duplicate_count: 4
6+
contradiction_count: 1
7+
stale_count: 3
8+
digest_tokens: 420
9+
build_history:
10+
- built_at: "2026-03-15T22:00:12.000000"
11+
node_count: 48
12+
duplicates_found: 4
13+
contradictions_found: 1
14+
stale_found: 3
15+
digest_tokens: 420
16+
# ── Walkthrough ──────────────────────────────────────────────────────────────
17+
# Nightly cron runs: python3 graph.py --build
18+
#
19+
# Memory Graph Builder — 2026-03-15 22:00
20+
# ────────────────────────────────────────────────────────────────
21+
# Memory lines : 52
22+
# Nodes : 48
23+
# Edges : 15
24+
# Duplicates : 4
25+
# Contradictions : 1
26+
# Stale : 3
27+
# Digest tokens : ~420
28+
#
29+
# Digest written to: ~/.openclaw/workspace/memory-digest.md
30+
#
31+
# python3 graph.py --duplicates
32+
# DUP: "User prefers dark mode for all applications"
33+
# ORIG: "User likes dark theme everywhere"
34+
#
35+
# python3 graph.py --contradictions
36+
# A: "User uses Python 3.8 for backend services"
37+
# B: "User recently upgraded to Python 3.12"
38+
# → Resolve by editing MEMORY.md
39+
#
40+
# python3 graph.py --prune --dry-run
41+
# Dry run — would prune 7 entries:
42+
# [duplicate] "User prefers dark mode for all applications"
43+
# [stale] "Working on migration to React 17"

0 commit comments

Comments
 (0)