A small, dependency-free deterministic trigger engine that answers one question for a proactive memory agent: when should the memory model run?
A proactive memory agent watches a long-running action agent, remembers what matters, and surfaces a reminder at the moment it's needed. The common wiring is a per-step timer — invoke the memory model on every step and let it vote "speak or stay silent." That's O(steps) model calls, and the timing judgment rides entirely on the model (which is a big part of why memory agents usually get fine-tuned).
This library replaces the timer with an explicit, deterministic invocation function computed from the trajectory and your memory state. The memory model is not invoked unless a gate opens, and every firing carries a recorded reason — timing becomes auditable, unit-testable, and O(triggers) instead of O(steps).
| Class | Fires on |
|---|---|
| State-delta events | a tool failure; a repeated failure under the same tool key; a contradiction between the current step and a known memory entry ("no longer …" vs a stored preference); a phase change |
| Thresholds | steps since the memory agent last ran; budget fraction consumed (one-shot latch) |
| Prospective rows | (trigger-condition, payload) reminders scheduled earlier — the model authors a deferred reminder ("remind me next time this tool is used"); the engine fires it at exactly that moment, with no model call at fire time |
| Heartbeat | a sparse backstop for gradual drift that produces no discrete event — backstop, not backbone; never per-step |
That authoring/firing split is the part we found load-bearing in practice: models naturally write deferred reminders, and something deterministic has to fire them at the right moment. A per-step loop can't act on a deferred reminder at all; a scheduler can.
from proactive_memory import TriggerEngine, StepEvent, ProspectiveRow
engine = TriggerEngine(total_budget_steps=200)
for step in agent_trajectory: # your action-agent loop
ev = StepEvent(idx=step.i, tool_key=step.tool, outcome=step.outcome,
text=step.note, phase_key=step.phase)
decision = engine.evaluate(ev, knowledge=my_memory_items) # (key, content) pairs
for payload in decision.prospective_payloads: # fired reminders — no model call
inject_into_context(payload)
if any(not r.startswith("prospective") for r in decision.reasons):
engine.note_consult(ev.idx)
ops = run_memory_model(ev, bank) # your model, your bank
# if the model authors a deferred reminder:
engine.add_prospective(ProspectiveRow(fire_on_tool_key=..., payload=...))The engine imposes no storage design and no model choice — knowledge is any
iterable of (key, content) pairs, and what you do on a firing is yours.
python -m pytest -q # 11 tests: every trigger class + determinism
python experiments/run_demo.py # model-free walkthrough of a 60-step trajectoryDemo output (deterministic):
per-step timer would invoke the model : 60 times
trigger engine invoked the model : 9 times (6.7x fewer)
scheduled reminders fired with NO model call: 2 (each exactly at the retry step)
In our own experiments pairing this engine with a full memory agent (a memory bank plus a schema-enforced tool-call contract), a local open model with no memory-specific fine-tuning served as the memory agent and covered every planted hazard in trigger mode — while the same model on a per-step timer wasted its deferred reminders because nothing could fire them. Those runs were on a synthetic task at small scale: a demonstration, not a benchmark. The trigger engine is the piece we're sharing; wire it into your own agent and see what you get on real traces.
The proactive memory-agent design this responds to and builds on:
Wu, Zhang, et al. (2026). Remember When It Matters: Proactive Memory Agent for Long-Horizon Agents. arXiv:2607.08716 (CC BY 4.0).
That paper introduces the two-phase memory agent (maintain a structured bank; decide whether to intervene), runs it on a fixed schedule, and trains the memory model. The design is theirs, and it's good work. This library is an independent, clean-room contribution to the when half — adding determinism to the invocation schedule instead of learning it. A step on top of their idea, not a replacement for it. It shares no code with that work.
MIT — see LICENSE.
Built by XSI. This engine came out of our work on XSI-AIMS™, an open specification for governing agent systems — memory, tool use, and the boundary between deterministic tooling and inference. Explore the standard: https://github.com/Extended-Systems-Intelligence/aims