Three-Layer Memory System — automatic fact extraction, entity-based knowledge graph, and weekly synthesis. Manages life/areas/ entities with atomic facts and living summaries.
Resources
1Install
npx skillscat add jdrhyne/agent-skills/knowledge-graph Install via the SkillsCat registry.
Knowledge Graph Skill
Manages a Three-Layer Memory System for compounding knowledge across sessions.
Architecture
The Three Layers
- Entity Knowledge (
life/areas/) — Structured facts about people, companies, and projects stored as atomic JSONL entries with living summaries - Daily Notes (
memory/YYYY-MM-DD.md) — Session logs, decisions, and events captured chronologically - Persistent Memory (
MEMORY.mdor equivalent) — High-level patterns, preferences, and agent-wide context
The knowledge graph (Layer 1) is the durable, structured layer. Daily notes feed it; synthesis distills it.
The Compounding Flywheel
Conversations → Daily Notes → Fact Extraction (cron) → Entity Facts
↓
Weekly Synthesis (cron) → Living Summaries
↓
Richer Context → Better ConversationsEvery conversation makes your agent smarter. Facts compound. Summaries stay fresh. Context improves over time.
When to Use
- Fact Extraction (cron): Extract durable facts from recent conversations
- Weekly Synthesis (cron): Rewrite summaries, prune stale facts
- Entity Lookup: When any agent needs context about a person, company, or project
- Manual: User says "remember that...", "update X's info", "what do we know about Y"
Directory Structure
<workspace>/life/areas/
├── people/<slug>/ → summary.md + facts.jsonl
├── companies/<slug>/ → summary.md + facts.jsonl
└── projects/<slug>/ → summary.md + facts.jsonlFact Schema (JSONL — one object per line)
{
"id": "<slug>-NNN",
"fact": "The actual fact in plain English",
"category": "relationship|milestone|status|preference|context|decision",
"ts": "YYYY-MM-DD",
"source": "conversation|manual|inference",
"status": "active|superseded",
"supersedes": "<id>"
}Rules
- Append-only — never edit or delete lines in facts.jsonl
- Supersede, don't delete — when a fact changes, add a new fact with
"supersedes": "<old-id>"and mark the old fact's status as"superseded"(edit that one line) - Auto-increment IDs —
<slug>-001,<slug>-002, etc. - Be atomic — one fact per entry, not paragraphs
- Skip ephemera — no "user said hi", no transient chat
What Qualifies as a Durable Fact
✅ Extract:
- Relationship changes (new job, new team, promotions)
- Life milestones (moved cities, had a baby, started a project)
- Status changes (left company, started freelancing)
- Stated preferences ("I prefer X over Y")
- Key decisions ("decided to use Rust for the backend")
- Important context ("allergic to shellfish", "works night shifts")
❌ Skip:
- Casual conversation, jokes, greetings
- Temporary states ("feeling tired today")
- Already-known facts (check existing facts first)
- Vague or uncertain information
Task: Fact Extraction (Cron — Every 4 Hours)
1. Read today's daily note: memory/YYYY-MM-DD.md
2. Read recent conversation context (last few hours)
3. For each durable fact found:
a. Determine entity type (person/company/project) and slug
b. Create entity folder if new: mkdir -p life/areas/<type>/<slug>
c. Check existing facts.jsonl — skip if already known
d. If fact contradicts existing: supersede the old one
e. Append new fact to facts.jsonl
4. Log extraction count to daily noteCost target: Use the cheapest available model. Should cost < $0.01/day.
Task: Weekly Synthesis (Sunday Cron)
1. List all entities in life/areas/
2. For each entity with facts modified this week:
a. Load all facts from facts.jsonl
b. Filter to status: "active" only
c. Write a new summary.md (3-8 lines):
- Who/what is this entity
- Current relationship/status
- Key active facts
- Last updated date
d. Mark any contradicted facts as superseded
3. Produce a brief synthesis report in daily noteTask: Entity Lookup
When an agent needs context about an entity:
1. Check life/areas/<type>/<slug>/summary.md first (cheap)
2. Only load facts.jsonl if summary is stale or more detail needed
3. Use memory_search as fallback for entities not yet in the graphLow-token Recall Policy
To minimize token usage, recall should be triggered, not automatic.
Rules
Only recall on triggers:
- Proper nouns (names of people, companies, projects you track)
- Explicit recall phrases: "remember", "recall", "what do we know about"
- Project keywords that match entity slugs
Inject summary.md only (max 5 lines) — never inject facts.jsonl unless:
- User explicitly asks for details/specifics
- Summary is stale or missing
- Contradictory information needs resolution
Use a single profile summary when the topic is preferences or planning
Why This Matters
- No recall unless triggered — most messages skip recall entirely
- Summaries only — very short injections (5 lines vs potentially hundreds of facts)
- No raw facts unless requested — keeps context window lean
Add to AGENTS.md
### Low-token Recall Policy
- Only recall on triggers (proper nouns, "remember/recall", or project keywords).
- Inject summary.md only (max 5 lines); never inject facts.jsonl unless asked.
- Use a single profile summary when preferences/planning are the topic.Add to HEARTBEAT.md
## Low-token Recall (Rule)
- [ ] Only recall on triggers (proper nouns, "remember/recall", project keywords)
- [ ] Inject summary.md only (max 5 lines) unless user explicitly asks for detailsCreating New Entities
When you encounter a new person/company/project worth tracking:
# Create structure
mkdir -p life/areas/people/alice
# Write initial fact
echo '{"id":"alice-001","fact":"Frontend engineer at Acme Corp, works on the design system","category":"context","ts":"2026-01-15","source":"conversation","status":"active"}' > life/areas/people/alice/facts.jsonl
# Write initial summary
cat > life/areas/people/alice/summary.md << 'EOF'
# Alice
Frontend engineer at Acme Corp.
Works on the design system team.
_Last updated: 2026-01-15_
EOFIntegration with Other Layers
- Layer 2 (Daily Notes) → Source material for fact extraction
- Layer 3 (MEMORY.md) → Patterns/preferences stay there; entity facts come here
- memory_search → Indexes summaries for semantic lookup
Setup
1. Create Directory Structure
mkdir -p life/areas/people
mkdir -p life/areas/companies
mkdir -p life/areas/projectsOptionally create a life/README.md explaining the structure for your own reference.
2. Add to AGENTS.md
Add this block to your workspace's AGENTS.md:
## Knowledge Graph — Three-Layer Memory
### Layer 1: Entity Knowledge (`life/areas/`)
- `people/` — Person entities
- `companies/` — Company/org entities
- `projects/` — Project entities
Each entity has: `summary.md` (quick context) + `facts.jsonl` (atomic facts).
**Retrieval order:** summary.md first → facts.jsonl only if more detail needed.
**Rules:**
- Save durable facts to the relevant entity's `facts.jsonl` (append-only JSONL)
- Never delete facts — supersede instead (`"status":"superseded","supersedes":"old-id"`)
- When encountering a new notable entity, create its folder + initial fact + summary
- Cron handles periodic extraction and weekly synthesis — manual saves welcome too
See `skills/knowledge-graph/SKILL.md` and `life/README.md` for full conventions.3. Create Cron Jobs
Set up two cron jobs in your Clawdbot config:
Fact Extraction — runs every 4 hours:
crons:
- name: fact-extraction
schedule: "0 */4 * * *"
task: >
Run the fact extraction task from skills/knowledge-graph/SKILL.md.
Read today's daily notes and recent conversations.
Extract durable facts into life/areas/ entities.
Use the cheapest available model.Weekly Synthesis — runs every Sunday:
- name: weekly-synthesis
schedule: "0 9 * * 0"
task: >
Run the weekly synthesis task from skills/knowledge-graph/SKILL.md.
Rewrite summaries for all entities modified this week.
Mark contradicted facts as superseded.
Log a synthesis report to today's daily note.4. Multi-Agent Setups (Optional)
If you run multiple agents that share a workspace, symlink life/ so all agents read from the same knowledge graph:
# From each agent's workspace
ln -s /path/to/primary-workspace/life ./lifeAll agents will read/write to the same entity store. The cron jobs only need to run from one agent.