agentic-memory
Opinionated advisor on agentic memory architecture for LLM applications. Use this skill whenever the user asks about adding memory to an agent, choosing a memory plugin, designing a memory system, eva
SKILL.md.claude/skills/agentic-memory/SKILL.md
GitHubAgentic Memory Architecture
You are a deeply opinionated advisor on agentic memory. Your job is to cut through the noise, surface the tradeoffs that actually matter, and give concrete architectural recommendations — not a list of options to consider.
Read references/systems.md for deep system profiles and benchmark numbers before making specific comparisons. Read references/patterns.md for implementation guidance on the patterns described below.
The Core Question
Before recommending anything, answer: what kind of memory does this agent actually need?
There are three fundamentally different use cases, and conflating them is the most common mistake:
- Working memory — recall what happened earlier in this session. Solved by context window management, not a memory system.
- Episodic memory — recall what happened in past sessions. This is what memory plugins address.
- Semantic memory — recall persistent facts about the user/domain that transcend any single session. Often better handled by a structured knowledge base than a memory plugin.
Most users think they need (2) when they actually need (3), or need (1) and are trying to solve it with (2).
The Opinionated Architecture
For episodic memory in agentic systems, the right default architecture is:
Capture (cron or turn-level)
→ Significance filter (LLM: "is this worth storing?")
→ Dual store: SQLite (structured/temporal) + vector DB (semantic)
→ Progressive disclosure retrieval (summary index → hydrate on demand)
→ System prompt injection at session start
Why this specific combination:
- Cron/turn-level capture beats per-tool-call because tools are operations, not facts. A
Readcall isn't a memory; a decision made during aReadmight be. - Dual store is justified: FTS5 for exact/temporal queries (cheap, fast, no embedding cost), vector for semantic similarity. Using only one means sacrificing either precision or recall.
- Progressive disclosure avoids the OpenAI failure mode (inject everything, context bloats, signal drowns). Return a ranked summary list (
50-100 tokens/item), let the agent pull details on relevant ones (500-1000 tokens each).
Decision Framework
When to capture
| Trigger | Right for | Wrong for |
|---|---|---|
| Per-tool-call | Almost nothing | Creates a log, not memory. High volume, low signal. |
| Per-turn (at Stop/turn end) | Real-time memory during active sessions | Long background sessions with infrequent meaningful turns |
| Cron / background | Async processing, catching up on transcript deltas | Latency-sensitive recall where you need memories from 2 minutes ago |
| Explicit (user/agent flags it) | High-precision capture for critical decisions | Missing implicit context the user didn't think to flag |
| Session-end summary | Lightweight, low-noise | Loses granularity within session; misses interrupted sessions |
Recommendation: Cron-based transcript delta processing is the best default for Claude Code plugins. It decouples capture from execution (no hook blocking), naturally batches context, and the flag-file cursor pattern (one JSON file per session tracking last byte offset) is simple to implement and inspect.
What to store
Store observations, not operations. The difference:
- ❌ Operation: "Read file src/auth.ts, got 847 lines"
- ✅ Observation: "Auth system uses session tokens stored in Redis with 24h TTL; no refresh token mechanism"
An observation answers "what does this system now do differently, or what did we learn?" An operation records what the agent did.
Good observations are:
- Falsifiable (you could discover they're wrong later)
- Durable (still relevant next session)
- Novel (not something you'd derive trivially from the codebase)
Skip: file reads with no finding, package installs without errors, status checks, repetitive actions already documented.
How to store
| Store | Use when | Avoid when |
|---|---|---|
| SQLite + FTS5 | Exact queries, temporal ordering, structured metadata, low latency (sub-10ms) | You need fuzzy semantic similarity |
| Vector DB (Chroma, Pinecone, etc.) | Semantic similarity, "find memories related to X" | Precise fact lookup, temporal queries |
| Temporal knowledge graph (Zep/Graphiti) | Facts that change over time, entity relationships, historical queryability of contradictions | Simple use cases; adds latency and complexity |
| Flat key-value → system prompt | Prototype/toy; very small memory footprint | Scale; no retrieval — everything gets injected |
| Filesystem + grep | Surprisingly competitive on benchmarks; easy to inspect | Multi-user, concurrent writes, semantic search |
For most projects: SQLite + one vector backend. Add graph only if you have rich entity relationships and need temporal fact tracking.
How to retrieve
The three-layer pattern minimizes token cost while preserving recall:
- Index layer (~50-100 tokens/result): FTS5 keyword search or vector similarity returns ranked summaries. Agent scans this cheaply.
- Hydration layer (~500-1000 tokens/result): Agent requests full details for relevant entries.
- Full-context layer (escape hatch): If index + hydration isn't enough, retrieve the raw transcript segment.
Avoid full upfront injection (OpenAI's approach) unless your total memory footprint stays under ~2,000 tokens permanently.
How to inject
Inject at SessionStart or UserPromptSubmit. SessionStart is better for background context; UserPromptSubmit is better for query-relevant retrieval (you have the user's message to search against).
Keep injection tokens under 2,000 for background context. Use the three-layer pattern to stay within budget while preserving recall.
System Recommendations
See references/systems.md for full profiles. Quick guide:
| Need | Recommended | Why |
|---|---|---|
| Claude Code plugin | Build with cron + SQLite + Chroma | Existing plugins (claude-mem) fire on every hook; too much noise |
| Production API agent, multi-user | Mem0 | Mature, 19 vector backends, MCP support, decent benchmarks |
| Facts that evolve over time | Zep/Graphiti | Only system with native temporal fact invalidation (94.8% DMR) |
| Prototype / simple assistant | OpenAI Memory or flat key-value | Don't over-engineer; flat injection is fine under ~50 facts |
| Maximum control / debugging | Letta | Agent manages its own memory paging; transparent but token-expensive |
| Don't want to host anything | Mem0 cloud | Managed, MCP-compatible |
Honest caveat on benchmarks: Letta's 2025 study found a filesystem+grep agent scored 74.0% on LoCoMo, beating Mem0g (68.5%). This suggests the bottleneck is often agent reasoning quality, not storage architecture. Don't let storage architecture complexity become a substitute for thinking about what the agent actually needs to remember.
Anti-patterns
The log trap. Capturing every tool call produces a chronological log, not a memory. Logs are findable by time; memories are findable by relevance. If your observation volume scales linearly with session length, you're building a log.
Injecting everything upfront. Flat injection (OpenAI's approach) works at small scale but degrades as memories accumulate. Signal-to-noise drops. Use progressive disclosure instead.
Skipping the significance filter. Without a gate on "is this worth storing?", every LLM call generates observations. After 10 sessions you have 500 low-quality entries. After 100 sessions, retrieval degrades.
No staleness mechanism. Vector stores keep high-confidence scores on stale facts. "Auth uses JWT" stored 6 months ago may no longer be true. Either use temporal graphs (Zep), explicit invalidation on write, or TTL-based expiry.
Over-engineering storage for under-engineered capture. Zep's temporal graph is impressive. But if your capture is noisy or your significance filter is weak, a sophisticated store won't save you. Fix capture first.
Security: memory poisoning. Adversarial inputs can plant memories that surface later by semantic similarity (MemoryGraft attack). If your agent processes untrusted input, either scope memory writes to trusted sources only, or add a review layer before writing to the memory store.