An experience engine for AI agents.
Memory systems answer "what should the agent remember?" Lamar asks "how should the agent change because of what it remembers?"
Memory is becoming solved. Learning after deployment is not.
Every agent memory layer today (Mem0, Zep, Letta, LangMem, Cursor's rules, Claude
Code's CLAUDE.md) does the same thing: extract facts, embed them, retrieve the
top-k by similarity, inject them into the prompt.
That pipeline has no idea whether any of it helped.
Consider two lessons sitting in your agent's memory:
When the build fails, clear DerivedData.
When the build fails, delete the lockfile.
They are equally on-topic. A similarity search cannot tell them apart, because similarity is blind to outcome. One of them works. The other wastes your morning.
And when the world changes (the repo moves, the API is deprecated, the convention is quietly abandoned), a memory store has no mechanism for ceasing to believe something. It will surface the fossil forever, with total confidence.
Retrieval is not a search problem. It is a decision problem under uncertainty.
Which experiences should influence this turn, given limited context, imperfect knowledge of what actually works, and a reward signal that arrives after the fact?
That is a contextual bandit. It is the same shape as the problem Yahoo solved for news recommendation in 2010:
| Yahoo (LinUCB, 2010) | Lamar |
|---|---|
| context = user + page | context = task, error class, project, language |
| arm = which article to show | arm = which experience to inject |
| reward = did they click | reward = did the human keep the work |
| policy = ranking | policy = retrieval |
Yahoo: every click improves recommendations. Lamar: every interaction reweights what the agent believes.
Once retrieval is a bandit, the rest of the system falls out of it rather than being bolted on:
- Forgetting is not a module. It is an arm's value estimate falling until it stops being selected.
- Testing your own beliefs is not a feature. It is the exploration bonus.
- Promotion is not an approval workflow. A new claim is a cold arm; curiosity tries it, and the outcomes decide.
from lamar import Lamar, ExperienceStore, Signal
lam = Lamar(ExperienceStore("~/.lamar/experience.json"))
# 1. which experiences should influence this turn?
rec = lam.recall({"project": "trace", "language": "swift", "task": "build_fails"})
run_my_agent(system_prompt + rec.as_prompt())
# 2. what did the human actually do about it?
lam.report(rec.episode_id, Signal.REVERTED)
# 3. offline, at night: consolidate, decay, retire, surface contradictions
lam.sleep(traces=todays_sessions)recall and report are cheap, synchronous, and call no model at all. All
the expensive and dangerous work happens in sleep, away from the user, where it
can be reviewed before it changes anything.
That split is the thesis, not an implementation detail:
Online, a system collects experience. Offline is where experience becomes intelligence.
Not a note. A claim that can be wrong, and that carries the evidence for and against itself.
Experience(
precondition="the Xcode build fails after a dependency bump",
action="clear DerivedData before rebuilding",
rationale="stale module maps survive a clean build",
scope=Scope(project="trace", language="swift"),
)It plays two roles at once, and everything in Lamar exists to keep them in sync:
- a lesson a human can read: when X, do Y, because Z
- an arm a bandit can pull: a feature vector with a running reward estimate
Free-text notes cannot be scored, decayed, contradicted, or retired. That is why every other memory layer can only accumulate.
Behavioral signals. Nothing else.
The outcome signal does not live in the model. It lives in your application. Only your app knows whether the developer reverted the patch, whether the deploy held, whether the quote won.
Signal.SHIPPED # committed, merged, deployed
Signal.ACCEPTED
Signal.REASKED # asked the same thing again
Signal.CORRECTED # "no, that's wrong"
Signal.REVERTED # undid the workLamar does not ask an LLM to grade its own trajectory. Models are cheerful about their own output, and the resulting reward is noise wearing a lab coat.
Not by adapter count. The bandit is arithmetic; scope matching is string comparison; reward comes from what a human did. None of it needs a model.
Exactly one job does: reading a pile of raw traces and noticing that the same thing keeps going wrong. So the protocol is one method.
class MyModel:
def complete(self, prompt: str, *, max_tokens: int = 2048) -> str: ...Anything with that method works. Adapters for Anthropic, OpenAI and Ollama ship in the box as lazy imports. Lamar has zero dependencies. Not even numpy.
examples/memory_gym is a falsifiable test, built to be fair rather than
flattering. Per task the store holds six claims that are all equally relevant:
one that helps, one that hurts, four that do nothing. Halfway through, the world
silently flips and one true lesson becomes false.
policy first 500 pre-flip post-flip recovered
--------------------------------------------------------
none 43.0% 39.4% 40.7% 38.4%
similarity 44.6% 39.8% 42.0% 35.2%
lamar 57.2% 59.4% 62.7% 55.8%
The success rate says Lamar wins. This says why:
share of injected slots, by what the lesson actually was:
TRUE FALSE INERT FOSSIL
similarity pre-flip 15.8% 15.2% 65.1% 4.0%
post-flip 16.6% 15.6% 64.2% 3.6% <- identical. it cannot learn.
lamar pre-flip 35.1% 1.0% 51.0% 12.9%
post-flip 49.5% 0.0% 50.2% 0.3% <- fossil: 12.9% -> 0.3%
Nobody told it the world had changed.
Note the middle row. After the flip, similarity retrieval performs worse than having no memory at all (35.2% vs 38.4%), because it cannot stop injecting a lesson that has become false. It has no capacity to disbelieve.
python3 examples/memory_gym/gym.py
python3 -m unittest discover -s tests
The moment experience is allowed to change behavior, you inherit reinforcement learning's stability problems without reinforcement learning's math. A memory store is immune to this only because it never learns.
The failure mode: the agent draws a wrong lesson from a lucky episode. The lesson starts influencing decisions. Those decisions generate more episodes that appear to confirm it. The system becomes stably, confidently, self-reinforcingly wrong.
Four defenses, each of which exists because a version of this code got it wrong first:
-
A single episode can never mint a belief. Promotion requires evidence that clears a confidence bound.
-
Every claim has its own control group. The episodes where it was eligible and did not fire. Without one, an experience is scored against a baseline it itself contaminated, its measured lift collapses toward zero, and noise retires it. (This executed two genuinely good lessons before it was fixed.)
-
All evidence decays. Scored on its whole life, a lesson that was true for a year and false since last month still shows a glowing record. Lifetime averages keep fossils alive forever.
-
Randomized holdout. Lamar sometimes deliberately withholds an experience the policy wanted to use, and watches what happens without it. This is the only estimator that survives "the agent would have succeeded anyway." It costs real performance, and that cost is explicit and tunable, because a system claiming to validate its own beliefs for free is lying.
And every decision is made on a confidence bound, never a point estimate, asymmetrically:
- promote when the pessimistic bound is still positive: even read uncharitably, it helps
- retire when the optimistic bound is still negative: even read charitably, it hurts
- otherwise, do nothing
Being useless is not grounds for retirement. A useless claim simply stops being selected, which costs nothing and reverses itself the moment it starts working. Retirement is reserved for demonstrable harm.
Stated because a benchmark that hides its failures is marketing.
-
Lamar is better at proving a lesson harmful than proving one helpful. A good lesson gets selected on nearly every episode, so it is almost never absent, so its control group stays thin and the bound will not clear. In the gym, only 1 of 4 genuinely helpful lessons earns a
helpsverdict; the rest sit atunknownwhile still being used. It never gets one wrong (0/4 helpful lessons were judged harmful, 0/4 harmful and 0/16 inert were judged helpful), but "I cannot prove this works" is a real gap in an auditability story. Targeting the holdout at the thinnest control group helps and does not close it. -
Reward attribution is approximate. Several experiences are injected, one outcome comes back, and each gets credited with the shared reward. This is correct in expectation only when arms appear in varied combinations. If your candidate pool is never larger than
k, the same experiences co-occur every episode and no algorithm can separate them. Make sure you have more candidates than slots. -
Low volume hurts. A solo developer's agent produces hundreds of episodes a week, not millions. Feature vectors are deliberately small and hand-built for this reason. Do not dump an embedding into
Experience.features: it will look sophisticated and never converge. -
Untested in production. Every number on this page comes from a synthetic environment. The next milestone is a real one.
Early. The core loop works and is tested. The Claude Code adapter is next.
Lamar Valley, in Yellowstone. People go there not to change the wolves, the bears or the bison, but to sit still and watch them.
Observation is the first layer.
MIT