|
| 1 | +--- |
| 2 | +name: dag-recall |
| 3 | +version: "1.0" |
| 4 | +category: openclaw-native |
| 5 | +description: Walks the memory DAG to recall detailed context on demand — query, expand, and assemble cited answers from hierarchical summaries without re-reading raw transcripts. |
| 6 | +stateful: true |
| 7 | +--- |
| 8 | + |
| 9 | +# DAG Recall |
| 10 | + |
| 11 | +## What it does |
| 12 | + |
| 13 | +When the agent needs to recall something from past sessions, reading raw transcripts is expensive and often exceeds context limits. DAG Recall walks the hierarchical summary DAG built by memory-dag-compactor — starting from high-level (d2/d3) nodes, expanding into detailed (d0/d1) children — and assembles a focused, cited answer. |
| 14 | + |
| 15 | +Inspired by [lossless-claw](https://github.com/Martian-Engineering/lossless-claw)'s sub-agent recall pattern, where a lightweight agent fetches and expands nodes on demand rather than loading entire conversation histories. |
| 16 | + |
| 17 | +## When to invoke |
| 18 | + |
| 19 | +- When the agent asks "what did we decide about X?" or "how did we implement Y?" |
| 20 | +- When context about a past session is needed but the transcript isn't loaded |
| 21 | +- When searching MEMORY.md returns only high-level summaries that need expansion |
| 22 | +- Before starting work that depends on decisions or patterns from earlier sessions |
| 23 | + |
| 24 | +## How to use |
| 25 | + |
| 26 | +```bash |
| 27 | +python3 recall.py --query "how did we handle auth migration" # Walk DAG + assemble answer |
| 28 | +python3 recall.py --query "deploy process" --depth 2 # Limit expansion depth |
| 29 | +python3 recall.py --query "API keys" --top 5 # Return top 5 matching nodes |
| 30 | +python3 recall.py --expand s-d1-003 # Expand a specific node |
| 31 | +python3 recall.py --trace s-d0-012 # Show full ancestor chain |
| 32 | +python3 recall.py --recent --hours 48 # Recall from recent nodes only |
| 33 | +python3 recall.py --status # Last recall summary |
| 34 | +python3 recall.py --format json # Machine-readable output |
| 35 | +``` |
| 36 | + |
| 37 | +## Recall algorithm |
| 38 | + |
| 39 | +1. **Search** — FTS5 query across all DAG node summaries |
| 40 | +2. **Rank** — Score by relevance × recency × depth (deeper = more detailed = higher score for recall) |
| 41 | +3. **Expand** — For each top-N match, walk to children (lower depth = more detail) |
| 42 | +4. **Assemble** — Combine expanded content into a coherent answer with node citations |
| 43 | +5. **Cache** — Store the assembled answer for fast re-retrieval |
| 44 | + |
| 45 | +### Expansion strategy |
| 46 | + |
| 47 | +``` |
| 48 | +Query: "auth migration" |
| 49 | + ↓ |
| 50 | +d3 node: "Infrastructure & Auth overhaul Q1" (score: 0.72) |
| 51 | + → expand d2: "Auth migration week of Feb 10" (score: 0.89) |
| 52 | + → expand d1: "Migrated JWT signing from HS256 to RS256" (score: 0.95) |
| 53 | + → expand d0: [raw operational detail — returned as-is] |
| 54 | +``` |
| 55 | + |
| 56 | +Expansion stops when: |
| 57 | +- Target depth reached (default: expand to d0) |
| 58 | +- Token budget exhausted (default: 4000 tokens) |
| 59 | +- No children exist (leaf node) |
| 60 | + |
| 61 | +## DAG structure expected |
| 62 | + |
| 63 | +Reads from `~/.openclaw/lcm-dag/` (same directory as memory-dag-compactor): |
| 64 | + |
| 65 | +``` |
| 66 | +~/.openclaw/lcm-dag/ |
| 67 | +├── index.json # Node metadata: id, depth, summary, children, created_at |
| 68 | +├── nodes/ |
| 69 | +│ ├── s-d0-001.md # Leaf node (operational detail) |
| 70 | +│ ├── s-d1-001.md # Condensed summary |
| 71 | +│ ├── s-d2-001.md # Arc summary |
| 72 | +│ └── s-d3-001.md # Durable summary |
| 73 | +└── fts.db # FTS5 index over node summaries |
| 74 | +``` |
| 75 | + |
| 76 | +## Procedure |
| 77 | + |
| 78 | +**Step 1 — Query the DAG** |
| 79 | + |
| 80 | +```bash |
| 81 | +python3 recall.py --query "how did we handle the database migration" |
| 82 | +``` |
| 83 | + |
| 84 | +Searches the FTS5 index, ranks results, expands top matches, and assembles a cited answer: |
| 85 | + |
| 86 | +``` |
| 87 | +Recall: "how did we handle the database migration" — 3 sources |
| 88 | +
|
| 89 | + We migrated the database schema using Alembic with a blue-green |
| 90 | + deployment strategy. The key decisions were: |
| 91 | +
|
| 92 | + 1. Zero-downtime migration using shadow tables [s-d1-003] |
| 93 | + 2. Rollback script tested against staging first [s-d0-012] |
| 94 | + 3. Data backfill ran as async job over 2 hours [s-d0-015] |
| 95 | +
|
| 96 | + Sources: |
| 97 | + [s-d1-003] "Database migration — shadow table approach" (Feb 12) |
| 98 | + [s-d0-012] "Alembic rollback script for users table" (Feb 12) |
| 99 | + [s-d0-015] "Async backfill job for legacy records" (Feb 13) |
| 100 | +``` |
| 101 | + |
| 102 | +**Step 2 — Expand a specific node** |
| 103 | + |
| 104 | +```bash |
| 105 | +python3 recall.py --expand s-d1-003 |
| 106 | +``` |
| 107 | + |
| 108 | +Shows the full content of a node and lists its children for further expansion. |
| 109 | + |
| 110 | +**Step 3 — Trace lineage** |
| 111 | + |
| 112 | +```bash |
| 113 | +python3 recall.py --trace s-d0-012 |
| 114 | +``` |
| 115 | + |
| 116 | +Shows the full ancestor chain from leaf to root, revealing how detail connects to high-level themes. |
| 117 | + |
| 118 | +## Integration with other skills |
| 119 | + |
| 120 | +- **memory-dag-compactor**: Produces the DAG that this skill reads — must be run first |
| 121 | +- **session-persistence**: Alternative data source — recall can fall back to SQLite search when DAG nodes are insufficient |
| 122 | +- **context-assembly-scorer**: Recall results feed into context assembly scoring |
| 123 | +- **memory-integrity-checker**: Ensures DAG is structurally sound before recall walks it |
| 124 | + |
| 125 | +## State |
| 126 | + |
| 127 | +Recall history and cache stored in `~/.openclaw/skill-state/dag-recall/state.yaml`. |
| 128 | + |
| 129 | +Fields: `last_query`, `last_query_at`, `cache_size`, `total_recalls`, `recall_history`. |
| 130 | + |
| 131 | +## Notes |
| 132 | + |
| 133 | +- Uses Python's built-in `sqlite3` and `json` modules — no external dependencies |
| 134 | +- FTS5 used for search when available; falls back to substring matching |
| 135 | +- Token budget prevents runaway expansion on large DAGs |
| 136 | +- Cache is LRU with configurable max size (default: 50 entries) |
| 137 | +- If DAG doesn't exist yet, prints a helpful message pointing to memory-dag-compactor |
0 commit comments