Patterns

May 4, 2026 · View on GitHub

Each file is a single memory pattern. Pick the one that matches your agent's failure mode.

Selection guide (decision tree)

Walk top-down; first match wins. The head-to-head bench in bench/run.py backs the recall column with measured numbers.

Is the conversation < 50 turns AND you can afford to drop old context?
└── yes  → sliding_window
└── no   → continue ↓

Do you need to recall a SPECIFIC older fact at query time
(rather than the gist of older context)?
└── yes  → does the query have a natural typed shape (task=X, env=prod, ...)?
           └── yes → structured_episodic
           └── no  → vector_retrieval
└── no   → continue ↓

Is the session very long AND you want graceful detail decay
(recent verbatim, older becomes a single rolled-up summary)?
└── yes  → hierarchical_summary
└── no   → summary_compression

Cheat-sheet by failure mode

What does your agent lose most often? That's the pattern you want.

  • Forgets recent turns under cost pressuresliding_window.py
  • Forgets older context but can afford a periodic summarize callsummary_compression.py
  • Forgets in a way correlated with topic, not recencyvector_retrieval.py
  • Sessions span hours/days; recall needs to degrade gracefully with agehierarchical_summary.py
  • Same task type repeats across sessions with structured outcomesstructured_episodic.py

Bench-measured tradeoff (10-seed run, target fact at turn 3 of 50)

patternrecall pass-ratecontext chars (min/avg/max)per-turn callback cost
sliding_window0/10273/311/3230
summary_compression0/101393/1446/1473summarize call per trigger overflow
hierarchical_summary0/10203/233/251summarize call per leaf chunk + cascade
vector_retrieval10/10273/311/323embed call per archived msg
structured_episodic10/10273/311/3230

Read this as: if the recall task is retrieve a specific old fact, only vector_retrieval and structured_episodic solve it — the recency-only patterns intentionally drop old turns. Compose them: sliding_window for the live context tail + vector_retrieval for retrieval is a real production shape this repo deliberately doesn't hide behind a single class.

Shared interface

class Pattern:
    def add(self, msg: Message) -> None: ...
    def view(self) -> list[Message]: ...

Drop-in replaceable. Your agent code doesn't change when you swap patterns.

Status

PatternFileStatus
Sliding windowsliding_window.py✅ v0
Summary compressionsummary_compression.py✅ v0, pluggable summarizer + mock for demo
Vector retrievalvector_retrieval.py✅ v0, pluggable embedder + stdlib hash-BOW fallback
Hierarchical summaryhierarchical_summary.py✅ v0, pyramid rollup with cascading fanout
Structured episodicstructured_episodic.py✅ v0, typed Episode(situation, action, outcome, tags) with structured-key recall

All five share the same add(msg) / view() -> list[Message] interface. Two add explicit recall: vector_retrieval.query(text, k) and structured_episodic.recall_episodes(situation, tags, k).

Design rules

  1. One file per pattern. Imports only from stdlib, except where absolutely necessary (e.g. vector retrieval needs an embedder — document the choice).
  2. Same public interface. New entry points get a strong reason in the PR.
  3. Behaviour documented in the docstring, including when not to use it. Patterns have failure modes; name them.
  4. A __main__-guarded _demo() function that shows the pattern in one terminal run.