Agent Memory¶
Getting started? The Agent Memory Quickstart is a progressive 6-level adoption guide. This page is the map: what each piece is for, how the pieces compose, and where the full reference for each one lives.
Popoto Agent Memory is a set of Redis-backed ORM primitives for programmable memory. Records decay over time, strengthen through use, carry confidence that moves with evidence, form associations, and get assembled into LLM context within a token budget.
They are generic field types, mixins, and query methods. They encode no
particular agent architecture. You compose them into memory models the same way
you compose KeyField, SortedField, and Relationship into any Popoto model.
What LLMs cannot do for themselves¶
A language model reasons well over whatever is in its context window. What it cannot do unaided is decide what belongs there:
- Prioritize by recency and importance. Know which records are hot now.
- Learn from outcomes. Track what worked and what did not.
- Manage certainty. Downweight contradicted knowledge automatically.
- Retrieve associatively. Surface related records without an explicit graph query.
- Filter noise. Keep low-value observations out of storage entirely.
Each primitive below covers one of those. Each is independently useful.
The 14 primitives¶
| Primitive | What it does | Full reference |
|---|---|---|
| DecayingSortedField | Time-weighted scoring: records lose relevance unless refreshed | page |
| CyclicDecayField | Temporal rhythms and homeostatic pressure on top of decay | page |
| ConfidenceField | Capped-evidence Bayesian certainty: corroboration strengthens, contradiction weakens | page |
| CoOccurrenceField | Weighted association edges, strengthened by co-access, traversed by BFS | page |
| BM25Field | Ranked keyword search in Redis sorted sets, and what makes retrieval query-sensitive | page |
| CompositeScoreQuery | Multi-factor retrieval: combine N sorted indexes with weights, server-side | page |
| ExistenceFilter | Bloom filter for O(1) "do I know anything about X?" | page |
| FrequencySketch | Count-Min Sketch for approximate frequency counting | reference |
| PredictionLedger | Record a prediction, observe the outcome, feed the error back into confidence | page |
| ObservationProtocol | Outcome-driven effects: acted, dismissed, deferred, contradicted, used | page |
| AccessTrackerMixin | Two-stage read tracking: reads stage cheaply, then promote on confirmation | reference |
| WriteFilterMixin | Gates persistence: low-value records are discarded before they reach Redis | reference |
| EventStreamMixin | Append-only mutation log via Redis Streams | reference |
| TagField | Optional multi-value scoping for a centrally hosted Redis serving many agents | reference |
The layers composed on top¶
These are recipes and policy layers built from the primitives above. They are counted separately, never summed into the primitive count.
| Layer | What it does | Full reference |
|---|---|---|
| ContextAssembler | The capstone: one assemble() call runs query-driven retrieval, proactive surfacing, budgeting, and formatting |
page |
| Hybrid Retrieval | Fuses keyword, vector, and graph signals via weighted Reciprocal Rank Fusion | page |
| SubconsciousMemory | Wraps a chat loop: inject before the model call, extract after, report outcomes | recipe |
| MemoryLifecycle | Episodic-to-semantic promotion, confidence-aware forgetting, restorable tombstones | recipe |
| PolicyCache | Reinforcement-learning-style action selection over crystallized state-action-outcome patterns | page |
| StreamConsumer | Consumer-group framework for background processing of the mutation stream | recipe |
| Metacognitive Layer | Retrieval-quality scoring, grouped error analysis, and a self-adjusting assembler | page |
How the pieces compose¶
┌──────────────────────────────┐
your turn loop ───► │ SubconsciousMemory │
└──────────────┬───────────────┘
▼
┌──────────────────────────────┐
│ ContextAssembler │
└──────────────┬───────────────┘
┌─────────────────┼─────────────────┐
▼ ▼ ▼
pull (query) push (proactive) post-effects
ExistenceFilter CyclicDecayField AccessTracker
BM25Field / RRF surfacing threshold ObservationProtocol
CompositeScoreQuery competitive suppression
CoOccurrence BFS
│ │ │
└─────────────────┼─────────────────┘
▼
merge, dedupe, token budget
▼
AssemblyResult → your prompt
underneath, on every record:
DecayingSortedField · ConfidenceField · WriteFilterMixin ·
PredictionLedger · TagField · EventStreamMixin
over time, across the corpus:
MemoryLifecycle: promote, forget, tombstone, restore
The pull path answers "what matches this query". The push path answers "what
should this agent be thinking about anyway". assemble() runs both, merges
them, and enforces one budget across the result.
Which arms actually run is decided by which fields your model declares. A model
with no CyclicDecayField has no push path. A model with no BM25Field
resolves to query-blind ranking, which is
the right choice for some workloads and quietly wrong for others.
Design principles¶
-
ORM primitives, not application logic. Popoto ships fields, mixins, hooks, and query methods. Domain-specific memory models are yours to build on top.
-
Redis-native everything. No external brokers, job queues, or Redis modules. Lua scripts, sorted sets, streams, and Bloom filters over
SETBIT/GETBITall run inside the Redis process, so the same code runs identically on Redis and Valkey. -
Composable, degrading gracefully. Each primitive is independently useful, and every layer adapts to whichever fields are present rather than requiring the full set.
-
Pipeline-safe. Every operation accepts an optional
pipelineparameter for atomic execution, consistent with all Popoto field hooks. -
Centralized tuning. Behavioral constants live in
Defaults, importable from the package root. Override globally or per field. Explicit kwargs always win.
from popoto import Defaults
# Global override: all DecayingSortedFields default to 0.7
Defaults.DECAY_RATE = 0.7
# Per-field override still wins
relevance = DecayingSortedField(decay_rate=0.3) # uses 0.3, not 0.7
See Defaults API reference and Tuning Magic Numbers for the full constant table and the empirical basis behind each value.
Where to go next¶
- Building something: Quickstart, then the SubconsciousMemory recipe
- Deciding whether retrieval will respond to your queries: Query-blind retrieval
- Checking the claims: Benchmarks, including the measurements that came out badly
- Tuning: Magic Numbers and Parametric Sweep
- Field-level API: Models and Fields and the API Reference